diff options
Diffstat (limited to 'net/mac80211')
74 files changed, 30630 insertions, 12254 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 4d6f8653ec8..97b5dcad502 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -2,10 +2,11 @@ config MAC80211  	tristate "Generic IEEE 802.11 Networking Stack (mac80211)"  	depends on CFG80211  	select CRYPTO -	select CRYPTO_ECB  	select CRYPTO_ARC4  	select CRYPTO_AES +	select CRYPTO_CCM  	select CRC32 +	select AVERAGE  	---help---  	  This option enables the hardware independent IEEE 802.11  	  networking stack. @@ -16,10 +17,10 @@ comment "CFG80211 needs to be enabled for MAC80211"  if MAC80211 != n  config MAC80211_HAS_RC -	def_bool n +	bool  config MAC80211_RC_PID -	bool "PID controller based rate control algorithm" if EMBEDDED +	bool "PID controller based rate control algorithm" if EXPERT  	select MAC80211_HAS_RC  	---help---  	  This option enables a TX rate control algorithm for @@ -27,14 +28,14 @@ config MAC80211_RC_PID  	  rate.  config MAC80211_RC_MINSTREL -	bool "Minstrel" if EMBEDDED +	bool "Minstrel" if EXPERT  	select MAC80211_HAS_RC  	default y  	---help---  	  This option enables the 'minstrel' TX rate control algorithm  config MAC80211_RC_MINSTREL_HT -	bool "Minstrel 802.11n support" if EMBEDDED +	bool "Minstrel 802.11n support" if EXPERT  	depends on MAC80211_RC_MINSTREL  	default y  	---help--- @@ -77,11 +78,11 @@ config MAC80211_RC_DEFAULT  endif  comment "Some wireless drivers require a rate control algorithm" -	depends on MAC80211_HAS_RC=n +	depends on MAC80211 && MAC80211_HAS_RC=n  config MAC80211_MESH  	bool "Enable mac80211 mesh networking (pre-802.11s) support" -	depends on MAC80211 && EXPERIMENTAL +	depends on MAC80211  	---help---  	 This options enables support of Draft 802.11s mesh networking.  	 The implementation is based on Draft 2.08 of the Mesh Networking @@ -92,7 +93,7 @@ config MAC80211_MESH  config MAC80211_LEDS  	bool "Enable LED triggers"  	depends on MAC80211 -	select NEW_LEDS +	depends on LEDS_CLASS  	select LEDS_TRIGGERS  	---help---  	  This option enables a few LED triggers for different @@ -107,6 +108,19 @@ config MAC80211_DEBUGFS  	  Say N unless you know you need this. +config MAC80211_MESSAGE_TRACING +	bool "Trace all mac80211 debug messages" +	depends on MAC80211 +	---help--- +	  Select this option to have mac80211 register the +	  mac80211_msg trace subsystem with tracepoints to +	  collect all debugging messages, independent of +	  printing them into the kernel log. + +	  The overhead in this option is that all the messages +	  need to be present in the binary and formatted at +	  runtime for tracing. +  menuconfig MAC80211_DEBUG_MENU  	bool "Select mac80211 debugging features"  	depends on MAC80211 @@ -140,26 +154,35 @@ config MAC80211_VERBOSE_DEBUG  	  Do not select this option. -config MAC80211_HT_DEBUG -	bool "Verbose HT debugging" +config MAC80211_MLME_DEBUG +	bool "Verbose managed MLME output"  	depends on MAC80211_DEBUG_MENU  	---help--- -	  This option enables 802.11n High Throughput features -	  debug tracing output. - -	  It should not be selected on production systems as some +	  Selecting this option causes mac80211 to print out +	  debugging messages for the managed-mode MLME. It +	  should not be selected on production systems as some  	  of the messages are remotely triggerable.  	  Do not select this option. -config MAC80211_TKIP_DEBUG -	bool "Verbose TKIP debugging" +config MAC80211_STA_DEBUG +	bool "Verbose station debugging"  	depends on MAC80211_DEBUG_MENU  	---help---  	  Selecting this option causes mac80211 to print out -	  very verbose TKIP debugging messages. It should not -	  be selected on production systems as those messages -	  are remotely triggerable. +	  debugging messages for station addition/removal. + +	  Do not select this option. + +config MAC80211_HT_DEBUG +	bool "Verbose HT debugging" +	depends on MAC80211_DEBUG_MENU +	---help--- +	  This option enables 802.11n High Throughput features +	  debug tracing output. + +	  It should not be selected on production systems as some +	  of the messages are remotely triggerable.  	  Do not select this option. @@ -174,7 +197,7 @@ config MAC80211_IBSS_DEBUG  	  Do not select this option. -config MAC80211_VERBOSE_PS_DEBUG +config MAC80211_PS_DEBUG  	bool "Verbose powersave mode debugging"  	depends on MAC80211_DEBUG_MENU  	---help--- @@ -186,7 +209,7 @@ config MAC80211_VERBOSE_PS_DEBUG  	  Do not select this option. -config MAC80211_VERBOSE_MPL_DEBUG +config MAC80211_MPL_DEBUG  	bool "Verbose mesh peer link debugging"  	depends on MAC80211_DEBUG_MENU  	depends on MAC80211_MESH @@ -199,7 +222,20 @@ config MAC80211_VERBOSE_MPL_DEBUG  	  Do not select this option. -config MAC80211_VERBOSE_MHWMP_DEBUG +config MAC80211_MPATH_DEBUG +	bool "Verbose mesh path debugging" +	depends on MAC80211_DEBUG_MENU +	depends on MAC80211_MESH +	---help--- +	  Selecting this option causes mac80211 to print out very +	  verbose mesh path selection debugging messages (when mac80211 +	  is taking part in a mesh network). +	  It should not be selected on production systems as those +	  messages are remotely triggerable. + +	  Do not select this option. + +config MAC80211_MHWMP_DEBUG  	bool "Verbose mesh HWMP routing debugging"  	depends on MAC80211_DEBUG_MENU  	depends on MAC80211_MESH @@ -212,6 +248,51 @@ config MAC80211_VERBOSE_MHWMP_DEBUG  	  Do not select this option. +config MAC80211_MESH_SYNC_DEBUG +	bool "Verbose mesh synchronization debugging" +	depends on MAC80211_DEBUG_MENU +	depends on MAC80211_MESH +	---help--- +	  Selecting this option causes mac80211 to print out very verbose mesh +	  synchronization debugging messages (when mac80211 is taking part in a +	  mesh network). + +	  Do not select this option. + +config MAC80211_MESH_CSA_DEBUG +	bool "Verbose mesh channel switch debugging" +	depends on MAC80211_DEBUG_MENU +	depends on MAC80211_MESH +	---help--- +	  Selecting this option causes mac80211 to print out very verbose mesh +	  channel switch debugging messages (when mac80211 is taking part in a +	  mesh network). + +	  Do not select this option. + +config MAC80211_MESH_PS_DEBUG +	bool "Verbose mesh powersave debugging" +	depends on MAC80211_DEBUG_MENU +	depends on MAC80211_MESH +	---help--- +	  Selecting this option causes mac80211 to print out very verbose mesh +	  powersave debugging messages (when mac80211 is taking part in a +	  mesh network). + +	  Do not select this option. + +config MAC80211_TDLS_DEBUG +	bool "Verbose TDLS debugging" +	depends on MAC80211_DEBUG_MENU +	---help--- +	  Selecting this option causes mac80211 to print out very +	  verbose TDLS selection debugging messages (when mac80211 +	  is a TDLS STA). +	  It should not be selected on production systems as those +	  messages are remotely triggerable. + +	  Do not select this option. +  config MAC80211_DEBUG_COUNTERS  	bool "Extra statistics for TX/RX debugging"  	depends on MAC80211_DEBUG_MENU @@ -222,15 +303,3 @@ config MAC80211_DEBUG_COUNTERS  	  and show them in debugfs.  	  If unsure, say N. - -config MAC80211_DRIVER_API_TRACER -	bool "Driver API tracer" -	depends on MAC80211_DEBUG_MENU -	depends on EVENT_TRACING -	help -	  Say Y here to make mac80211 register with the ftrace -	  framework for the driver API -- you can then see which -	  driver methods it is calling and which API functions -	  drivers are calling by looking at the trace. - -	  If unsure, say Y. diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index fdb54e61d63..1e46ffa6916 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -8,8 +8,8 @@ mac80211-y := \  	wpa.o \  	scan.o offchannel.o \  	ht.o agg-tx.o agg-rx.o \ +	vht.o \  	ibss.o \ -	mlme.o work.o \  	iface.o \  	rate.o \  	michael.o \ @@ -24,7 +24,9 @@ mac80211-y := \  	util.o \  	wme.o \  	event.o \ -	chan.o +	chan.o \ +	trace.o mlme.o \ +	tdls.o  mac80211-$(CONFIG_MAC80211_LEDS) += led.o  mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ @@ -37,12 +39,13 @@ mac80211-$(CONFIG_MAC80211_MESH) += \  	mesh.o \  	mesh_pathtbl.o \  	mesh_plink.o \ -	mesh_hwmp.o +	mesh_hwmp.o \ +	mesh_sync.o \ +	mesh_ps.o  mac80211-$(CONFIG_PM) += pm.o -mac80211-$(CONFIG_MAC80211_DRIVER_API_TRACER) += driver-trace.o -CFLAGS_driver-trace.o := -I$(src) +CFLAGS_trace.o := -I$(src)  # objects for PID algorithm  rc80211_pid-y := rc80211_pid_algo.o @@ -58,4 +61,4 @@ mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y)  mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)  mac80211-$(CONFIG_MAC80211_RC_MINSTREL_HT) += $(rc80211_minstrel_ht-y) -ccflags-y += -D__CHECK_ENDIAN__ +ccflags-y += -D__CHECK_ENDIAN__ -DDEBUG diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c index 4bd6ef0be38..ec24378caaa 100644 --- a/net/mac80211/aes_ccm.c +++ b/net/mac80211/aes_ccm.c @@ -2,6 +2,8 @@   * Copyright 2003-2004, Instant802 Networks, Inc.   * Copyright 2005-2006, Devicescape Software, Inc.   * + * Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org> + *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License version 2 as   * published by the Free Software Foundation. @@ -11,141 +13,82 @@  #include <linux/types.h>  #include <linux/crypto.h>  #include <linux/err.h> +#include <crypto/aes.h>  #include <net/mac80211.h>  #include "key.h"  #include "aes_ccm.h" -static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a) +void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, +			       u8 *data, size_t data_len, u8 *mic)  { -	int i; -	u8 *b_0, *aad, *b, *s_0; - -	b_0 = scratch + 3 * AES_BLOCK_LEN; -	aad = scratch + 4 * AES_BLOCK_LEN; -	b = scratch; -	s_0 = scratch + AES_BLOCK_LEN; - -	crypto_cipher_encrypt_one(tfm, b, b_0); +	struct scatterlist assoc, pt, ct[2]; -	/* Extra Authenticate-only data (always two AES blocks) */ -	for (i = 0; i < AES_BLOCK_LEN; i++) -		aad[i] ^= b[i]; -	crypto_cipher_encrypt_one(tfm, b, 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; -	aad += AES_BLOCK_LEN; +	memset(aead_req, 0, sizeof(aead_req_data)); -	for (i = 0; i < AES_BLOCK_LEN; i++) -		aad[i] ^= b[i]; -	crypto_cipher_encrypt_one(tfm, a, aad); +	sg_init_one(&pt, data, data_len); +	sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad)); +	sg_init_table(ct, 2); +	sg_set_buf(&ct[0], data, data_len); +	sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN); -	/* Mask out bits from auth-only-b_0 */ -	b_0[0] &= 0x07; +	aead_request_set_tfm(aead_req, tfm); +	aead_request_set_assoc(aead_req, &assoc, assoc.length); +	aead_request_set_crypt(aead_req, &pt, ct, data_len, b_0); -	/* 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); +	crypto_aead_encrypt(aead_req);  } - -void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, -			       u8 *data, size_t data_len, -			       u8 *cdata, u8 *mic) +int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, +			      u8 *data, size_t data_len, u8 *mic)  { -	int i, j, last_len, num_blocks; -	u8 *pos, *cpos, *b, *s_0, *e, *b_0, *aad; - -	b = scratch; -	s_0 = scratch + AES_BLOCK_LEN; -	e = scratch + 2 * AES_BLOCK_LEN; -	b_0 = scratch + 3 * AES_BLOCK_LEN; -	aad = scratch + 4 * AES_BLOCK_LEN; - -	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); -	last_len = data_len % AES_BLOCK_LEN; -	aes_ccm_prepare(tfm, scratch, b); - -	/* Process payload blocks */ -	pos = data; -	cpos = cdata; -	for (j = 1; j <= num_blocks; j++) { -		int blen = (j == num_blocks && last_len) ? -			last_len : AES_BLOCK_LEN; - -		/* Authentication followed by encryption */ -		for (i = 0; i < blen; i++) -			b[i] ^= pos[i]; -		crypto_cipher_encrypt_one(tfm, b, b); - -		b_0[14] = (j >> 8) & 0xff; -		b_0[15] = j & 0xff; -		crypto_cipher_encrypt_one(tfm, e, b_0); -		for (i = 0; i < blen; i++) -			*cpos++ = *pos++ ^ e[i]; -	} - -	for (i = 0; i < CCMP_MIC_LEN; i++) -		mic[i] = b[i] ^ s_0[i]; +	struct scatterlist assoc, pt, ct[2]; +	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; + +	memset(aead_req, 0, sizeof(aead_req_data)); + +	sg_init_one(&pt, data, data_len); +	sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad)); +	sg_init_table(ct, 2); +	sg_set_buf(&ct[0], data, data_len); +	sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN); + +	aead_request_set_tfm(aead_req, tfm); +	aead_request_set_assoc(aead_req, &assoc, assoc.length); +	aead_request_set_crypt(aead_req, ct, &pt, +			       data_len + IEEE80211_CCMP_MIC_LEN, b_0); + +	return crypto_aead_decrypt(aead_req);  } - -int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, -			      u8 *cdata, size_t data_len, u8 *mic, u8 *data) +struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[])  { -	int i, j, last_len, num_blocks; -	u8 *pos, *cpos, *b, *s_0, *a, *b_0, *aad; - -	b = scratch; -	s_0 = scratch + AES_BLOCK_LEN; -	a = scratch + 2 * AES_BLOCK_LEN; -	b_0 = scratch + 3 * AES_BLOCK_LEN; -	aad = scratch + 4 * AES_BLOCK_LEN; - -	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); -	last_len = data_len % AES_BLOCK_LEN; -	aes_ccm_prepare(tfm, scratch, a); - -	/* Process payload blocks */ -	cpos = cdata; -	pos = data; -	for (j = 1; j <= num_blocks; j++) { -		int blen = (j == num_blocks && last_len) ? -			last_len : AES_BLOCK_LEN; - -		/* 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 < CCMP_MIC_LEN; i++) { -		if ((mic[i] ^ s_0[i]) != a[i]) -			return -1; -	} - -	return 0; -} - +	struct crypto_aead *tfm; +	int err; -struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]) -{ -	struct crypto_cipher *tfm; +	tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC); +	if (IS_ERR(tfm)) +		return tfm; -	tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); -	if (!IS_ERR(tfm)) -		crypto_cipher_setkey(tfm, key, ALG_CCMP_KEY_LEN); +	err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP); +	if (!err) +		err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN); +	if (!err) +		return tfm; -	return tfm; +	crypto_free_aead(tfm); +	return ERR_PTR(err);  } - -void ieee80211_aes_key_free(struct crypto_cipher *tfm) +void ieee80211_aes_key_free(struct crypto_aead *tfm)  { -	crypto_free_cipher(tfm); +	crypto_free_aead(tfm);  } diff --git a/net/mac80211/aes_ccm.h b/net/mac80211/aes_ccm.h index 6e7820ef344..2c7ab1948a2 100644 --- a/net/mac80211/aes_ccm.h +++ b/net/mac80211/aes_ccm.h @@ -12,15 +12,11 @@  #include <linux/crypto.h> -#define AES_BLOCK_LEN 16 - -struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]); -void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, -			       u8 *data, size_t data_len, -			       u8 *cdata, u8 *mic); -int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, -			      u8 *cdata, size_t data_len, -			      u8 *mic, u8 *data); -void ieee80211_aes_key_free(struct crypto_cipher *tfm); +struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]); +void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, +			       u8 *data, size_t data_len, u8 *mic); +int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, +			      u8 *data, size_t data_len, u8 *mic); +void ieee80211_aes_key_free(struct crypto_aead *tfm);  #endif /* AES_CCM_H */ diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c index d502b2684a6..9b9009f9955 100644 --- a/net/mac80211/aes_cmac.c +++ b/net/mac80211/aes_cmac.c @@ -10,13 +10,14 @@  #include <linux/kernel.h>  #include <linux/types.h>  #include <linux/crypto.h> +#include <linux/export.h>  #include <linux/err.h> +#include <crypto/aes.h>  #include <net/mac80211.h>  #include "key.h"  #include "aes_cmac.h" -#define AES_BLOCK_SIZE 16  #define AES_CMAC_KEY_LEN 16  #define CMAC_TLEN 8 /* CMAC TLen = 64 bits (8 octets) */  #define AAD_LEN 20 @@ -35,17 +36,13 @@ static void gf_mulx(u8 *pad)  } -static void aes_128_cmac_vector(struct crypto_cipher *tfm, u8 *scratch, -				size_t num_elem, +static void aes_128_cmac_vector(struct crypto_cipher *tfm, size_t num_elem,  				const u8 *addr[], const size_t *len, u8 *mac)  { -	u8 *cbc, *pad; +	u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE];  	const u8 *pos, *end;  	size_t i, e, left, total_len; -	cbc = scratch; -	pad = scratch + AES_BLOCK_SIZE; -  	memset(cbc, 0, AES_BLOCK_SIZE);  	total_len = 0; @@ -95,7 +92,7 @@ static void aes_128_cmac_vector(struct crypto_cipher *tfm, u8 *scratch,  } -void ieee80211_aes_cmac(struct crypto_cipher *tfm, u8 *scratch, const u8 *aad, +void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,  			const u8 *data, size_t data_len, u8 *mic)  {  	const u8 *addr[3]; @@ -110,11 +107,11 @@ void ieee80211_aes_cmac(struct crypto_cipher *tfm, u8 *scratch, const u8 *aad,  	addr[2] = zero;  	len[2] = CMAC_TLEN; -	aes_128_cmac_vector(tfm, scratch, 3, addr, len, mic); +	aes_128_cmac_vector(tfm, 3, addr, len, mic);  } -struct crypto_cipher * ieee80211_aes_cmac_key_setup(const u8 key[]) +struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[])  {  	struct crypto_cipher *tfm; @@ -130,3 +127,20 @@ void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm)  {  	crypto_free_cipher(tfm);  } + +void ieee80211_aes_cmac_calculate_k1_k2(struct ieee80211_key_conf *keyconf, +					u8 *k1, u8 *k2) +{ +	u8 l[AES_BLOCK_SIZE] = {}; +	struct ieee80211_key *key = +		container_of(keyconf, struct ieee80211_key, conf); + +	crypto_cipher_encrypt_one(key->u.aes_cmac.tfm, l, l); + +	memcpy(k1, l, AES_BLOCK_SIZE); +	gf_mulx(k1); + +	memcpy(k2, k1, AES_BLOCK_SIZE); +	gf_mulx(k2); +} +EXPORT_SYMBOL(ieee80211_aes_cmac_calculate_k1_k2); diff --git a/net/mac80211/aes_cmac.h b/net/mac80211/aes_cmac.h index 0eb9a483150..0ce6487af79 100644 --- a/net/mac80211/aes_cmac.h +++ b/net/mac80211/aes_cmac.h @@ -11,8 +11,8 @@  #include <linux/crypto.h> -struct crypto_cipher * ieee80211_aes_cmac_key_setup(const u8 key[]); -void ieee80211_aes_cmac(struct crypto_cipher *tfm, u8 *scratch, const u8 *aad, +struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[]); +void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,  			const u8 *data, size_t data_len, u8 *mic);  void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm); diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 720b7a84af5..31bf2586fb8 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -38,6 +38,7 @@  #include <linux/ieee80211.h>  #include <linux/slab.h> +#include <linux/export.h>  #include <net/mac80211.h>  #include "ieee80211_i.h"  #include "driver-ops.h" @@ -48,6 +49,8 @@ static void ieee80211_free_tid_rx(struct rcu_head *h)  		container_of(h, struct tid_ampdu_rx, rcu_head);  	int i; +	del_timer_sync(&tid_rx->reorder_timer); +  	for (i = 0; i < tid_rx->buf_size; i++)  		dev_kfree_skb(tid_rx->reorder_buf[i]);  	kfree(tid_rx->reorder_buf); @@ -63,30 +66,32 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,  	lockdep_assert_held(&sta->ampdu_mlme.mtx); -	tid_rx = sta->ampdu_mlme.tid_rx[tid]; +	tid_rx = rcu_dereference_protected(sta->ampdu_mlme.tid_rx[tid], +					lockdep_is_held(&sta->ampdu_mlme.mtx));  	if (!tid_rx)  		return; -	rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], NULL); +	RCU_INIT_POINTER(sta->ampdu_mlme.tid_rx[tid], NULL); -#ifdef CONFIG_MAC80211_HT_DEBUG -	printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n", -	       sta->sta.addr, tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ +	ht_dbg(sta->sdata, +	       "Rx BA session stop requested for %pM tid %u %s reason: %d\n", +	       sta->sta.addr, tid, +	       initiator == WLAN_BACK_RECIPIENT ? "recipient" : "inititator", +	       (int)reason);  	if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP, -			     &sta->sta, tid, NULL)) -		printk(KERN_DEBUG "HW problem - can not stop rx " -				"aggregation for tid %d\n", tid); +			     &sta->sta, tid, NULL, 0)) +		sdata_info(sta->sdata, +			   "HW problem - can not stop rx aggregation for %pM tid %d\n", +			   sta->sta.addr, tid);  	/* check if this is a self generated aggregation halt */  	if (initiator == WLAN_BACK_RECIPIENT && tx)  		ieee80211_send_delba(sta->sdata, sta->sta.addr, -				     tid, 0, reason); +				     tid, WLAN_BACK_RECIPIENT, reason);  	del_timer_sync(&tid_rx->session_timer); -	del_timer_sync(&tid_rx->reorder_timer);  	call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx);  } @@ -99,6 +104,29 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,  	mutex_unlock(&sta->ampdu_mlme.mtx);  } +void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, +				  const u8 *addr) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); +	struct sta_info *sta; +	int i; + +	rcu_read_lock(); +	sta = sta_info_get_bss(sdata, addr); +	if (!sta) { +		rcu_read_unlock(); +		return; +	} + +	for (i = 0; i < IEEE80211_NUM_TIDS; i++) +		if (ba_rx_bitmap & BIT(i)) +			set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested); + +	ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); +	rcu_read_unlock(); +} +EXPORT_SYMBOL(ieee80211_stop_rx_ba_session); +  /*   * After accepting the AddBA Request we activated a timer,   * resetting it after each frame that arrives from the originator. @@ -113,10 +141,27 @@ static void sta_rx_agg_session_timer_expired(unsigned long data)  	u8 *timer_to_id = ptid - *ptid;  	struct sta_info *sta = container_of(timer_to_id, struct sta_info,  					 timer_to_tid[0]); +	struct tid_ampdu_rx *tid_rx; +	unsigned long timeout; + +	rcu_read_lock(); +	tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[*ptid]); +	if (!tid_rx) { +		rcu_read_unlock(); +		return; +	} + +	timeout = tid_rx->last_rx + TU_TO_JIFFIES(tid_rx->timeout); +	if (time_is_after_jiffies(timeout)) { +		mod_timer(&tid_rx->session_timer, timeout); +		rcu_read_unlock(); +		return; +	} +	rcu_read_unlock(); + +	ht_dbg(sta->sdata, "RX session timer expired on %pM tid %d\n", +	       sta->sta.addr, (u16)*ptid); -#ifdef CONFIG_MAC80211_HT_DEBUG -	printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid); -#endif  	set_bit(*ptid, sta->ampdu_mlme.tid_rx_timer_expired);  	ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work);  } @@ -129,9 +174,7 @@ static void sta_rx_agg_reorder_timer_expired(unsigned long data)  			timer_to_tid[0]);  	rcu_read_lock(); -	spin_lock(&sta->lock);  	ieee80211_release_reorder_timeout(sta, *ptid); -	spin_unlock(&sta->lock);  	rcu_read_unlock();  } @@ -145,12 +188,8 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d  	u16 capab;  	skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); - -	if (!skb) { -		printk(KERN_DEBUG "%s: failed to allocate buffer " -		       "for addba resp frame\n", sdata->name); +	if (!skb)  		return; -	}  	skb_reserve(skb, local->hw.extra_tx_headroom);  	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); @@ -158,10 +197,13 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d  	memcpy(mgmt->da, da, ETH_ALEN);  	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);  	if (sdata->vif.type == NL80211_IFTYPE_AP || -	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN || +	    sdata->vif.type == NL80211_IFTYPE_MESH_POINT)  		memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);  	else if (sdata->vif.type == NL80211_IFTYPE_STATION)  		memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); +	else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) +		memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN);  	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |  					  IEEE80211_STYPE_ACTION); @@ -187,8 +229,6 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,  				     struct ieee80211_mgmt *mgmt,  				     size_t len)  { -	struct ieee80211_hw *hw = &local->hw; -	struct ieee80211_conf *conf = &hw->conf;  	struct tid_ampdu_rx *tid_agg_rx;  	u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;  	u8 dialog_token; @@ -207,11 +247,10 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,  	status = WLAN_STATUS_REQUEST_DECLINED; -	if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		printk(KERN_DEBUG "Suspend in progress. " -		       "Denying ADDBA request\n"); -#endif +	if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { +		ht_dbg(sta->sdata, +		       "Suspend in progress - Denying ADDBA request (%pM tid %d)\n", +		       sta->sta.addr, tid);  		goto end_no_lock;  	} @@ -223,55 +262,44 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,  	     (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA))) ||  	    (buf_size > IEEE80211_MAX_AMPDU_BUF)) {  		status = WLAN_STATUS_INVALID_QOS_PARAM; -#ifdef CONFIG_MAC80211_HT_DEBUG -		if (net_ratelimit()) -			printk(KERN_DEBUG "AddBA Req with bad params from " -				"%pM on tid %u. policy %d, buffer size %d\n", -				mgmt->sa, tid, ba_policy, -				buf_size); -#endif /* CONFIG_MAC80211_HT_DEBUG */ +		ht_dbg_ratelimited(sta->sdata, +				   "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n", +				   mgmt->sa, tid, ba_policy, buf_size);  		goto end_no_lock;  	}  	/* determine default buffer size */ -	if (buf_size == 0) { -		struct ieee80211_supported_band *sband; - -		sband = local->hw.wiphy->bands[conf->channel->band]; -		buf_size = IEEE80211_MIN_AMPDU_BUF; -		buf_size = buf_size << sband->ht_cap.ampdu_factor; -	} +	if (buf_size == 0) +		buf_size = IEEE80211_MAX_AMPDU_BUF; +	/* make sure the size doesn't exceed the maximum supported by the hw */ +	if (buf_size > local->hw.max_rx_aggregation_subframes) +		buf_size = local->hw.max_rx_aggregation_subframes;  	/* examine state machine */  	mutex_lock(&sta->ampdu_mlme.mtx);  	if (sta->ampdu_mlme.tid_rx[tid]) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		if (net_ratelimit()) -			printk(KERN_DEBUG "unexpected AddBA Req from " -				"%pM on tid %u\n", -				mgmt->sa, tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ -		goto end; +		ht_dbg_ratelimited(sta->sdata, +				   "unexpected AddBA Req from %pM on tid %u\n", +				   mgmt->sa, tid); + +		/* delete existing Rx BA session on the same tid */ +		___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, +						WLAN_STATUS_UNSPECIFIED_QOS, +						false);  	}  	/* prepare A-MPDU MLME for Rx aggregation */ -	tid_agg_rx = kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC); -	if (!tid_agg_rx) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		if (net_ratelimit()) -			printk(KERN_ERR "allocate rx mlme to tid %d failed\n", -					tid); -#endif +	tid_agg_rx = kmalloc(sizeof(struct tid_ampdu_rx), GFP_KERNEL); +	if (!tid_agg_rx)  		goto end; -	}  	spin_lock_init(&tid_agg_rx->reorder_lock);  	/* rx timer */  	tid_agg_rx->session_timer.function = sta_rx_agg_session_timer_expired;  	tid_agg_rx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid]; -	init_timer(&tid_agg_rx->session_timer); +	init_timer_deferrable(&tid_agg_rx->session_timer);  	/* rx reorder timer */  	tid_agg_rx->reorder_timer.function = sta_rx_agg_reorder_timer_expired; @@ -280,15 +308,10 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,  	/* prepare reordering buffer */  	tid_agg_rx->reorder_buf = -		kcalloc(buf_size, sizeof(struct sk_buff *), GFP_ATOMIC); +		kcalloc(buf_size, sizeof(struct sk_buff *), GFP_KERNEL);  	tid_agg_rx->reorder_time = -		kcalloc(buf_size, sizeof(unsigned long), GFP_ATOMIC); +		kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL);  	if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		if (net_ratelimit()) -			printk(KERN_ERR "can not allocate reordering buffer " -			       "to tid %d\n", tid); -#endif  		kfree(tid_agg_rx->reorder_buf);  		kfree(tid_agg_rx->reorder_time);  		kfree(tid_agg_rx); @@ -296,11 +319,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,  	}  	ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START, -			       &sta->sta, tid, &start_seq_num); -#ifdef CONFIG_MAC80211_HT_DEBUG -	printk(KERN_DEBUG "Rx A-MPDU request on tid %d result %d\n", tid, ret); -#endif /* CONFIG_MAC80211_HT_DEBUG */ - +			       &sta->sta, tid, &start_seq_num, 0); +	ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n", +	       sta->sta.addr, tid, ret);  	if (ret) {  		kfree(tid_agg_rx->reorder_buf);  		kfree(tid_agg_rx->reorder_time); @@ -320,8 +341,10 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,  	/* activate it for RX */  	rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], tid_agg_rx); -	if (timeout) +	if (timeout) {  		mod_timer(&tid_agg_rx->session_timer, TU_TO_EXP_TIME(timeout)); +		tid_agg_rx->last_rx = jiffies; +	}  end:  	mutex_unlock(&sta->ampdu_mlme.mtx); diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index d4679b265ba..ce9633a3cfb 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -15,6 +15,7 @@  #include <linux/ieee80211.h>  #include <linux/slab.h> +#include <linux/export.h>  #include <net/mac80211.h>  #include "ieee80211_i.h"  #include "driver-ops.h" @@ -54,6 +55,8 @@   * @ampdu_action function will be called with the action   * %IEEE80211_AMPDU_TX_STOP. In this case, the call must not fail,   * and the driver must later call ieee80211_stop_tx_ba_cb_irqsafe(). + * Note that the sta can get destroyed before the BA tear down is + * complete.   */  static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, @@ -68,21 +71,22 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,  	skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); -	if (!skb) { -		printk(KERN_ERR "%s: failed to allocate buffer " -				"for addba request frame\n", sdata->name); +	if (!skb)  		return; -	} +  	skb_reserve(skb, local->hw.extra_tx_headroom);  	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);  	memset(mgmt, 0, 24);  	memcpy(mgmt->da, da, ETH_ALEN);  	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);  	if (sdata->vif.type == NL80211_IFTYPE_AP || -	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN || +	    sdata->vif.type == NL80211_IFTYPE_MESH_POINT)  		memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);  	else if (sdata->vif.type == NL80211_IFTYPE_STATION)  		memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); +	else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) +		memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN);  	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |  					  IEEE80211_STYPE_ACTION); @@ -106,19 +110,18 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,  	ieee80211_tx_skb(sdata, skb);  } -void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn) +void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)  { +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);  	struct ieee80211_local *local = sdata->local;  	struct sk_buff *skb;  	struct ieee80211_bar *bar;  	u16 bar_control = 0;  	skb = dev_alloc_skb(sizeof(*bar) + local->hw.extra_tx_headroom); -	if (!skb) { -		printk(KERN_ERR "%s: failed to allocate buffer for " -			"bar frame\n", sdata->name); +	if (!skb)  		return; -	} +  	skb_reserve(skb, local->hw.extra_tx_headroom);  	bar = (struct ieee80211_bar *)skb_put(skb, sizeof(*bar));  	memset(bar, 0, sizeof(*bar)); @@ -128,55 +131,191 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1  	memcpy(bar->ta, sdata->vif.addr, ETH_ALEN);  	bar_control |= (u16)IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL;  	bar_control |= (u16)IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA; -	bar_control |= (u16)(tid << 12); +	bar_control |= (u16)(tid << IEEE80211_BAR_CTRL_TID_INFO_SHIFT);  	bar->control = cpu_to_le16(bar_control);  	bar->start_seq_num = cpu_to_le16(ssn); -	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; -	ieee80211_tx_skb(sdata, skb); +	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | +					IEEE80211_TX_CTL_REQ_TX_STATUS; +	ieee80211_tx_skb_tid(sdata, skb, tid); +} +EXPORT_SYMBOL(ieee80211_send_bar); + +void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, +			     struct tid_ampdu_tx *tid_tx) +{ +	lockdep_assert_held(&sta->ampdu_mlme.mtx); +	lockdep_assert_held(&sta->lock); +	rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); +} + +static inline int ieee80211_ac_from_tid(int tid) +{ +	return ieee802_1d_to_ac[tid & 7]; +} + +/* + * When multiple aggregation sessions on multiple stations + * are being created/destroyed simultaneously, we need to + * refcount the global queue stop caused by that in order + * to not get into a situation where one of the aggregation + * setup or teardown re-enables queues before the other is + * ready to handle that. + * + * These two functions take care of this issue by keeping + * a global "agg_queue_stop" refcount. + */ +static void __acquires(agg_queue) +ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) +{ +	int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; + +	if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1) +		ieee80211_stop_queue_by_reason( +			&sdata->local->hw, queue, +			IEEE80211_QUEUE_STOP_REASON_AGGREGATION); +	__acquire(agg_queue); +} + +static void __releases(agg_queue) +ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) +{ +	int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; + +	if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0) +		ieee80211_wake_queue_by_reason( +			&sdata->local->hw, queue, +			IEEE80211_QUEUE_STOP_REASON_AGGREGATION); +	__release(agg_queue);  } -static void kfree_tid_tx(struct rcu_head *rcu_head) +/* + * splice packets from the STA's pending to the local pending, + * requires a call to ieee80211_agg_splice_finish later + */ +static void __acquires(agg_queue) +ieee80211_agg_splice_packets(struct ieee80211_sub_if_data *sdata, +			     struct tid_ampdu_tx *tid_tx, u16 tid)  { -	struct tid_ampdu_tx *tid_tx = -	    container_of(rcu_head, struct tid_ampdu_tx, rcu_head); +	struct ieee80211_local *local = sdata->local; +	int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; +	unsigned long flags; + +	ieee80211_stop_queue_agg(sdata, tid); -	kfree(tid_tx); +	if (WARN(!tid_tx, +		 "TID %d gone but expected when splicing aggregates from the pending queue\n", +		 tid)) +		return; + +	if (!skb_queue_empty(&tid_tx->pending)) { +		spin_lock_irqsave(&local->queue_stop_reason_lock, flags); +		/* copy over remaining packets */ +		skb_queue_splice_tail_init(&tid_tx->pending, +					   &local->pending[queue]); +		spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); +	} +} + +static void __releases(agg_queue) +ieee80211_agg_splice_finish(struct ieee80211_sub_if_data *sdata, u16 tid) +{ +	ieee80211_wake_queue_agg(sdata, tid); +} + +static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid) +{ +	struct tid_ampdu_tx *tid_tx; + +	lockdep_assert_held(&sta->ampdu_mlme.mtx); +	lockdep_assert_held(&sta->lock); + +	tid_tx = rcu_dereference_protected_tid_tx(sta, tid); + +	/* +	 * When we get here, the TX path will not be lockless any more wrt. +	 * aggregation, since the OPERATIONAL bit has long been cleared. +	 * Thus it will block on getting the lock, if it occurs. So if we +	 * stop the queue now, we will not get any more packets, and any +	 * that might be being processed will wait for us here, thereby +	 * guaranteeing that no packets go to the tid_tx pending queue any +	 * more. +	 */ + +	ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid); + +	/* future packets must not find the tid_tx struct any more */ +	ieee80211_assign_tid_tx(sta, tid, NULL); + +	ieee80211_agg_splice_finish(sta->sdata, tid); + +	kfree_rcu(tid_tx, rcu_head);  }  int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, -				    enum ieee80211_back_parties initiator, -				    bool tx) +				    enum ieee80211_agg_stop_reason reason)  {  	struct ieee80211_local *local = sta->local; -	struct tid_ampdu_tx *tid_tx = sta->ampdu_mlme.tid_tx[tid]; +	struct tid_ampdu_tx *tid_tx; +	enum ieee80211_ampdu_mlme_action action;  	int ret;  	lockdep_assert_held(&sta->ampdu_mlme.mtx); -	if (!tid_tx) -		return -ENOENT; +	switch (reason) { +	case AGG_STOP_DECLINED: +	case AGG_STOP_LOCAL_REQUEST: +	case AGG_STOP_PEER_REQUEST: +		action = IEEE80211_AMPDU_TX_STOP_CONT; +		break; +	case AGG_STOP_DESTROY_STA: +		action = IEEE80211_AMPDU_TX_STOP_FLUSH; +		break; +	default: +		WARN_ON_ONCE(1); +		return -EINVAL; +	}  	spin_lock_bh(&sta->lock); +	tid_tx = rcu_dereference_protected_tid_tx(sta, tid); +	if (!tid_tx) { +		spin_unlock_bh(&sta->lock); +		return -ENOENT; +	} + +	/* +	 * if we're already stopping ignore any new requests to stop +	 * unless we're destroying it in which case notify the driver +	 */ +	if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { +		spin_unlock_bh(&sta->lock); +		if (reason != AGG_STOP_DESTROY_STA) +			return -EALREADY; +		ret = drv_ampdu_action(local, sta->sdata, +				       IEEE80211_AMPDU_TX_STOP_FLUSH_CONT, +				       &sta->sta, tid, NULL, 0); +		WARN_ON_ONCE(ret); +		return 0; +	} +  	if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {  		/* not even started yet! */ -		rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL); +		ieee80211_assign_tid_tx(sta, tid, NULL);  		spin_unlock_bh(&sta->lock); -		call_rcu(&tid_tx->rcu_head, kfree_tid_tx); +		kfree_rcu(tid_tx, rcu_head);  		return 0;  	} +	set_bit(HT_AGG_STATE_STOPPING, &tid_tx->state); +  	spin_unlock_bh(&sta->lock); -#ifdef CONFIG_MAC80211_HT_DEBUG -	printk(KERN_DEBUG "Tx BA session stop requested for %pM tid %u\n", +	ht_dbg(sta->sdata, "Tx BA session stop requested for %pM tid %u\n",  	       sta->sta.addr, tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ - -	set_bit(HT_AGG_STATE_STOPPING, &tid_tx->state);  	del_timer_sync(&tid_tx->addba_resp_timer); +	del_timer_sync(&tid_tx->session_timer);  	/*  	 * After this packets are no longer handed right through @@ -185,12 +324,27 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,  	 */  	clear_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state); -	tid_tx->stop_initiator = initiator; -	tid_tx->tx_stop = tx; +	/* +	 * There might be a few packets being processed right now (on +	 * another CPU) that have already gotten past the aggregation +	 * check when it was still OPERATIONAL and consequently have +	 * IEEE80211_TX_CTL_AMPDU set. In that case, this code might +	 * call into the driver at the same time or even before the +	 * TX paths calls into it, which could confuse the driver. +	 * +	 * Wait for all currently running TX paths to finish before +	 * telling the driver. New packets will not go through since +	 * the aggregation session is no longer OPERATIONAL. +	 */ +	synchronize_net(); + +	tid_tx->stop_initiator = reason == AGG_STOP_PEER_REQUEST ? +					WLAN_BACK_RECIPIENT : +					WLAN_BACK_INITIATOR; +	tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST; -	ret = drv_ampdu_action(local, sta->sdata, -			       IEEE80211_AMPDU_TX_STOP, -			       &sta->sta, tid, NULL); +	ret = drv_ampdu_action(local, sta->sdata, action, +			       &sta->sta, tid, NULL, 0);  	/* HW shall not deny going back to legacy */  	if (WARN_ON(ret)) { @@ -200,7 +354,17 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,  		 */  	} -	return ret; +	/* +	 * In the case of AGG_STOP_DESTROY_STA, the driver won't +	 * necessarily call ieee80211_stop_tx_ba_cb(), so this may +	 * seem like we can leave the tid_tx data pending forever. +	 * This is true, in a way, but "forever" is only until the +	 * station struct is actually destroyed. In the meantime, +	 * leaving it around ensures that we don't transmit packets +	 * to the driver on this TID which might confuse it. +	 */ + +	return 0;  }  /* @@ -225,127 +389,120 @@ static void sta_addba_resp_timer_expired(unsigned long data)  	if (!tid_tx ||  	    test_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) {  		rcu_read_unlock(); -#ifdef CONFIG_MAC80211_HT_DEBUG -		printk(KERN_DEBUG "timer expired on tid %d but we are not " -				"(or no longer) expecting addBA response there\n", -			tid); -#endif +		ht_dbg(sta->sdata, +		       "timer expired on %pM tid %d but we are not (or no longer) expecting addBA response there\n", +		       sta->sta.addr, tid);  		return;  	} -#ifdef CONFIG_MAC80211_HT_DEBUG -	printk(KERN_DEBUG "addBA response timer expired on tid %d\n", tid); -#endif +	ht_dbg(sta->sdata, "addBA response timer expired on %pM tid %d\n", +	       sta->sta.addr, tid);  	ieee80211_stop_tx_ba_session(&sta->sta, tid);  	rcu_read_unlock();  } -static inline int ieee80211_ac_from_tid(int tid) -{ -	return ieee802_1d_to_ac[tid & 7]; -} - -/* - * When multiple aggregation sessions on multiple stations - * are being created/destroyed simultaneously, we need to - * refcount the global queue stop caused by that in order - * to not get into a situation where one of the aggregation - * setup or teardown re-enables queues before the other is - * ready to handle that. - * - * These two functions take care of this issue by keeping - * a global "agg_queue_stop" refcount. - */ -static void __acquires(agg_queue) -ieee80211_stop_queue_agg(struct ieee80211_local *local, int tid) -{ -	int queue = ieee80211_ac_from_tid(tid); - -	if (atomic_inc_return(&local->agg_queue_stop[queue]) == 1) -		ieee80211_stop_queue_by_reason( -			&local->hw, queue, -			IEEE80211_QUEUE_STOP_REASON_AGGREGATION); -	__acquire(agg_queue); -} - -static void __releases(agg_queue) -ieee80211_wake_queue_agg(struct ieee80211_local *local, int tid) -{ -	int queue = ieee80211_ac_from_tid(tid); - -	if (atomic_dec_return(&local->agg_queue_stop[queue]) == 0) -		ieee80211_wake_queue_by_reason( -			&local->hw, queue, -			IEEE80211_QUEUE_STOP_REASON_AGGREGATION); -	__release(agg_queue); -} -  void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)  { -	struct tid_ampdu_tx *tid_tx = sta->ampdu_mlme.tid_tx[tid]; +	struct tid_ampdu_tx *tid_tx;  	struct ieee80211_local *local = sta->local;  	struct ieee80211_sub_if_data *sdata = sta->sdata;  	u16 start_seq_num;  	int ret; -	lockdep_assert_held(&sta->ampdu_mlme.mtx); +	tid_tx = rcu_dereference_protected_tid_tx(sta, tid);  	/* -	 * While we're asking the driver about the aggregation, -	 * stop the AC queue so that we don't have to worry -	 * about frames that came in while we were doing that, -	 * which would require us to put them to the AC pending -	 * afterwards which just makes the code more complex. +	 * Start queuing up packets for this aggregation session. +	 * We're going to release them once the driver is OK with +	 * that.  	 */ -	ieee80211_stop_queue_agg(local, tid); -  	clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state);  	/* -	 * make sure no packets are being processed to get -	 * valid starting sequence number +	 * Make sure no packets are being processed. This ensures that +	 * we have a valid starting sequence number and that in-flight +	 * packets have been flushed out and no packets for this TID +	 * will go into the driver during the ampdu_action call.  	 */  	synchronize_net();  	start_seq_num = sta->tid_seq[tid] >> 4;  	ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START, -			       &sta->sta, tid, &start_seq_num); +			       &sta->sta, tid, &start_seq_num, 0);  	if (ret) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		printk(KERN_DEBUG "BA request denied - HW unavailable for" -					" tid %d\n", tid); -#endif +		ht_dbg(sdata, +		       "BA request denied - HW unavailable for %pM tid %d\n", +		       sta->sta.addr, tid);  		spin_lock_bh(&sta->lock); -		rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL); +		ieee80211_agg_splice_packets(sdata, tid_tx, tid); +		ieee80211_assign_tid_tx(sta, tid, NULL); +		ieee80211_agg_splice_finish(sdata, tid);  		spin_unlock_bh(&sta->lock); -		ieee80211_wake_queue_agg(local, tid); -		call_rcu(&tid_tx->rcu_head, kfree_tid_tx); +		kfree_rcu(tid_tx, rcu_head);  		return;  	} -	/* we can take packets again now */ -	ieee80211_wake_queue_agg(local, tid); -  	/* activate the timer for the recipient's addBA response */  	mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL); -#ifdef CONFIG_MAC80211_HT_DEBUG -	printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid); -#endif +	ht_dbg(sdata, "activated addBA response timer on %pM tid %d\n", +	       sta->sta.addr, tid);  	spin_lock_bh(&sta->lock); +	sta->ampdu_mlme.last_addba_req_time[tid] = jiffies;  	sta->ampdu_mlme.addba_req_num[tid]++;  	spin_unlock_bh(&sta->lock);  	/* send AddBA request */  	ieee80211_send_addba_request(sdata, sta->sta.addr, tid,  				     tid_tx->dialog_token, start_seq_num, -				     0x40, 5000); +				     local->hw.max_tx_aggregation_subframes, +				     tid_tx->timeout); +} + +/* + * After accepting the AddBA Response we activated a timer, + * resetting it after each frame that we send. + */ +static void sta_tx_agg_session_timer_expired(unsigned long data) +{ +	/* not an elegant detour, but there is no choice as the timer passes +	 * only one argument, and various sta_info are needed here, so init +	 * flow in sta_info_create gives the TID as data, while the timer_to_id +	 * array gives the sta through container_of */ +	u8 *ptid = (u8 *)data; +	u8 *timer_to_id = ptid - *ptid; +	struct sta_info *sta = container_of(timer_to_id, struct sta_info, +					 timer_to_tid[0]); +	struct tid_ampdu_tx *tid_tx; +	unsigned long timeout; + +	rcu_read_lock(); +	tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[*ptid]); +	if (!tid_tx || test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { +		rcu_read_unlock(); +		return; +	} + +	timeout = tid_tx->last_tx + TU_TO_JIFFIES(tid_tx->timeout); +	if (time_is_after_jiffies(timeout)) { +		mod_timer(&tid_tx->session_timer, timeout); +		rcu_read_unlock(); +		return; +	} + +	rcu_read_unlock(); + +	ht_dbg(sta->sdata, "tx session timer expired on %pM tid %d\n", +	       sta->sta.addr, (u16)*ptid); + +	ieee80211_stop_tx_ba_session(&sta->sta, *ptid);  } -int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) +int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, +				  u16 timeout)  {  	struct sta_info *sta = container_of(pubsta, struct sta_info, sta);  	struct ieee80211_sub_if_data *sdata = sta->sdata; @@ -355,34 +512,48 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)  	trace_api_start_tx_ba_session(pubsta, tid); -	if (WARN_ON(!local->ops->ampdu_action)) +	if (WARN_ON_ONCE(!local->ops->ampdu_action))  		return -EINVAL; -	if ((tid >= STA_TID_NUM) || -	    !(local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) +	if ((tid >= IEEE80211_NUM_TIDS) || +	    !(local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) || +	    (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW))  		return -EINVAL; -#ifdef CONFIG_MAC80211_HT_DEBUG -	printk(KERN_DEBUG "Open BA session requested for %pM tid %u\n", +	ht_dbg(sdata, "Open BA session requested for %pM tid %u\n",  	       pubsta->addr, tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ -	/* -	 * The aggregation code is not prepared to handle -	 * anything but STA/AP due to the BSSID handling. -	 * IBSS could work in the code but isn't supported -	 * by drivers or the standard. -	 */  	if (sdata->vif.type != NL80211_IFTYPE_STATION && +	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&  	    sdata->vif.type != NL80211_IFTYPE_AP_VLAN && -	    sdata->vif.type != NL80211_IFTYPE_AP) +	    sdata->vif.type != NL80211_IFTYPE_AP && +	    sdata->vif.type != NL80211_IFTYPE_ADHOC) +		return -EINVAL; + +	if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { +		ht_dbg(sdata, +		       "BA sessions blocked - Denying BA session request %pM tid %d\n", +		       sta->sta.addr, tid);  		return -EINVAL; +	} -	if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		printk(KERN_DEBUG "BA sessions blocked. " -		       "Denying BA session request\n"); -#endif +	/* +	 * 802.11n-2009 11.5.1.1: If the initiating STA is an HT STA, is a +	 * member of an IBSS, and has no other existing Block Ack agreement +	 * with the recipient STA, then the initiating STA shall transmit a +	 * Probe Request frame to the recipient STA and shall not transmit an +	 * ADDBA Request frame unless it receives a Probe Response frame +	 * from the recipient within dot11ADDBAFailureTimeout. +	 * +	 * The probe request mechanism for ADDBA is currently not implemented, +	 * but we only build up Block Ack session with HT STAs. This information +	 * is set when we receive a bss info from a probe response or a beacon. +	 */ +	if (sta->sdata->vif.type == NL80211_IFTYPE_ADHOC && +	    !sta->sta.ht_cap.ht_supported) { +		ht_dbg(sdata, +		       "BA request denied - IBSS STA %pM does not advertise HT support\n", +		       pubsta->addr);  		return -EINVAL;  	} @@ -394,13 +565,27 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)  		goto err_unlock_sta;  	} -	tid_tx = sta->ampdu_mlme.tid_tx[tid]; +	/* +	 * if we have tried more than HT_AGG_BURST_RETRIES times we +	 * will spread our requests in time to avoid stalling connection +	 * for too long +	 */ +	if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_BURST_RETRIES && +	    time_before(jiffies, sta->ampdu_mlme.last_addba_req_time[tid] + +			HT_AGG_RETRIES_PERIOD)) { +		ht_dbg(sdata, +		       "BA request denied - waiting a grace period after %d failed requests on %pM tid %u\n", +		       sta->ampdu_mlme.addba_req_num[tid], sta->sta.addr, tid); +		ret = -EBUSY; +		goto err_unlock_sta; +	} + +	tid_tx = rcu_dereference_protected_tid_tx(sta, tid);  	/* check if the TID is not in aggregation flow already */ -	if (tid_tx) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		printk(KERN_DEBUG "BA request denied - session is not " -				 "idle on tid %u\n", tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ +	if (tid_tx || sta->ampdu_mlme.tid_start_tx[tid]) { +		ht_dbg(sdata, +		       "BA request denied - session is not idle on %pM tid %u\n", +		       sta->sta.addr, tid);  		ret = -EAGAIN;  		goto err_unlock_sta;  	} @@ -408,11 +593,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)  	/* prepare A-MPDU MLME for Tx aggregation */  	tid_tx = kzalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC);  	if (!tid_tx) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		if (net_ratelimit()) -			printk(KERN_ERR "allocate tx mlme to tid %d failed\n", -					tid); -#endif  		ret = -ENOMEM;  		goto err_unlock_sta;  	} @@ -420,17 +600,27 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)  	skb_queue_head_init(&tid_tx->pending);  	__set_bit(HT_AGG_STATE_WANT_START, &tid_tx->state); -	/* Tx timer */ +	tid_tx->timeout = timeout; + +	/* response timer */  	tid_tx->addba_resp_timer.function = sta_addba_resp_timer_expired;  	tid_tx->addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[tid];  	init_timer(&tid_tx->addba_resp_timer); +	/* tx timer */ +	tid_tx->session_timer.function = sta_tx_agg_session_timer_expired; +	tid_tx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid]; +	init_timer_deferrable(&tid_tx->session_timer); +  	/* assign a dialog token */  	sta->ampdu_mlme.dialog_token_allocator++;  	tid_tx->dialog_token = sta->ampdu_mlme.dialog_token_allocator; -	/* finally, assign it to the array */ -	rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); +	/* +	 * Finally, assign it to the start array; the work item will +	 * collect it and move it to the normal array. +	 */ +	sta->ampdu_mlme.tid_start_tx[tid] = tid_tx;  	ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work); @@ -441,50 +631,21 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)  }  EXPORT_SYMBOL(ieee80211_start_tx_ba_session); -/* - * splice packets from the STA's pending to the local pending, - * requires a call to ieee80211_agg_splice_finish later - */ -static void __acquires(agg_queue) -ieee80211_agg_splice_packets(struct ieee80211_local *local, -			     struct tid_ampdu_tx *tid_tx, u16 tid) -{ -	int queue = ieee80211_ac_from_tid(tid); -	unsigned long flags; - -	ieee80211_stop_queue_agg(local, tid); - -	if (WARN(!tid_tx, "TID %d gone but expected when splicing aggregates" -			  " from the pending queue\n", tid)) -		return; - -	if (!skb_queue_empty(&tid_tx->pending)) { -		spin_lock_irqsave(&local->queue_stop_reason_lock, flags); -		/* copy over remaining packets */ -		skb_queue_splice_tail_init(&tid_tx->pending, -					   &local->pending[queue]); -		spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); -	} -} - -static void __releases(agg_queue) -ieee80211_agg_splice_finish(struct ieee80211_local *local, u16 tid) -{ -	ieee80211_wake_queue_agg(local, tid); -} -  static void ieee80211_agg_tx_operational(struct ieee80211_local *local,  					 struct sta_info *sta, u16 tid)  { +	struct tid_ampdu_tx *tid_tx; +  	lockdep_assert_held(&sta->ampdu_mlme.mtx); -#ifdef CONFIG_MAC80211_HT_DEBUG -	printk(KERN_DEBUG "Aggregation is on for tid %d\n", tid); -#endif +	tid_tx = rcu_dereference_protected_tid_tx(sta, tid); + +	ht_dbg(sta->sdata, "Aggregation is on for %pM tid %d\n", +	       sta->sta.addr, tid);  	drv_ampdu_action(local, sta->sdata,  			 IEEE80211_AMPDU_TX_OPERATIONAL, -			 &sta->sta, tid, NULL); +			 &sta->sta, tid, NULL, tid_tx->buf_size);  	/*  	 * synchronize with TX path, while splicing the TX path @@ -492,14 +653,14 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,  	 */  	spin_lock_bh(&sta->lock); -	ieee80211_agg_splice_packets(local, sta->ampdu_mlme.tid_tx[tid], tid); +	ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid);  	/*  	 * Now mark as operational. This will be visible  	 * in the TX path, and lets it go lock-free in  	 * the common case.  	 */ -	set_bit(HT_AGG_STATE_OPERATIONAL, &sta->ampdu_mlme.tid_tx[tid]->state); -	ieee80211_agg_splice_finish(local, tid); +	set_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state); +	ieee80211_agg_splice_finish(sta->sdata, tid);  	spin_unlock_bh(&sta->lock);  } @@ -513,31 +674,25 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid)  	trace_api_start_tx_ba_cb(sdata, ra, tid); -	if (tid >= STA_TID_NUM) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n", -				tid, STA_TID_NUM); -#endif +	if (tid >= IEEE80211_NUM_TIDS) { +		ht_dbg(sdata, "Bad TID value: tid = %d (>= %d)\n", +		       tid, IEEE80211_NUM_TIDS);  		return;  	}  	mutex_lock(&local->sta_mtx); -	sta = sta_info_get(sdata, ra); +	sta = sta_info_get_bss(sdata, ra);  	if (!sta) {  		mutex_unlock(&local->sta_mtx); -#ifdef CONFIG_MAC80211_HT_DEBUG -		printk(KERN_DEBUG "Could not find station: %pM\n", ra); -#endif +		ht_dbg(sdata, "Could not find station: %pM\n", ra);  		return;  	}  	mutex_lock(&sta->ampdu_mlme.mtx); -	tid_tx = sta->ampdu_mlme.tid_tx[tid]; +	tid_tx = rcu_dereference_protected_tid_tx(sta, tid);  	if (WARN_ON(!tid_tx)) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		printk(KERN_DEBUG "addBA was not requested!\n"); -#endif +		ht_dbg(sdata, "addBA was not requested!\n");  		goto unlock;  	} @@ -560,14 +715,9 @@ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif,  	struct ieee80211_ra_tid *ra_tid;  	struct sk_buff *skb = dev_alloc_skb(0); -	if (unlikely(!skb)) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		if (net_ratelimit()) -			printk(KERN_WARNING "%s: Not enough memory, " -			       "dropping start BA session", sdata->name); -#endif +	if (unlikely(!skb))  		return; -	} +  	ra_tid = (struct ieee80211_ra_tid *) &skb->cb;  	memcpy(&ra_tid->ra, ra, ETH_ALEN);  	ra_tid->tid = tid; @@ -579,14 +729,13 @@ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif,  EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe);  int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, -				   enum ieee80211_back_parties initiator, -				   bool tx) +				   enum ieee80211_agg_stop_reason reason)  {  	int ret;  	mutex_lock(&sta->ampdu_mlme.mtx); -	ret = ___ieee80211_stop_tx_ba_session(sta, tid, initiator, tx); +	ret = ___ieee80211_stop_tx_ba_session(sta, tid, reason);  	mutex_unlock(&sta->ampdu_mlme.mtx); @@ -606,11 +755,11 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)  	if (!local->ops->ampdu_action)  		return -EINVAL; -	if (tid >= STA_TID_NUM) +	if (tid >= IEEE80211_NUM_TIDS)  		return -EINVAL;  	spin_lock_bh(&sta->lock); -	tid_tx = sta->ampdu_mlme.tid_tx[tid]; +	tid_tx = rcu_dereference_protected_tid_tx(sta, tid);  	if (!tid_tx) {  		ret = -ENOENT; @@ -641,37 +790,30 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)  	trace_api_stop_tx_ba_cb(sdata, ra, tid); -	if (tid >= STA_TID_NUM) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n", -				tid, STA_TID_NUM); -#endif +	if (tid >= IEEE80211_NUM_TIDS) { +		ht_dbg(sdata, "Bad TID value: tid = %d (>= %d)\n", +		       tid, IEEE80211_NUM_TIDS);  		return;  	} -#ifdef CONFIG_MAC80211_HT_DEBUG -	printk(KERN_DEBUG "Stopping Tx BA session for %pM tid %d\n", -	       ra, tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ +	ht_dbg(sdata, "Stopping Tx BA session for %pM tid %d\n", ra, tid);  	mutex_lock(&local->sta_mtx); -	sta = sta_info_get(sdata, ra); +	sta = sta_info_get_bss(sdata, ra);  	if (!sta) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		printk(KERN_DEBUG "Could not find station: %pM\n", ra); -#endif +		ht_dbg(sdata, "Could not find station: %pM\n", ra);  		goto unlock;  	}  	mutex_lock(&sta->ampdu_mlme.mtx);  	spin_lock_bh(&sta->lock); -	tid_tx = sta->ampdu_mlme.tid_tx[tid]; +	tid_tx = rcu_dereference_protected_tid_tx(sta, tid);  	if (!tid_tx || !test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n"); -#endif +		ht_dbg(sdata, +		       "unexpected callback to A-MPDU stop for %pM tid %d\n", +		       sta->sta.addr, tid);  		goto unlock_sta;  	} @@ -679,24 +821,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)  		ieee80211_send_delba(sta->sdata, ra, tid,  			WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); -	/* -	 * When we get here, the TX path will not be lockless any more wrt. -	 * aggregation, since the OPERATIONAL bit has long been cleared. -	 * Thus it will block on getting the lock, if it occurs. So if we -	 * stop the queue now, we will not get any more packets, and any -	 * that might be being processed will wait for us here, thereby -	 * guaranteeing that no packets go to the tid_tx pending queue any -	 * more. -	 */ - -	ieee80211_agg_splice_packets(local, tid_tx, tid); - -	/* future packets must not find the tid_tx struct any more */ -	rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL); - -	ieee80211_agg_splice_finish(local, tid); - -	call_rcu(&tid_tx->rcu_head, kfree_tid_tx); +	ieee80211_remove_tid_tx(sta, tid);   unlock_sta:  	spin_unlock_bh(&sta->lock); @@ -713,14 +838,9 @@ void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif,  	struct ieee80211_ra_tid *ra_tid;  	struct sk_buff *skb = dev_alloc_skb(0); -	if (unlikely(!skb)) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		if (net_ratelimit()) -			printk(KERN_WARNING "%s: Not enough memory, " -			       "dropping stop BA session", sdata->name); -#endif +	if (unlikely(!skb))  		return; -	} +  	ra_tid = (struct ieee80211_ra_tid *) &skb->cb;  	memcpy(&ra_tid->ra, ra, ETH_ALEN);  	ra_tid->tid = tid; @@ -739,44 +859,71 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,  {  	struct tid_ampdu_tx *tid_tx;  	u16 capab, tid; +	u8 buf_size;  	capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);  	tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; +	buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;  	mutex_lock(&sta->ampdu_mlme.mtx); -	tid_tx = sta->ampdu_mlme.tid_tx[tid]; +	tid_tx = rcu_dereference_protected_tid_tx(sta, tid);  	if (!tid_tx)  		goto out;  	if (mgmt->u.action.u.addba_resp.dialog_token != tid_tx->dialog_token) { -#ifdef CONFIG_MAC80211_HT_DEBUG -		printk(KERN_DEBUG "wrong addBA response token, tid %d\n", tid); -#endif +		ht_dbg(sta->sdata, "wrong addBA response token, %pM tid %d\n", +		       sta->sta.addr, tid);  		goto out;  	} -	del_timer(&tid_tx->addba_resp_timer); +	del_timer_sync(&tid_tx->addba_resp_timer); -#ifdef CONFIG_MAC80211_HT_DEBUG -	printk(KERN_DEBUG "switched off addBA timer for tid %d\n", tid); -#endif +	ht_dbg(sta->sdata, "switched off addBA timer for %pM tid %d\n", +	       sta->sta.addr, tid); +	/* +	 * addba_resp_timer may have fired before we got here, and +	 * caused WANT_STOP to be set. If the stop then was already +	 * processed further, STOPPING might be set. +	 */ +	if (test_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state) || +	    test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { +		ht_dbg(sta->sdata, +		       "got addBA resp for %pM tid %d but we already gave up\n", +		       sta->sta.addr, tid); +		goto out; +	} + +	/* +	 * IEEE 802.11-2007 7.3.1.14: +	 * In an ADDBA Response frame, when the Status Code field +	 * is set to 0, the Buffer Size subfield is set to a value +	 * of at least 1. +	 */  	if (le16_to_cpu(mgmt->u.action.u.addba_resp.status) -			== WLAN_STATUS_SUCCESS) { +			== WLAN_STATUS_SUCCESS && buf_size) {  		if (test_and_set_bit(HT_AGG_STATE_RESPONSE_RECEIVED,  				     &tid_tx->state)) {  			/* ignore duplicate response */  			goto out;  		} +		tid_tx->buf_size = buf_size; +  		if (test_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state))  			ieee80211_agg_tx_operational(local, sta, tid);  		sta->ampdu_mlme.addba_req_num[tid] = 0; + +		if (tid_tx->timeout) { +			mod_timer(&tid_tx->session_timer, +				  TU_TO_EXP_TIME(tid_tx->timeout)); +			tid_tx->last_tx = jiffies; +		} +  	} else { -		___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR, -						true); +		___ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_DECLINED);  	}   out: diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 0c544074479..592f4b152ba 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -12,6 +12,7 @@  #include <linux/slab.h>  #include <net/net_namespace.h>  #include <linux/rcupdate.h> +#include <linux/if_ether.h>  #include <net/cfg80211.h>  #include "ieee80211_i.h"  #include "driver-ops.h" @@ -19,27 +20,32 @@  #include "rate.h"  #include "mesh.h" -static int ieee80211_add_iface(struct wiphy *wiphy, char *name, -			       enum nl80211_iftype type, u32 *flags, -			       struct vif_params *params) +static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, +						const char *name, +						enum nl80211_iftype type, +						u32 *flags, +						struct vif_params *params)  {  	struct ieee80211_local *local = wiphy_priv(wiphy); -	struct net_device *dev; +	struct wireless_dev *wdev;  	struct ieee80211_sub_if_data *sdata;  	int err; -	err = ieee80211_if_add(local, name, &dev, type, params); -	if (err || type != NL80211_IFTYPE_MONITOR || !flags) -		return err; +	err = ieee80211_if_add(local, name, &wdev, type, params); +	if (err) +		return ERR_PTR(err); -	sdata = IEEE80211_DEV_TO_SUB_IF(dev); -	sdata->u.mntr_flags = *flags; -	return 0; +	if (type == NL80211_IFTYPE_MONITOR && flags) { +		sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); +		sdata->u.mntr_flags = *flags; +	} + +	return wdev;  } -static int ieee80211_del_iface(struct wiphy *wiphy, struct net_device *dev) +static int ieee80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)  { -	ieee80211_if_remove(IEEE80211_DEV_TO_SUB_IF(dev)); +	ieee80211_if_remove(IEEE80211_WDEV_TO_SUB_IF(wdev));  	return 0;  } @@ -56,14 +62,9 @@ static int ieee80211_change_iface(struct wiphy *wiphy,  	if (ret)  		return ret; -	if (ieee80211_vif_is_mesh(&sdata->vif) && params->mesh_id_len) -		ieee80211_sdata_set_mesh_id(sdata, -					    params->mesh_id_len, -					    params->mesh_id); -  	if (type == NL80211_IFTYPE_AP_VLAN &&  	    params && params->use_4addr == 0) -		rcu_assign_pointer(sdata->u.vlan.sta, NULL); +		RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);  	else if (type == NL80211_IFTYPE_STATION &&  		 params && params->use_4addr >= 0)  		sdata->u.mgd.use_4addr = params->use_4addr; @@ -72,16 +73,19 @@ static int ieee80211_change_iface(struct wiphy *wiphy,  		struct ieee80211_local *local = sdata->local;  		if (ieee80211_sdata_running(sdata)) { +			u32 mask = MONITOR_FLAG_COOK_FRAMES | +				   MONITOR_FLAG_ACTIVE; +  			/* -			 * Prohibit MONITOR_FLAG_COOK_FRAMES to be -			 * changed while the interface is up. +			 * Prohibit MONITOR_FLAG_COOK_FRAMES and +			 * MONITOR_FLAG_ACTIVE to be changed while the +			 * interface is up.  			 * Else we would need to add a lot of cruft  			 * to update everything:  			 *	cooked_mntrs, monitor and all fif_* counters  			 *	reconfigure hardware  			 */ -			if ((*flags & MONITOR_FLAG_COOK_FRAMES) != -			    (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)) +			if ((*flags & mask) != (sdata->u.mntr_flags & mask))  				return -EBUSY;  			ieee80211_adjust_monitor_flags(sdata, -1); @@ -102,12 +106,45 @@ static int ieee80211_change_iface(struct wiphy *wiphy,  	return 0;  } +static int ieee80211_start_p2p_device(struct wiphy *wiphy, +				      struct wireless_dev *wdev) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); +	int ret; + +	mutex_lock(&sdata->local->chanctx_mtx); +	ret = ieee80211_check_combinations(sdata, NULL, 0, 0); +	mutex_unlock(&sdata->local->chanctx_mtx); +	if (ret < 0) +		return ret; + +	return ieee80211_do_open(wdev, true); +} + +static void ieee80211_stop_p2p_device(struct wiphy *wiphy, +				      struct wireless_dev *wdev) +{ +	ieee80211_sdata_stop(IEEE80211_WDEV_TO_SUB_IF(wdev)); +} + +static int ieee80211_set_noack_map(struct wiphy *wiphy, +				  struct net_device *dev, +				  u16 noack_map) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + +	sdata->noack_map = noack_map; +	return 0; +} +  static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,  			     u8 key_idx, bool pairwise, const u8 *mac_addr,  			     struct key_params *params)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local;  	struct sta_info *sta = NULL; +	const struct ieee80211_cipher_scheme *cs = NULL;  	struct ieee80211_key *key;  	int err; @@ -119,38 +156,90 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,  	case WLAN_CIPHER_SUITE_WEP40:  	case WLAN_CIPHER_SUITE_TKIP:  	case WLAN_CIPHER_SUITE_WEP104: -		if (IS_ERR(sdata->local->wep_tx_tfm)) +		if (IS_ERR(local->wep_tx_tfm))  			return -EINVAL;  		break; +	case WLAN_CIPHER_SUITE_CCMP: +	case WLAN_CIPHER_SUITE_AES_CMAC: +	case WLAN_CIPHER_SUITE_GCMP: +		break;  	default: +		cs = ieee80211_cs_get(local, params->cipher, sdata->vif.type);  		break;  	}  	key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len, -				  params->key, params->seq_len, params->seq); +				  params->key, params->seq_len, params->seq, +				  cs);  	if (IS_ERR(key))  		return PTR_ERR(key);  	if (pairwise)  		key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE; -	mutex_lock(&sdata->local->sta_mtx); +	mutex_lock(&local->sta_mtx);  	if (mac_addr) { -		sta = sta_info_get_bss(sdata, mac_addr); -		if (!sta) { -			ieee80211_key_free(sdata->local, key); +		if (ieee80211_vif_is_mesh(&sdata->vif)) +			sta = sta_info_get(sdata, mac_addr); +		else +			sta = sta_info_get_bss(sdata, mac_addr); +		/* +		 * The ASSOC test makes sure the driver is ready to +		 * receive the key. When wpa_supplicant has roamed +		 * using FT, it attempts to set the key before +		 * association has completed, this rejects that attempt +		 * so it will set the key again after assocation. +		 * +		 * TODO: accept the key if we have a station entry and +		 *       add it to the device after the station. +		 */ +		if (!sta || !test_sta_flag(sta, WLAN_STA_ASSOC)) { +			ieee80211_key_free_unused(key);  			err = -ENOENT;  			goto out_unlock;  		}  	} +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_STATION: +		if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED) +			key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; +		break; +	case NL80211_IFTYPE_AP: +	case NL80211_IFTYPE_AP_VLAN: +		/* Keys without a station are used for TX only */ +		if (key->sta && test_sta_flag(key->sta, WLAN_STA_MFP)) +			key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; +		break; +	case NL80211_IFTYPE_ADHOC: +		/* no MFP (yet) */ +		break; +	case NL80211_IFTYPE_MESH_POINT: +#ifdef CONFIG_MAC80211_MESH +		if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE) +			key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; +		break; +#endif +	case NL80211_IFTYPE_WDS: +	case NL80211_IFTYPE_MONITOR: +	case NL80211_IFTYPE_P2P_DEVICE: +	case NL80211_IFTYPE_UNSPECIFIED: +	case NUM_NL80211_IFTYPES: +	case NL80211_IFTYPE_P2P_CLIENT: +	case NL80211_IFTYPE_P2P_GO: +		/* shouldn't happen */ +		WARN_ON_ONCE(1); +		break; +	} + +	if (sta) +		sta->cipher_scheme = cs; +  	err = ieee80211_key_link(key, sdata, sta); -	if (err) -		ieee80211_key_free(sdata->local, key);   out_unlock: -	mutex_unlock(&sdata->local->sta_mtx); +	mutex_unlock(&local->sta_mtx);  	return err;  } @@ -158,13 +247,14 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,  static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,  			     u8 key_idx, bool pairwise, const u8 *mac_addr)  { -	struct ieee80211_sub_if_data *sdata; +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local;  	struct sta_info *sta; +	struct ieee80211_key *key = NULL;  	int ret; -	sdata = IEEE80211_DEV_TO_SUB_IF(dev); - -	mutex_lock(&sdata->local->sta_mtx); +	mutex_lock(&local->sta_mtx); +	mutex_lock(&local->key_mtx);  	if (mac_addr) {  		ret = -ENOENT; @@ -173,33 +263,24 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,  		if (!sta)  			goto out_unlock; -		if (pairwise) { -			if (sta->ptk) { -				ieee80211_key_free(sdata->local, sta->ptk); -				ret = 0; -			} -		} else { -			if (sta->gtk[key_idx]) { -				ieee80211_key_free(sdata->local, -						   sta->gtk[key_idx]); -				ret = 0; -			} -		} - -		goto out_unlock; -	} +		if (pairwise) +			key = key_mtx_dereference(local, sta->ptk[key_idx]); +		else +			key = key_mtx_dereference(local, sta->gtk[key_idx]); +	} else +		key = key_mtx_dereference(local, sdata->keys[key_idx]); -	if (!sdata->keys[key_idx]) { +	if (!key) {  		ret = -ENOENT;  		goto out_unlock;  	} -	ieee80211_key_free(sdata->local, sdata->keys[key_idx]); -	WARN_ON(sdata->keys[key_idx]); +	ieee80211_key_free(key, true);  	ret = 0;   out_unlock: -	mutex_unlock(&sdata->local->sta_mtx); +	mutex_unlock(&local->key_mtx); +	mutex_unlock(&local->sta_mtx);  	return ret;  } @@ -215,6 +296,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,  	u8 seq[6] = {0};  	struct key_params params;  	struct ieee80211_key *key = NULL; +	u64 pn64;  	u32 iv32;  	u16 iv16;  	int err = -ENOENT; @@ -228,12 +310,13 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,  		if (!sta)  			goto out; -		if (pairwise) -			key = sta->ptk; -		else if (key_idx < NUM_DEFAULT_KEYS) -			key = sta->gtk[key_idx]; +		if (pairwise && key_idx < NUM_DEFAULT_KEYS) +			key = rcu_dereference(sta->ptk[key_idx]); +		else if (!pairwise && +			 key_idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) +			key = rcu_dereference(sta->gtk[key_idx]);  	} else -		key = sdata->keys[key_idx]; +		key = rcu_dereference(sdata->keys[key_idx]);  	if (!key)  		goto out; @@ -262,22 +345,24 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,  		params.seq_len = 6;  		break;  	case WLAN_CIPHER_SUITE_CCMP: -		seq[0] = key->u.ccmp.tx_pn[5]; -		seq[1] = key->u.ccmp.tx_pn[4]; -		seq[2] = key->u.ccmp.tx_pn[3]; -		seq[3] = key->u.ccmp.tx_pn[2]; -		seq[4] = key->u.ccmp.tx_pn[1]; -		seq[5] = key->u.ccmp.tx_pn[0]; +		pn64 = atomic64_read(&key->u.ccmp.tx_pn); +		seq[0] = pn64; +		seq[1] = pn64 >> 8; +		seq[2] = pn64 >> 16; +		seq[3] = pn64 >> 24; +		seq[4] = pn64 >> 32; +		seq[5] = pn64 >> 40;  		params.seq = seq;  		params.seq_len = 6;  		break;  	case WLAN_CIPHER_SUITE_AES_CMAC: -		seq[0] = key->u.aes_cmac.tx_pn[5]; -		seq[1] = key->u.aes_cmac.tx_pn[4]; -		seq[2] = key->u.aes_cmac.tx_pn[3]; -		seq[3] = key->u.aes_cmac.tx_pn[2]; -		seq[4] = key->u.aes_cmac.tx_pn[1]; -		seq[5] = key->u.aes_cmac.tx_pn[0]; +		pn64 = atomic64_read(&key->u.aes_cmac.tx_pn); +		seq[0] = pn64; +		seq[1] = pn64 >> 8; +		seq[2] = pn64 >> 16; +		seq[3] = pn64 >> 24; +		seq[4] = pn64 >> 32; +		seq[5] = pn64 >> 40;  		params.seq = seq;  		params.seq_len = 6;  		break; @@ -296,11 +381,12 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,  static int ieee80211_config_default_key(struct wiphy *wiphy,  					struct net_device *dev, -					u8 key_idx) +					u8 key_idx, bool uni, +					bool multi)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); -	ieee80211_set_default_key(sdata, key_idx); +	ieee80211_set_default_key(sdata, key_idx, uni, multi);  	return 0;  } @@ -316,76 +402,405 @@ static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy,  	return 0;  } +void sta_set_rate_info_tx(struct sta_info *sta, +			  const struct ieee80211_tx_rate *rate, +			  struct rate_info *rinfo) +{ +	rinfo->flags = 0; +	if (rate->flags & IEEE80211_TX_RC_MCS) { +		rinfo->flags |= RATE_INFO_FLAGS_MCS; +		rinfo->mcs = rate->idx; +	} else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { +		rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; +		rinfo->mcs = ieee80211_rate_get_vht_mcs(rate); +		rinfo->nss = ieee80211_rate_get_vht_nss(rate); +	} else { +		struct ieee80211_supported_band *sband; +		int shift = ieee80211_vif_get_shift(&sta->sdata->vif); +		u16 brate; + +		sband = sta->local->hw.wiphy->bands[ +				ieee80211_get_sdata_band(sta->sdata)]; +		brate = sband->bitrates[rate->idx].bitrate; +		rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift); +	} +	if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) +		rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; +	if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) +		rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; +	if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) +		rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; +	if (rate->flags & IEEE80211_TX_RC_SHORT_GI) +		rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; +} + +void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo) +{ +	rinfo->flags = 0; + +	if (sta->last_rx_rate_flag & RX_FLAG_HT) { +		rinfo->flags |= RATE_INFO_FLAGS_MCS; +		rinfo->mcs = sta->last_rx_rate_idx; +	} else if (sta->last_rx_rate_flag & RX_FLAG_VHT) { +		rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; +		rinfo->nss = sta->last_rx_rate_vht_nss; +		rinfo->mcs = sta->last_rx_rate_idx; +	} else { +		struct ieee80211_supported_band *sband; +		int shift = ieee80211_vif_get_shift(&sta->sdata->vif); +		u16 brate; + +		sband = sta->local->hw.wiphy->bands[ +				ieee80211_get_sdata_band(sta->sdata)]; +		brate = sband->bitrates[sta->last_rx_rate_idx].bitrate; +		rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift); +	} + +	if (sta->last_rx_rate_flag & RX_FLAG_40MHZ) +		rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; +	if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI) +		rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; +	if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80MHZ) +		rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; +	if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80P80MHZ) +		rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; +	if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_160MHZ) +		rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; +} +  static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)  {  	struct ieee80211_sub_if_data *sdata = sta->sdata; +	struct ieee80211_local *local = sdata->local; +	struct rate_control_ref *ref = NULL; +	struct timespec uptime; +	u64 packets = 0; +	u32 thr = 0; +	int i, ac; + +	if (test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) +		ref = local->rate_ctrl;  	sinfo->generation = sdata->local->sta_generation;  	sinfo->filled = STATION_INFO_INACTIVE_TIME | -			STATION_INFO_RX_BYTES | -			STATION_INFO_TX_BYTES | +			STATION_INFO_RX_BYTES64 | +			STATION_INFO_TX_BYTES64 |  			STATION_INFO_RX_PACKETS |  			STATION_INFO_TX_PACKETS |  			STATION_INFO_TX_RETRIES |  			STATION_INFO_TX_FAILED |  			STATION_INFO_TX_BITRATE | -			STATION_INFO_RX_DROP_MISC; +			STATION_INFO_RX_BITRATE | +			STATION_INFO_RX_DROP_MISC | +			STATION_INFO_BSS_PARAM | +			STATION_INFO_CONNECTED_TIME | +			STATION_INFO_STA_FLAGS | +			STATION_INFO_BEACON_LOSS_COUNT; + +	do_posix_clock_monotonic_gettime(&uptime); +	sinfo->connected_time = uptime.tv_sec - sta->last_connected;  	sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); +	sinfo->tx_bytes = 0; +	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { +		sinfo->tx_bytes += sta->tx_bytes[ac]; +		packets += sta->tx_packets[ac]; +	} +	sinfo->tx_packets = packets;  	sinfo->rx_bytes = sta->rx_bytes; -	sinfo->tx_bytes = sta->tx_bytes;  	sinfo->rx_packets = sta->rx_packets; -	sinfo->tx_packets = sta->tx_packets;  	sinfo->tx_retries = sta->tx_retry_count;  	sinfo->tx_failed = sta->tx_retry_failed;  	sinfo->rx_dropped_misc = sta->rx_dropped; +	sinfo->beacon_loss_count = sta->beacon_loss_count;  	if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||  	    (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) { -		sinfo->filled |= STATION_INFO_SIGNAL; -		sinfo->signal = (s8)sta->last_signal; +		sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG; +		if (!local->ops->get_rssi || +		    drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal)) +			sinfo->signal = (s8)sta->last_signal; +		sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal); +	} +	if (sta->chains) { +		sinfo->filled |= STATION_INFO_CHAIN_SIGNAL | +				 STATION_INFO_CHAIN_SIGNAL_AVG; + +		sinfo->chains = sta->chains; +		for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) { +			sinfo->chain_signal[i] = sta->chain_signal_last[i]; +			sinfo->chain_signal_avg[i] = +				(s8) -ewma_read(&sta->chain_signal_avg[i]); +		}  	} -	sinfo->txrate.flags = 0; -	if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS) -		sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; -	if (sta->last_tx_rate.flags & IEEE80211_TX_RC_40_MHZ_WIDTH) -		sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; -	if (sta->last_tx_rate.flags & IEEE80211_TX_RC_SHORT_GI) -		sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; - -	if (!(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) { -		struct ieee80211_supported_band *sband; -		sband = sta->local->hw.wiphy->bands[ -				sta->local->hw.conf.channel->band]; -		sinfo->txrate.legacy = -			sband->bitrates[sta->last_tx_rate.idx].bitrate; -	} else -		sinfo->txrate.mcs = sta->last_tx_rate.idx; +	sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); +	sta_set_rate_info_rx(sta, &sinfo->rxrate);  	if (ieee80211_vif_is_mesh(&sdata->vif)) {  #ifdef CONFIG_MAC80211_MESH  		sinfo->filled |= STATION_INFO_LLID |  				 STATION_INFO_PLID | -				 STATION_INFO_PLINK_STATE; +				 STATION_INFO_PLINK_STATE | +				 STATION_INFO_LOCAL_PM | +				 STATION_INFO_PEER_PM | +				 STATION_INFO_NONPEER_PM; -		sinfo->llid = le16_to_cpu(sta->llid); -		sinfo->plid = le16_to_cpu(sta->plid); +		sinfo->llid = sta->llid; +		sinfo->plid = sta->plid;  		sinfo->plink_state = sta->plink_state; +		if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { +			sinfo->filled |= STATION_INFO_T_OFFSET; +			sinfo->t_offset = sta->t_offset; +		} +		sinfo->local_pm = sta->local_pm; +		sinfo->peer_pm = sta->peer_pm; +		sinfo->nonpeer_pm = sta->nonpeer_pm;  #endif  	} + +	sinfo->bss_param.flags = 0; +	if (sdata->vif.bss_conf.use_cts_prot) +		sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT; +	if (sdata->vif.bss_conf.use_short_preamble) +		sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; +	if (sdata->vif.bss_conf.use_short_slot) +		sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; +	sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period; +	sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int; + +	sinfo->sta_flags.set = 0; +	sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | +				BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | +				BIT(NL80211_STA_FLAG_WME) | +				BIT(NL80211_STA_FLAG_MFP) | +				BIT(NL80211_STA_FLAG_AUTHENTICATED) | +				BIT(NL80211_STA_FLAG_ASSOCIATED) | +				BIT(NL80211_STA_FLAG_TDLS_PEER); +	if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) +		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED); +	if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE)) +		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); +	if (test_sta_flag(sta, WLAN_STA_WME)) +		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME); +	if (test_sta_flag(sta, WLAN_STA_MFP)) +		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP); +	if (test_sta_flag(sta, WLAN_STA_AUTH)) +		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); +	if (test_sta_flag(sta, WLAN_STA_ASSOC)) +		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED); +	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) +		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); + +	/* check if the driver has a SW RC implementation */ +	if (ref && ref->ops->get_expected_throughput) +		thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv); +	else +		thr = drv_get_expected_throughput(local, &sta->sta); + +	if (thr != 0) { +		sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT; +		sinfo->expected_throughput = thr; +	} +} + +static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { +	"rx_packets", "rx_bytes", "wep_weak_iv_count", +	"rx_duplicates", "rx_fragments", "rx_dropped", +	"tx_packets", "tx_bytes", "tx_fragments", +	"tx_filtered", "tx_retry_failed", "tx_retries", +	"beacon_loss", "sta_state", "txrate", "rxrate", "signal", +	"channel", "noise", "ch_time", "ch_time_busy", +	"ch_time_ext_busy", "ch_time_rx", "ch_time_tx" +}; +#define STA_STATS_LEN	ARRAY_SIZE(ieee80211_gstrings_sta_stats) + +static int ieee80211_get_et_sset_count(struct wiphy *wiphy, +				       struct net_device *dev, +				       int sset) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	int rv = 0; + +	if (sset == ETH_SS_STATS) +		rv += STA_STATS_LEN; + +	rv += drv_get_et_sset_count(sdata, sset); + +	if (rv == 0) +		return -EOPNOTSUPP; +	return rv; +} + +static void ieee80211_get_et_stats(struct wiphy *wiphy, +				   struct net_device *dev, +				   struct ethtool_stats *stats, +				   u64 *data) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_chanctx_conf *chanctx_conf; +	struct ieee80211_channel *channel; +	struct sta_info *sta; +	struct ieee80211_local *local = sdata->local; +	struct station_info sinfo; +	struct survey_info survey; +	int i, q; +#define STA_STATS_SURVEY_LEN 7 + +	memset(data, 0, sizeof(u64) * STA_STATS_LEN); + +#define ADD_STA_STATS(sta)				\ +	do {						\ +		data[i++] += sta->rx_packets;		\ +		data[i++] += sta->rx_bytes;		\ +		data[i++] += sta->wep_weak_iv_count;	\ +		data[i++] += sta->num_duplicates;	\ +		data[i++] += sta->rx_fragments;		\ +		data[i++] += sta->rx_dropped;		\ +							\ +		data[i++] += sinfo.tx_packets;		\ +		data[i++] += sinfo.tx_bytes;		\ +		data[i++] += sta->tx_fragments;		\ +		data[i++] += sta->tx_filtered_count;	\ +		data[i++] += sta->tx_retry_failed;	\ +		data[i++] += sta->tx_retry_count;	\ +		data[i++] += sta->beacon_loss_count;	\ +	} while (0) + +	/* For Managed stations, find the single station based on BSSID +	 * and use that.  For interface types, iterate through all available +	 * stations and add stats for any station that is assigned to this +	 * network device. +	 */ + +	mutex_lock(&local->sta_mtx); + +	if (sdata->vif.type == NL80211_IFTYPE_STATION) { +		sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid); + +		if (!(sta && !WARN_ON(sta->sdata->dev != dev))) +			goto do_survey; + +		sinfo.filled = 0; +		sta_set_sinfo(sta, &sinfo); + +		i = 0; +		ADD_STA_STATS(sta); + +		data[i++] = sta->sta_state; + + +		if (sinfo.filled & STATION_INFO_TX_BITRATE) +			data[i] = 100000 * +				cfg80211_calculate_bitrate(&sinfo.txrate); +		i++; +		if (sinfo.filled & STATION_INFO_RX_BITRATE) +			data[i] = 100000 * +				cfg80211_calculate_bitrate(&sinfo.rxrate); +		i++; + +		if (sinfo.filled & STATION_INFO_SIGNAL_AVG) +			data[i] = (u8)sinfo.signal_avg; +		i++; +	} else { +		list_for_each_entry(sta, &local->sta_list, list) { +			/* Make sure this station belongs to the proper dev */ +			if (sta->sdata->dev != dev) +				continue; + +			sinfo.filled = 0; +			sta_set_sinfo(sta, &sinfo); +			i = 0; +			ADD_STA_STATS(sta); +		} +	} + +do_survey: +	i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; +	/* Get survey stats for current channel */ +	survey.filled = 0; + +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	if (chanctx_conf) +		channel = chanctx_conf->def.chan; +	else +		channel = NULL; +	rcu_read_unlock(); + +	if (channel) { +		q = 0; +		do { +			survey.filled = 0; +			if (drv_get_survey(local, q, &survey) != 0) { +				survey.filled = 0; +				break; +			} +			q++; +		} while (channel != survey.channel); +	} + +	if (survey.filled) +		data[i++] = survey.channel->center_freq; +	else +		data[i++] = 0; +	if (survey.filled & SURVEY_INFO_NOISE_DBM) +		data[i++] = (u8)survey.noise; +	else +		data[i++] = -1LL; +	if (survey.filled & SURVEY_INFO_CHANNEL_TIME) +		data[i++] = survey.channel_time; +	else +		data[i++] = -1LL; +	if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY) +		data[i++] = survey.channel_time_busy; +	else +		data[i++] = -1LL; +	if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) +		data[i++] = survey.channel_time_ext_busy; +	else +		data[i++] = -1LL; +	if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX) +		data[i++] = survey.channel_time_rx; +	else +		data[i++] = -1LL; +	if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX) +		data[i++] = survey.channel_time_tx; +	else +		data[i++] = -1LL; + +	mutex_unlock(&local->sta_mtx); + +	if (WARN_ON(i != STA_STATS_LEN)) +		return; + +	drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN]));  } +static void ieee80211_get_et_strings(struct wiphy *wiphy, +				     struct net_device *dev, +				     u32 sset, u8 *data) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	int sz_sta_stats = 0; + +	if (sset == ETH_SS_STATS) { +		sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats); +		memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats); +	} +	drv_get_et_strings(sdata, sset, &(data[sz_sta_stats])); +}  static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, -				 int idx, u8 *mac, struct station_info *sinfo) +				  int idx, u8 *mac, struct station_info *sinfo)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local;  	struct sta_info *sta;  	int ret = -ENOENT; -	rcu_read_lock(); +	mutex_lock(&local->sta_mtx);  	sta = sta_info_get_by_idx(sdata, idx);  	if (sta) { @@ -394,7 +809,7 @@ static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,  		sta_set_sinfo(sta, sinfo);  	} -	rcu_read_unlock(); +	mutex_unlock(&local->sta_mtx);  	return ret;  } @@ -408,13 +823,14 @@ static int ieee80211_dump_survey(struct wiphy *wiphy, struct net_device *dev,  }  static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, -				 u8 *mac, struct station_info *sinfo) +				 const u8 *mac, struct station_info *sinfo)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local;  	struct sta_info *sta;  	int ret = -ENOENT; -	rcu_read_lock(); +	mutex_lock(&local->sta_mtx);  	sta = sta_info_get_bss(sdata, mac);  	if (sta) { @@ -422,46 +838,83 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,  		sta_set_sinfo(sta, sinfo);  	} -	rcu_read_unlock(); +	mutex_unlock(&local->sta_mtx);  	return ret;  } -/* - * This handles both adding a beacon and setting new beacon info - */ -static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, -				   struct beacon_parameters *params) +static int ieee80211_set_monitor_channel(struct wiphy *wiphy, +					 struct cfg80211_chan_def *chandef) +{ +	struct ieee80211_local *local = wiphy_priv(wiphy); +	struct ieee80211_sub_if_data *sdata; +	int ret = 0; + +	if (cfg80211_chandef_identical(&local->monitor_chandef, chandef)) +		return 0; + +	mutex_lock(&local->mtx); +	mutex_lock(&local->iflist_mtx); +	if (local->use_chanctx) { +		sdata = rcu_dereference_protected( +				local->monitor_sdata, +				lockdep_is_held(&local->iflist_mtx)); +		if (sdata) { +			ieee80211_vif_release_channel(sdata); +			ret = ieee80211_vif_use_channel(sdata, chandef, +					IEEE80211_CHANCTX_EXCLUSIVE); +		} +	} else if (local->open_count == local->monitors) { +		local->_oper_chandef = *chandef; +		ieee80211_hw_config(local, 0); +	} + +	if (ret == 0) +		local->monitor_chandef = *chandef; +	mutex_unlock(&local->iflist_mtx); +	mutex_unlock(&local->mtx); + +	return ret; +} + +static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, +				    const u8 *resp, size_t resp_len) +{ +	struct probe_resp *new, *old; + +	if (!resp || !resp_len) +		return 1; + +	old = sdata_dereference(sdata->u.ap.probe_resp, sdata); + +	new = kzalloc(sizeof(struct probe_resp) + resp_len, GFP_KERNEL); +	if (!new) +		return -ENOMEM; + +	new->len = resp_len; +	memcpy(new->data, resp, resp_len); + +	rcu_assign_pointer(sdata->u.ap.probe_resp, new); +	if (old) +		kfree_rcu(old, rcu_head); + +	return 0; +} + +static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, +				   struct cfg80211_beacon_data *params)  {  	struct beacon_data *new, *old;  	int new_head_len, new_tail_len; -	int size; -	int err = -EINVAL; - -	old = sdata->u.ap.beacon; +	int size, err; +	u32 changed = BSS_CHANGED_BEACON; -	/* head must not be zero-length */ -	if (params->head && !params->head_len) -		return -EINVAL; +	old = sdata_dereference(sdata->u.ap.beacon, sdata); -	/* -	 * This is a kludge. beacon interval should really be part -	 * of the beacon information. -	 */ -	if (params->interval && -	    (sdata->vif.bss_conf.beacon_int != params->interval)) { -		sdata->vif.bss_conf.beacon_int = params->interval; -		ieee80211_bss_info_change_notify(sdata, -						 BSS_CHANGED_BEACON_INT); -	}  	/* Need to have a beacon head if we don't have one yet */  	if (!params->head && !old) -		return err; - -	/* sorry, no way to start beaconing without dtim period */ -	if (!params->dtim_period && !old) -		return err; +		return -EINVAL;  	/* new or old head? */  	if (params->head) @@ -484,12 +937,6 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,  	/* start filling the new info now */ -	/* new or old dtim period? */ -	if (params->dtim_period) -		new->dtim_period = params->dtim_period; -	else -		new->dtim_period = old->dtim_period; -  	/*  	 * pointers go into the block we allocated,  	 * memory is | beacon_data | head | tail | @@ -512,68 +959,240 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,  		if (old)  			memcpy(new->tail, old->tail, new_tail_len); -	sdata->vif.bss_conf.dtim_period = new->dtim_period; +	err = ieee80211_set_probe_resp(sdata, params->probe_resp, +				       params->probe_resp_len); +	if (err < 0) +		return err; +	if (err == 0) +		changed |= BSS_CHANGED_AP_PROBE_RESP;  	rcu_assign_pointer(sdata->u.ap.beacon, new); -	synchronize_rcu(); - -	kfree(old); +	if (old) +		kfree_rcu(old, rcu_head); -	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | -						BSS_CHANGED_BEACON); -	return 0; +	return changed;  } -static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, -				struct beacon_parameters *params) +static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, +			      struct cfg80211_ap_settings *params)  { -	struct ieee80211_sub_if_data *sdata; +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local;  	struct beacon_data *old; +	struct ieee80211_sub_if_data *vlan; +	u32 changed = BSS_CHANGED_BEACON_INT | +		      BSS_CHANGED_BEACON_ENABLED | +		      BSS_CHANGED_BEACON | +		      BSS_CHANGED_SSID | +		      BSS_CHANGED_P2P_PS; +	int err; -	sdata = IEEE80211_DEV_TO_SUB_IF(dev); - -	old = sdata->u.ap.beacon; - +	old = sdata_dereference(sdata->u.ap.beacon, sdata);  	if (old)  		return -EALREADY; -	return ieee80211_config_beacon(sdata, params); +	/* TODO: make hostapd tell us what it wants */ +	sdata->smps_mode = IEEE80211_SMPS_OFF; +	sdata->needed_rx_chains = sdata->local->rx_chains; + +	mutex_lock(&local->mtx); +	err = ieee80211_vif_use_channel(sdata, ¶ms->chandef, +					IEEE80211_CHANCTX_SHARED); +	if (!err) +		ieee80211_vif_copy_chanctx_to_vlans(sdata, false); +	mutex_unlock(&local->mtx); +	if (err) +		return err; + +	/* +	 * Apply control port protocol, this allows us to +	 * not encrypt dynamic WEP control frames. +	 */ +	sdata->control_port_protocol = params->crypto.control_port_ethertype; +	sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt; +	sdata->encrypt_headroom = ieee80211_cs_headroom(sdata->local, +							¶ms->crypto, +							sdata->vif.type); + +	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { +		vlan->control_port_protocol = +			params->crypto.control_port_ethertype; +		vlan->control_port_no_encrypt = +			params->crypto.control_port_no_encrypt; +		vlan->encrypt_headroom = +			ieee80211_cs_headroom(sdata->local, +					      ¶ms->crypto, +					      vlan->vif.type); +	} + +	sdata->vif.bss_conf.beacon_int = params->beacon_interval; +	sdata->vif.bss_conf.dtim_period = params->dtim_period; +	sdata->vif.bss_conf.enable_beacon = true; + +	sdata->vif.bss_conf.ssid_len = params->ssid_len; +	if (params->ssid_len) +		memcpy(sdata->vif.bss_conf.ssid, params->ssid, +		       params->ssid_len); +	sdata->vif.bss_conf.hidden_ssid = +		(params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE); + +	memset(&sdata->vif.bss_conf.p2p_noa_attr, 0, +	       sizeof(sdata->vif.bss_conf.p2p_noa_attr)); +	sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow = +		params->p2p_ctwindow & IEEE80211_P2P_OPPPS_CTWINDOW_MASK; +	if (params->p2p_opp_ps) +		sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |= +					IEEE80211_P2P_OPPPS_ENABLE_BIT; + +	err = ieee80211_assign_beacon(sdata, ¶ms->beacon); +	if (err < 0) { +		ieee80211_vif_release_channel(sdata); +		return err; +	} +	changed |= err; + +	err = drv_start_ap(sdata->local, sdata); +	if (err) { +		old = sdata_dereference(sdata->u.ap.beacon, sdata); + +		if (old) +			kfree_rcu(old, rcu_head); +		RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); +		ieee80211_vif_release_channel(sdata); +		return err; +	} + +	ieee80211_recalc_dtim(local, sdata); +	ieee80211_bss_info_change_notify(sdata, changed); + +	netif_carrier_on(dev); +	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) +		netif_carrier_on(vlan->dev); + +	return 0;  } -static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, -				struct beacon_parameters *params) +static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, +				   struct cfg80211_beacon_data *params)  {  	struct ieee80211_sub_if_data *sdata;  	struct beacon_data *old; +	int err;  	sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	sdata_assert_lock(sdata); -	old = sdata->u.ap.beacon; +	/* don't allow changing the beacon while CSA is in place - offset +	 * of channel switch counter may change +	 */ +	if (sdata->vif.csa_active) +		return -EBUSY; +	old = sdata_dereference(sdata->u.ap.beacon, sdata);  	if (!old)  		return -ENOENT; -	return ieee80211_config_beacon(sdata, params); +	err = ieee80211_assign_beacon(sdata, params); +	if (err < 0) +		return err; +	ieee80211_bss_info_change_notify(sdata, err); +	return 0;  } -static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) +bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local)  {  	struct ieee80211_sub_if_data *sdata; -	struct beacon_data *old; -	sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	lockdep_assert_held(&local->mtx); -	old = sdata->u.ap.beacon; +	rcu_read_lock(); +	list_for_each_entry_rcu(sdata, &local->interfaces, list) { +		if (!ieee80211_sdata_running(sdata)) +			continue; -	if (!old) -		return -ENOENT; +		if (!sdata->vif.csa_active) +			continue; -	rcu_assign_pointer(sdata->u.ap.beacon, NULL); -	synchronize_rcu(); -	kfree(old); +		if (!sdata->csa_block_tx) +			continue; +		rcu_read_unlock(); +		return true; +	} +	rcu_read_unlock(); + +	return false; +} + +static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_sub_if_data *vlan; +	struct ieee80211_local *local = sdata->local; +	struct beacon_data *old_beacon; +	struct probe_resp *old_probe_resp; +	struct cfg80211_chan_def chandef; + +	sdata_assert_lock(sdata); + +	old_beacon = sdata_dereference(sdata->u.ap.beacon, sdata); +	if (!old_beacon) +		return -ENOENT; +	old_probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata); + +	/* abort any running channel switch */ +	mutex_lock(&local->mtx); +	sdata->vif.csa_active = false; +	if (!ieee80211_csa_needs_block_tx(local)) +		ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA); +	mutex_unlock(&local->mtx); + +	kfree(sdata->u.ap.next_beacon); +	sdata->u.ap.next_beacon = NULL; + +	/* turn off carrier for this interface and dependent VLANs */ +	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) +		netif_carrier_off(vlan->dev); +	netif_carrier_off(dev); + +	/* remove beacon and probe response */ +	RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); +	RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL); +	kfree_rcu(old_beacon, rcu_head); +	if (old_probe_resp) +		kfree_rcu(old_probe_resp, rcu_head); +	sdata->u.ap.driver_smps_mode = IEEE80211_SMPS_OFF; + +	__sta_info_flush(sdata, true); +	ieee80211_free_keys(sdata, true); + +	sdata->vif.bss_conf.enable_beacon = false; +	sdata->vif.bss_conf.ssid_len = 0; +	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);  	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); + +	if (sdata->wdev.cac_started) { +		chandef = sdata->vif.bss_conf.chandef; +		cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); +		cfg80211_cac_event(sdata->dev, &chandef, +				   NL80211_RADAR_CAC_ABORTED, +				   GFP_KERNEL); +	} + +	drv_stop_ap(sdata->local, sdata); + +	/* free all potentially still buffered bcast frames */ +	local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf); +	skb_queue_purge(&sdata->u.ap.ps.bc_buf); + +	mutex_lock(&local->mtx); +	ieee80211_vif_copy_chanctx_to_vlans(sdata, true); +	ieee80211_vif_release_channel(sdata); +	mutex_unlock(&local->mtx); +  	return 0;  } @@ -604,7 +1223,7 @@ static void ieee80211_send_layer2_update(struct sta_info *sta)  	/* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)  	 * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */ -	memset(msg->da, 0xff, ETH_ALEN); +	eth_broadcast_addr(msg->da);  	memcpy(msg->sa, sta->sta.addr, ETH_ALEN);  	msg->len = htons(6);  	msg->dsap = 0; @@ -621,47 +1240,135 @@ static void ieee80211_send_layer2_update(struct sta_info *sta)  	netif_rx_ni(skb);  } -static void sta_apply_parameters(struct ieee80211_local *local, -				 struct sta_info *sta, -				 struct station_parameters *params) +static int sta_apply_auth_flags(struct ieee80211_local *local, +				struct sta_info *sta, +				u32 mask, u32 set)  { -	unsigned long flags; -	u32 rates; -	int i, j; +	int ret; + +	if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) && +	    set & BIT(NL80211_STA_FLAG_AUTHENTICATED) && +	    !test_sta_flag(sta, WLAN_STA_AUTH)) { +		ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); +		if (ret) +			return ret; +	} + +	if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) && +	    set & BIT(NL80211_STA_FLAG_ASSOCIATED) && +	    !test_sta_flag(sta, WLAN_STA_ASSOC)) { +		ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); +		if (ret) +			return ret; +	} + +	if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { +		if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) +			ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); +		else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) +			ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); +		else +			ret = 0; +		if (ret) +			return ret; +	} + +	if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) && +	    !(set & BIT(NL80211_STA_FLAG_ASSOCIATED)) && +	    test_sta_flag(sta, WLAN_STA_ASSOC)) { +		ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); +		if (ret) +			return ret; +	} + +	if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) && +	    !(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) && +	    test_sta_flag(sta, WLAN_STA_AUTH)) { +		ret = sta_info_move_state(sta, IEEE80211_STA_NONE); +		if (ret) +			return ret; +	} + +	return 0; +} + +static int sta_apply_parameters(struct ieee80211_local *local, +				struct sta_info *sta, +				struct station_parameters *params) +{ +	int ret = 0;  	struct ieee80211_supported_band *sband;  	struct ieee80211_sub_if_data *sdata = sta->sdata; +	enum ieee80211_band band = ieee80211_get_sdata_band(sdata);  	u32 mask, set; -	sband = local->hw.wiphy->bands[local->oper_channel->band]; +	sband = local->hw.wiphy->bands[band]; -	spin_lock_irqsave(&sta->flaglock, flags);  	mask = params->sta_flags_mask;  	set = params->sta_flags_set; -	if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { -		sta->flags &= ~WLAN_STA_AUTHORIZED; -		if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) -			sta->flags |= WLAN_STA_AUTHORIZED; +	if (ieee80211_vif_is_mesh(&sdata->vif)) { +		/* +		 * In mesh mode, ASSOCIATED isn't part of the nl80211 +		 * API but must follow AUTHENTICATED for driver state. +		 */ +		if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) +			mask |= BIT(NL80211_STA_FLAG_ASSOCIATED); +		if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) +			set |= BIT(NL80211_STA_FLAG_ASSOCIATED); +	} else if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { +		/* +		 * TDLS -- everything follows authorized, but +		 * only becoming authorized is possible, not +		 * going back +		 */ +		if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) { +			set |= BIT(NL80211_STA_FLAG_AUTHENTICATED) | +			       BIT(NL80211_STA_FLAG_ASSOCIATED); +			mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED) | +				BIT(NL80211_STA_FLAG_ASSOCIATED); +		}  	} +	ret = sta_apply_auth_flags(local, sta, mask, set); +	if (ret) +		return ret; +  	if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) { -		sta->flags &= ~WLAN_STA_SHORT_PREAMBLE;  		if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) -			sta->flags |= WLAN_STA_SHORT_PREAMBLE; +			set_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE); +		else +			clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);  	}  	if (mask & BIT(NL80211_STA_FLAG_WME)) { -		sta->flags &= ~WLAN_STA_WME; -		if (set & BIT(NL80211_STA_FLAG_WME)) -			sta->flags |= WLAN_STA_WME; +		if (set & BIT(NL80211_STA_FLAG_WME)) { +			set_sta_flag(sta, WLAN_STA_WME); +			sta->sta.wme = true; +		} else { +			clear_sta_flag(sta, WLAN_STA_WME); +			sta->sta.wme = false; +		}  	}  	if (mask & BIT(NL80211_STA_FLAG_MFP)) { -		sta->flags &= ~WLAN_STA_MFP;  		if (set & BIT(NL80211_STA_FLAG_MFP)) -			sta->flags |= WLAN_STA_MFP; +			set_sta_flag(sta, WLAN_STA_MFP); +		else +			clear_sta_flag(sta, WLAN_STA_MFP); +	} + +	if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) { +		if (set & BIT(NL80211_STA_FLAG_TDLS_PEER)) +			set_sta_flag(sta, WLAN_STA_TDLS_PEER); +		else +			clear_sta_flag(sta, WLAN_STA_TDLS_PEER); +	} + +	if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) { +		sta->sta.uapsd_queues = params->uapsd_queues; +		sta->sta.max_sp = params->max_sp;  	} -	spin_unlock_irqrestore(&sta->flaglock, flags);  	/*  	 * cfg80211 validates this (1-2007) and allows setting the AID @@ -671,47 +1378,103 @@ static void sta_apply_parameters(struct ieee80211_local *local,  		sta->sta.aid = params->aid;  	/* -	 * FIXME: updating the following information is racy when this -	 *	  function is called from ieee80211_change_station(). -	 *	  However, all this information should be static so -	 *	  maybe we should just reject attemps to change it. +	 * Some of the following updates would be racy if called on an +	 * existing station, via ieee80211_change_station(). However, +	 * all such changes are rejected by cfg80211 except for updates +	 * changing the supported rates on an existing but not yet used +	 * TDLS peer.  	 */  	if (params->listen_interval >= 0)  		sta->listen_interval = params->listen_interval;  	if (params->supported_rates) { -		rates = 0; - -		for (i = 0; i < params->supported_rates_len; i++) { -			int rate = (params->supported_rates[i] & 0x7f) * 5; -			for (j = 0; j < sband->n_bitrates; j++) { -				if (sband->bitrates[j].bitrate == rate) -					rates |= BIT(j); -			} -		} -		sta->sta.supp_rates[local->oper_channel->band] = rates; +		ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef, +					 sband, params->supported_rates, +					 params->supported_rates_len, +					 &sta->sta.supp_rates[band]);  	}  	if (params->ht_capa) -		ieee80211_ht_cap_ie_to_sta_ht_cap(sband, -						  params->ht_capa, -						  &sta->sta.ht_cap); +		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, +						  params->ht_capa, sta); + +	if (params->vht_capa) +		ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, +						    params->vht_capa, sta); + +	if (params->opmode_notif_used) { +		/* returned value is only needed for rc update, but the +		 * rc isn't initialized here yet, so ignore it +		 */ +		__ieee80211_vht_handle_opmode(sdata, sta, +					      params->opmode_notif, +					      band, false); +	} + +	if (ieee80211_vif_is_mesh(&sdata->vif)) { +#ifdef CONFIG_MAC80211_MESH +		u32 changed = 0; + +		if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) { +			switch (params->plink_state) { +			case NL80211_PLINK_ESTAB: +				if (sta->plink_state != NL80211_PLINK_ESTAB) +					changed = mesh_plink_inc_estab_count( +							sdata); +				sta->plink_state = params->plink_state; + +				ieee80211_mps_sta_status_update(sta); +				changed |= ieee80211_mps_set_sta_local_pm(sta, +					      sdata->u.mesh.mshcfg.power_mode); +				break; +			case NL80211_PLINK_LISTEN: +			case NL80211_PLINK_BLOCKED: +			case NL80211_PLINK_OPN_SNT: +			case NL80211_PLINK_OPN_RCVD: +			case NL80211_PLINK_CNF_RCVD: +			case NL80211_PLINK_HOLDING: +				if (sta->plink_state == NL80211_PLINK_ESTAB) +					changed = mesh_plink_dec_estab_count( +							sdata); +				sta->plink_state = params->plink_state; + +				ieee80211_mps_sta_status_update(sta); +				changed |= ieee80211_mps_set_sta_local_pm(sta, +						NL80211_MESH_POWER_UNKNOWN); +				break; +			default: +				/*  nothing  */ +				break; +			} +		} -	if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) {  		switch (params->plink_action) { -		case PLINK_ACTION_OPEN: -			mesh_plink_open(sta); +		case NL80211_PLINK_ACTION_NO_ACTION: +			/* nothing */ +			break; +		case NL80211_PLINK_ACTION_OPEN: +			changed |= mesh_plink_open(sta);  			break; -		case PLINK_ACTION_BLOCK: -			mesh_plink_block(sta); +		case NL80211_PLINK_ACTION_BLOCK: +			changed |= mesh_plink_block(sta);  			break;  		} + +		if (params->local_pm) +			changed |= +			      ieee80211_mps_set_sta_local_pm(sta, +							     params->local_pm); +		ieee80211_mbss_info_change_notify(sdata, changed); +#endif  	} + +	return 0;  }  static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, -				 u8 *mac, struct station_parameters *params) +				 const u8 *mac, +				 struct station_parameters *params)  {  	struct ieee80211_local *local = wiphy_priv(wiphy);  	struct sta_info *sta; @@ -728,7 +1491,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,  	} else  		sdata = IEEE80211_DEV_TO_SUB_IF(dev); -	if (compare_ether_addr(mac, sdata->vif.addr) == 0) +	if (ether_addr_equal(mac, sdata->vif.addr))  		return -EINVAL;  	if (is_multicast_ether_addr(mac)) @@ -738,11 +1501,29 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,  	if (!sta)  		return -ENOMEM; -	sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; +	/* +	 * defaults -- if userspace wants something else we'll +	 * change it accordingly in sta_apply_parameters() +	 */ +	if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) { +		sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); +		sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); +	} else { +		sta->sta.tdls = true; +	} -	sta_apply_parameters(local, sta, params); +	err = sta_apply_parameters(local, sta, params); +	if (err) { +		sta_info_free(local, sta); +		return err; +	} -	rate_control_rate_init(sta); +	/* +	 * for TDLS, rate control should be initialized only when +	 * rates are known and station is marked authorized +	 */ +	if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) +		rate_control_rate_init(sta);  	layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||  		sdata->vif.type == NL80211_IFTYPE_AP; @@ -762,9 +1543,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,  }  static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, -				 u8 *mac) +				 const u8 *mac)  { -	struct ieee80211_local *local = wiphy_priv(wiphy);  	struct ieee80211_sub_if_data *sdata;  	sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -772,65 +1552,141 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,  	if (mac)  		return sta_info_destroy_addr_bss(sdata, mac); -	sta_info_flush(local, sdata); +	sta_info_flush(sdata);  	return 0;  }  static int ieee80211_change_station(struct wiphy *wiphy, -				    struct net_device *dev, -				    u8 *mac, +				    struct net_device *dev, const u8 *mac,  				    struct station_parameters *params)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = wiphy_priv(wiphy);  	struct sta_info *sta;  	struct ieee80211_sub_if_data *vlansdata; +	enum cfg80211_station_type statype; +	int err; -	rcu_read_lock(); +	mutex_lock(&local->sta_mtx);  	sta = sta_info_get_bss(sdata, mac);  	if (!sta) { -		rcu_read_unlock(); -		return -ENOENT; +		err = -ENOENT; +		goto out_err;  	} +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_MESH_POINT: +		if (sdata->u.mesh.user_mpm) +			statype = CFG80211_STA_MESH_PEER_USER; +		else +			statype = CFG80211_STA_MESH_PEER_KERNEL; +		break; +	case NL80211_IFTYPE_ADHOC: +		statype = CFG80211_STA_IBSS; +		break; +	case NL80211_IFTYPE_STATION: +		if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { +			statype = CFG80211_STA_AP_STA; +			break; +		} +		if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) +			statype = CFG80211_STA_TDLS_PEER_ACTIVE; +		else +			statype = CFG80211_STA_TDLS_PEER_SETUP; +		break; +	case NL80211_IFTYPE_AP: +	case NL80211_IFTYPE_AP_VLAN: +		statype = CFG80211_STA_AP_CLIENT; +		break; +	default: +		err = -EOPNOTSUPP; +		goto out_err; +	} + +	err = cfg80211_check_station_change(wiphy, params, statype); +	if (err) +		goto out_err; +  	if (params->vlan && params->vlan != sta->sdata->dev) { -		vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); +		bool prev_4addr = false; +		bool new_4addr = false; -		if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN && -		    vlansdata->vif.type != NL80211_IFTYPE_AP) { -			rcu_read_unlock(); -			return -EINVAL; -		} +		vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);  		if (params->vlan->ieee80211_ptr->use_4addr) {  			if (vlansdata->u.vlan.sta) { -				rcu_read_unlock(); -				return -EBUSY; +				err = -EBUSY; +				goto out_err;  			}  			rcu_assign_pointer(vlansdata->u.vlan.sta, sta); +			new_4addr = true; +		} + +		if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && +		    sta->sdata->u.vlan.sta) { +			RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL); +			prev_4addr = true;  		}  		sta->sdata = vlansdata; + +		if (sta->sta_state == IEEE80211_STA_AUTHORIZED && +		    prev_4addr != new_4addr) { +			if (new_4addr) +				atomic_dec(&sta->sdata->bss->num_mcast_sta); +			else +				atomic_inc(&sta->sdata->bss->num_mcast_sta); +		} +  		ieee80211_send_layer2_update(sta);  	} -	sta_apply_parameters(local, sta, params); +	err = sta_apply_parameters(local, sta, params); +	if (err) +		goto out_err; + +	/* When peer becomes authorized, init rate control as well */ +	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && +	    test_sta_flag(sta, WLAN_STA_AUTHORIZED)) +		rate_control_rate_init(sta); + +	mutex_unlock(&local->sta_mtx); + +	if ((sdata->vif.type == NL80211_IFTYPE_AP || +	     sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && +	    sta->known_smps_mode != sta->sdata->bss->req_smps && +	    test_sta_flag(sta, WLAN_STA_AUTHORIZED) && +	    sta_info_tx_streams(sta) != 1) { +		ht_dbg(sta->sdata, +		       "%pM just authorized and MIMO capable - update SMPS\n", +		       sta->sta.addr); +		ieee80211_send_smps_action(sta->sdata, +			sta->sdata->bss->req_smps, +			sta->sta.addr, +			sta->sdata->vif.bss_conf.bssid); +	} -	rcu_read_unlock(); +	if (sdata->vif.type == NL80211_IFTYPE_STATION && +	    params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { +		ieee80211_recalc_ps(local, -1); +		ieee80211_recalc_ps_vif(sdata); +	}  	return 0; +out_err: +	mutex_unlock(&local->sta_mtx); +	return err;  }  #ifdef CONFIG_MAC80211_MESH  static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, -				 u8 *dst, u8 *next_hop) +			       const u8 *dst, const u8 *next_hop)  {  	struct ieee80211_sub_if_data *sdata;  	struct mesh_path *mpath;  	struct sta_info *sta; -	int err;  	sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -841,17 +1697,12 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,  		return -ENOENT;  	} -	err = mesh_path_add(dst, sdata); -	if (err) { +	mpath = mesh_path_add(sdata, dst); +	if (IS_ERR(mpath)) {  		rcu_read_unlock(); -		return err; +		return PTR_ERR(mpath);  	} -	mpath = mesh_path_lookup(dst, sdata); -	if (!mpath) { -		rcu_read_unlock(); -		return -ENXIO; -	}  	mesh_path_fix_nexthop(mpath, sta);  	rcu_read_unlock(); @@ -859,20 +1710,19 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,  }  static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev, -				 u8 *dst) +			       const u8 *dst)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	if (dst) -		return mesh_path_del(dst, sdata); +		return mesh_path_del(sdata, dst); -	mesh_path_flush(sdata); +	mesh_path_flush_by_iface(sdata);  	return 0;  } -static int ieee80211_change_mpath(struct wiphy *wiphy, -				    struct net_device *dev, -				    u8 *dst, u8 *next_hop) +static int ieee80211_change_mpath(struct wiphy *wiphy, struct net_device *dev, +				  const u8 *dst, const u8 *next_hop)  {  	struct ieee80211_sub_if_data *sdata;  	struct mesh_path *mpath; @@ -888,7 +1738,7 @@ static int ieee80211_change_mpath(struct wiphy *wiphy,  		return -ENOENT;  	} -	mpath = mesh_path_lookup(dst, sdata); +	mpath = mesh_path_lookup(sdata, dst);  	if (!mpath) {  		rcu_read_unlock();  		return -ENOENT; @@ -903,11 +1753,15 @@ static int ieee80211_change_mpath(struct wiphy *wiphy,  static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,  			    struct mpath_info *pinfo)  { -	if (mpath->next_hop) -		memcpy(next_hop, mpath->next_hop->sta.addr, ETH_ALEN); +	struct sta_info *next_hop_sta = rcu_dereference(mpath->next_hop); + +	if (next_hop_sta) +		memcpy(next_hop, next_hop_sta->sta.addr, ETH_ALEN);  	else  		memset(next_hop, 0, ETH_ALEN); +	memset(pinfo, 0, sizeof(*pinfo)); +  	pinfo->generation = mesh_paths_generation;  	pinfo->filled = MPATH_INFO_FRAME_QLEN | @@ -926,7 +1780,6 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,  	pinfo->discovery_timeout =  			jiffies_to_msecs(mpath->discovery_timeout);  	pinfo->discovery_retries = mpath->discovery_retries; -	pinfo->flags = 0;  	if (mpath->flags & MESH_PATH_ACTIVE)  		pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE;  	if (mpath->flags & MESH_PATH_RESOLVING) @@ -935,10 +1788,8 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,  		pinfo->flags |= NL80211_MPATH_FLAG_SN_VALID;  	if (mpath->flags & MESH_PATH_FIXED)  		pinfo->flags |= NL80211_MPATH_FLAG_FIXED; -	if (mpath->flags & MESH_PATH_RESOLVING) -		pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING; - -	pinfo->flags = mpath->flags; +	if (mpath->flags & MESH_PATH_RESOLVED) +		pinfo->flags |= NL80211_MPATH_FLAG_RESOLVED;  }  static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev, @@ -951,7 +1802,7 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,  	sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	rcu_read_lock(); -	mpath = mesh_path_lookup(dst, sdata); +	mpath = mesh_path_lookup(sdata, dst);  	if (!mpath) {  		rcu_read_unlock();  		return -ENOENT; @@ -963,8 +1814,8 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,  }  static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev, -				 int idx, u8 *dst, u8 *next_hop, -				 struct mpath_info *pinfo) +				int idx, u8 *dst, u8 *next_hop, +				struct mpath_info *pinfo)  {  	struct ieee80211_sub_if_data *sdata;  	struct mesh_path *mpath; @@ -972,7 +1823,7 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,  	sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	rcu_read_lock(); -	mpath = mesh_path_lookup_by_idx(idx, sdata); +	mpath = mesh_path_lookup_by_idx(sdata, idx);  	if (!mpath) {  		rcu_read_unlock();  		return -ENOENT; @@ -983,7 +1834,7 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,  	return 0;  } -static int ieee80211_get_mesh_params(struct wiphy *wiphy, +static int ieee80211_get_mesh_config(struct wiphy *wiphy,  				struct net_device *dev,  				struct mesh_config *conf)  { @@ -999,9 +1850,56 @@ static inline bool _chg_mesh_attr(enum nl80211_meshconf_params parm, u32 mask)  	return (mask >> (parm-1)) & 0x1;  } -static int ieee80211_set_mesh_params(struct wiphy *wiphy, -				struct net_device *dev, -				const struct mesh_config *nconf, u32 mask) +static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, +		const struct mesh_setup *setup) +{ +	u8 *new_ie; +	const u8 *old_ie; +	struct ieee80211_sub_if_data *sdata = container_of(ifmsh, +					struct ieee80211_sub_if_data, u.mesh); + +	/* allocate information elements */ +	new_ie = NULL; +	old_ie = ifmsh->ie; + +	if (setup->ie_len) { +		new_ie = kmemdup(setup->ie, setup->ie_len, +				GFP_KERNEL); +		if (!new_ie) +			return -ENOMEM; +	} +	ifmsh->ie_len = setup->ie_len; +	ifmsh->ie = new_ie; +	kfree(old_ie); + +	/* now copy the rest of the setup parameters */ +	ifmsh->mesh_id_len = setup->mesh_id_len; +	memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len); +	ifmsh->mesh_sp_id = setup->sync_method; +	ifmsh->mesh_pp_id = setup->path_sel_proto; +	ifmsh->mesh_pm_id = setup->path_metric; +	ifmsh->user_mpm = setup->user_mpm; +	ifmsh->mesh_auth_id = setup->auth_id; +	ifmsh->security = IEEE80211_MESH_SEC_NONE; +	if (setup->is_authenticated) +		ifmsh->security |= IEEE80211_MESH_SEC_AUTHED; +	if (setup->is_secure) +		ifmsh->security |= IEEE80211_MESH_SEC_SECURED; + +	/* mcast rate setting in Mesh Node */ +	memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate, +						sizeof(setup->mcast_rate)); +	sdata->vif.bss_conf.basic_rates = setup->basic_rates; + +	sdata->vif.bss_conf.beacon_int = setup->beacon_interval; +	sdata->vif.bss_conf.dtim_period = setup->dtim_period; + +	return 0; +} + +static int ieee80211_update_mesh_config(struct wiphy *wiphy, +					struct net_device *dev, u32 mask, +					const struct mesh_config *nconf)  {  	struct mesh_config *conf;  	struct ieee80211_sub_if_data *sdata; @@ -1024,8 +1922,16 @@ static int ieee80211_set_mesh_params(struct wiphy *wiphy,  		conf->dot11MeshMaxRetries = nconf->dot11MeshMaxRetries;  	if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask))  		conf->dot11MeshTTL = nconf->dot11MeshTTL; -	if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) +	if (_chg_mesh_attr(NL80211_MESHCONF_ELEMENT_TTL, mask)) +		conf->element_ttl = nconf->element_ttl; +	if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) { +		if (ifmsh->user_mpm) +			return -EBUSY;  		conf->auto_open_plinks = nconf->auto_open_plinks; +	} +	if (_chg_mesh_attr(NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, mask)) +		conf->dot11MeshNbrOffsetMaxNeighbor = +			nconf->dot11MeshNbrOffsetMaxNeighbor;  	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, mask))  		conf->dot11MeshHWMPmaxPREQretries =  			nconf->dot11MeshHWMPmaxPREQretries; @@ -1039,6 +1945,9 @@ static int ieee80211_set_mesh_params(struct wiphy *wiphy,  	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, mask))  		conf->dot11MeshHWMPpreqMinInterval =  			nconf->dot11MeshHWMPpreqMinInterval; +	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, mask)) +		conf->dot11MeshHWMPperrMinInterval = +			nconf->dot11MeshHWMPperrMinInterval;  	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,  			   mask))  		conf->dot11MeshHWMPnetDiameterTraversalTime = @@ -1047,19 +1956,110 @@ static int ieee80211_set_mesh_params(struct wiphy *wiphy,  		conf->dot11MeshHWMPRootMode = nconf->dot11MeshHWMPRootMode;  		ieee80211_mesh_root_setup(ifmsh);  	} +	if (_chg_mesh_attr(NL80211_MESHCONF_GATE_ANNOUNCEMENTS, mask)) { +		/* our current gate announcement implementation rides on root +		 * announcements, so require this ifmsh to also be a root node +		 * */ +		if (nconf->dot11MeshGateAnnouncementProtocol && +		    !(conf->dot11MeshHWMPRootMode > IEEE80211_ROOTMODE_ROOT)) { +			conf->dot11MeshHWMPRootMode = IEEE80211_PROACTIVE_RANN; +			ieee80211_mesh_root_setup(ifmsh); +		} +		conf->dot11MeshGateAnnouncementProtocol = +			nconf->dot11MeshGateAnnouncementProtocol; +	} +	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_RANN_INTERVAL, mask)) +		conf->dot11MeshHWMPRannInterval = +			nconf->dot11MeshHWMPRannInterval; +	if (_chg_mesh_attr(NL80211_MESHCONF_FORWARDING, mask)) +		conf->dot11MeshForwarding = nconf->dot11MeshForwarding; +	if (_chg_mesh_attr(NL80211_MESHCONF_RSSI_THRESHOLD, mask)) { +		/* our RSSI threshold implementation is supported only for +		 * devices that report signal in dBm. +		 */ +		if (!(sdata->local->hw.flags & IEEE80211_HW_SIGNAL_DBM)) +			return -ENOTSUPP; +		conf->rssi_threshold = nconf->rssi_threshold; +	} +	if (_chg_mesh_attr(NL80211_MESHCONF_HT_OPMODE, mask)) { +		conf->ht_opmode = nconf->ht_opmode; +		sdata->vif.bss_conf.ht_operation_mode = nconf->ht_opmode; +		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT); +	} +	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, mask)) +		conf->dot11MeshHWMPactivePathToRootTimeout = +			nconf->dot11MeshHWMPactivePathToRootTimeout; +	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ROOT_INTERVAL, mask)) +		conf->dot11MeshHWMProotInterval = +			nconf->dot11MeshHWMProotInterval; +	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, mask)) +		conf->dot11MeshHWMPconfirmationInterval = +			nconf->dot11MeshHWMPconfirmationInterval; +	if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask)) { +		conf->power_mode = nconf->power_mode; +		ieee80211_mps_local_status_update(sdata); +	} +	if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask)) +		conf->dot11MeshAwakeWindowDuration = +			nconf->dot11MeshAwakeWindowDuration; +	if (_chg_mesh_attr(NL80211_MESHCONF_PLINK_TIMEOUT, mask)) +		conf->plink_timeout = nconf->plink_timeout; +	ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);  	return 0;  } +static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, +			       const struct mesh_config *conf, +			       const struct mesh_setup *setup) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	int err; + +	memcpy(&ifmsh->mshcfg, conf, sizeof(struct mesh_config)); +	err = copy_mesh_setup(ifmsh, setup); +	if (err) +		return err; + +	/* can mesh use other SMPS modes? */ +	sdata->smps_mode = IEEE80211_SMPS_OFF; +	sdata->needed_rx_chains = sdata->local->rx_chains; + +	mutex_lock(&sdata->local->mtx); +	err = ieee80211_vif_use_channel(sdata, &setup->chandef, +					IEEE80211_CHANCTX_SHARED); +	mutex_unlock(&sdata->local->mtx); +	if (err) +		return err; + +	return ieee80211_start_mesh(sdata); +} + +static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + +	ieee80211_stop_mesh(sdata); +	mutex_lock(&sdata->local->mtx); +	ieee80211_vif_release_channel(sdata); +	mutex_unlock(&sdata->local->mtx); + +	return 0; +}  #endif  static int ieee80211_change_bss(struct wiphy *wiphy,  				struct net_device *dev,  				struct bss_parameters *params)  { -	struct ieee80211_sub_if_data *sdata; +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	enum ieee80211_band band;  	u32 changed = 0; -	sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	if (!sdata_dereference(sdata->u.ap.beacon, sdata)) +		return -ENOENT; + +	band = ieee80211_get_sdata_band(sdata);  	if (params->use_cts_prot >= 0) {  		sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot; @@ -1072,7 +2072,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,  	}  	if (!sdata->vif.bss_conf.use_short_slot && -	    sdata->local->hw.conf.channel->band == IEEE80211_BAND_5GHZ) { +	    band == IEEE80211_BAND_5GHZ) {  		sdata->vif.bss_conf.use_short_slot = true;  		changed |= BSS_CHANGED_ERP_SLOT;  	} @@ -1084,20 +2084,11 @@ static int ieee80211_change_bss(struct wiphy *wiphy,  	}  	if (params->basic_rates) { -		int i, j; -		u32 rates = 0; -		struct ieee80211_local *local = wiphy_priv(wiphy); -		struct ieee80211_supported_band *sband = -			wiphy->bands[local->oper_channel->band]; - -		for (i = 0; i < params->basic_rates_len; i++) { -			int rate = (params->basic_rates[i] & 0x7f) * 5; -			for (j = 0; j < sband->n_bitrates; j++) { -				if (sband->bitrates[j].bitrate == rate) -					rates |= BIT(j); -			} -		} -		sdata->vif.bss_conf.basic_rates = rates; +		ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef, +					 wiphy->bands[band], +					 params->basic_rates, +					 params->basic_rates_len, +					 &sdata->vif.bss_conf.basic_rates);  		changed |= BSS_CHANGED_BASIC_RATES;  	} @@ -1108,20 +2099,49 @@ static int ieee80211_change_bss(struct wiphy *wiphy,  			sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS;  	} +	if (params->ht_opmode >= 0) { +		sdata->vif.bss_conf.ht_operation_mode = +			(u16) params->ht_opmode; +		changed |= BSS_CHANGED_HT; +	} + +	if (params->p2p_ctwindow >= 0) { +		sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow &= +					~IEEE80211_P2P_OPPPS_CTWINDOW_MASK; +		sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |= +			params->p2p_ctwindow & IEEE80211_P2P_OPPPS_CTWINDOW_MASK; +		changed |= BSS_CHANGED_P2P_PS; +	} + +	if (params->p2p_opp_ps > 0) { +		sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |= +					IEEE80211_P2P_OPPPS_ENABLE_BIT; +		changed |= BSS_CHANGED_P2P_PS; +	} else if (params->p2p_opp_ps == 0) { +		sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow &= +					~IEEE80211_P2P_OPPPS_ENABLE_BIT; +		changed |= BSS_CHANGED_P2P_PS; +	} +  	ieee80211_bss_info_change_notify(sdata, changed);  	return 0;  }  static int ieee80211_set_txq_params(struct wiphy *wiphy, +				    struct net_device *dev,  				    struct ieee80211_txq_params *params)  {  	struct ieee80211_local *local = wiphy_priv(wiphy); +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_tx_queue_params p;  	if (!local->ops->conf_tx)  		return -EOPNOTSUPP; +	if (local->hw.queues < IEEE80211_NUM_ACS) +		return -EOPNOTSUPP; +  	memset(&p, 0, sizeof(p));  	p.aifs = params->aifs;  	p.cw_max = params->cwmax; @@ -1134,56 +2154,24 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy,  	 */  	p.uapsd = false; -	if (drv_conf_tx(local, params->queue, &p)) { +	sdata->tx_conf[params->ac] = p; +	if (drv_conf_tx(local, sdata, params->ac, &p)) {  		wiphy_debug(local->hw.wiphy, -			    "failed to set TX queue parameters for queue %d\n", -			    params->queue); +			    "failed to set TX queue parameters for AC %d\n", +			    params->ac);  		return -EINVAL;  	} -	return 0; -} - -static int ieee80211_set_channel(struct wiphy *wiphy, -				 struct net_device *netdev, -				 struct ieee80211_channel *chan, -				 enum nl80211_channel_type channel_type) -{ -	struct ieee80211_local *local = wiphy_priv(wiphy); -	struct ieee80211_sub_if_data *sdata = NULL; - -	if (netdev) -		sdata = IEEE80211_DEV_TO_SUB_IF(netdev); - -	switch (ieee80211_get_channel_mode(local, NULL)) { -	case CHAN_MODE_HOPPING: -		return -EBUSY; -	case CHAN_MODE_FIXED: -		if (local->oper_channel != chan) -			return -EBUSY; -		if (!sdata && local->_oper_channel_type == channel_type) -			return 0; -		break; -	case CHAN_MODE_UNDEFINED: -		break; -	} - -	local->oper_channel = chan; - -	if (!ieee80211_set_channel_type(local, sdata, channel_type)) -		return -EBUSY; - -	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); -	if (sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR) -		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT); +	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS);  	return 0;  }  #ifdef CONFIG_PM -static int ieee80211_suspend(struct wiphy *wiphy) +static int ieee80211_suspend(struct wiphy *wiphy, +			     struct cfg80211_wowlan *wowlan)  { -	return __ieee80211_suspend(wiphy_priv(wiphy)); +	return __ieee80211_suspend(wiphy_priv(wiphy), wowlan);  }  static int ieee80211_resume(struct wiphy *wiphy) @@ -1196,24 +2184,38 @@ static int ieee80211_resume(struct wiphy *wiphy)  #endif  static int ieee80211_scan(struct wiphy *wiphy, -			  struct net_device *dev,  			  struct cfg80211_scan_request *req)  { -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_sub_if_data *sdata; + +	sdata = IEEE80211_WDEV_TO_SUB_IF(req->wdev);  	switch (ieee80211_vif_type_p2p(&sdata->vif)) {  	case NL80211_IFTYPE_STATION:  	case NL80211_IFTYPE_ADHOC:  	case NL80211_IFTYPE_MESH_POINT:  	case NL80211_IFTYPE_P2P_CLIENT: +	case NL80211_IFTYPE_P2P_DEVICE:  		break;  	case NL80211_IFTYPE_P2P_GO:  		if (sdata->local->ops->hw_scan)  			break; -		/* FIXME: implement NoA while scanning in software */ -		return -EOPNOTSUPP; +		/* +		 * FIXME: implement NoA while scanning in software, +		 * for now fall through to allow scanning only when +		 * beaconing hasn't been configured yet +		 */  	case NL80211_IFTYPE_AP: -		if (sdata->u.ap.beacon) +		/* +		 * If the scan has been forced (and the driver supports +		 * forcing), don't care about being beaconing already. +		 * This will create problems to the attached stations (e.g. all +		 * the  frames sent while scanning on other channel will be +		 * lost) +		 */ +		if (sdata->u.ap.beacon && +		    (!(wiphy->features & NL80211_FEATURE_AP_SCAN) || +		     !(req->flags & NL80211_SCAN_FLAG_AP)))  			return -EOPNOTSUPP;  		break;  	default: @@ -1223,6 +2225,30 @@ static int ieee80211_scan(struct wiphy *wiphy,  	return ieee80211_request_scan(sdata, req);  } +static int +ieee80211_sched_scan_start(struct wiphy *wiphy, +			   struct net_device *dev, +			   struct cfg80211_sched_scan_request *req) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + +	if (!sdata->local->ops->sched_scan_start) +		return -EOPNOTSUPP; + +	return ieee80211_request_sched_scan_start(sdata, req); +} + +static int +ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + +	if (!sdata->local->ops->sched_scan_stop) +		return -EOPNOTSUPP; + +	return ieee80211_request_sched_scan_stop(sdata); +} +  static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,  			  struct cfg80211_auth_request *req)  { @@ -1232,66 +2258,41 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,  static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,  			   struct cfg80211_assoc_request *req)  { -	struct ieee80211_local *local = wiphy_priv(wiphy); -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - -	switch (ieee80211_get_channel_mode(local, sdata)) { -	case CHAN_MODE_HOPPING: -		return -EBUSY; -	case CHAN_MODE_FIXED: -		if (local->oper_channel == req->bss->channel) -			break; -		return -EBUSY; -	case CHAN_MODE_UNDEFINED: -		break; -	} -  	return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req);  }  static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev, -			    struct cfg80211_deauth_request *req, -			    void *cookie) +			    struct cfg80211_deauth_request *req)  { -	return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), -				    req, cookie); +	return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), req);  }  static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, -			      struct cfg80211_disassoc_request *req, -			      void *cookie) +			      struct cfg80211_disassoc_request *req)  { -	return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), -				      req, cookie); +	return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), req);  }  static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,  			       struct cfg80211_ibss_params *params)  { -	struct ieee80211_local *local = wiphy_priv(wiphy); -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - -	switch (ieee80211_get_channel_mode(local, sdata)) { -	case CHAN_MODE_HOPPING: -		return -EBUSY; -	case CHAN_MODE_FIXED: -		if (!params->channel_fixed) -			return -EBUSY; -		if (local->oper_channel == params->channel) -			break; -		return -EBUSY; -	case CHAN_MODE_UNDEFINED: -		break; -	} - -	return ieee80211_ibss_join(sdata, params); +	return ieee80211_ibss_join(IEEE80211_DEV_TO_SUB_IF(dev), params);  }  static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)  { +	return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev)); +} + +static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev, +				    int rate[IEEE80211_NUM_BANDS]) +{  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); -	return ieee80211_ibss_leave(sdata); +	memcpy(sdata->vif.bss_conf.mcast_rate, rate, +	       sizeof(int) * IEEE80211_NUM_BANDS); + +	return 0;  }  static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) @@ -1320,10 +2321,16 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)  			return err;  	} -	if (changed & WIPHY_PARAM_RETRY_SHORT) +	if (changed & WIPHY_PARAM_RETRY_SHORT) { +		if (wiphy->retry_short > IEEE80211_MAX_TX_RETRY) +			return -EINVAL;  		local->hw.conf.short_frame_max_tx_count = wiphy->retry_short; -	if (changed & WIPHY_PARAM_RETRY_LONG) +	} +	if (changed & WIPHY_PARAM_RETRY_LONG) { +		if (wiphy->retry_long > IEEE80211_MAX_TX_RETRY) +			return -EINVAL;  		local->hw.conf.long_frame_max_tx_count = wiphy->retry_long; +	}  	if (changed &  	    (WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG))  		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS); @@ -1332,41 +2339,65 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)  }  static int ieee80211_set_tx_power(struct wiphy *wiphy, +				  struct wireless_dev *wdev,  				  enum nl80211_tx_power_setting type, int mbm)  {  	struct ieee80211_local *local = wiphy_priv(wiphy); -	struct ieee80211_channel *chan = local->hw.conf.channel; -	u32 changes = 0; +	struct ieee80211_sub_if_data *sdata; + +	if (wdev) { +		sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + +		switch (type) { +		case NL80211_TX_POWER_AUTOMATIC: +			sdata->user_power_level = IEEE80211_UNSET_POWER_LEVEL; +			break; +		case NL80211_TX_POWER_LIMITED: +		case NL80211_TX_POWER_FIXED: +			if (mbm < 0 || (mbm % 100)) +				return -EOPNOTSUPP; +			sdata->user_power_level = MBM_TO_DBM(mbm); +			break; +		} + +		ieee80211_recalc_txpower(sdata); + +		return 0; +	}  	switch (type) {  	case NL80211_TX_POWER_AUTOMATIC: -		local->user_power_level = -1; +		local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;  		break;  	case NL80211_TX_POWER_LIMITED: -		if (mbm < 0 || (mbm % 100)) -			return -EOPNOTSUPP; -		local->user_power_level = MBM_TO_DBM(mbm); -		break;  	case NL80211_TX_POWER_FIXED:  		if (mbm < 0 || (mbm % 100))  			return -EOPNOTSUPP; -		/* TODO: move to cfg80211 when it knows the channel */ -		if (MBM_TO_DBM(mbm) > chan->max_power) -			return -EINVAL;  		local->user_power_level = MBM_TO_DBM(mbm);  		break;  	} -	ieee80211_hw_config(local, changes); +	mutex_lock(&local->iflist_mtx); +	list_for_each_entry(sdata, &local->interfaces, list) +		sdata->user_power_level = local->user_power_level; +	list_for_each_entry(sdata, &local->interfaces, list) +		ieee80211_recalc_txpower(sdata); +	mutex_unlock(&local->iflist_mtx);  	return 0;  } -static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm) +static int ieee80211_get_tx_power(struct wiphy *wiphy, +				  struct wireless_dev *wdev, +				  int *dbm)  {  	struct ieee80211_local *local = wiphy_priv(wiphy); +	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); -	*dbm = local->hw.conf.power_level; +	if (!local->use_chanctx) +		*dbm = local->hw.conf.power_level; +	else +		*dbm = sdata->vif.bss_conf.txpower;  	return 0;  } @@ -1389,24 +2420,137 @@ static void ieee80211_rfkill_poll(struct wiphy *wiphy)  }  #ifdef CONFIG_NL80211_TESTMODE -static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len) +static int ieee80211_testmode_cmd(struct wiphy *wiphy, +				  struct wireless_dev *wdev, +				  void *data, int len)  {  	struct ieee80211_local *local = wiphy_priv(wiphy); +	struct ieee80211_vif *vif = NULL;  	if (!local->ops->testmode_cmd)  		return -EOPNOTSUPP; -	return local->ops->testmode_cmd(&local->hw, data, len); +	if (wdev) { +		struct ieee80211_sub_if_data *sdata; + +		sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); +		if (sdata->flags & IEEE80211_SDATA_IN_DRIVER) +			vif = &sdata->vif; +	} + +	return local->ops->testmode_cmd(&local->hw, vif, data, len); +} + +static int ieee80211_testmode_dump(struct wiphy *wiphy, +				   struct sk_buff *skb, +				   struct netlink_callback *cb, +				   void *data, int len) +{ +	struct ieee80211_local *local = wiphy_priv(wiphy); + +	if (!local->ops->testmode_dump) +		return -EOPNOTSUPP; + +	return local->ops->testmode_dump(&local->hw, skb, cb, data, len);  }  #endif -int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, -			     enum ieee80211_smps_mode smps_mode) +int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, +				enum ieee80211_smps_mode smps_mode) +{ +	struct sta_info *sta; +	enum ieee80211_smps_mode old_req; +	int i; + +	if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP)) +		return -EINVAL; + +	if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) +		return 0; + +	old_req = sdata->u.ap.req_smps; +	sdata->u.ap.req_smps = smps_mode; + +	/* AUTOMATIC doesn't mean much for AP - don't allow it */ +	if (old_req == smps_mode || +	    smps_mode == IEEE80211_SMPS_AUTOMATIC) +		return 0; + +	 /* If no associated stations, there's no need to do anything */ +	if (!atomic_read(&sdata->u.ap.num_mcast_sta)) { +		sdata->smps_mode = smps_mode; +		ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps); +		return 0; +	} + +	ht_dbg(sdata, +	       "SMSP %d requested in AP mode, sending Action frame to %d stations\n", +	       smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta)); + +	mutex_lock(&sdata->local->sta_mtx); +	for (i = 0; i < STA_HASH_SIZE; i++) { +		for (sta = rcu_dereference_protected(sdata->local->sta_hash[i], +				lockdep_is_held(&sdata->local->sta_mtx)); +		     sta; +		     sta = rcu_dereference_protected(sta->hnext, +				lockdep_is_held(&sdata->local->sta_mtx))) { +			/* +			 * Only stations associated to our AP and +			 * associated VLANs +			 */ +			if (sta->sdata->bss != &sdata->u.ap) +				continue; + +			/* This station doesn't support MIMO - skip it */ +			if (sta_info_tx_streams(sta) == 1) +				continue; + +			/* +			 * Don't wake up a STA just to send the action frame +			 * unless we are getting more restrictive. +			 */ +			if (test_sta_flag(sta, WLAN_STA_PS_STA) && +			    !ieee80211_smps_is_restrictive(sta->known_smps_mode, +							   smps_mode)) { +				ht_dbg(sdata, +				       "Won't send SMPS to sleeping STA %pM\n", +				       sta->sta.addr); +				continue; +			} + +			/* +			 * If the STA is not authorized, wait until it gets +			 * authorized and the action frame will be sent then. +			 */ +			if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED)) +				continue; + +			ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr); +			ieee80211_send_smps_action(sdata, smps_mode, +						   sta->sta.addr, +						   sdata->vif.bss_conf.bssid); +		} +	} +	mutex_unlock(&sdata->local->sta_mtx); + +	sdata->smps_mode = smps_mode; +	ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps); + +	return 0; +} + +int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, +				 enum ieee80211_smps_mode smps_mode)  {  	const u8 *ap;  	enum ieee80211_smps_mode old_req;  	int err; +	lockdep_assert_held(&sdata->wdev.mtx); + +	if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) +		return -EINVAL; +  	old_req = sdata->u.mgd.req_smps;  	sdata->u.mgd.req_smps = smps_mode; @@ -1416,15 +2560,12 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,  	/*  	 * If not associated, or current association is not an HT -	 * association, there's no need to send an action frame. +	 * association, there's no need to do anything, just store +	 * the new value until we associate.  	 */  	if (!sdata->u.mgd.associated || -	    sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) { -		mutex_lock(&sdata->local->iflist_mtx); -		ieee80211_recalc_smps(sdata->local); -		mutex_unlock(&sdata->local->iflist_mtx); +	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)  		return 0; -	}  	ap = sdata->u.mgd.associated->bssid; @@ -1464,14 +2605,15 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,  	local->dynamic_ps_forced_timeout = timeout;  	/* no change, but if automatic follow powersave */ -	mutex_lock(&sdata->u.mgd.mtx); -	__ieee80211_request_smps(sdata, sdata->u.mgd.req_smps); -	mutex_unlock(&sdata->u.mgd.mtx); +	sdata_lock(sdata); +	__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps); +	sdata_unlock(sdata);  	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)  		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);  	ieee80211_recalc_ps(local, -1); +	ieee80211_recalc_ps_vif(sdata);  	return 0;  } @@ -1481,7 +2623,6 @@ static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,  					 s32 rssi_thold, u32 rssi_hyst)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); -	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);  	struct ieee80211_vif *vif = &sdata->vif;  	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; @@ -1492,14 +2633,9 @@ static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,  	bss_conf->cqm_rssi_thold = rssi_thold;  	bss_conf->cqm_rssi_hyst = rssi_hyst; -	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) { -		if (sdata->vif.type != NL80211_IFTYPE_STATION) -			return -EOPNOTSUPP; -		return 0; -	} -  	/* tell the driver upon association, unless already associated */ -	if (sdata->u.mgd.associated) +	if (sdata->u.mgd.associated && +	    sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)  		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM);  	return 0; @@ -1512,75 +2648,807 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); -	int i; +	int i, ret; + +	if (!ieee80211_sdata_running(sdata)) +		return -ENETDOWN; + +	if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) { +		ret = drv_set_bitrate_mask(local, sdata, mask); +		if (ret) +			return ret; +	} + +	for (i = 0; i < IEEE80211_NUM_BANDS; i++) { +		struct ieee80211_supported_band *sband = wiphy->bands[i]; +		int j; + +		sdata->rc_rateidx_mask[i] = mask->control[i].legacy; +		memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs, +		       sizeof(mask->control[i].ht_mcs)); + +		sdata->rc_has_mcs_mask[i] = false; +		if (!sband) +			continue; + +		for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++) +			if (~sdata->rc_rateidx_mcs_mask[i][j]) { +				sdata->rc_has_mcs_mask[i] = true; +				break; +			} +	} + +	return 0; +} + +static int ieee80211_start_roc_work(struct ieee80211_local *local, +				    struct ieee80211_sub_if_data *sdata, +				    struct ieee80211_channel *channel, +				    unsigned int duration, u64 *cookie, +				    struct sk_buff *txskb, +				    enum ieee80211_roc_type type) +{ +	struct ieee80211_roc_work *roc, *tmp; +	bool queued = false; +	int ret; + +	lockdep_assert_held(&local->mtx); + +	if (local->use_chanctx && !local->ops->remain_on_channel) +		return -EOPNOTSUPP; + +	roc = kzalloc(sizeof(*roc), GFP_KERNEL); +	if (!roc) +		return -ENOMEM;  	/* -	 * This _could_ be supported by providing a hook for -	 * drivers for this function, but at this point it -	 * doesn't seem worth bothering. +	 * If the duration is zero, then the driver +	 * wouldn't actually do anything. Set it to +	 * 10 for now. +	 * +	 * TODO: cancel the off-channel operation +	 *       when we get the SKB's TX status and +	 *       the wait time was zero before.  	 */ -	if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) -		return -EOPNOTSUPP; +	if (!duration) +		duration = 10; + +	roc->chan = channel; +	roc->duration = duration; +	roc->req_duration = duration; +	roc->frame = txskb; +	roc->type = type; +	roc->mgmt_tx_cookie = (unsigned long)txskb; +	roc->sdata = sdata; +	INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work); +	INIT_LIST_HEAD(&roc->dependents); +	/* +	 * cookie is either the roc cookie (for normal roc) +	 * or the SKB (for mgmt TX) +	 */ +	if (!txskb) { +		/* local->mtx protects this */ +		local->roc_cookie_counter++; +		roc->cookie = local->roc_cookie_counter; +		/* wow, you wrapped 64 bits ... more likely a bug */ +		if (WARN_ON(roc->cookie == 0)) { +			roc->cookie = 1; +			local->roc_cookie_counter++; +		} +		*cookie = roc->cookie; +	} else { +		*cookie = (unsigned long)txskb; +	} -	for (i = 0; i < IEEE80211_NUM_BANDS; i++) -		sdata->rc_rateidx_mask[i] = mask->control[i].legacy; +	/* if there's one pending or we're scanning, queue this one */ +	if (!list_empty(&local->roc_list) || +	    local->scanning || local->radar_detect_enabled) +		goto out_check_combine; + +	/* if not HW assist, just queue & schedule work */ +	if (!local->ops->remain_on_channel) { +		ieee80211_queue_delayed_work(&local->hw, &roc->work, 0); +		goto out_queue; +	} + +	/* otherwise actually kick it off here (for error handling) */ + +	ret = drv_remain_on_channel(local, sdata, channel, duration, type); +	if (ret) { +		kfree(roc); +		return ret; +	} + +	roc->started = true; +	goto out_queue; + + out_check_combine: +	list_for_each_entry(tmp, &local->roc_list, list) { +		if (tmp->chan != channel || tmp->sdata != sdata) +			continue; + +		/* +		 * Extend this ROC if possible: +		 * +		 * If it hasn't started yet, just increase the duration +		 * and add the new one to the list of dependents. +		 * If the type of the new ROC has higher priority, modify the +		 * type of the previous one to match that of the new one. +		 */ +		if (!tmp->started) { +			list_add_tail(&roc->list, &tmp->dependents); +			tmp->duration = max(tmp->duration, roc->duration); +			tmp->type = max(tmp->type, roc->type); +			queued = true; +			break; +		} + +		/* If it has already started, it's more difficult ... */ +		if (local->ops->remain_on_channel) { +			unsigned long j = jiffies; + +			/* +			 * In the offloaded ROC case, if it hasn't begun, add +			 * this new one to the dependent list to be handled +			 * when the master one begins. If it has begun, +			 * check that there's still a minimum time left and +			 * if so, start this one, transmitting the frame, but +			 * add it to the list directly after this one with +			 * a reduced time so we'll ask the driver to execute +			 * it right after finishing the previous one, in the +			 * hope that it'll also be executed right afterwards, +			 * effectively extending the old one. +			 * If there's no minimum time left, just add it to the +			 * normal list. +			 * TODO: the ROC type is ignored here, assuming that it +			 * is better to immediately use the current ROC. +			 */ +			if (!tmp->hw_begun) { +				list_add_tail(&roc->list, &tmp->dependents); +				queued = true; +				break; +			} + +			if (time_before(j + IEEE80211_ROC_MIN_LEFT, +					tmp->hw_start_time + +					msecs_to_jiffies(tmp->duration))) { +				int new_dur; + +				ieee80211_handle_roc_started(roc); + +				new_dur = roc->duration - +					  jiffies_to_msecs(tmp->hw_start_time + +							   msecs_to_jiffies( +								tmp->duration) - +							   j); + +				if (new_dur > 0) { +					/* add right after tmp */ +					list_add(&roc->list, &tmp->list); +				} else { +					list_add_tail(&roc->list, +						      &tmp->dependents); +				} +				queued = true; +			} +		} else if (del_timer_sync(&tmp->work.timer)) { +			unsigned long new_end; + +			/* +			 * In the software ROC case, cancel the timer, if +			 * that fails then the finish work is already +			 * queued/pending and thus we queue the new ROC +			 * normally, if that succeeds then we can extend +			 * the timer duration and TX the frame (if any.) +			 */ + +			list_add_tail(&roc->list, &tmp->dependents); +			queued = true; + +			new_end = jiffies + msecs_to_jiffies(roc->duration); + +			/* ok, it was started & we canceled timer */ +			if (time_after(new_end, tmp->work.timer.expires)) +				mod_timer(&tmp->work.timer, new_end); +			else +				add_timer(&tmp->work.timer); + +			ieee80211_handle_roc_started(roc); +		} +		break; +	} + + out_queue: +	if (!queued) +		list_add_tail(&roc->list, &local->roc_list);  	return 0;  }  static int ieee80211_remain_on_channel(struct wiphy *wiphy, -				       struct net_device *dev, +				       struct wireless_dev *wdev,  				       struct ieee80211_channel *chan, -				       enum nl80211_channel_type channel_type,  				       unsigned int duration,  				       u64 *cookie)  { -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); +	struct ieee80211_local *local = sdata->local; +	int ret; + +	mutex_lock(&local->mtx); +	ret = ieee80211_start_roc_work(local, sdata, chan, +				       duration, cookie, NULL, +				       IEEE80211_ROC_TYPE_NORMAL); +	mutex_unlock(&local->mtx); -	return ieee80211_wk_remain_on_channel(sdata, chan, channel_type, -					      duration, cookie); +	return ret; +} + +static int ieee80211_cancel_roc(struct ieee80211_local *local, +				u64 cookie, bool mgmt_tx) +{ +	struct ieee80211_roc_work *roc, *tmp, *found = NULL; +	int ret; + +	mutex_lock(&local->mtx); +	list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { +		struct ieee80211_roc_work *dep, *tmp2; + +		list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) { +			if (!mgmt_tx && dep->cookie != cookie) +				continue; +			else if (mgmt_tx && dep->mgmt_tx_cookie != cookie) +				continue; +			/* found dependent item -- just remove it */ +			list_del(&dep->list); +			mutex_unlock(&local->mtx); + +			ieee80211_roc_notify_destroy(dep, true); +			return 0; +		} + +		if (!mgmt_tx && roc->cookie != cookie) +			continue; +		else if (mgmt_tx && roc->mgmt_tx_cookie != cookie) +			continue; + +		found = roc; +		break; +	} + +	if (!found) { +		mutex_unlock(&local->mtx); +		return -ENOENT; +	} + +	/* +	 * We found the item to cancel, so do that. Note that it +	 * may have dependents, which we also cancel (and send +	 * the expired signal for.) Not doing so would be quite +	 * tricky here, but we may need to fix it later. +	 */ + +	if (local->ops->remain_on_channel) { +		if (found->started) { +			ret = drv_cancel_remain_on_channel(local); +			if (WARN_ON_ONCE(ret)) { +				mutex_unlock(&local->mtx); +				return ret; +			} +		} + +		list_del(&found->list); + +		if (found->started) +			ieee80211_start_next_roc(local); +		mutex_unlock(&local->mtx); + +		ieee80211_roc_notify_destroy(found, true); +	} else { +		/* work may be pending so use it all the time */ +		found->abort = true; +		ieee80211_queue_delayed_work(&local->hw, &found->work, 0); + +		mutex_unlock(&local->mtx); + +		/* work will clean up etc */ +		flush_delayed_work(&found->work); +		WARN_ON(!found->to_be_freed); +		kfree(found); +	} + +	return 0;  }  static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, -					      struct net_device *dev, +					      struct wireless_dev *wdev,  					      u64 cookie)  { +	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); +	struct ieee80211_local *local = sdata->local; + +	return ieee80211_cancel_roc(local, cookie, false); +} + +static int ieee80211_start_radar_detection(struct wiphy *wiphy, +					   struct net_device *dev, +					   struct cfg80211_chan_def *chandef, +					   u32 cac_time_ms) +{  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local; +	int err; + +	mutex_lock(&local->mtx); +	if (!list_empty(&local->roc_list) || local->scanning) { +		err = -EBUSY; +		goto out_unlock; +	} + +	/* whatever, but channel contexts should not complain about that one */ +	sdata->smps_mode = IEEE80211_SMPS_OFF; +	sdata->needed_rx_chains = local->rx_chains; + +	err = ieee80211_vif_use_channel(sdata, chandef, +					IEEE80211_CHANCTX_SHARED); +	if (err) +		goto out_unlock; + +	ieee80211_queue_delayed_work(&sdata->local->hw, +				     &sdata->dfs_cac_timer_work, +				     msecs_to_jiffies(cac_time_ms)); + + out_unlock: +	mutex_unlock(&local->mtx); +	return err; +} + +static struct cfg80211_beacon_data * +cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon) +{ +	struct cfg80211_beacon_data *new_beacon; +	u8 *pos; +	int len; + +	len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len + +	      beacon->proberesp_ies_len + beacon->assocresp_ies_len + +	      beacon->probe_resp_len; + +	new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL); +	if (!new_beacon) +		return NULL; + +	pos = (u8 *)(new_beacon + 1); +	if (beacon->head_len) { +		new_beacon->head_len = beacon->head_len; +		new_beacon->head = pos; +		memcpy(pos, beacon->head, beacon->head_len); +		pos += beacon->head_len; +	} +	if (beacon->tail_len) { +		new_beacon->tail_len = beacon->tail_len; +		new_beacon->tail = pos; +		memcpy(pos, beacon->tail, beacon->tail_len); +		pos += beacon->tail_len; +	} +	if (beacon->beacon_ies_len) { +		new_beacon->beacon_ies_len = beacon->beacon_ies_len; +		new_beacon->beacon_ies = pos; +		memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len); +		pos += beacon->beacon_ies_len; +	} +	if (beacon->proberesp_ies_len) { +		new_beacon->proberesp_ies_len = beacon->proberesp_ies_len; +		new_beacon->proberesp_ies = pos; +		memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len); +		pos += beacon->proberesp_ies_len; +	} +	if (beacon->assocresp_ies_len) { +		new_beacon->assocresp_ies_len = beacon->assocresp_ies_len; +		new_beacon->assocresp_ies = pos; +		memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len); +		pos += beacon->assocresp_ies_len; +	} +	if (beacon->probe_resp_len) { +		new_beacon->probe_resp_len = beacon->probe_resp_len; +		beacon->probe_resp = pos; +		memcpy(pos, beacon->probe_resp, beacon->probe_resp_len); +		pos += beacon->probe_resp_len; +	} + +	return new_beacon; +} + +void ieee80211_csa_finish(struct ieee80211_vif *vif) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); -	return ieee80211_wk_cancel_remain_on_channel(sdata, cookie); +	ieee80211_queue_work(&sdata->local->hw, +			     &sdata->csa_finalize_work);  } +EXPORT_SYMBOL(ieee80211_csa_finish); -static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, -			     struct ieee80211_channel *chan, -			     enum nl80211_channel_type channel_type, -			     bool channel_type_valid, -			     const u8 *buf, size_t len, u64 *cookie) +static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata, +					  u32 *changed) +{ +	int err; + +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_AP: +		err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon); +		kfree(sdata->u.ap.next_beacon); +		sdata->u.ap.next_beacon = NULL; + +		if (err < 0) +			return err; +		*changed |= err; +		break; +	case NL80211_IFTYPE_ADHOC: +		err = ieee80211_ibss_finish_csa(sdata); +		if (err < 0) +			return err; +		*changed |= err; +		break; +#ifdef CONFIG_MAC80211_MESH +	case NL80211_IFTYPE_MESH_POINT: +		err = ieee80211_mesh_finish_csa(sdata); +		if (err < 0) +			return err; +		*changed |= err; +		break; +#endif +	default: +		WARN_ON(1); +		return -EINVAL; +	} + +	return 0; +} + +static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local; +	u32 changed = 0; +	int err; + +	sdata_assert_lock(sdata); +	lockdep_assert_held(&local->mtx); + +	sdata->radar_required = sdata->csa_radar_required; +	err = ieee80211_vif_change_channel(sdata, &changed); +	if (err < 0) +		return err; + +	if (!local->use_chanctx) { +		local->_oper_chandef = sdata->csa_chandef; +		ieee80211_hw_config(local, 0); +	} + +	sdata->vif.csa_active = false; + +	err = ieee80211_set_after_csa_beacon(sdata, &changed); +	if (err) +		return err; + +	ieee80211_bss_info_change_notify(sdata, changed); +	cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); + +	if (!ieee80211_csa_needs_block_tx(local)) +		ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA); + +	return 0; +} + +static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) +{ +	if (__ieee80211_csa_finalize(sdata)) { +		sdata_info(sdata, "failed to finalize CSA, disconnecting\n"); +		cfg80211_stop_iface(sdata->local->hw.wiphy, &sdata->wdev, +				    GFP_KERNEL); +	} +} + +void ieee80211_csa_finalize_work(struct work_struct *work) +{ +	struct ieee80211_sub_if_data *sdata = +		container_of(work, struct ieee80211_sub_if_data, +			     csa_finalize_work); +	struct ieee80211_local *local = sdata->local; + +	sdata_lock(sdata); +	mutex_lock(&local->mtx); + +	/* AP might have been stopped while waiting for the lock. */ +	if (!sdata->vif.csa_active) +		goto unlock; + +	if (!ieee80211_sdata_running(sdata)) +		goto unlock; + +	ieee80211_csa_finalize(sdata); + +unlock: +	mutex_unlock(&local->mtx); +	sdata_unlock(sdata); +} + +static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, +				    struct cfg80211_csa_settings *params, +				    u32 *changed) +{ +	int err; + +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_AP: +		sdata->u.ap.next_beacon = +			cfg80211_beacon_dup(¶ms->beacon_after); +		if (!sdata->u.ap.next_beacon) +			return -ENOMEM; + +		/* +		 * With a count of 0, we don't have to wait for any +		 * TBTT before switching, so complete the CSA +		 * immediately.  In theory, with a count == 1 we +		 * should delay the switch until just before the next +		 * TBTT, but that would complicate things so we switch +		 * immediately too.  If we would delay the switch +		 * until the next TBTT, we would have to set the probe +		 * response here. +		 * +		 * TODO: A channel switch with count <= 1 without +		 * sending a CSA action frame is kind of useless, +		 * because the clients won't know we're changing +		 * channels.  The action frame must be implemented +		 * either here or in the userspace. +		 */ +		if (params->count <= 1) +			break; + +		if ((params->n_counter_offsets_beacon > +		     IEEE80211_MAX_CSA_COUNTERS_NUM) || +		    (params->n_counter_offsets_presp > +		     IEEE80211_MAX_CSA_COUNTERS_NUM)) +			return -EINVAL; + +		/* make sure we don't have garbage in other counters */ +		memset(sdata->csa_counter_offset_beacon, 0, +		       sizeof(sdata->csa_counter_offset_beacon)); +		memset(sdata->csa_counter_offset_presp, 0, +		       sizeof(sdata->csa_counter_offset_presp)); + +		memcpy(sdata->csa_counter_offset_beacon, +		       params->counter_offsets_beacon, +		       params->n_counter_offsets_beacon * sizeof(u16)); +		memcpy(sdata->csa_counter_offset_presp, +		       params->counter_offsets_presp, +		       params->n_counter_offsets_presp * sizeof(u16)); + +		err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa); +		if (err < 0) { +			kfree(sdata->u.ap.next_beacon); +			return err; +		} +		*changed |= err; + +		break; +	case NL80211_IFTYPE_ADHOC: +		if (!sdata->vif.bss_conf.ibss_joined) +			return -EINVAL; + +		if (params->chandef.width != sdata->u.ibss.chandef.width) +			return -EINVAL; + +		switch (params->chandef.width) { +		case NL80211_CHAN_WIDTH_40: +			if (cfg80211_get_chandef_type(¶ms->chandef) != +			    cfg80211_get_chandef_type(&sdata->u.ibss.chandef)) +				return -EINVAL; +		case NL80211_CHAN_WIDTH_5: +		case NL80211_CHAN_WIDTH_10: +		case NL80211_CHAN_WIDTH_20_NOHT: +		case NL80211_CHAN_WIDTH_20: +			break; +		default: +			return -EINVAL; +		} + +		/* changes into another band are not supported */ +		if (sdata->u.ibss.chandef.chan->band != +		    params->chandef.chan->band) +			return -EINVAL; + +		/* see comments in the NL80211_IFTYPE_AP block */ +		if (params->count > 1) { +			err = ieee80211_ibss_csa_beacon(sdata, params); +			if (err < 0) +				return err; +			*changed |= err; +		} + +		ieee80211_send_action_csa(sdata, params); + +		break; +#ifdef CONFIG_MAC80211_MESH +	case NL80211_IFTYPE_MESH_POINT: { +		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + +		if (params->chandef.width != sdata->vif.bss_conf.chandef.width) +			return -EINVAL; + +		/* changes into another band are not supported */ +		if (sdata->vif.bss_conf.chandef.chan->band != +		    params->chandef.chan->band) +			return -EINVAL; + +		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE) { +			ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT; +			if (!ifmsh->pre_value) +				ifmsh->pre_value = 1; +			else +				ifmsh->pre_value++; +		} + +		/* see comments in the NL80211_IFTYPE_AP block */ +		if (params->count > 1) { +			err = ieee80211_mesh_csa_beacon(sdata, params); +			if (err < 0) { +				ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE; +				return err; +			} +			*changed |= err; +		} + +		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT) +			ieee80211_send_action_csa(sdata, params); + +		break; +		} +#endif +	default: +		return -EOPNOTSUPP; +	} + +	return 0; +} + +static int +__ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, +			   struct cfg80211_csa_settings *params)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = sdata->local; -	struct sk_buff *skb; -	struct sta_info *sta; -	const struct ieee80211_mgmt *mgmt = (void *)buf; -	u32 flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | -		    IEEE80211_TX_CTL_REQ_TX_STATUS; +	struct ieee80211_chanctx_conf *conf; +	struct ieee80211_chanctx *chanctx; +	int err, num_chanctx, changed = 0; -	/* Check that we are on the requested channel for transmission */ -	if (chan != local->tmp_channel && -	    chan != local->oper_channel) +	sdata_assert_lock(sdata); +	lockdep_assert_held(&local->mtx); + +	if (!list_empty(&local->roc_list) || local->scanning) +		return -EBUSY; + +	if (sdata->wdev.cac_started) +		return -EBUSY; + +	if (cfg80211_chandef_identical(¶ms->chandef, +				       &sdata->vif.bss_conf.chandef)) +		return -EINVAL; + +	mutex_lock(&local->chanctx_mtx); +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	if (!conf) { +		mutex_unlock(&local->chanctx_mtx); +		return -EBUSY; +	} + +	/* don't handle for multi-VIF cases */ +	chanctx = container_of(conf, struct ieee80211_chanctx, conf); +	if (ieee80211_chanctx_refcount(local, chanctx) > 1) { +		mutex_unlock(&local->chanctx_mtx); +		return -EBUSY; +	} +	num_chanctx = 0; +	list_for_each_entry_rcu(chanctx, &local->chanctx_list, list) +		num_chanctx++; +	mutex_unlock(&local->chanctx_mtx); + +	if (num_chanctx > 1)  		return -EBUSY; -	if (channel_type_valid && -	    (channel_type != local->tmp_channel_type && -	     channel_type != local->_oper_channel_type)) + +	/* don't allow another channel switch if one is already active. */ +	if (sdata->vif.csa_active)  		return -EBUSY; +	err = ieee80211_set_csa_beacon(sdata, params, &changed); +	if (err) +		return err; + +	sdata->csa_radar_required = params->radar_required; +	sdata->csa_chandef = params->chandef; +	sdata->csa_block_tx = params->block_tx; +	sdata->csa_current_counter = params->count; +	sdata->vif.csa_active = true; + +	if (sdata->csa_block_tx) +		ieee80211_stop_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA); + +	if (changed) { +		ieee80211_bss_info_change_notify(sdata, changed); +		drv_channel_switch_beacon(sdata, ¶ms->chandef); +	} else { +		/* if the beacon didn't change, we can finalize immediately */ +		ieee80211_csa_finalize(sdata); +	} + +	return 0; +} + +int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, +			     struct cfg80211_csa_settings *params) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local; +	int err; + +	mutex_lock(&local->mtx); +	err = __ieee80211_channel_switch(wiphy, dev, params); +	mutex_unlock(&local->mtx); + +	return err; +} + +static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, +			     struct cfg80211_mgmt_tx_params *params, +			     u64 *cookie) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); +	struct ieee80211_local *local = sdata->local; +	struct sk_buff *skb; +	struct sta_info *sta; +	const struct ieee80211_mgmt *mgmt = (void *)params->buf; +	bool need_offchan = false; +	u32 flags; +	int ret; +	u8 *data; + +	if (params->dont_wait_for_ack) +		flags = IEEE80211_TX_CTL_NO_ACK; +	else +		flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | +			IEEE80211_TX_CTL_REQ_TX_STATUS; + +	if (params->no_cck) +		flags |= IEEE80211_TX_CTL_NO_CCK_RATE; +  	switch (sdata->vif.type) {  	case NL80211_IFTYPE_ADHOC: +		if (!sdata->vif.bss_conf.ibss_joined) +			need_offchan = true; +		/* fall through */ +#ifdef CONFIG_MAC80211_MESH +	case NL80211_IFTYPE_MESH_POINT: +		if (ieee80211_vif_is_mesh(&sdata->vif) && +		    !sdata->u.mesh.mesh_id_len) +			need_offchan = true; +		/* fall through */ +#endif  	case NL80211_IFTYPE_AP:  	case NL80211_IFTYPE_AP_VLAN:  	case NL80211_IFTYPE_P2P_GO: +		if (sdata->vif.type != NL80211_IFTYPE_ADHOC && +		    !ieee80211_vif_is_mesh(&sdata->vif) && +		    !rcu_access_pointer(sdata->bss->beacon)) +			need_offchan = true;  		if (!ieee80211_is_action(mgmt->frame_control) || -		    mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) +		    mgmt->u.action.category == WLAN_CATEGORY_PUBLIC || +		    mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED || +		    mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)  			break;  		rcu_read_lock();  		sta = sta_info_get(sdata, mgmt->da); @@ -1590,42 +3458,130 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,  		break;  	case NL80211_IFTYPE_STATION:  	case NL80211_IFTYPE_P2P_CLIENT: +		if (!sdata->u.mgd.associated) +			need_offchan = true; +		break; +	case NL80211_IFTYPE_P2P_DEVICE: +		need_offchan = true;  		break;  	default:  		return -EOPNOTSUPP;  	} -	skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); -	if (!skb) -		return -ENOMEM; +	/* configurations requiring offchan cannot work if no channel has been +	 * specified +	 */ +	if (need_offchan && !params->chan) +		return -EINVAL; + +	mutex_lock(&local->mtx); + +	/* Check if the operating channel is the requested channel */ +	if (!need_offchan) { +		struct ieee80211_chanctx_conf *chanctx_conf; + +		rcu_read_lock(); +		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + +		if (chanctx_conf) { +			need_offchan = params->chan && +				       (params->chan != +					chanctx_conf->def.chan); +		} else if (!params->chan) { +			ret = -EINVAL; +			rcu_read_unlock(); +			goto out_unlock; +		} else { +			need_offchan = true; +		} +		rcu_read_unlock(); +	} + +	if (need_offchan && !params->offchan) { +		ret = -EBUSY; +		goto out_unlock; +	} + +	skb = dev_alloc_skb(local->hw.extra_tx_headroom + params->len); +	if (!skb) { +		ret = -ENOMEM; +		goto out_unlock; +	}  	skb_reserve(skb, local->hw.extra_tx_headroom); -	memcpy(skb_put(skb, len), buf, len); +	data = skb_put(skb, params->len); +	memcpy(data, params->buf, params->len); + +	/* Update CSA counters */ +	if (sdata->vif.csa_active && +	    (sdata->vif.type == NL80211_IFTYPE_AP || +	     sdata->vif.type == NL80211_IFTYPE_ADHOC) && +	    params->n_csa_offsets) { +		int i; +		u8 c = sdata->csa_current_counter; + +		for (i = 0; i < params->n_csa_offsets; i++) +			data[params->csa_offsets[i]] = c; +	}  	IEEE80211_SKB_CB(skb)->flags = flags;  	skb->dev = sdata->dev; -	ieee80211_tx_skb(sdata, skb); -	*cookie = (unsigned long) skb; -	return 0; +	if (!need_offchan) { +		*cookie = (unsigned long) skb; +		ieee80211_tx_skb(sdata, skb); +		ret = 0; +		goto out_unlock; +	} + +	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN | +					IEEE80211_TX_INTFL_OFFCHAN_TX_OK; +	if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) +		IEEE80211_SKB_CB(skb)->hw_queue = +			local->hw.offchannel_tx_hw_queue; + +	/* This will handle all kinds of coalescing and immediate TX */ +	ret = ieee80211_start_roc_work(local, sdata, params->chan, +				       params->wait, cookie, skb, +				       IEEE80211_ROC_TYPE_MGMT_TX); +	if (ret) +		kfree_skb(skb); + out_unlock: +	mutex_unlock(&local->mtx); +	return ret; +} + +static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, +					 struct wireless_dev *wdev, +					 u64 cookie) +{ +	struct ieee80211_local *local = wiphy_priv(wiphy); + +	return ieee80211_cancel_roc(local, cookie, true);  }  static void ieee80211_mgmt_frame_register(struct wiphy *wiphy, -					  struct net_device *dev, +					  struct wireless_dev *wdev,  					  u16 frame_type, bool reg)  {  	struct ieee80211_local *local = wiphy_priv(wiphy); -	if (frame_type != (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ)) -		return; +	switch (frame_type) { +	case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ: +		if (reg) +			local->probe_req_reg++; +		else +			local->probe_req_reg--; -	if (reg) -		local->probe_req_reg++; -	else -		local->probe_req_reg--; +		if (!local->open_count) +			break; -	ieee80211_queue_work(&local->hw, &local->reconfig_filter); +		ieee80211_queue_work(&local->hw, &local->reconfig_filter); +		break; +	default: +		break; +	}  }  static int ieee80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) @@ -1645,18 +3601,203 @@ static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant)  	return drv_get_antenna(local, tx_ant, rx_ant);  } -struct cfg80211_ops mac80211_config_ops = { +static int ieee80211_set_ringparam(struct wiphy *wiphy, u32 tx, u32 rx) +{ +	struct ieee80211_local *local = wiphy_priv(wiphy); + +	return drv_set_ringparam(local, tx, rx); +} + +static void ieee80211_get_ringparam(struct wiphy *wiphy, +				    u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max) +{ +	struct ieee80211_local *local = wiphy_priv(wiphy); + +	drv_get_ringparam(local, tx, tx_max, rx, rx_max); +} + +static int ieee80211_set_rekey_data(struct wiphy *wiphy, +				    struct net_device *dev, +				    struct cfg80211_gtk_rekey_data *data) +{ +	struct ieee80211_local *local = wiphy_priv(wiphy); +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + +	if (!local->ops->set_rekey_data) +		return -EOPNOTSUPP; + +	drv_set_rekey_data(local, sdata, data); + +	return 0; +} + +static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, +				  const u8 *peer, u64 *cookie) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_qos_hdr *nullfunc; +	struct sk_buff *skb; +	int size = sizeof(*nullfunc); +	__le16 fc; +	bool qos; +	struct ieee80211_tx_info *info; +	struct sta_info *sta; +	struct ieee80211_chanctx_conf *chanctx_conf; +	enum ieee80211_band band; + +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	if (WARN_ON(!chanctx_conf)) { +		rcu_read_unlock(); +		return -EINVAL; +	} +	band = chanctx_conf->def.chan->band; +	sta = sta_info_get_bss(sdata, peer); +	if (sta) { +		qos = test_sta_flag(sta, WLAN_STA_WME); +	} else { +		rcu_read_unlock(); +		return -ENOLINK; +	} + +	if (qos) { +		fc = cpu_to_le16(IEEE80211_FTYPE_DATA | +				 IEEE80211_STYPE_QOS_NULLFUNC | +				 IEEE80211_FCTL_FROMDS); +	} else { +		size -= 2; +		fc = cpu_to_le16(IEEE80211_FTYPE_DATA | +				 IEEE80211_STYPE_NULLFUNC | +				 IEEE80211_FCTL_FROMDS); +	} + +	skb = dev_alloc_skb(local->hw.extra_tx_headroom + size); +	if (!skb) { +		rcu_read_unlock(); +		return -ENOMEM; +	} + +	skb->dev = dev; + +	skb_reserve(skb, local->hw.extra_tx_headroom); + +	nullfunc = (void *) skb_put(skb, size); +	nullfunc->frame_control = fc; +	nullfunc->duration_id = 0; +	memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN); +	memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); +	memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN); +	nullfunc->seq_ctrl = 0; + +	info = IEEE80211_SKB_CB(skb); + +	info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS | +		       IEEE80211_TX_INTFL_NL80211_FRAME_TX; + +	skb_set_queue_mapping(skb, IEEE80211_AC_VO); +	skb->priority = 7; +	if (qos) +		nullfunc->qos_ctrl = cpu_to_le16(7); + +	local_bh_disable(); +	ieee80211_xmit(sdata, skb, band); +	local_bh_enable(); +	rcu_read_unlock(); + +	*cookie = (unsigned long) skb; +	return 0; +} + +static int ieee80211_cfg_get_channel(struct wiphy *wiphy, +				     struct wireless_dev *wdev, +				     struct cfg80211_chan_def *chandef) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); +	struct ieee80211_local *local = wiphy_priv(wiphy); +	struct ieee80211_chanctx_conf *chanctx_conf; +	int ret = -ENODATA; + +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	if (chanctx_conf) { +		*chandef = chanctx_conf->def; +		ret = 0; +	} else if (local->open_count > 0 && +		   local->open_count == local->monitors && +		   sdata->vif.type == NL80211_IFTYPE_MONITOR) { +		if (local->use_chanctx) +			*chandef = local->monitor_chandef; +		else +			*chandef = local->_oper_chandef; +		ret = 0; +	} +	rcu_read_unlock(); + +	return ret; +} + +#ifdef CONFIG_PM +static void ieee80211_set_wakeup(struct wiphy *wiphy, bool enabled) +{ +	drv_set_wakeup(wiphy_priv(wiphy), enabled); +} +#endif + +static int ieee80211_set_qos_map(struct wiphy *wiphy, +				 struct net_device *dev, +				 struct cfg80211_qos_map *qos_map) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct mac80211_qos_map *new_qos_map, *old_qos_map; + +	if (qos_map) { +		new_qos_map = kzalloc(sizeof(*new_qos_map), GFP_KERNEL); +		if (!new_qos_map) +			return -ENOMEM; +		memcpy(&new_qos_map->qos_map, qos_map, sizeof(*qos_map)); +	} else { +		/* A NULL qos_map was passed to disable QoS mapping */ +		new_qos_map = NULL; +	} + +	old_qos_map = sdata_dereference(sdata->qos_map, sdata); +	rcu_assign_pointer(sdata->qos_map, new_qos_map); +	if (old_qos_map) +		kfree_rcu(old_qos_map, rcu_head); + +	return 0; +} + +static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy, +				      struct net_device *dev, +				      struct cfg80211_chan_def *chandef) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	int ret; +	u32 changed = 0; + +	ret = ieee80211_vif_change_bandwidth(sdata, chandef, &changed); +	if (ret == 0) +		ieee80211_bss_info_change_notify(sdata, changed); + +	return ret; +} + +const struct cfg80211_ops mac80211_config_ops = {  	.add_virtual_intf = ieee80211_add_iface,  	.del_virtual_intf = ieee80211_del_iface,  	.change_virtual_intf = ieee80211_change_iface, +	.start_p2p_device = ieee80211_start_p2p_device, +	.stop_p2p_device = ieee80211_stop_p2p_device,  	.add_key = ieee80211_add_key,  	.del_key = ieee80211_del_key,  	.get_key = ieee80211_get_key,  	.set_default_key = ieee80211_config_default_key,  	.set_default_mgmt_key = ieee80211_config_default_mgmt_key, -	.add_beacon = ieee80211_add_beacon, -	.set_beacon = ieee80211_set_beacon, -	.del_beacon = ieee80211_del_beacon, +	.start_ap = ieee80211_start_ap, +	.change_beacon = ieee80211_change_beacon, +	.stop_ap = ieee80211_stop_ap,  	.add_station = ieee80211_add_station,  	.del_station = ieee80211_del_station,  	.change_station = ieee80211_change_station, @@ -1669,34 +3810,59 @@ struct cfg80211_ops mac80211_config_ops = {  	.change_mpath = ieee80211_change_mpath,  	.get_mpath = ieee80211_get_mpath,  	.dump_mpath = ieee80211_dump_mpath, -	.set_mesh_params = ieee80211_set_mesh_params, -	.get_mesh_params = ieee80211_get_mesh_params, +	.update_mesh_config = ieee80211_update_mesh_config, +	.get_mesh_config = ieee80211_get_mesh_config, +	.join_mesh = ieee80211_join_mesh, +	.leave_mesh = ieee80211_leave_mesh,  #endif  	.change_bss = ieee80211_change_bss,  	.set_txq_params = ieee80211_set_txq_params, -	.set_channel = ieee80211_set_channel, +	.set_monitor_channel = ieee80211_set_monitor_channel,  	.suspend = ieee80211_suspend,  	.resume = ieee80211_resume,  	.scan = ieee80211_scan, +	.sched_scan_start = ieee80211_sched_scan_start, +	.sched_scan_stop = ieee80211_sched_scan_stop,  	.auth = ieee80211_auth,  	.assoc = ieee80211_assoc,  	.deauth = ieee80211_deauth,  	.disassoc = ieee80211_disassoc,  	.join_ibss = ieee80211_join_ibss,  	.leave_ibss = ieee80211_leave_ibss, +	.set_mcast_rate = ieee80211_set_mcast_rate,  	.set_wiphy_params = ieee80211_set_wiphy_params,  	.set_tx_power = ieee80211_set_tx_power,  	.get_tx_power = ieee80211_get_tx_power,  	.set_wds_peer = ieee80211_set_wds_peer,  	.rfkill_poll = ieee80211_rfkill_poll,  	CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) +	CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump)  	.set_power_mgmt = ieee80211_set_power_mgmt,  	.set_bitrate_mask = ieee80211_set_bitrate_mask,  	.remain_on_channel = ieee80211_remain_on_channel,  	.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,  	.mgmt_tx = ieee80211_mgmt_tx, +	.mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,  	.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,  	.mgmt_frame_register = ieee80211_mgmt_frame_register,  	.set_antenna = ieee80211_set_antenna,  	.get_antenna = ieee80211_get_antenna, +	.set_ringparam = ieee80211_set_ringparam, +	.get_ringparam = ieee80211_get_ringparam, +	.set_rekey_data = ieee80211_set_rekey_data, +	.tdls_oper = ieee80211_tdls_oper, +	.tdls_mgmt = ieee80211_tdls_mgmt, +	.probe_client = ieee80211_probe_client, +	.set_noack_map = ieee80211_set_noack_map, +#ifdef CONFIG_PM +	.set_wakeup = ieee80211_set_wakeup, +#endif +	.get_et_sset_count = ieee80211_get_et_sset_count, +	.get_et_stats = ieee80211_get_et_stats, +	.get_et_strings = ieee80211_get_et_strings, +	.get_channel = ieee80211_cfg_get_channel, +	.start_radar_detection = ieee80211_start_radar_detection, +	.channel_switch = ieee80211_channel_switch, +	.set_qos_map = ieee80211_set_qos_map, +	.set_ap_chanwidth = ieee80211_set_ap_chanwidth,  }; diff --git a/net/mac80211/cfg.h b/net/mac80211/cfg.h index 7d7879f5b00..2d51f62dc76 100644 --- a/net/mac80211/cfg.h +++ b/net/mac80211/cfg.h @@ -4,6 +4,6 @@  #ifndef __CFG_H  #define __CFG_H -extern struct cfg80211_ops mac80211_config_ops; +extern const struct cfg80211_ops mac80211_config_ops;  #endif /* __CFG_H */ diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 5b24740fc0b..a310e33972d 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -3,125 +3,1132 @@   */  #include <linux/nl80211.h> +#include <linux/export.h> +#include <linux/rtnetlink.h> +#include <net/cfg80211.h>  #include "ieee80211_i.h" +#include "driver-ops.h" -static enum ieee80211_chan_mode -__ieee80211_get_channel_mode(struct ieee80211_local *local, -			     struct ieee80211_sub_if_data *ignore) +static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local, +					  struct ieee80211_chanctx *ctx)  {  	struct ieee80211_sub_if_data *sdata; +	int num = 0; -	lockdep_assert_held(&local->iflist_mtx); +	lockdep_assert_held(&local->chanctx_mtx); -	list_for_each_entry(sdata, &local->interfaces, list) { -		if (sdata == ignore) +	list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list) +		num++; + +	return num; +} + +static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local, +					  struct ieee80211_chanctx *ctx) +{ +	struct ieee80211_sub_if_data *sdata; +	int num = 0; + +	lockdep_assert_held(&local->chanctx_mtx); + +	list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list) +		num++; + +	return num; +} + +int ieee80211_chanctx_refcount(struct ieee80211_local *local, +			       struct ieee80211_chanctx *ctx) +{ +	return ieee80211_chanctx_num_assigned(local, ctx) + +	       ieee80211_chanctx_num_reserved(local, ctx); +} + +static int ieee80211_num_chanctx(struct ieee80211_local *local) +{ +	struct ieee80211_chanctx *ctx; +	int num = 0; + +	lockdep_assert_held(&local->chanctx_mtx); + +	list_for_each_entry(ctx, &local->chanctx_list, list) +		num++; + +	return num; +} + +static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local) +{ +	lockdep_assert_held(&local->chanctx_mtx); +	return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local); +} + +static const struct cfg80211_chan_def * +ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local, +				   struct ieee80211_chanctx *ctx, +				   const struct cfg80211_chan_def *compat) +{ +	struct ieee80211_sub_if_data *sdata; + +	lockdep_assert_held(&local->chanctx_mtx); + +	list_for_each_entry(sdata, &ctx->reserved_vifs, +			    reserved_chanctx_list) { +		if (!compat) +			compat = &sdata->reserved_chandef; + +		compat = cfg80211_chandef_compatible(&sdata->reserved_chandef, +						     compat); +		if (!compat) +			break; +	} + +	return compat; +} + +static const struct cfg80211_chan_def * +ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local, +				       struct ieee80211_chanctx *ctx, +				       const struct cfg80211_chan_def *compat) +{ +	struct ieee80211_sub_if_data *sdata; + +	lockdep_assert_held(&local->chanctx_mtx); + +	list_for_each_entry(sdata, &ctx->assigned_vifs, +			    assigned_chanctx_list) { +		if (sdata->reserved_chanctx != NULL) +			continue; + +		if (!compat) +			compat = &sdata->vif.bss_conf.chandef; + +		compat = cfg80211_chandef_compatible( +				&sdata->vif.bss_conf.chandef, compat); +		if (!compat) +			break; +	} + +	return compat; +} + +static const struct cfg80211_chan_def * +ieee80211_chanctx_combined_chandef(struct ieee80211_local *local, +				   struct ieee80211_chanctx *ctx, +				   const struct cfg80211_chan_def *compat) +{ +	lockdep_assert_held(&local->chanctx_mtx); + +	compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat); +	if (!compat) +		return NULL; + +	compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat); +	if (!compat) +		return NULL; + +	return compat; +} + +static bool +ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local, +				      struct ieee80211_chanctx *ctx, +				      const struct cfg80211_chan_def *def) +{ +	lockdep_assert_held(&local->chanctx_mtx); + +	if (ieee80211_chanctx_combined_chandef(local, ctx, def)) +		return true; + +	if (!list_empty(&ctx->reserved_vifs) && +	    ieee80211_chanctx_reserved_chandef(local, ctx, def)) +		return true; + +	return false; +} + +static struct ieee80211_chanctx * +ieee80211_find_reservation_chanctx(struct ieee80211_local *local, +				   const struct cfg80211_chan_def *chandef, +				   enum ieee80211_chanctx_mode mode) +{ +	struct ieee80211_chanctx *ctx; + +	lockdep_assert_held(&local->chanctx_mtx); + +	if (mode == IEEE80211_CHANCTX_EXCLUSIVE) +		return NULL; + +	list_for_each_entry(ctx, &local->chanctx_list, list) { +		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) +			continue; + +		if (!ieee80211_chanctx_can_reserve_chandef(local, ctx, +							   chandef)) +			continue; + +		return ctx; +	} + +	return NULL; +} + +static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta) +{ +	switch (sta->bandwidth) { +	case IEEE80211_STA_RX_BW_20: +		if (sta->ht_cap.ht_supported) +			return NL80211_CHAN_WIDTH_20; +		else +			return NL80211_CHAN_WIDTH_20_NOHT; +	case IEEE80211_STA_RX_BW_40: +		return NL80211_CHAN_WIDTH_40; +	case IEEE80211_STA_RX_BW_80: +		return NL80211_CHAN_WIDTH_80; +	case IEEE80211_STA_RX_BW_160: +		/* +		 * This applied for both 160 and 80+80. since we use +		 * the returned value to consider degradation of +		 * ctx->conf.min_def, we have to make sure to take +		 * the bigger one (NL80211_CHAN_WIDTH_160). +		 * Otherwise we might try degrading even when not +		 * needed, as the max required sta_bw returned (80+80) +		 * might be smaller than the configured bw (160). +		 */ +		return NL80211_CHAN_WIDTH_160; +	default: +		WARN_ON(1); +		return NL80211_CHAN_WIDTH_20; +	} +} + +static enum nl80211_chan_width +ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata) +{ +	enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; +	struct sta_info *sta; + +	rcu_read_lock(); +	list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { +		if (sdata != sta->sdata && +		    !(sta->sdata->bss && sta->sdata->bss == sdata->bss)) +			continue; + +		if (!sta->uploaded)  			continue; +		max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta)); +	} +	rcu_read_unlock(); + +	return max_bw; +} + +static enum nl80211_chan_width +ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, +				      struct ieee80211_chanctx_conf *conf) +{ +	struct ieee80211_sub_if_data *sdata; +	enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT; + +	rcu_read_lock(); +	list_for_each_entry_rcu(sdata, &local->interfaces, list) { +		struct ieee80211_vif *vif = &sdata->vif; +		enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT; +  		if (!ieee80211_sdata_running(sdata))  			continue; -		if (sdata->vif.type == NL80211_IFTYPE_MONITOR) +		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)  			continue; -		if (sdata->vif.type == NL80211_IFTYPE_STATION && -		    !sdata->u.mgd.associated) +		switch (vif->type) { +		case NL80211_IFTYPE_AP: +		case NL80211_IFTYPE_AP_VLAN: +			width = ieee80211_get_max_required_bw(sdata); +			break; +		case NL80211_IFTYPE_P2P_DEVICE:  			continue; +		case NL80211_IFTYPE_STATION: +		case NL80211_IFTYPE_ADHOC: +		case NL80211_IFTYPE_WDS: +		case NL80211_IFTYPE_MESH_POINT: +			width = vif->bss_conf.chandef.width; +			break; +		case NL80211_IFTYPE_UNSPECIFIED: +		case NUM_NL80211_IFTYPES: +		case NL80211_IFTYPE_MONITOR: +		case NL80211_IFTYPE_P2P_CLIENT: +		case NL80211_IFTYPE_P2P_GO: +			WARN_ON_ONCE(1); +		} +		max_bw = max(max_bw, width); +	} -		if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { -			if (!sdata->u.ibss.ssid_len) -				continue; -			if (!sdata->u.ibss.fixed_channel) -				return CHAN_MODE_HOPPING; +	/* use the configured bandwidth in case of monitor interface */ +	sdata = rcu_dereference(local->monitor_sdata); +	if (sdata && rcu_access_pointer(sdata->vif.chanctx_conf) == conf) +		max_bw = max(max_bw, conf->def.width); + +	rcu_read_unlock(); + +	return max_bw; +} + +/* + * recalc the min required chan width of the channel context, which is + * the max of min required widths of all the interfaces bound to this + * channel context. + */ +void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, +				      struct ieee80211_chanctx *ctx) +{ +	enum nl80211_chan_width max_bw; +	struct cfg80211_chan_def min_def; + +	lockdep_assert_held(&local->chanctx_mtx); + +	/* don't optimize 5MHz, 10MHz, and radar_enabled confs */ +	if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 || +	    ctx->conf.def.width == NL80211_CHAN_WIDTH_10 || +	    ctx->conf.radar_enabled) { +		ctx->conf.min_def = ctx->conf.def; +		return; +	} + +	max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf); + +	/* downgrade chandef up to max_bw */ +	min_def = ctx->conf.def; +	while (min_def.width > max_bw) +		ieee80211_chandef_downgrade(&min_def); + +	if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def)) +		return; + +	ctx->conf.min_def = min_def; +	if (!ctx->driver_present) +		return; + +	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH); +} + +static void ieee80211_change_chanctx(struct ieee80211_local *local, +				     struct ieee80211_chanctx *ctx, +				     const struct cfg80211_chan_def *chandef) +{ +	if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) +		return; + +	WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef)); + +	ctx->conf.def = *chandef; +	drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH); +	ieee80211_recalc_chanctx_min_def(local, ctx); + +	if (!local->use_chanctx) { +		local->_oper_chandef = *chandef; +		ieee80211_hw_config(local, 0); +	} +} + +static struct ieee80211_chanctx * +ieee80211_find_chanctx(struct ieee80211_local *local, +		       const struct cfg80211_chan_def *chandef, +		       enum ieee80211_chanctx_mode mode) +{ +	struct ieee80211_chanctx *ctx; + +	lockdep_assert_held(&local->chanctx_mtx); + +	if (mode == IEEE80211_CHANCTX_EXCLUSIVE) +		return NULL; + +	list_for_each_entry(ctx, &local->chanctx_list, list) { +		const struct cfg80211_chan_def *compat; + +		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) +			continue; + +		compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef); +		if (!compat) +			continue; + +		compat = ieee80211_chanctx_reserved_chandef(local, ctx, +							    compat); +		if (!compat) +			continue; + +		ieee80211_change_chanctx(local, ctx, compat); + +		return ctx; +	} + +	return NULL; +} + +static bool ieee80211_is_radar_required(struct ieee80211_local *local) +{ +	struct ieee80211_sub_if_data *sdata; + +	lockdep_assert_held(&local->mtx); + +	rcu_read_lock(); +	list_for_each_entry_rcu(sdata, &local->interfaces, list) { +		if (sdata->radar_required) { +			rcu_read_unlock(); +			return true; +		} +	} +	rcu_read_unlock(); + +	return false; +} + +static struct ieee80211_chanctx * +ieee80211_alloc_chanctx(struct ieee80211_local *local, +			const struct cfg80211_chan_def *chandef, +			enum ieee80211_chanctx_mode mode) +{ +	struct ieee80211_chanctx *ctx; + +	lockdep_assert_held(&local->chanctx_mtx); + +	ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); +	if (!ctx) +		return NULL; + +	INIT_LIST_HEAD(&ctx->assigned_vifs); +	INIT_LIST_HEAD(&ctx->reserved_vifs); +	ctx->conf.def = *chandef; +	ctx->conf.rx_chains_static = 1; +	ctx->conf.rx_chains_dynamic = 1; +	ctx->mode = mode; +	ctx->conf.radar_enabled = ieee80211_is_radar_required(local); +	ieee80211_recalc_chanctx_min_def(local, ctx); + +	return ctx; +} + +static int ieee80211_add_chanctx(struct ieee80211_local *local, +				 struct ieee80211_chanctx *ctx) +{ +	u32 changed; +	int err; + +	lockdep_assert_held(&local->mtx); +	lockdep_assert_held(&local->chanctx_mtx); + +	if (!local->use_chanctx) +		local->hw.conf.radar_enabled = ctx->conf.radar_enabled; + +	/* turn idle off *before* setting channel -- some drivers need that */ +	changed = ieee80211_idle_off(local); +	if (changed) +		ieee80211_hw_config(local, changed); + +	if (!local->use_chanctx) { +		local->_oper_chandef = ctx->conf.def; +		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); +	} else { +		err = drv_add_chanctx(local, ctx); +		if (err) { +			ieee80211_recalc_idle(local); +			return err;  		} +	} + +	return 0; +} + +static struct ieee80211_chanctx * +ieee80211_new_chanctx(struct ieee80211_local *local, +		      const struct cfg80211_chan_def *chandef, +		      enum ieee80211_chanctx_mode mode) +{ +	struct ieee80211_chanctx *ctx; +	int err; + +	lockdep_assert_held(&local->mtx); +	lockdep_assert_held(&local->chanctx_mtx); + +	ctx = ieee80211_alloc_chanctx(local, chandef, mode); +	if (!ctx) +		return ERR_PTR(-ENOMEM); + +	err = ieee80211_add_chanctx(local, ctx); +	if (err) { +		kfree(ctx); +		return ERR_PTR(err); +	} -		if (sdata->vif.type == NL80211_IFTYPE_AP && -		    !sdata->u.ap.beacon) +	list_add_rcu(&ctx->list, &local->chanctx_list); +	return ctx; +} + +static void ieee80211_del_chanctx(struct ieee80211_local *local, +				  struct ieee80211_chanctx *ctx) +{ +	lockdep_assert_held(&local->chanctx_mtx); + +	if (!local->use_chanctx) { +		struct cfg80211_chan_def *chandef = &local->_oper_chandef; +		chandef->width = NL80211_CHAN_WIDTH_20_NOHT; +		chandef->center_freq1 = chandef->chan->center_freq; +		chandef->center_freq2 = 0; + +		/* NOTE: Disabling radar is only valid here for +		 * single channel context. To be sure, check it ... +		 */ +		WARN_ON(local->hw.conf.radar_enabled && +			!list_empty(&local->chanctx_list)); + +		local->hw.conf.radar_enabled = false; + +		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); +	} else { +		drv_remove_chanctx(local, ctx); +	} + +	ieee80211_recalc_idle(local); +} + +static void ieee80211_free_chanctx(struct ieee80211_local *local, +				   struct ieee80211_chanctx *ctx) +{ +	lockdep_assert_held(&local->chanctx_mtx); + +	WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0); + +	list_del_rcu(&ctx->list); +	ieee80211_del_chanctx(local, ctx); +	kfree_rcu(ctx, rcu_head); +} + +static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, +					      struct ieee80211_chanctx *ctx) +{ +	struct ieee80211_chanctx_conf *conf = &ctx->conf; +	struct ieee80211_sub_if_data *sdata; +	const struct cfg80211_chan_def *compat = NULL; + +	lockdep_assert_held(&local->chanctx_mtx); + +	rcu_read_lock(); +	list_for_each_entry_rcu(sdata, &local->interfaces, list) { + +		if (!ieee80211_sdata_running(sdata))  			continue; +		if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) +			continue; + +		if (!compat) +			compat = &sdata->vif.bss_conf.chandef; + +		compat = cfg80211_chandef_compatible( +				&sdata->vif.bss_conf.chandef, compat); +		if (!compat) +			break; +	} +	rcu_read_unlock(); + +	if (WARN_ON_ONCE(!compat)) +		return; + +	ieee80211_change_chanctx(local, ctx, compat); +} + +static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, +					   struct ieee80211_chanctx *chanctx) +{ +	bool radar_enabled; + +	lockdep_assert_held(&local->chanctx_mtx); +	/* for setting local->radar_detect_enabled */ +	lockdep_assert_held(&local->mtx); + +	radar_enabled = ieee80211_is_radar_required(local); + +	if (radar_enabled == chanctx->conf.radar_enabled) +		return; + +	chanctx->conf.radar_enabled = radar_enabled; +	local->radar_detect_enabled = chanctx->conf.radar_enabled; + +	if (!local->use_chanctx) { +		local->hw.conf.radar_enabled = chanctx->conf.radar_enabled; +		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); +	} + +	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); +} + +static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, +					struct ieee80211_chanctx *new_ctx) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx_conf *conf; +	struct ieee80211_chanctx *curr_ctx = NULL; +	int ret = 0; + +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); + +	if (conf) { +		curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); + +		drv_unassign_vif_chanctx(local, sdata, curr_ctx); +		conf = NULL; +		list_del(&sdata->assigned_chanctx_list); +	} + +	if (new_ctx) { +		ret = drv_assign_vif_chanctx(local, sdata, new_ctx); +		if (ret) +			goto out; + +		conf = &new_ctx->conf; +		list_add(&sdata->assigned_chanctx_list, +			 &new_ctx->assigned_vifs); +	} + +out: +	rcu_assign_pointer(sdata->vif.chanctx_conf, conf); + +	sdata->vif.bss_conf.idle = !conf; -		return CHAN_MODE_FIXED; +	if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) { +		ieee80211_recalc_chanctx_chantype(local, curr_ctx); +		ieee80211_recalc_smps_chanctx(local, curr_ctx); +		ieee80211_recalc_radar_chanctx(local, curr_ctx); +		ieee80211_recalc_chanctx_min_def(local, curr_ctx);  	} -	return CHAN_MODE_UNDEFINED; +	if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) { +		ieee80211_recalc_txpower(sdata); +		ieee80211_recalc_chanctx_min_def(local, new_ctx); +	} + +	if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && +	    sdata->vif.type != NL80211_IFTYPE_MONITOR) +		ieee80211_bss_info_change_notify(sdata, +						 BSS_CHANGED_IDLE); + +	return ret;  } -enum ieee80211_chan_mode -ieee80211_get_channel_mode(struct ieee80211_local *local, -			   struct ieee80211_sub_if_data *ignore) +static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)  { -	enum ieee80211_chan_mode mode; +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx_conf *conf; +	struct ieee80211_chanctx *ctx; + +	lockdep_assert_held(&local->chanctx_mtx); + +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	if (!conf) +		return; + +	ctx = container_of(conf, struct ieee80211_chanctx, conf); -	mutex_lock(&local->iflist_mtx); -	mode = __ieee80211_get_channel_mode(local, ignore); -	mutex_unlock(&local->iflist_mtx); +	if (sdata->reserved_chanctx) +		ieee80211_vif_unreserve_chanctx(sdata); -	return mode; +	ieee80211_assign_vif_chanctx(sdata, NULL); +	if (ieee80211_chanctx_refcount(local, ctx) == 0) +		ieee80211_free_chanctx(local, ctx);  } -bool ieee80211_set_channel_type(struct ieee80211_local *local, -				struct ieee80211_sub_if_data *sdata, -				enum nl80211_channel_type chantype) +void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, +				   struct ieee80211_chanctx *chanctx)  { -	struct ieee80211_sub_if_data *tmp; -	enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT; -	bool result; +	struct ieee80211_sub_if_data *sdata; +	u8 rx_chains_static, rx_chains_dynamic; + +	lockdep_assert_held(&local->chanctx_mtx); + +	rx_chains_static = 1; +	rx_chains_dynamic = 1; -	mutex_lock(&local->iflist_mtx); +	rcu_read_lock(); +	list_for_each_entry_rcu(sdata, &local->interfaces, list) { +		u8 needed_static, needed_dynamic; -	list_for_each_entry(tmp, &local->interfaces, list) { -		if (tmp == sdata) +		if (!ieee80211_sdata_running(sdata))  			continue; -		if (!ieee80211_sdata_running(tmp)) +		if (rcu_access_pointer(sdata->vif.chanctx_conf) != +						&chanctx->conf)  			continue; -		switch (tmp->vif.bss_conf.channel_type) { -		case NL80211_CHAN_NO_HT: -		case NL80211_CHAN_HT20: -			superchan = tmp->vif.bss_conf.channel_type; -			break; -		case NL80211_CHAN_HT40PLUS: -			WARN_ON(superchan == NL80211_CHAN_HT40MINUS); -			superchan = NL80211_CHAN_HT40PLUS; +		switch (sdata->vif.type) { +		case NL80211_IFTYPE_P2P_DEVICE: +			continue; +		case NL80211_IFTYPE_STATION: +			if (!sdata->u.mgd.associated) +				continue;  			break; -		case NL80211_CHAN_HT40MINUS: -			WARN_ON(superchan == NL80211_CHAN_HT40PLUS); -			superchan = NL80211_CHAN_HT40MINUS; +		case NL80211_IFTYPE_AP_VLAN: +			continue; +		case NL80211_IFTYPE_AP: +		case NL80211_IFTYPE_ADHOC: +		case NL80211_IFTYPE_WDS: +		case NL80211_IFTYPE_MESH_POINT:  			break; +		default: +			WARN_ON_ONCE(1);  		} -	} -	switch (superchan) { -	case NL80211_CHAN_NO_HT: -	case NL80211_CHAN_HT20: -		/* -		 * allow any change that doesn't go to no-HT -		 * (if it already is no-HT no change is needed) -		 */ -		if (chantype == NL80211_CHAN_NO_HT) +		switch (sdata->smps_mode) { +		default: +			WARN_ONCE(1, "Invalid SMPS mode %d\n", +				  sdata->smps_mode); +			/* fall through */ +		case IEEE80211_SMPS_OFF: +			needed_static = sdata->needed_rx_chains; +			needed_dynamic = sdata->needed_rx_chains;  			break; -		superchan = chantype; -		break; -	case NL80211_CHAN_HT40PLUS: -	case NL80211_CHAN_HT40MINUS: -		/* allow smaller bandwidth and same */ -		if (chantype == NL80211_CHAN_NO_HT) +		case IEEE80211_SMPS_DYNAMIC: +			needed_static = 1; +			needed_dynamic = sdata->needed_rx_chains;  			break; -		if (chantype == NL80211_CHAN_HT20) +		case IEEE80211_SMPS_STATIC: +			needed_static = 1; +			needed_dynamic = 1;  			break; -		if (superchan == chantype) -			break; -		result = false; +		} + +		rx_chains_static = max(rx_chains_static, needed_static); +		rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); +	} + +	/* Disable SMPS for the monitor interface */ +	sdata = rcu_dereference(local->monitor_sdata); +	if (sdata && +	    rcu_access_pointer(sdata->vif.chanctx_conf) == &chanctx->conf) +		rx_chains_dynamic = rx_chains_static = local->rx_chains; + +	rcu_read_unlock(); + +	if (!local->use_chanctx) { +		if (rx_chains_static > 1) +			local->smps_mode = IEEE80211_SMPS_OFF; +		else if (rx_chains_dynamic > 1) +			local->smps_mode = IEEE80211_SMPS_DYNAMIC; +		else +			local->smps_mode = IEEE80211_SMPS_STATIC; +		ieee80211_hw_config(local, 0); +	} + +	if (rx_chains_static == chanctx->conf.rx_chains_static && +	    rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) +		return; + +	chanctx->conf.rx_chains_static = rx_chains_static; +	chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; +	drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); +} + +int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, +			      const struct cfg80211_chan_def *chandef, +			      enum ieee80211_chanctx_mode mode) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx *ctx; +	u8 radar_detect_width = 0; +	int ret; + +	lockdep_assert_held(&local->mtx); + +	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); + +	mutex_lock(&local->chanctx_mtx); + +	ret = cfg80211_chandef_dfs_required(local->hw.wiphy, +					    chandef, +					    sdata->wdev.iftype); +	if (ret < 0) +		goto out; +	if (ret > 0) +		radar_detect_width = BIT(chandef->width); + +	sdata->radar_required = ret; + +	ret = ieee80211_check_combinations(sdata, chandef, mode, +					   radar_detect_width); +	if (ret < 0) +		goto out; + +	__ieee80211_vif_release_channel(sdata); + +	ctx = ieee80211_find_chanctx(local, chandef, mode); +	if (!ctx) +		ctx = ieee80211_new_chanctx(local, chandef, mode); +	if (IS_ERR(ctx)) { +		ret = PTR_ERR(ctx); +		goto out; +	} + +	sdata->vif.bss_conf.chandef = *chandef; + +	ret = ieee80211_assign_vif_chanctx(sdata, ctx); +	if (ret) { +		/* if assign fails refcount stays the same */ +		if (ieee80211_chanctx_refcount(local, ctx) == 0) +			ieee80211_free_chanctx(local, ctx); +		goto out; +	} + +	ieee80211_recalc_smps_chanctx(local, ctx); +	ieee80211_recalc_radar_chanctx(local, ctx); + out: +	mutex_unlock(&local->chanctx_mtx); +	return ret; +} + +static int __ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, +					  struct ieee80211_chanctx *ctx, +					  u32 *changed) +{ +	struct ieee80211_local *local = sdata->local; +	const struct cfg80211_chan_def *chandef = &sdata->csa_chandef; +	u32 chanctx_changed = 0; + +	if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, +				     IEEE80211_CHAN_DISABLED)) +		return -EINVAL; + +	if (ieee80211_chanctx_refcount(local, ctx) != 1) +		return -EINVAL; + +	if (sdata->vif.bss_conf.chandef.width != chandef->width) { +		chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH; +		*changed |= BSS_CHANGED_BANDWIDTH; +	} + +	sdata->vif.bss_conf.chandef = *chandef; +	ctx->conf.def = *chandef; + +	chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL; +	drv_change_chanctx(local, ctx, chanctx_changed); + +	ieee80211_recalc_chanctx_chantype(local, ctx); +	ieee80211_recalc_smps_chanctx(local, ctx); +	ieee80211_recalc_radar_chanctx(local, ctx); +	ieee80211_recalc_chanctx_min_def(local, ctx); + +	return 0; +} + +int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, +				 u32 *changed) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx_conf *conf; +	struct ieee80211_chanctx *ctx; +	int ret; + +	lockdep_assert_held(&local->mtx); + +	/* should never be called if not performing a channel switch. */ +	if (WARN_ON(!sdata->vif.csa_active)) +		return -EINVAL; + +	mutex_lock(&local->chanctx_mtx); +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	if (!conf) { +		ret = -EINVAL; +		goto out; +	} + +	ctx = container_of(conf, struct ieee80211_chanctx, conf); + +	ret = __ieee80211_vif_change_channel(sdata, ctx, changed); + out: +	mutex_unlock(&local->chanctx_mtx); +	return ret; +} + +static void +__ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, +				      bool clear) +{ +	struct ieee80211_local *local __maybe_unused = sdata->local; +	struct ieee80211_sub_if_data *vlan; +	struct ieee80211_chanctx_conf *conf; + +	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) +		return; + +	lockdep_assert_held(&local->mtx); + +	/* Check that conf exists, even when clearing this function +	 * must be called with the AP's channel context still there +	 * as it would otherwise cause VLANs to have an invalid +	 * channel context pointer for a while, possibly pointing +	 * to a channel context that has already been freed. +	 */ +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	WARN_ON(!conf); + +	if (clear) +		conf = NULL; + +	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) +		rcu_assign_pointer(vlan->vif.chanctx_conf, conf); +} + +void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, +					 bool clear) +{ +	struct ieee80211_local *local = sdata->local; + +	mutex_lock(&local->chanctx_mtx); + +	__ieee80211_vif_copy_chanctx_to_vlans(sdata, clear); + +	mutex_unlock(&local->chanctx_mtx); +} + +int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_chanctx *ctx = sdata->reserved_chanctx; + +	lockdep_assert_held(&sdata->local->chanctx_mtx); + +	if (WARN_ON(!ctx)) +		return -EINVAL; + +	list_del(&sdata->reserved_chanctx_list); +	sdata->reserved_chanctx = NULL; + +	if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) +		ieee80211_free_chanctx(sdata->local, ctx); + +	return 0; +} + +int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, +				  const struct cfg80211_chan_def *chandef, +				  enum ieee80211_chanctx_mode mode, +				  bool radar_required) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx_conf *conf; +	struct ieee80211_chanctx *new_ctx, *curr_ctx; +	int ret = 0; + +	mutex_lock(&local->chanctx_mtx); + +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	if (!conf) { +		ret = -EINVAL; +		goto out; +	} + +	curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); + +	new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); +	if (!new_ctx) { +		if (ieee80211_chanctx_refcount(local, curr_ctx) == 1 && +		    (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) { +			/* if we're the only users of the chanctx and +			 * the driver supports changing a running +			 * context, reserve our current context +			 */ +			new_ctx = curr_ctx; +		} else if (ieee80211_can_create_new_chanctx(local)) { +			/* create a new context and reserve it */ +			new_ctx = ieee80211_new_chanctx(local, chandef, mode); +			if (IS_ERR(new_ctx)) { +				ret = PTR_ERR(new_ctx); +				goto out; +			} +		} else { +			ret = -EBUSY; +			goto out; +		} +	} + +	list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs); +	sdata->reserved_chanctx = new_ctx; +	sdata->reserved_chandef = *chandef; +	sdata->reserved_radar_required = radar_required; +out: +	mutex_unlock(&local->chanctx_mtx); +	return ret; +} + +int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata, +				       u32 *changed) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx *ctx; +	struct ieee80211_chanctx *old_ctx; +	struct ieee80211_chanctx_conf *conf; +	int ret; +	u32 tmp_changed = *changed; + +	/* TODO: need to recheck if the chandef is usable etc.? */ + +	lockdep_assert_held(&local->mtx); + +	mutex_lock(&local->chanctx_mtx); + +	ctx = sdata->reserved_chanctx; +	if (WARN_ON(!ctx)) { +		ret = -EINVAL; +		goto out; +	} + +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	if (!conf) { +		ret = -EINVAL; +		goto out; +	} + +	old_ctx = container_of(conf, struct ieee80211_chanctx, conf); + +	if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width) +		tmp_changed |= BSS_CHANGED_BANDWIDTH; + +	sdata->vif.bss_conf.chandef = sdata->reserved_chandef; + +	/* unref our reservation */ +	sdata->reserved_chanctx = NULL; +	sdata->radar_required = sdata->reserved_radar_required; +	list_del(&sdata->reserved_chanctx_list); + +	if (old_ctx == ctx) { +		/* This is our own context, just change it */ +		ret = __ieee80211_vif_change_channel(sdata, old_ctx, +						     &tmp_changed); +		if (ret) +			goto out; +	} else { +		ret = ieee80211_assign_vif_chanctx(sdata, ctx); +		if (ieee80211_chanctx_refcount(local, old_ctx) == 0) +			ieee80211_free_chanctx(local, old_ctx); +		if (ret) { +			/* if assign fails refcount stays the same */ +			if (ieee80211_chanctx_refcount(local, ctx) == 0) +				ieee80211_free_chanctx(local, ctx); +			goto out; +		} + +		if (sdata->vif.type == NL80211_IFTYPE_AP) +			__ieee80211_vif_copy_chanctx_to_vlans(sdata, false); +	} + +	*changed = tmp_changed; + +	ieee80211_recalc_chanctx_chantype(local, ctx); +	ieee80211_recalc_smps_chanctx(local, ctx); +	ieee80211_recalc_radar_chanctx(local, ctx); +	ieee80211_recalc_chanctx_min_def(local, ctx); +out: +	mutex_unlock(&local->chanctx_mtx); +	return ret; +} + +int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, +				   const struct cfg80211_chan_def *chandef, +				   u32 *changed) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx_conf *conf; +	struct ieee80211_chanctx *ctx; +	int ret; + +	if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, +				     IEEE80211_CHAN_DISABLED)) +		return -EINVAL; + +	mutex_lock(&local->chanctx_mtx); +	if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) { +		ret = 0; +		goto out; +	} + +	if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT || +	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) { +		ret = -EINVAL; +		goto out; +	} + +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	if (!conf) { +		ret = -EINVAL; +		goto out; +	} + +	ctx = container_of(conf, struct ieee80211_chanctx, conf); +	if (!cfg80211_chandef_compatible(&conf->def, chandef)) { +		ret = -EINVAL;  		goto out;  	} -	local->_oper_channel_type = superchan; +	sdata->vif.bss_conf.chandef = *chandef; -	if (sdata) -		sdata->vif.bss_conf.channel_type = chantype; +	ieee80211_recalc_chanctx_chantype(local, ctx); -	result = true; +	*changed |= BSS_CHANGED_BANDWIDTH; +	ret = 0;   out: -	mutex_unlock(&local->iflist_mtx); +	mutex_unlock(&local->chanctx_mtx); +	return ret; +} + +void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) +{ +	WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); + +	lockdep_assert_held(&sdata->local->mtx); + +	mutex_lock(&sdata->local->chanctx_mtx); +	__ieee80211_vif_release_channel(sdata); +	mutex_unlock(&sdata->local->chanctx_mtx); +} + +void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_sub_if_data *ap; +	struct ieee80211_chanctx_conf *conf; + +	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss)) +		return; + +	ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); + +	mutex_lock(&local->chanctx_mtx); + +	conf = rcu_dereference_protected(ap->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	rcu_assign_pointer(sdata->vif.chanctx_conf, conf); +	mutex_unlock(&local->chanctx_mtx); +} + +void ieee80211_iter_chan_contexts_atomic( +	struct ieee80211_hw *hw, +	void (*iter)(struct ieee80211_hw *hw, +		     struct ieee80211_chanctx_conf *chanctx_conf, +		     void *data), +	void *iter_data) +{ +	struct ieee80211_local *local = hw_to_local(hw); +	struct ieee80211_chanctx *ctx; -	return result; +	rcu_read_lock(); +	list_for_each_entry_rcu(ctx, &local->chanctx_list, list) +		if (ctx->driver_present) +			iter(hw, &ctx->conf, iter_data); +	rcu_read_unlock();  } +EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h new file mode 100644 index 00000000000..493d68061f0 --- /dev/null +++ b/net/mac80211/debug.h @@ -0,0 +1,190 @@ +#ifndef __MAC80211_DEBUG_H +#define __MAC80211_DEBUG_H +#include <net/cfg80211.h> + +#ifdef CONFIG_MAC80211_IBSS_DEBUG +#define MAC80211_IBSS_DEBUG 1 +#else +#define MAC80211_IBSS_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_PS_DEBUG +#define MAC80211_PS_DEBUG 1 +#else +#define MAC80211_PS_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_HT_DEBUG +#define MAC80211_HT_DEBUG 1 +#else +#define MAC80211_HT_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_MPL_DEBUG +#define MAC80211_MPL_DEBUG 1 +#else +#define MAC80211_MPL_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_MPATH_DEBUG +#define MAC80211_MPATH_DEBUG 1 +#else +#define MAC80211_MPATH_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_MHWMP_DEBUG +#define MAC80211_MHWMP_DEBUG 1 +#else +#define MAC80211_MHWMP_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_MESH_SYNC_DEBUG +#define MAC80211_MESH_SYNC_DEBUG 1 +#else +#define MAC80211_MESH_SYNC_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_MESH_CSA_DEBUG +#define MAC80211_MESH_CSA_DEBUG 1 +#else +#define MAC80211_MESH_CSA_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_MESH_PS_DEBUG +#define MAC80211_MESH_PS_DEBUG 1 +#else +#define MAC80211_MESH_PS_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_TDLS_DEBUG +#define MAC80211_TDLS_DEBUG 1 +#else +#define MAC80211_TDLS_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_STA_DEBUG +#define MAC80211_STA_DEBUG 1 +#else +#define MAC80211_STA_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_MLME_DEBUG +#define MAC80211_MLME_DEBUG 1 +#else +#define MAC80211_MLME_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_MESSAGE_TRACING +void __sdata_info(const char *fmt, ...) __printf(1, 2); +void __sdata_dbg(bool print, const char *fmt, ...) __printf(2, 3); +void __sdata_err(const char *fmt, ...) __printf(1, 2); +void __wiphy_dbg(struct wiphy *wiphy, bool print, const char *fmt, ...) +	__printf(3, 4); + +#define _sdata_info(sdata, fmt, ...)					\ +	__sdata_info("%s: " fmt, (sdata)->name, ##__VA_ARGS__) +#define _sdata_dbg(print, sdata, fmt, ...)				\ +	__sdata_dbg(print, "%s: " fmt, (sdata)->name, ##__VA_ARGS__) +#define _sdata_err(sdata, fmt, ...)					\ +	__sdata_err("%s: " fmt, (sdata)->name, ##__VA_ARGS__) +#define _wiphy_dbg(print, wiphy, fmt, ...)				\ +	__wiphy_dbg(wiphy, print, fmt, ##__VA_ARGS__) +#else +#define _sdata_info(sdata, fmt, ...)					\ +do {									\ +	pr_info("%s: " fmt,						\ +		(sdata)->name, ##__VA_ARGS__);				\ +} while (0) + +#define _sdata_dbg(print, sdata, fmt, ...)				\ +do {									\ +	if (print)							\ +		pr_debug("%s: " fmt,					\ +			 (sdata)->name, ##__VA_ARGS__);			\ +} while (0) + +#define _sdata_err(sdata, fmt, ...)					\ +do {									\ +	pr_err("%s: " fmt,						\ +	       (sdata)->name, ##__VA_ARGS__);				\ +} while (0) + +#define _wiphy_dbg(print, wiphy, fmt, ...)				\ +do {									\ +	if (print)							\ +		wiphy_dbg((wiphy), fmt, ##__VA_ARGS__);			\ +} while (0) +#endif + +#define sdata_info(sdata, fmt, ...)					\ +	_sdata_info(sdata, fmt, ##__VA_ARGS__) +#define sdata_err(sdata, fmt, ...)					\ +	_sdata_err(sdata, fmt, ##__VA_ARGS__) +#define sdata_dbg(sdata, fmt, ...)					\ +	_sdata_dbg(1, sdata, fmt, ##__VA_ARGS__) + +#define ht_dbg(sdata, fmt, ...)						\ +	_sdata_dbg(MAC80211_HT_DEBUG,					\ +		   sdata, fmt, ##__VA_ARGS__) + +#define ht_dbg_ratelimited(sdata, fmt, ...)				\ +	_sdata_dbg(MAC80211_HT_DEBUG && net_ratelimit(),		\ +		   sdata, fmt, ##__VA_ARGS__) + +#define ibss_dbg(sdata, fmt, ...)					\ +	_sdata_dbg(MAC80211_IBSS_DEBUG,					\ +		   sdata, fmt, ##__VA_ARGS__) + +#define ps_dbg(sdata, fmt, ...)						\ +	_sdata_dbg(MAC80211_PS_DEBUG,					\ +		   sdata, fmt, ##__VA_ARGS__) + +#define ps_dbg_hw(hw, fmt, ...)						\ +	_wiphy_dbg(MAC80211_PS_DEBUG,					\ +		   (hw)->wiphy, fmt, ##__VA_ARGS__) + +#define ps_dbg_ratelimited(sdata, fmt, ...)				\ +	_sdata_dbg(MAC80211_PS_DEBUG && net_ratelimit(),		\ +		   sdata, fmt, ##__VA_ARGS__) + +#define mpl_dbg(sdata, fmt, ...)					\ +	_sdata_dbg(MAC80211_MPL_DEBUG,					\ +		   sdata, fmt, ##__VA_ARGS__) + +#define mpath_dbg(sdata, fmt, ...)					\ +	_sdata_dbg(MAC80211_MPATH_DEBUG,				\ +		   sdata, fmt, ##__VA_ARGS__) + +#define mhwmp_dbg(sdata, fmt, ...)					\ +	_sdata_dbg(MAC80211_MHWMP_DEBUG,				\ +		   sdata, fmt, ##__VA_ARGS__) + +#define msync_dbg(sdata, fmt, ...)					\ +	_sdata_dbg(MAC80211_MESH_SYNC_DEBUG,				\ +		   sdata, fmt, ##__VA_ARGS__) + +#define mcsa_dbg(sdata, fmt, ...)					\ +	_sdata_dbg(MAC80211_MESH_CSA_DEBUG,				\ +		   sdata, fmt, ##__VA_ARGS__) + +#define mps_dbg(sdata, fmt, ...)					\ +	_sdata_dbg(MAC80211_MESH_PS_DEBUG,				\ +		   sdata, fmt, ##__VA_ARGS__) + +#define tdls_dbg(sdata, fmt, ...)					\ +	_sdata_dbg(MAC80211_TDLS_DEBUG,					\ +		   sdata, fmt, ##__VA_ARGS__) + +#define sta_dbg(sdata, fmt, ...)					\ +	_sdata_dbg(MAC80211_STA_DEBUG,					\ +		   sdata, fmt, ##__VA_ARGS__) + +#define mlme_dbg(sdata, fmt, ...)					\ +	_sdata_dbg(MAC80211_MLME_DEBUG,					\ +		   sdata, fmt, ##__VA_ARGS__) + +#define mlme_dbg_ratelimited(sdata, fmt, ...)				\ +	_sdata_dbg(MAC80211_MLME_DEBUG && net_ratelimit(),		\ +		   sdata, fmt, ##__VA_ARGS__) + +#endif /* __MAC80211_DEBUG_H */ diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 1f02e599a31..0e963bc1cea 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -15,13 +15,173 @@  #include "rate.h"  #include "debugfs.h" -int mac80211_open_file_generic(struct inode *inode, struct file *file) +#define DEBUGFS_FORMAT_BUFFER_SIZE 100 + +#define TX_LATENCY_BIN_DELIMTER_C ',' +#define TX_LATENCY_BIN_DELIMTER_S "," +#define TX_LATENCY_BINS_DISABLED "enable(bins disabled)\n" +#define TX_LATENCY_DISABLED "disable\n" + + +/* + * Display if Tx latency statistics & bins are enabled/disabled + */ +static ssize_t sta_tx_latency_stat_read(struct file *file, +					char __user *userbuf, +					size_t count, loff_t *ppos)  { -	file->private_data = inode->i_private; -	return 0; +	struct ieee80211_local *local = file->private_data; +	struct ieee80211_tx_latency_bin_ranges  *tx_latency; +	char *buf; +	int bufsz, i, ret; +	int pos = 0; + +	rcu_read_lock(); + +	tx_latency = rcu_dereference(local->tx_latency); + +	if (tx_latency && tx_latency->n_ranges) { +		bufsz = tx_latency->n_ranges * 15; +		buf = kzalloc(bufsz, GFP_ATOMIC); +		if (!buf) +			goto err; + +		for (i = 0; i < tx_latency->n_ranges; i++) +			pos += scnprintf(buf + pos, bufsz - pos, "%d,", +					 tx_latency->ranges[i]); +		pos += scnprintf(buf + pos, bufsz - pos, "\n"); +	} else if (tx_latency) { +		bufsz = sizeof(TX_LATENCY_BINS_DISABLED) + 1; +		buf = kzalloc(bufsz, GFP_ATOMIC); +		if (!buf) +			goto err; + +		pos += scnprintf(buf + pos, bufsz - pos, "%s\n", +				 TX_LATENCY_BINS_DISABLED); +	} else { +		bufsz = sizeof(TX_LATENCY_DISABLED) + 1; +		buf = kzalloc(bufsz, GFP_ATOMIC); +		if (!buf) +			goto err; + +		pos += scnprintf(buf + pos, bufsz - pos, "%s\n", +				 TX_LATENCY_DISABLED); +	} + +	rcu_read_unlock(); + +	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); +	kfree(buf); + +	return ret; +err: +	rcu_read_unlock(); +	return -ENOMEM;  } -#define DEBUGFS_FORMAT_BUFFER_SIZE 100 +/* + * Receive input from user regarding Tx latency statistics + * The input should indicate if Tx latency statistics and bins are + * enabled/disabled. + * If bins are enabled input should indicate the amount of different bins and + * their ranges. Each bin will count how many Tx frames transmitted within the + * appropriate latency. + * Legal input is: + * a) "enable(bins disabled)" - to enable only general statistics + * b) "a,b,c,d,...z" - to enable general statistics and bins, where all are + * numbers and a < b < c < d.. < z + * c) "disable" - disable all statistics + * NOTE: must configure Tx latency statistics bins before stations connected. + */ + +static ssize_t sta_tx_latency_stat_write(struct file *file, +					 const char __user *userbuf, +					 size_t count, loff_t *ppos) +{ +	struct ieee80211_local *local = file->private_data; +	char buf[128] = {}; +	char *bins = buf; +	char *token; +	int buf_size, i, alloc_size; +	int prev_bin = 0; +	int n_ranges = 0; +	int ret = count; +	struct ieee80211_tx_latency_bin_ranges  *tx_latency; + +	if (sizeof(buf) <= count) +		return -EINVAL; +	buf_size = count; +	if (copy_from_user(buf, userbuf, buf_size)) +		return -EFAULT; + +	mutex_lock(&local->sta_mtx); + +	/* cannot change config once we have stations */ +	if (local->num_sta) +		goto unlock; + +	tx_latency = +		rcu_dereference_protected(local->tx_latency, +					  lockdep_is_held(&local->sta_mtx)); + +	/* disable Tx statistics */ +	if (!strcmp(buf, TX_LATENCY_DISABLED)) { +		if (!tx_latency) +			goto unlock; +		RCU_INIT_POINTER(local->tx_latency, NULL); +		synchronize_rcu(); +		kfree(tx_latency); +		goto unlock; +	} + +	/* Tx latency already enabled */ +	if (tx_latency) +		goto unlock; + +	if (strcmp(TX_LATENCY_BINS_DISABLED, buf)) { +		/* check how many bins and between what ranges user requested */ +		token = buf; +		while (*token != '\0') { +			if (*token == TX_LATENCY_BIN_DELIMTER_C) +				n_ranges++; +			token++; +		} +		n_ranges++; +	} + +	alloc_size = sizeof(struct ieee80211_tx_latency_bin_ranges) + +		     n_ranges * sizeof(u32); +	tx_latency = kzalloc(alloc_size, GFP_ATOMIC); +	if (!tx_latency) { +		ret = -ENOMEM; +		goto unlock; +	} +	tx_latency->n_ranges = n_ranges; +	for (i = 0; i < n_ranges; i++) { /* setting bin ranges */ +		token = strsep(&bins, TX_LATENCY_BIN_DELIMTER_S); +		sscanf(token, "%d", &tx_latency->ranges[i]); +		/* bins values should be in ascending order */ +		if (prev_bin >= tx_latency->ranges[i]) { +			ret = -EINVAL; +			kfree(tx_latency); +			goto unlock; +		} +		prev_bin = tx_latency->ranges[i]; +	} +	rcu_assign_pointer(local->tx_latency, tx_latency); + +unlock: +	mutex_unlock(&local->sta_mtx); + +	return ret; +} + +static const struct file_operations stats_tx_latency_ops = { +	.write = sta_tx_latency_stat_write, +	.read = sta_tx_latency_stat_read, +	.open = simple_open, +	.llseek = generic_file_llseek, +};  int mac80211_format_buffer(char __user *userbuf, size_t count,  				  loff_t *ppos, char *fmt, ...) @@ -37,7 +197,7 @@ int mac80211_format_buffer(char __user *userbuf, size_t count,  	return simple_read_from_buffer(userbuf, count, ppos, buf, res);  } -#define DEBUGFS_READONLY_FILE(name, fmt, value...)			\ +#define DEBUGFS_READONLY_FILE_FN(name, fmt, value...)			\  static ssize_t name## _read(struct file *file, char __user *userbuf,	\  			    size_t count, loff_t *ppos)			\  {									\ @@ -45,14 +205,19 @@ static ssize_t name## _read(struct file *file, char __user *userbuf,	\  									\  	return mac80211_format_buffer(userbuf, count, ppos, 		\  				      fmt "\n", ##value);		\ -}									\ -									\ +} + +#define DEBUGFS_READONLY_FILE_OPS(name)			\  static const struct file_operations name## _ops = {			\  	.read = name## _read,						\ -	.open = mac80211_open_file_generic,				\ +	.open = simple_open,						\  	.llseek = generic_file_llseek,					\  }; +#define DEBUGFS_READONLY_FILE(name, fmt, value...)		\ +	DEBUGFS_READONLY_FILE_FN(name, fmt, value)		\ +	DEBUGFS_READONLY_FILE_OPS(name) +  #define DEBUGFS_ADD(name)						\  	debugfs_create_file(#name, 0400, phyd, local, &name## _ops); @@ -60,8 +225,10 @@ static const struct file_operations name## _ops = {			\  	debugfs_create_file(#name, mode, phyd, local, &name## _ops); -DEBUGFS_READONLY_FILE(frequency, "%d", -		      local->hw.conf.channel->center_freq); +DEBUGFS_READONLY_FILE(user_power, "%d", +		      local->user_power_level); +DEBUGFS_READONLY_FILE(power, "%d", +		      local->hw.conf.power_level);  DEBUGFS_READONLY_FILE(total_ps_buffered, "%d",  		      local->total_ps_buffered);  DEBUGFS_READONLY_FILE(wep_iv, "%#08x", @@ -69,64 +236,14 @@ DEBUGFS_READONLY_FILE(wep_iv, "%#08x",  DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s",  	local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver"); -static ssize_t tsf_read(struct file *file, char __user *user_buf, -			     size_t count, loff_t *ppos) -{ -	struct ieee80211_local *local = file->private_data; -	u64 tsf; - -	tsf = drv_get_tsf(local); - -	return mac80211_format_buffer(user_buf, count, ppos, "0x%016llx\n", -				      (unsigned long long) tsf); -} - -static ssize_t tsf_write(struct file *file, -                         const char __user *user_buf, -                         size_t count, loff_t *ppos) -{ -	struct ieee80211_local *local = file->private_data; -	unsigned long long tsf; -	char buf[100]; -	size_t len; - -	len = min(count, sizeof(buf) - 1); -	if (copy_from_user(buf, user_buf, len)) -		return -EFAULT; -	buf[len] = '\0'; - -	if (strncmp(buf, "reset", 5) == 0) { -		if (local->ops->reset_tsf) { -			drv_reset_tsf(local); -			wiphy_info(local->hw.wiphy, "debugfs reset TSF\n"); -		} -	} else { -		tsf = simple_strtoul(buf, NULL, 0); -		if (local->ops->set_tsf) { -			drv_set_tsf(local, tsf); -			wiphy_info(local->hw.wiphy, -				   "debugfs set TSF to %#018llx\n", tsf); - -		} -	} - -	return count; -} - -static const struct file_operations tsf_ops = { -	.read = tsf_read, -	.write = tsf_write, -	.open = mac80211_open_file_generic, -	.llseek = default_llseek, -}; - +#ifdef CONFIG_PM  static ssize_t reset_write(struct file *file, const char __user *user_buf,  			   size_t count, loff_t *ppos)  {  	struct ieee80211_local *local = file->private_data;  	rtnl_lock(); -	__ieee80211_suspend(&local->hw); +	__ieee80211_suspend(&local->hw, NULL);  	__ieee80211_resume(&local->hw);  	rtnl_unlock(); @@ -135,164 +252,80 @@ static ssize_t reset_write(struct file *file, const char __user *user_buf,  static const struct file_operations reset_ops = {  	.write = reset_write, -	.open = mac80211_open_file_generic, +	.open = simple_open,  	.llseek = noop_llseek,  }; +#endif -static ssize_t noack_read(struct file *file, char __user *user_buf, -			  size_t count, loff_t *ppos) -{ -	struct ieee80211_local *local = file->private_data; - -	return mac80211_format_buffer(user_buf, count, ppos, "%d\n", -				      local->wifi_wme_noack_test); -} - -static ssize_t noack_write(struct file *file, -			   const char __user *user_buf, -			   size_t count, loff_t *ppos) -{ -	struct ieee80211_local *local = file->private_data; -	char buf[10]; -	size_t len; - -	len = min(count, sizeof(buf) - 1); -	if (copy_from_user(buf, user_buf, len)) -		return -EFAULT; -	buf[len] = '\0'; - -	local->wifi_wme_noack_test = !!simple_strtoul(buf, NULL, 0); - -	return count; -} - -static const struct file_operations noack_ops = { -	.read = noack_read, -	.write = noack_write, -	.open = mac80211_open_file_generic, -	.llseek = default_llseek, -}; - -static ssize_t uapsd_queues_read(struct file *file, char __user *user_buf, -				 size_t count, loff_t *ppos) -{ -	struct ieee80211_local *local = file->private_data; -	return mac80211_format_buffer(user_buf, count, ppos, "0x%x\n", -				      local->uapsd_queues); -} - -static ssize_t uapsd_queues_write(struct file *file, -				  const char __user *user_buf, -				  size_t count, loff_t *ppos) -{ -	struct ieee80211_local *local = file->private_data; -	unsigned long val; -	char buf[10]; -	size_t len; -	int ret; - -	len = min(count, sizeof(buf) - 1); -	if (copy_from_user(buf, user_buf, len)) -		return -EFAULT; -	buf[len] = '\0'; - -	ret = strict_strtoul(buf, 0, &val); - -	if (ret) -		return -EINVAL; - -	if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) -		return -ERANGE; - -	local->uapsd_queues = val; - -	return count; -} - -static const struct file_operations uapsd_queues_ops = { -	.read = uapsd_queues_read, -	.write = uapsd_queues_write, -	.open = mac80211_open_file_generic, -	.llseek = default_llseek, -}; - -static ssize_t uapsd_max_sp_len_read(struct file *file, char __user *user_buf, -				     size_t count, loff_t *ppos) -{ -	struct ieee80211_local *local = file->private_data; - -	return mac80211_format_buffer(user_buf, count, ppos, "0x%x\n", -				      local->uapsd_max_sp_len); -} - -static ssize_t uapsd_max_sp_len_write(struct file *file, -				      const char __user *user_buf, -				      size_t count, loff_t *ppos) -{ -	struct ieee80211_local *local = file->private_data; -	unsigned long val; -	char buf[10]; -	size_t len; -	int ret; - -	len = min(count, sizeof(buf) - 1); -	if (copy_from_user(buf, user_buf, len)) -		return -EFAULT; -	buf[len] = '\0'; - -	ret = strict_strtoul(buf, 0, &val); - -	if (ret) -		return -EINVAL; - -	if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) -		return -ERANGE; - -	local->uapsd_max_sp_len = val; - -	return count; -} - -static const struct file_operations uapsd_max_sp_len_ops = { -	.read = uapsd_max_sp_len_read, -	.write = uapsd_max_sp_len_write, -	.open = mac80211_open_file_generic, -	.llseek = default_llseek, -}; - -static ssize_t channel_type_read(struct file *file, char __user *user_buf, -		       size_t count, loff_t *ppos) +static ssize_t hwflags_read(struct file *file, char __user *user_buf, +			    size_t count, loff_t *ppos)  {  	struct ieee80211_local *local = file->private_data; -	const char *buf; - -	switch (local->hw.conf.channel_type) { -	case NL80211_CHAN_NO_HT: -		buf = "no ht\n"; -		break; -	case NL80211_CHAN_HT20: -		buf = "ht20\n"; -		break; -	case NL80211_CHAN_HT40MINUS: -		buf = "ht40-\n"; -		break; -	case NL80211_CHAN_HT40PLUS: -		buf = "ht40+\n"; -		break; -	default: -		buf = "???"; -		break; -	} - -	return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); +	int mxln = 500; +	ssize_t rv; +	char *buf = kzalloc(mxln, GFP_KERNEL); +	int sf = 0; /* how many written so far */ + +	if (!buf) +		return 0; + +	sf += scnprintf(buf, mxln - sf, "0x%x\n", local->hw.flags); +	if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) +		sf += scnprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n"); +	if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) +		sf += scnprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n"); +	if (local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) +		sf += scnprintf(buf + sf, mxln - sf, +				"HOST_BCAST_PS_BUFFERING\n"); +	if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE) +		sf += scnprintf(buf + sf, mxln - sf, +				"2GHZ_SHORT_SLOT_INCAPABLE\n"); +	if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE) +		sf += scnprintf(buf + sf, mxln - sf, +				"2GHZ_SHORT_PREAMBLE_INCAPABLE\n"); +	if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) +		sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n"); +	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) +		sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n"); +	if (local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC) +		sf += scnprintf(buf + sf, mxln - sf, +				"NEED_DTIM_BEFORE_ASSOC\n"); +	if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT) +		sf += scnprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n"); +	if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) +		sf += scnprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n"); +	if (local->hw.flags & IEEE80211_HW_SUPPORTS_PS) +		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n"); +	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) +		sf += scnprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n"); +	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) +		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n"); +	if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE) +		sf += scnprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n"); +	if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) +		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n"); +	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) +		sf += scnprintf(buf + sf, mxln - sf, +				"SUPPORTS_DYNAMIC_SMPS\n"); +	if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) +		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n"); +	if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) +		sf += scnprintf(buf + sf, mxln - sf, +				"REPORTS_TX_ACK_STATUS\n"); +	if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) +		sf += scnprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n"); +	if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK) +		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n"); +	if (local->hw.flags & IEEE80211_HW_AP_LINK_PS) +		sf += scnprintf(buf + sf, mxln - sf, "AP_LINK_PS\n"); +	if (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW) +		sf += scnprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n"); + +	rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); +	kfree(buf); +	return rv;  } -static const struct file_operations channel_type_ops = { -	.read = channel_type_read, -	.open = mac80211_open_file_generic, -	.llseek = default_llseek, -}; -  static ssize_t queues_read(struct file *file, char __user *user_buf,  			   size_t count, loff_t *ppos)  { @@ -311,11 +344,8 @@ static ssize_t queues_read(struct file *file, char __user *user_buf,  	return simple_read_from_buffer(user_buf, count, ppos, buf, res);  } -static const struct file_operations queues_ops = { -	.read = queues_read, -	.open = mac80211_open_file_generic, -	.llseek = default_llseek, -}; +DEBUGFS_READONLY_FILE_OPS(hwflags); +DEBUGFS_READONLY_FILE_OPS(queues);  /* statistics stuff */ @@ -357,7 +387,7 @@ static ssize_t stats_ ##name## _read(struct file *file,			\  									\  static const struct file_operations stats_ ##name## _ops = {		\  	.read = stats_ ##name## _read,					\ -	.open = mac80211_open_file_generic,				\ +	.open = simple_open,						\  	.llseek = generic_file_llseek,					\  }; @@ -381,16 +411,15 @@ void debugfs_hw_add(struct ieee80211_local *local)  	local->debugfs.keys = debugfs_create_dir("keys", phyd); -	DEBUGFS_ADD(frequency);  	DEBUGFS_ADD(total_ps_buffered);  	DEBUGFS_ADD(wep_iv); -	DEBUGFS_ADD(tsf);  	DEBUGFS_ADD(queues); +#ifdef CONFIG_PM  	DEBUGFS_ADD_MODE(reset, 0200); -	DEBUGFS_ADD(noack); -	DEBUGFS_ADD(uapsd_queues); -	DEBUGFS_ADD(uapsd_max_sp_len); -	DEBUGFS_ADD(channel_type); +#endif +	DEBUGFS_ADD(hwflags); +	DEBUGFS_ADD(user_power); +	DEBUGFS_ADD(power);  	statsd = debugfs_create_dir("statistics", phyd); @@ -435,8 +464,6 @@ void debugfs_hw_add(struct ieee80211_local *local)  		local->rx_handlers_drop_defrag);  	DEBUGFS_STATS_ADD(rx_handlers_drop_short,  		local->rx_handlers_drop_short); -	DEBUGFS_STATS_ADD(rx_handlers_drop_passive_scan, -		local->rx_handlers_drop_passive_scan);  	DEBUGFS_STATS_ADD(tx_expand_skb_head,  		local->tx_expand_skb_head);  	DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned, @@ -454,4 +481,6 @@ void debugfs_hw_add(struct ieee80211_local *local)  	DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount);  	DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount);  	DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount); + +	DEBUGFS_DEVSTATS_ADD(tx_latency);  } diff --git a/net/mac80211/debugfs.h b/net/mac80211/debugfs.h index 7c87529630f..60c35afee29 100644 --- a/net/mac80211/debugfs.h +++ b/net/mac80211/debugfs.h @@ -1,11 +1,12 @@  #ifndef __MAC80211_DEBUGFS_H  #define __MAC80211_DEBUGFS_H +#include "ieee80211_i.h" +  #ifdef CONFIG_MAC80211_DEBUGFS -extern void debugfs_hw_add(struct ieee80211_local *local); -extern int mac80211_open_file_generic(struct inode *inode, struct file *file); -extern int mac80211_format_buffer(char __user *userbuf, size_t count, -				  loff_t *ppos, char *fmt, ...); +void debugfs_hw_add(struct ieee80211_local *local); +int __printf(4, 5) mac80211_format_buffer(char __user *userbuf, size_t count, +					  loff_t *ppos, char *fmt, ...);  #else  static inline void debugfs_hw_add(struct ieee80211_local *local)  { diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 5822a6ce767..1521cabad3d 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -30,7 +30,7 @@ static ssize_t key_##name##_read(struct file *file,			\  #define KEY_OPS(name)							\  static const struct file_operations key_ ##name## _ops = {		\  	.read = key_##name##_read,					\ -	.open = mac80211_open_file_generic,				\ +	.open = simple_open,						\  	.llseek = generic_file_llseek,					\  } @@ -45,7 +45,7 @@ static const struct file_operations key_ ##name## _ops = {		\  #define KEY_CONF_OPS(name)						\  static const struct file_operations key_ ##name## _ops = {		\  	.read = key_conf_##name##_read,					\ -	.open = mac80211_open_file_generic,				\ +	.open = simple_open,						\  	.llseek = generic_file_llseek,					\  } @@ -78,7 +78,7 @@ KEY_OPS(algorithm);  static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,  				size_t count, loff_t *ppos)  { -	const u8 *tpn; +	u64 pn;  	char buf[20];  	int len;  	struct ieee80211_key *key = file->private_data; @@ -94,15 +94,16 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,  				key->u.tkip.tx.iv16);  		break;  	case WLAN_CIPHER_SUITE_CCMP: -		tpn = key->u.ccmp.tx_pn; +		pn = atomic64_read(&key->u.ccmp.tx_pn);  		len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", -				tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]); +				(u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24), +				(u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);  		break;  	case WLAN_CIPHER_SUITE_AES_CMAC: -		tpn = key->u.aes_cmac.tx_pn; +		pn = atomic64_read(&key->u.aes_cmac.tx_pn);  		len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", -				tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], -				tpn[5]); +				(u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24), +				(u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);  		break;  	default:  		return 0; @@ -115,7 +116,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,  				size_t count, loff_t *ppos)  {  	struct ieee80211_key *key = file->private_data; -	char buf[14*NUM_RX_DATA_QUEUES+1], *p = buf; +	char buf[14*IEEE80211_NUM_TIDS+1], *p = buf;  	int i, len;  	const u8 *rpn; @@ -125,7 +126,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,  		len = scnprintf(buf, sizeof(buf), "\n");  		break;  	case WLAN_CIPHER_SUITE_TKIP: -		for (i = 0; i < NUM_RX_DATA_QUEUES; i++) +		for (i = 0; i < IEEE80211_NUM_TIDS; i++)  			p += scnprintf(p, sizeof(buf)+buf-p,  				       "%08x %04x\n",  				       key->u.tkip.rx[i].iv32, @@ -133,7 +134,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,  		len = p - buf;  		break;  	case WLAN_CIPHER_SUITE_CCMP: -		for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++) { +		for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {  			rpn = key->u.ccmp.rx_pn[i];  			p += scnprintf(p, sizeof(buf)+buf-p,  				       "%02x%02x%02x%02x%02x%02x\n", @@ -198,6 +199,22 @@ static ssize_t key_icverrors_read(struct file *file, char __user *userbuf,  }  KEY_OPS(icverrors); +static ssize_t key_mic_failures_read(struct file *file, char __user *userbuf, +				     size_t count, loff_t *ppos) +{ +	struct ieee80211_key *key = file->private_data; +	char buf[20]; +	int len; + +	if (key->conf.cipher != WLAN_CIPHER_SUITE_TKIP) +		return -EINVAL; + +	len = scnprintf(buf, sizeof(buf), "%u\n", key->u.tkip.mic_failures); + +	return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} +KEY_OPS(mic_failures); +  static ssize_t key_key_read(struct file *file, char __user *userbuf,  			    size_t count, loff_t *ppos)  { @@ -224,9 +241,9 @@ KEY_OPS(key);  			    key, &key_##name##_ops);  void ieee80211_debugfs_key_add(struct ieee80211_key *key) -  { +{  	static int keycount; -	char buf[50]; +	char buf[100];  	struct sta_info *sta;  	if (!key->local->debugfs.keys) @@ -241,16 +258,13 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)  	if (!key->debugfs.dir)  		return; -	rcu_read_lock(); -	sta = rcu_dereference(key->sta); -	if (sta) -		sprintf(buf, "../../stations/%pM", sta->sta.addr); -	rcu_read_unlock(); - -	/* using sta as a boolean is fine outside RCU lock */ -	if (sta) +	sta = key->sta; +	if (sta) { +		sprintf(buf, "../../netdev:%s/stations/%pM", +			sta->sdata->name, sta->sta.addr);  		key->debugfs.stalink =  			debugfs_create_symlink("station", key->debugfs.dir, buf); +	}  	DEBUGFS_ADD(keylen);  	DEBUGFS_ADD(flags); @@ -262,6 +276,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)  	DEBUGFS_ADD(rx_spec);  	DEBUGFS_ADD(replays);  	DEBUGFS_ADD(icverrors); +	DEBUGFS_ADD(mic_failures);  	DEBUGFS_ADD(key);  	DEBUGFS_ADD(ifindex);  }; @@ -274,33 +289,44 @@ void ieee80211_debugfs_key_remove(struct ieee80211_key *key)  	debugfs_remove_recursive(key->debugfs.dir);  	key->debugfs.dir = NULL;  } -void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata) + +void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)  {  	char buf[50];  	struct ieee80211_key *key; -	if (!sdata->debugfs.dir) +	if (!sdata->vif.debugfs_dir)  		return; -	/* this is running under the key lock */ +	lockdep_assert_held(&sdata->local->key_mtx); -	key = sdata->default_key; -	if (key) { +	if (sdata->debugfs.default_unicast_key) { +		debugfs_remove(sdata->debugfs.default_unicast_key); +		sdata->debugfs.default_unicast_key = NULL; +	} + +	if (sdata->default_unicast_key) { +		key = key_mtx_dereference(sdata->local, +					  sdata->default_unicast_key);  		sprintf(buf, "../keys/%d", key->debugfs.cnt); -		sdata->debugfs.default_key = -			debugfs_create_symlink("default_key", -					       sdata->debugfs.dir, buf); -	} else -		ieee80211_debugfs_key_remove_default(sdata); -} +		sdata->debugfs.default_unicast_key = +			debugfs_create_symlink("default_unicast_key", +					       sdata->vif.debugfs_dir, buf); +	} -void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata) -{ -	if (!sdata) -		return; +	if (sdata->debugfs.default_multicast_key) { +		debugfs_remove(sdata->debugfs.default_multicast_key); +		sdata->debugfs.default_multicast_key = NULL; +	} -	debugfs_remove(sdata->debugfs.default_key); -	sdata->debugfs.default_key = NULL; +	if (sdata->default_multicast_key) { +		key = key_mtx_dereference(sdata->local, +					  sdata->default_multicast_key); +		sprintf(buf, "../keys/%d", key->debugfs.cnt); +		sdata->debugfs.default_multicast_key = +			debugfs_create_symlink("default_multicast_key", +					       sdata->vif.debugfs_dir, buf); +	}  }  void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata) @@ -308,17 +334,16 @@ void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata)  	char buf[50];  	struct ieee80211_key *key; -	if (!sdata->debugfs.dir) +	if (!sdata->vif.debugfs_dir)  		return; -	/* this is running under the key lock */ - -	key = sdata->default_mgmt_key; +	key = key_mtx_dereference(sdata->local, +				  sdata->default_mgmt_key);  	if (key) {  		sprintf(buf, "../keys/%d", key->debugfs.cnt);  		sdata->debugfs.default_mgmt_key =  			debugfs_create_symlink("default_mgmt_key", -					       sdata->debugfs.dir, buf); +					       sdata->vif.debugfs_dir, buf);  	} else  		ieee80211_debugfs_key_remove_mgmt_default(sdata);  } diff --git a/net/mac80211/debugfs_key.h b/net/mac80211/debugfs_key.h index 54717b4e137..32adc77e9c7 100644 --- a/net/mac80211/debugfs_key.h +++ b/net/mac80211/debugfs_key.h @@ -4,8 +4,7 @@  #ifdef CONFIG_MAC80211_DEBUGFS  void ieee80211_debugfs_key_add(struct ieee80211_key *key);  void ieee80211_debugfs_key_remove(struct ieee80211_key *key); -void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata); -void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata); +void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata);  void ieee80211_debugfs_key_add_mgmt_default(  	struct ieee80211_sub_if_data *sdata);  void ieee80211_debugfs_key_remove_mgmt_default( @@ -17,10 +16,7 @@ static inline void ieee80211_debugfs_key_add(struct ieee80211_key *key)  {}  static inline void ieee80211_debugfs_key_remove(struct ieee80211_key *key)  {} -static inline void ieee80211_debugfs_key_add_default( -	struct ieee80211_sub_if_data *sdata) -{} -static inline void ieee80211_debugfs_key_remove_default( +static inline void ieee80211_debugfs_key_update_default(  	struct ieee80211_sub_if_data *sdata)  {}  static inline void ieee80211_debugfs_key_add_mgmt_default( diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index cbdf36d7841..e205ebabfa5 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -10,6 +10,7 @@  #include <linux/kernel.h>  #include <linux/device.h>  #include <linux/if.h> +#include <linux/if_ether.h>  #include <linux/interrupt.h>  #include <linux/netdevice.h>  #include <linux/rtnetlink.h> @@ -21,6 +22,7 @@  #include "rate.h"  #include "debugfs.h"  #include "debugfs_netdev.h" +#include "driver-ops.h"  static ssize_t ieee80211_if_read(  	struct ieee80211_sub_if_data *sdata, @@ -32,11 +34,10 @@ static ssize_t ieee80211_if_read(  	ssize_t ret = -EINVAL;  	read_lock(&dev_base_lock); -	if (sdata->dev->reg_state == NETREG_REGISTERED) -		ret = (*format)(sdata, buf, sizeof(buf)); +	ret = (*format)(sdata, buf, sizeof(buf));  	read_unlock(&dev_base_lock); -	if (ret != -EINVAL) +	if (ret >= 0)  		ret = simple_read_from_buffer(userbuf, count, ppos, buf, ret);  	return ret; @@ -48,25 +49,21 @@ static ssize_t ieee80211_if_write(  	size_t count, loff_t *ppos,  	ssize_t (*write)(struct ieee80211_sub_if_data *, const char *, int))  { -	u8 *buf; +	char buf[64];  	ssize_t ret; -	buf = kmalloc(count, GFP_KERNEL); -	if (!buf) -		return -ENOMEM; +	if (count >= sizeof(buf)) +		return -E2BIG; -	ret = -EFAULT;  	if (copy_from_user(buf, userbuf, count)) -		goto freebuf; +		return -EFAULT; +	buf[count] = '\0';  	ret = -ENODEV;  	rtnl_lock(); -	if (sdata->dev->reg_state == NETREG_REGISTERED) -		ret = (*write)(sdata, buf, count); +	ret = (*write)(sdata, buf, count);  	rtnl_unlock(); -freebuf: -	kfree(buf);  	return ret;  } @@ -81,9 +78,26 @@ static ssize_t ieee80211_if_fmt_##name(					\  		IEEE80211_IF_FMT(name, field, "%d\n")  #define IEEE80211_IF_FMT_HEX(name, field)				\  		IEEE80211_IF_FMT(name, field, "%#x\n") +#define IEEE80211_IF_FMT_LHEX(name, field)				\ +		IEEE80211_IF_FMT(name, field, "%#lx\n")  #define IEEE80211_IF_FMT_SIZE(name, field)				\  		IEEE80211_IF_FMT(name, field, "%zd\n") +#define IEEE80211_IF_FMT_HEXARRAY(name, field)				\ +static ssize_t ieee80211_if_fmt_##name(					\ +	const struct ieee80211_sub_if_data *sdata,			\ +	char *buf, int buflen)						\ +{									\ +	char *p = buf;							\ +	int i;								\ +	for (i = 0; i < sizeof(sdata->field); i++) {			\ +		p += scnprintf(p, buflen + buf - p, "%.2x ",		\ +				 sdata->field[i]);			\ +	}								\ +	p += scnprintf(p, buflen + buf - p, "\n");			\ +	return p - buf;							\ +} +  #define IEEE80211_IF_FMT_ATOMIC(name, field)				\  static ssize_t ieee80211_if_fmt_##name(					\  	const struct ieee80211_sub_if_data *sdata,			\ @@ -108,7 +122,24 @@ static ssize_t ieee80211_if_fmt_##name(					\  	return scnprintf(buf, buflen, "%d\n", sdata->field / 16);	\  } -#define __IEEE80211_IF_FILE(name, _write)				\ +#define IEEE80211_IF_FMT_JIFFIES_TO_MS(name, field)			\ +static ssize_t ieee80211_if_fmt_##name(					\ +	const struct ieee80211_sub_if_data *sdata,			\ +	char *buf, int buflen)						\ +{									\ +	return scnprintf(buf, buflen, "%d\n",				\ +			 jiffies_to_msecs(sdata->field));		\ +} + +#define _IEEE80211_IF_FILE_OPS(name, _read, _write)			\ +static const struct file_operations name##_ops = {			\ +	.read = (_read),						\ +	.write = (_write),						\ +	.open = simple_open,						\ +	.llseek = generic_file_llseek,					\ +} + +#define _IEEE80211_IF_FILE_R_FN(name)					\  static ssize_t ieee80211_if_read_##name(struct file *file,		\  					char __user *userbuf,		\  					size_t count, loff_t *ppos)	\ @@ -116,28 +147,34 @@ static ssize_t ieee80211_if_read_##name(struct file *file,		\  	return ieee80211_if_read(file->private_data,			\  				 userbuf, count, ppos,			\  				 ieee80211_if_fmt_##name);		\ -}									\ -static const struct file_operations name##_ops = {			\ -	.read = ieee80211_if_read_##name,				\ -	.write = (_write),						\ -	.open = mac80211_open_file_generic,				\ -	.llseek = generic_file_llseek,					\  } -#define __IEEE80211_IF_FILE_W(name)					\ +#define _IEEE80211_IF_FILE_W_FN(name)					\  static ssize_t ieee80211_if_write_##name(struct file *file,		\  					 const char __user *userbuf,	\  					 size_t count, loff_t *ppos)	\  {									\  	return ieee80211_if_write(file->private_data, userbuf, count,	\  				  ppos, ieee80211_if_parse_##name);	\ -}									\ -__IEEE80211_IF_FILE(name, ieee80211_if_write_##name) +} + +#define IEEE80211_IF_FILE_R(name)					\ +	_IEEE80211_IF_FILE_R_FN(name)					\ +	_IEEE80211_IF_FILE_OPS(name, ieee80211_if_read_##name, NULL) +#define IEEE80211_IF_FILE_W(name)					\ +	_IEEE80211_IF_FILE_W_FN(name)					\ +	_IEEE80211_IF_FILE_OPS(name, NULL, ieee80211_if_write_##name) + +#define IEEE80211_IF_FILE_RW(name)					\ +	_IEEE80211_IF_FILE_R_FN(name)					\ +	_IEEE80211_IF_FILE_W_FN(name)					\ +	_IEEE80211_IF_FILE_OPS(name, ieee80211_if_read_##name,		\ +			       ieee80211_if_write_##name)  #define IEEE80211_IF_FILE(name, field, format)				\ -		IEEE80211_IF_FMT_##format(name, field)			\ -		__IEEE80211_IF_FILE(name, NULL) +	IEEE80211_IF_FMT_##format(name, field)				\ +	IEEE80211_IF_FILE_R(name)  /* common attributes */  IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); @@ -145,12 +182,43 @@ 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],  		  HEX); +IEEE80211_IF_FILE(rc_rateidx_mcs_mask_2ghz, +		  rc_rateidx_mcs_mask[IEEE80211_BAND_2GHZ], HEXARRAY); +IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz, +		  rc_rateidx_mcs_mask[IEEE80211_BAND_5GHZ], HEXARRAY); + +IEEE80211_IF_FILE(flags, flags, HEX); +IEEE80211_IF_FILE(state, state, LHEX); +IEEE80211_IF_FILE(txpower, vif.bss_conf.txpower, DEC); +IEEE80211_IF_FILE(ap_power_level, ap_power_level, DEC); +IEEE80211_IF_FILE(user_power_level, user_power_level, DEC); + +static ssize_t +ieee80211_if_fmt_hw_queues(const struct ieee80211_sub_if_data *sdata, +			   char *buf, int buflen) +{ +	int len; + +	len = scnprintf(buf, buflen, "AC queues: VO:%d VI:%d BE:%d BK:%d\n", +			sdata->vif.hw_queue[IEEE80211_AC_VO], +			sdata->vif.hw_queue[IEEE80211_AC_VI], +			sdata->vif.hw_queue[IEEE80211_AC_BE], +			sdata->vif.hw_queue[IEEE80211_AC_BK]); + +	if (sdata->vif.type == NL80211_IFTYPE_AP) +		len += scnprintf(buf + len, buflen - len, "cab queue: %d\n", +				 sdata->vif.cab_queue); + +	return len; +} +IEEE80211_IF_FILE_R(hw_queues);  /* STA attributes */  IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);  IEEE80211_IF_FILE(aid, u.mgd.aid, DEC);  IEEE80211_IF_FILE(last_beacon, u.mgd.last_beacon_signal, DEC);  IEEE80211_IF_FILE(ave_beacon, u.mgd.ave_beacon_signal, DEC_DIV_16); +IEEE80211_IF_FILE(beacon_timeout, u.mgd.beacon_timeout, JIFFIES_TO_MS);  static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,  			      enum ieee80211_smps_mode smps_mode) @@ -168,13 +236,16 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,  	     smps_mode == IEEE80211_SMPS_AUTOMATIC))  		return -EINVAL; -	/* supported only on managed interfaces for now */ -	if (sdata->vif.type != NL80211_IFTYPE_STATION) +	if (sdata->vif.type != NL80211_IFTYPE_STATION && +	    sdata->vif.type != NL80211_IFTYPE_AP)  		return -EOPNOTSUPP; -	mutex_lock(&local->iflist_mtx); -	err = __ieee80211_request_smps(sdata, smps_mode); -	mutex_unlock(&local->iflist_mtx); +	sdata_lock(sdata); +	if (sdata->vif.type == NL80211_IFTYPE_STATION) +		err = __ieee80211_request_smps_mgd(sdata, smps_mode); +	else +		err = __ieee80211_request_smps_ap(sdata, smps_mode); +	sdata_unlock(sdata);  	return err;  } @@ -189,12 +260,15 @@ static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {  static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,  				     char *buf, int buflen)  { -	if (sdata->vif.type != NL80211_IFTYPE_STATION) -		return -EOPNOTSUPP; - -	return snprintf(buf, buflen, "request: %s\nused: %s\n", -			smps_modes[sdata->u.mgd.req_smps], -			smps_modes[sdata->u.mgd.ap_smps]); +	if (sdata->vif.type == NL80211_IFTYPE_STATION) +		return snprintf(buf, buflen, "request: %s\nused: %s\n", +				smps_modes[sdata->u.mgd.req_smps], +				smps_modes[sdata->smps_mode]); +	if (sdata->vif.type == NL80211_IFTYPE_AP) +		return snprintf(buf, buflen, "request: %s\nused: %s\n", +				smps_modes[sdata->u.ap.req_smps], +				smps_modes[sdata->smps_mode]); +	return -EINVAL;  }  static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, @@ -213,123 +287,335 @@ static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,  	return -EINVAL;  } +IEEE80211_IF_FILE_RW(smps); + +static ssize_t ieee80211_if_parse_tkip_mic_test( +	struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ +	struct ieee80211_local *local = sdata->local; +	u8 addr[ETH_ALEN]; +	struct sk_buff *skb; +	struct ieee80211_hdr *hdr; +	__le16 fc; + +	if (!mac_pton(buf, addr)) +		return -EINVAL; + +	if (!ieee80211_sdata_running(sdata)) +		return -ENOTCONN; + +	skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 100); +	if (!skb) +		return -ENOMEM; +	skb_reserve(skb, local->hw.extra_tx_headroom); + +	hdr = (struct ieee80211_hdr *) skb_put(skb, 24); +	memset(hdr, 0, 24); +	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); + +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_AP: +		fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); +		/* DA BSSID SA */ +		memcpy(hdr->addr1, addr, ETH_ALEN); +		memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); +		memcpy(hdr->addr3, sdata->vif.addr, ETH_ALEN); +		break; +	case NL80211_IFTYPE_STATION: +		fc |= cpu_to_le16(IEEE80211_FCTL_TODS); +		/* BSSID SA DA */ +		sdata_lock(sdata); +		if (!sdata->u.mgd.associated) { +			sdata_unlock(sdata); +			dev_kfree_skb(skb); +			return -ENOTCONN; +		} +		memcpy(hdr->addr1, sdata->u.mgd.associated->bssid, ETH_ALEN); +		memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); +		memcpy(hdr->addr3, addr, ETH_ALEN); +		sdata_unlock(sdata); +		break; +	default: +		dev_kfree_skb(skb); +		return -EOPNOTSUPP; +	} +	hdr->frame_control = fc; + +	/* +	 * Add some length to the test frame to make it look bit more valid. +	 * The exact contents does not matter since the recipient is required +	 * to drop this because of the Michael MIC failure. +	 */ +	memset(skb_put(skb, 50), 0, 50); + +	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_TKIP_MIC_FAILURE; + +	ieee80211_tx_skb(sdata, skb); + +	return buflen; +} +IEEE80211_IF_FILE_W(tkip_mic_test); + +static ssize_t ieee80211_if_parse_beacon_loss( +	struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ +	if (!ieee80211_sdata_running(sdata) || !sdata->vif.bss_conf.assoc) +		return -ENOTCONN; + +	ieee80211_beacon_loss(&sdata->vif); + +	return buflen; +} +IEEE80211_IF_FILE_W(beacon_loss); + +static ssize_t ieee80211_if_fmt_uapsd_queues( +	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ +	const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + +	return snprintf(buf, buflen, "0x%x\n", ifmgd->uapsd_queues); +} + +static ssize_t ieee80211_if_parse_uapsd_queues( +	struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	u8 val; +	int ret; + +	ret = kstrtou8(buf, 0, &val); +	if (ret) +		return ret; -__IEEE80211_IF_FILE_W(smps); +	if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) +		return -ERANGE; + +	ifmgd->uapsd_queues = val; + +	return buflen; +} +IEEE80211_IF_FILE_RW(uapsd_queues); + +static ssize_t ieee80211_if_fmt_uapsd_max_sp_len( +	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ +	const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + +	return snprintf(buf, buflen, "0x%x\n", ifmgd->uapsd_max_sp_len); +} + +static ssize_t ieee80211_if_parse_uapsd_max_sp_len( +	struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	unsigned long val; +	int ret; + +	ret = kstrtoul(buf, 0, &val); +	if (ret) +		return -EINVAL; + +	if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) +		return -ERANGE; + +	ifmgd->uapsd_max_sp_len = val; + +	return buflen; +} +IEEE80211_IF_FILE_RW(uapsd_max_sp_len);  /* AP attributes */ -IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); -IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); +IEEE80211_IF_FILE(num_mcast_sta, u.ap.num_mcast_sta, ATOMIC); +IEEE80211_IF_FILE(num_sta_ps, u.ap.ps.num_sta_ps, ATOMIC); +IEEE80211_IF_FILE(dtim_count, u.ap.ps.dtim_count, DEC);  static ssize_t ieee80211_if_fmt_num_buffered_multicast(  	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)  {  	return scnprintf(buf, buflen, "%u\n", -			 skb_queue_len(&sdata->u.ap.ps_bc_buf)); +			 skb_queue_len(&sdata->u.ap.ps.bc_buf)); +} +IEEE80211_IF_FILE_R(num_buffered_multicast); + +/* IBSS attributes */ +static ssize_t ieee80211_if_fmt_tsf( +	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ +	struct ieee80211_local *local = sdata->local; +	u64 tsf; + +	tsf = drv_get_tsf(local, (struct ieee80211_sub_if_data *)sdata); + +	return scnprintf(buf, buflen, "0x%016llx\n", (unsigned long long) tsf);  } -__IEEE80211_IF_FILE(num_buffered_multicast, NULL); + +static ssize_t ieee80211_if_parse_tsf( +	struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ +	struct ieee80211_local *local = sdata->local; +	unsigned long long tsf; +	int ret; +	int tsf_is_delta = 0; + +	if (strncmp(buf, "reset", 5) == 0) { +		if (local->ops->reset_tsf) { +			drv_reset_tsf(local, sdata); +			wiphy_info(local->hw.wiphy, "debugfs reset TSF\n"); +		} +	} else { +		if (buflen > 10 && buf[1] == '=') { +			if (buf[0] == '+') +				tsf_is_delta = 1; +			else if (buf[0] == '-') +				tsf_is_delta = -1; +			else +				return -EINVAL; +			buf += 2; +		} +		ret = kstrtoull(buf, 10, &tsf); +		if (ret < 0) +			return ret; +		if (tsf_is_delta) +			tsf = drv_get_tsf(local, sdata) + tsf_is_delta * tsf; +		if (local->ops->set_tsf) { +			drv_set_tsf(local, sdata, tsf); +			wiphy_info(local->hw.wiphy, +				   "debugfs set TSF to %#018llx\n", tsf); +		} +	} + +	ieee80211_recalc_dtim(local, sdata); +	return buflen; +} +IEEE80211_IF_FILE_RW(tsf); +  /* WDS attributes */  IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);  #ifdef CONFIG_MAC80211_MESH +IEEE80211_IF_FILE(estab_plinks, u.mesh.estab_plinks, ATOMIC); +  /* Mesh stats attributes */  IEEE80211_IF_FILE(fwded_mcast, u.mesh.mshstats.fwded_mcast, DEC);  IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC);  IEEE80211_IF_FILE(fwded_frames, u.mesh.mshstats.fwded_frames, DEC);  IEEE80211_IF_FILE(dropped_frames_ttl, u.mesh.mshstats.dropped_frames_ttl, DEC); +IEEE80211_IF_FILE(dropped_frames_congestion, +		  u.mesh.mshstats.dropped_frames_congestion, DEC);  IEEE80211_IF_FILE(dropped_frames_no_route, -		u.mesh.mshstats.dropped_frames_no_route, DEC); -IEEE80211_IF_FILE(estab_plinks, u.mesh.mshstats.estab_plinks, ATOMIC); +		  u.mesh.mshstats.dropped_frames_no_route, DEC);  /* Mesh parameters */  IEEE80211_IF_FILE(dot11MeshMaxRetries, -		u.mesh.mshcfg.dot11MeshMaxRetries, DEC); +		  u.mesh.mshcfg.dot11MeshMaxRetries, DEC);  IEEE80211_IF_FILE(dot11MeshRetryTimeout, -		u.mesh.mshcfg.dot11MeshRetryTimeout, DEC); +		  u.mesh.mshcfg.dot11MeshRetryTimeout, DEC);  IEEE80211_IF_FILE(dot11MeshConfirmTimeout, -		u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC); +		  u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC);  IEEE80211_IF_FILE(dot11MeshHoldingTimeout, -		u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC); +		  u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC);  IEEE80211_IF_FILE(dot11MeshTTL, u.mesh.mshcfg.dot11MeshTTL, DEC); +IEEE80211_IF_FILE(element_ttl, u.mesh.mshcfg.element_ttl, DEC);  IEEE80211_IF_FILE(auto_open_plinks, u.mesh.mshcfg.auto_open_plinks, DEC);  IEEE80211_IF_FILE(dot11MeshMaxPeerLinks, -		u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC); +		  u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC);  IEEE80211_IF_FILE(dot11MeshHWMPactivePathTimeout, -		u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC); +		  u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC);  IEEE80211_IF_FILE(dot11MeshHWMPpreqMinInterval, -		u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC); +		  u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC); +IEEE80211_IF_FILE(dot11MeshHWMPperrMinInterval, +		  u.mesh.mshcfg.dot11MeshHWMPperrMinInterval, DEC);  IEEE80211_IF_FILE(dot11MeshHWMPnetDiameterTraversalTime, -		u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC); +		  u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC);  IEEE80211_IF_FILE(dot11MeshHWMPmaxPREQretries, -		u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC); +		  u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC);  IEEE80211_IF_FILE(path_refresh_time, -		u.mesh.mshcfg.path_refresh_time, DEC); +		  u.mesh.mshcfg.path_refresh_time, DEC);  IEEE80211_IF_FILE(min_discovery_timeout, -		u.mesh.mshcfg.min_discovery_timeout, DEC); +		  u.mesh.mshcfg.min_discovery_timeout, DEC);  IEEE80211_IF_FILE(dot11MeshHWMPRootMode, -		u.mesh.mshcfg.dot11MeshHWMPRootMode, DEC); +		  u.mesh.mshcfg.dot11MeshHWMPRootMode, DEC); +IEEE80211_IF_FILE(dot11MeshGateAnnouncementProtocol, +		  u.mesh.mshcfg.dot11MeshGateAnnouncementProtocol, DEC); +IEEE80211_IF_FILE(dot11MeshHWMPRannInterval, +		  u.mesh.mshcfg.dot11MeshHWMPRannInterval, DEC); +IEEE80211_IF_FILE(dot11MeshForwarding, u.mesh.mshcfg.dot11MeshForwarding, DEC); +IEEE80211_IF_FILE(rssi_threshold, u.mesh.mshcfg.rssi_threshold, DEC); +IEEE80211_IF_FILE(ht_opmode, u.mesh.mshcfg.ht_opmode, DEC); +IEEE80211_IF_FILE(dot11MeshHWMPactivePathToRootTimeout, +		  u.mesh.mshcfg.dot11MeshHWMPactivePathToRootTimeout, DEC); +IEEE80211_IF_FILE(dot11MeshHWMProotInterval, +		  u.mesh.mshcfg.dot11MeshHWMProotInterval, DEC); +IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval, +		  u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval, DEC); +IEEE80211_IF_FILE(power_mode, u.mesh.mshcfg.power_mode, DEC); +IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration, +		  u.mesh.mshcfg.dot11MeshAwakeWindowDuration, DEC);  #endif - -#define DEBUGFS_ADD(name) \ -	debugfs_create_file(#name, 0400, sdata->debugfs.dir, \ -			    sdata, &name##_ops); -  #define DEBUGFS_ADD_MODE(name, mode) \ -	debugfs_create_file(#name, mode, sdata->debugfs.dir, \ +	debugfs_create_file(#name, mode, sdata->vif.debugfs_dir, \  			    sdata, &name##_ops); -static void add_sta_files(struct ieee80211_sub_if_data *sdata) +#define DEBUGFS_ADD(name) DEBUGFS_ADD_MODE(name, 0400) + +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); +	DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); +	DEBUGFS_ADD(hw_queues); +} +static void add_sta_files(struct ieee80211_sub_if_data *sdata) +{  	DEBUGFS_ADD(bssid);  	DEBUGFS_ADD(aid);  	DEBUGFS_ADD(last_beacon);  	DEBUGFS_ADD(ave_beacon); +	DEBUGFS_ADD(beacon_timeout);  	DEBUGFS_ADD_MODE(smps, 0600); +	DEBUGFS_ADD_MODE(tkip_mic_test, 0200); +	DEBUGFS_ADD_MODE(beacon_loss, 0200); +	DEBUGFS_ADD_MODE(uapsd_queues, 0600); +	DEBUGFS_ADD_MODE(uapsd_max_sp_len, 0600);  }  static void add_ap_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(num_mcast_sta); +	DEBUGFS_ADD_MODE(smps, 0600);  	DEBUGFS_ADD(num_sta_ps);  	DEBUGFS_ADD(dtim_count);  	DEBUGFS_ADD(num_buffered_multicast); +	DEBUGFS_ADD_MODE(tkip_mic_test, 0200);  } -static void add_wds_files(struct ieee80211_sub_if_data *sdata) +static void add_ibss_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(peer); +	DEBUGFS_ADD_MODE(tsf, 0600);  } -static void add_vlan_files(struct ieee80211_sub_if_data *sdata) +static void add_wds_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(peer);  } -static void add_monitor_files(struct ieee80211_sub_if_data *sdata) +#ifdef CONFIG_MAC80211_MESH + +static void add_mesh_files(struct ieee80211_sub_if_data *sdata)  { +	DEBUGFS_ADD_MODE(tsf, 0600); +	DEBUGFS_ADD_MODE(estab_plinks, 0400);  } -#ifdef CONFIG_MAC80211_MESH -  static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)  {  	struct dentry *dir = debugfs_create_dir("mesh_stats", -						sdata->debugfs.dir); - +						sdata->vif.debugfs_dir);  #define MESHSTATS_ADD(name)\  	debugfs_create_file(#name, 0400, dir, sdata, &name##_ops); @@ -338,14 +624,14 @@ static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)  	MESHSTATS_ADD(fwded_frames);  	MESHSTATS_ADD(dropped_frames_ttl);  	MESHSTATS_ADD(dropped_frames_no_route); -	MESHSTATS_ADD(estab_plinks); +	MESHSTATS_ADD(dropped_frames_congestion);  #undef MESHSTATS_ADD  }  static void add_mesh_config(struct ieee80211_sub_if_data *sdata)  {  	struct dentry *dir = debugfs_create_dir("mesh_config", -						sdata->debugfs.dir); +						sdata->vif.debugfs_dir);  #define MESHPARAMS_ADD(name) \  	debugfs_create_file(#name, 0600, dir, sdata, &name##_ops); @@ -355,27 +641,49 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata)  	MESHPARAMS_ADD(dot11MeshConfirmTimeout);  	MESHPARAMS_ADD(dot11MeshHoldingTimeout);  	MESHPARAMS_ADD(dot11MeshTTL); +	MESHPARAMS_ADD(element_ttl);  	MESHPARAMS_ADD(auto_open_plinks);  	MESHPARAMS_ADD(dot11MeshMaxPeerLinks);  	MESHPARAMS_ADD(dot11MeshHWMPactivePathTimeout);  	MESHPARAMS_ADD(dot11MeshHWMPpreqMinInterval); +	MESHPARAMS_ADD(dot11MeshHWMPperrMinInterval);  	MESHPARAMS_ADD(dot11MeshHWMPnetDiameterTraversalTime);  	MESHPARAMS_ADD(dot11MeshHWMPmaxPREQretries);  	MESHPARAMS_ADD(path_refresh_time);  	MESHPARAMS_ADD(min_discovery_timeout); - +	MESHPARAMS_ADD(dot11MeshHWMPRootMode); +	MESHPARAMS_ADD(dot11MeshHWMPRannInterval); +	MESHPARAMS_ADD(dot11MeshForwarding); +	MESHPARAMS_ADD(dot11MeshGateAnnouncementProtocol); +	MESHPARAMS_ADD(rssi_threshold); +	MESHPARAMS_ADD(ht_opmode); +	MESHPARAMS_ADD(dot11MeshHWMPactivePathToRootTimeout); +	MESHPARAMS_ADD(dot11MeshHWMProotInterval); +	MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval); +	MESHPARAMS_ADD(power_mode); +	MESHPARAMS_ADD(dot11MeshAwakeWindowDuration);  #undef MESHPARAMS_ADD  }  #endif  static void add_files(struct ieee80211_sub_if_data *sdata)  { -	if (!sdata->debugfs.dir) +	if (!sdata->vif.debugfs_dir)  		return; +	DEBUGFS_ADD(flags); +	DEBUGFS_ADD(state); +	DEBUGFS_ADD(txpower); +	DEBUGFS_ADD(user_power_level); +	DEBUGFS_ADD(ap_power_level); + +	if (sdata->vif.type != NL80211_IFTYPE_MONITOR) +		add_common_files(sdata); +  	switch (sdata->vif.type) {  	case NL80211_IFTYPE_MESH_POINT:  #ifdef CONFIG_MAC80211_MESH +		add_mesh_files(sdata);  		add_mesh_stats(sdata);  		add_mesh_config(sdata);  #endif @@ -384,7 +692,7 @@ static void add_files(struct ieee80211_sub_if_data *sdata)  		add_sta_files(sdata);  		break;  	case NL80211_IFTYPE_ADHOC: -		/* XXX */ +		add_ibss_files(sdata);  		break;  	case NL80211_IFTYPE_AP:  		add_ap_files(sdata); @@ -392,12 +700,6 @@ static void add_files(struct ieee80211_sub_if_data *sdata)  	case NL80211_IFTYPE_WDS:  		add_wds_files(sdata);  		break; -	case NL80211_IFTYPE_MONITOR: -		add_monitor_files(sdata); -		break; -	case NL80211_IFTYPE_AP_VLAN: -		add_vlan_files(sdata); -		break;  	default:  		break;  	} @@ -408,21 +710,21 @@ void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata)  	char buf[10+IFNAMSIZ];  	sprintf(buf, "netdev:%s", sdata->name); -	sdata->debugfs.dir = debugfs_create_dir(buf, +	sdata->vif.debugfs_dir = debugfs_create_dir(buf,  		sdata->local->hw.wiphy->debugfsdir); -	if (sdata->debugfs.dir) +	if (sdata->vif.debugfs_dir)  		sdata->debugfs.subdir_stations = debugfs_create_dir("stations", -			sdata->debugfs.dir); +			sdata->vif.debugfs_dir);  	add_files(sdata);  }  void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata)  { -	if (!sdata->debugfs.dir) +	if (!sdata->vif.debugfs_dir)  		return; -	debugfs_remove_recursive(sdata->debugfs.dir); -	sdata->debugfs.dir = NULL; +	debugfs_remove_recursive(sdata->vif.debugfs_dir); +	sdata->vif.debugfs_dir = NULL;  }  void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata) @@ -430,13 +732,14 @@ void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata)  	struct dentry *dir;  	char buf[10 + IFNAMSIZ]; -	dir = sdata->debugfs.dir; +	dir = sdata->vif.debugfs_dir;  	if (!dir)  		return;  	sprintf(buf, "netdev:%s", sdata->name);  	if (!debugfs_rename(dir->d_parent, dir, dir->d_parent, buf)) -		printk(KERN_ERR "mac80211: debugfs: failed to rename debugfs " -		       "dir to %s\n", buf); +		sdata_err(sdata, +			  "debugfs: failed to rename debugfs dir to %s\n", +			  buf);  } diff --git a/net/mac80211/debugfs_netdev.h b/net/mac80211/debugfs_netdev.h index 79025e79f4d..9f5501a9a79 100644 --- a/net/mac80211/debugfs_netdev.h +++ b/net/mac80211/debugfs_netdev.h @@ -3,6 +3,8 @@  #ifndef __IEEE80211_DEBUGFS_NETDEV_H  #define __IEEE80211_DEBUGFS_NETDEV_H +#include "ieee80211_i.h" +  #ifdef CONFIG_MAC80211_DEBUGFS  void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata);  void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index f0fce37f406..2ecb4deddb5 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -14,6 +14,7 @@  #include "debugfs.h"  #include "debugfs_sta.h"  #include "sta_info.h" +#include "driver-ops.h"  /* sta attributtes */ @@ -33,7 +34,14 @@ static ssize_t sta_ ##name## _read(struct file *file,			\  #define STA_OPS(name)							\  static const struct file_operations sta_ ##name## _ops = {		\  	.read = sta_##name##_read,					\ -	.open = mac80211_open_file_generic,				\ +	.open = simple_open,						\ +	.llseek = generic_file_llseek,					\ +} + +#define STA_OPS_W(name)							\ +static const struct file_operations sta_ ##name## _ops = {		\ +	.write = sta_##name##_write,					\ +	.open = simple_open,						\  	.llseek = generic_file_llseek,					\  } @@ -41,7 +49,7 @@ static const struct file_operations sta_ ##name## _ops = {		\  static const struct file_operations sta_ ##name## _ops = {		\  	.read = sta_##name##_read,					\  	.write = sta_##name##_write,					\ -	.open = mac80211_open_file_generic,				\ +	.open = simple_open,						\  	.llseek = generic_file_llseek,					\  } @@ -52,23 +60,31 @@ static const struct file_operations sta_ ##name## _ops = {		\  STA_FILE(aid, sta.aid, D);  STA_FILE(dev, sdata->name, S);  STA_FILE(last_signal, last_signal, D); +STA_FILE(last_ack_signal, last_ack_signal, D); +STA_FILE(beacon_loss_count, beacon_loss_count, D);  static ssize_t sta_flags_read(struct file *file, char __user *userbuf,  			      size_t count, loff_t *ppos)  { -	char buf[100]; +	char buf[121];  	struct sta_info *sta = file->private_data; -	u32 staflags = get_sta_flags(sta); -	int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s", -		staflags & WLAN_STA_AUTH ? "AUTH\n" : "", -		staflags & WLAN_STA_ASSOC ? "ASSOC\n" : "", -		staflags & WLAN_STA_PS_STA ? "PS (sta)\n" : "", -		staflags & WLAN_STA_PS_DRIVER ? "PS (driver)\n" : "", -		staflags & WLAN_STA_AUTHORIZED ? "AUTHORIZED\n" : "", -		staflags & WLAN_STA_SHORT_PREAMBLE ? "SHORT PREAMBLE\n" : "", -		staflags & WLAN_STA_WME ? "WME\n" : "", -		staflags & WLAN_STA_WDS ? "WDS\n" : "", -		staflags & WLAN_STA_MFP ? "MFP\n" : ""); + +#define TEST(flg) \ +	test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : "" + +	int res = scnprintf(buf, sizeof(buf), +			    "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", +			    TEST(AUTH), TEST(ASSOC), TEST(PS_STA), +			    TEST(PS_DRIVER), TEST(AUTHORIZED), +			    TEST(SHORT_PREAMBLE), +			    TEST(WME), TEST(WDS), TEST(CLEAR_PS_FILT), +			    TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL), +			    TEST(UAPSD), TEST(SP), TEST(TDLS_PEER), +			    TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT), +			    TEST(INSERTED), TEST(RATE_CONTROL), +			    TEST(TOFFSET_KNOWN), TEST(MPSP_OWNER), +			    TEST(MPSP_RECIPIENT)); +#undef TEST  	return simple_read_from_buffer(userbuf, count, ppos, buf, res);  }  STA_OPS(flags); @@ -78,8 +94,14 @@ static ssize_t sta_num_ps_buf_frames_read(struct file *file,  					  size_t count, loff_t *ppos)  {  	struct sta_info *sta = file->private_data; -	return mac80211_format_buffer(userbuf, count, ppos, "%u\n", -				      skb_queue_len(&sta->ps_tx_buf)); +	char buf[17*IEEE80211_NUM_ACS], *p = buf; +	int ac; + +	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) +		p += scnprintf(p, sizeof(buf)+buf-p, "AC%d: %d\n", ac, +			       skb_queue_len(&sta->ps_tx_buf[ac]) + +			       skb_queue_len(&sta->tx_filtered[ac])); +	return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);  }  STA_OPS(num_ps_buf_frames); @@ -92,13 +114,38 @@ static ssize_t sta_inactive_ms_read(struct file *file, char __user *userbuf,  }  STA_OPS(inactive_ms); + +static ssize_t sta_connected_time_read(struct file *file, char __user *userbuf, +					size_t count, loff_t *ppos) +{ +	struct sta_info *sta = file->private_data; +	struct timespec uptime; +	struct tm result; +	long connected_time_secs; +	char buf[100]; +	int res; +	do_posix_clock_monotonic_gettime(&uptime); +	connected_time_secs = uptime.tv_sec - sta->last_connected; +	time_to_tm(connected_time_secs, 0, &result); +	result.tm_year -= 70; +	result.tm_mday -= 1; +	res = scnprintf(buf, sizeof(buf), +		"years  - %ld\nmonths - %d\ndays   - %d\nclock  - %d:%d:%d\n\n", +			result.tm_year, result.tm_mon, result.tm_mday, +			result.tm_hour, result.tm_min, result.tm_sec); +	return simple_read_from_buffer(userbuf, count, ppos, buf, res); +} +STA_OPS(connected_time); + + +  static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf,  				      size_t count, loff_t *ppos)  { -	char buf[15*NUM_RX_DATA_QUEUES], *p = buf; +	char buf[15*IEEE80211_NUM_TIDS], *p = buf;  	int i;  	struct sta_info *sta = file->private_data; -	for (i = 0; i < NUM_RX_DATA_QUEUES; i++) +	for (i = 0; i < IEEE80211_NUM_TIDS; i++)  		p += scnprintf(p, sizeof(buf)+buf-p, "%x ",  			       le16_to_cpu(sta->last_seq_ctrl[i]));  	p += scnprintf(p, sizeof(buf)+buf-p, "\n"); @@ -109,37 +156,38 @@ STA_OPS(last_seq_ctrl);  static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,  					size_t count, loff_t *ppos)  { -	char buf[71 + STA_TID_NUM * 40], *p = buf; +	char buf[71 + IEEE80211_NUM_TIDS * 40], *p = buf;  	int i;  	struct sta_info *sta = file->private_data; +	struct tid_ampdu_rx *tid_rx; +	struct tid_ampdu_tx *tid_tx; + +	rcu_read_lock(); -	spin_lock_bh(&sta->lock);  	p += scnprintf(p, sizeof(buf) + buf - p, "next dialog_token: %#02x\n",  			sta->ampdu_mlme.dialog_token_allocator + 1);  	p += scnprintf(p, sizeof(buf) + buf - p,  		       "TID\t\tRX active\tDTKN\tSSN\t\tTX\tDTKN\tpending\n"); -	for (i = 0; i < STA_TID_NUM; i++) { + +	for (i = 0; i < IEEE80211_NUM_TIDS; i++) { +		tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[i]); +		tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[i]); +  		p += scnprintf(p, sizeof(buf) + buf - p, "%02d", i); -		p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", -				!!sta->ampdu_mlme.tid_rx[i]); +		p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", !!tid_rx);  		p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x", -				sta->ampdu_mlme.tid_rx[i] ? -				sta->ampdu_mlme.tid_rx[i]->dialog_token : 0); +				tid_rx ? tid_rx->dialog_token : 0);  		p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x", -				sta->ampdu_mlme.tid_rx[i] ? -				sta->ampdu_mlme.tid_rx[i]->ssn : 0); +				tid_rx ? tid_rx->ssn : 0); -		p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", -				!!sta->ampdu_mlme.tid_tx[i]); +		p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", !!tid_tx);  		p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x", -				sta->ampdu_mlme.tid_tx[i] ? -				sta->ampdu_mlme.tid_tx[i]->dialog_token : 0); +				tid_tx ? tid_tx->dialog_token : 0);  		p += scnprintf(p, sizeof(buf) + buf - p, "\t%03d", -				sta->ampdu_mlme.tid_tx[i] ? -				skb_queue_len(&sta->ampdu_mlme.tid_tx[i]->pending) : 0); +				tid_tx ? skb_queue_len(&tid_tx->pending) : 0);  		p += scnprintf(p, sizeof(buf) + buf - p, "\n");  	} -	spin_unlock_bh(&sta->lock); +	rcu_read_unlock();  	return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);  } @@ -147,7 +195,7 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,  static ssize_t sta_agg_status_write(struct file *file, const char __user *userbuf,  				    size_t count, loff_t *ppos)  { -	char _buf[12], *buf = _buf; +	char _buf[12] = {}, *buf = _buf;  	struct sta_info *sta = file->private_data;  	bool start, tx;  	unsigned long tid; @@ -181,14 +229,16 @@ static ssize_t sta_agg_status_write(struct file *file, const char __user *userbu  	} else  		return -EINVAL; -	tid = simple_strtoul(buf, NULL, 0); +	ret = kstrtoul(buf, 0, &tid); +	if (ret) +		return ret; -	if (tid >= STA_TID_NUM) +	if (tid >= IEEE80211_NUM_TIDS)  		return -EINVAL;  	if (tx) {  		if (start) -			ret = ieee80211_start_tx_ba_session(&sta->sta, tid); +			ret = ieee80211_start_tx_ba_session(&sta->sta, tid, 5000);  		else  			ret = ieee80211_stop_tx_ba_session(&sta->sta, tid);  	} else { @@ -239,9 +289,9 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,  		PRINT_HT_CAP((htc->cap & BIT(10)), "HT Delayed Block Ack"); -		PRINT_HT_CAP((htc->cap & BIT(11)), "Max AMSDU length: " -			     "3839 bytes");  		PRINT_HT_CAP(!(htc->cap & BIT(11)), "Max AMSDU length: " +			     "3839 bytes"); +		PRINT_HT_CAP((htc->cap & BIT(11)), "Max AMSDU length: "  			     "7935 bytes");  		/* @@ -283,6 +333,193 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,  }  STA_OPS(ht_capa); +static ssize_t sta_vht_capa_read(struct file *file, char __user *userbuf, +				 size_t count, loff_t *ppos) +{ +	char buf[128], *p = buf; +	struct sta_info *sta = file->private_data; +	struct ieee80211_sta_vht_cap *vhtc = &sta->sta.vht_cap; + +	p += scnprintf(p, sizeof(buf) + buf - p, "VHT %ssupported\n", +			vhtc->vht_supported ? "" : "not "); +	if (vhtc->vht_supported) { +		p += scnprintf(p, sizeof(buf)+buf-p, "cap: %#.8x\n", vhtc->cap); + +		p += scnprintf(p, sizeof(buf)+buf-p, "RX MCS: %.4x\n", +			       le16_to_cpu(vhtc->vht_mcs.rx_mcs_map)); +		if (vhtc->vht_mcs.rx_highest) +			p += scnprintf(p, sizeof(buf)+buf-p, +				       "MCS RX highest: %d Mbps\n", +				       le16_to_cpu(vhtc->vht_mcs.rx_highest)); +		p += scnprintf(p, sizeof(buf)+buf-p, "TX MCS: %.4x\n", +			       le16_to_cpu(vhtc->vht_mcs.tx_mcs_map)); +		if (vhtc->vht_mcs.tx_highest) +			p += scnprintf(p, sizeof(buf)+buf-p, +				       "MCS TX highest: %d Mbps\n", +				       le16_to_cpu(vhtc->vht_mcs.tx_highest)); +	} + +	return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); +} +STA_OPS(vht_capa); + +static ssize_t sta_current_tx_rate_read(struct file *file, char __user *userbuf, +					size_t count, loff_t *ppos) +{ +	struct sta_info *sta = file->private_data; +	struct rate_info rinfo; +	u16 rate; +	sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo); +	rate = cfg80211_calculate_bitrate(&rinfo); + +	return mac80211_format_buffer(userbuf, count, ppos, +				      "%d.%d MBit/s\n", +				      rate/10, rate%10); +} +STA_OPS(current_tx_rate); + +static ssize_t sta_last_rx_rate_read(struct file *file, char __user *userbuf, +				     size_t count, loff_t *ppos) +{ +	struct sta_info *sta = file->private_data; +	struct rate_info rinfo; +	u16 rate; + +	sta_set_rate_info_rx(sta, &rinfo); + +	rate = cfg80211_calculate_bitrate(&rinfo); + +	return mac80211_format_buffer(userbuf, count, ppos, +				      "%d.%d MBit/s\n", +				      rate/10, rate%10); +} +STA_OPS(last_rx_rate); + +static int +sta_tx_latency_stat_header(struct ieee80211_tx_latency_bin_ranges *tx_latency, +			   char *buf, int pos, int bufsz) +{ +	int i; +	int range_count = tx_latency->n_ranges; +	u32 *bin_ranges = tx_latency->ranges; + +	pos += scnprintf(buf + pos, bufsz - pos, +			  "Station\t\t\tTID\tMax\tAvg"); +	if (range_count) { +		pos += scnprintf(buf + pos, bufsz - pos, +				  "\t<=%d", bin_ranges[0]); +		for (i = 0; i < range_count - 1; i++) +			pos += scnprintf(buf + pos, bufsz - pos, "\t%d-%d", +					  bin_ranges[i], bin_ranges[i+1]); +		pos += scnprintf(buf + pos, bufsz - pos, +				  "\t%d<", bin_ranges[range_count - 1]); +	} + +	pos += scnprintf(buf + pos, bufsz - pos, "\n"); + +	return pos; +} + +static int +sta_tx_latency_stat_table(struct ieee80211_tx_latency_bin_ranges *tx_lat_range, +			  struct ieee80211_tx_latency_stat *tx_lat, +			  char *buf, int pos, int bufsz, int tid) +{ +	u32 avg = 0; +	int j; +	int bin_count = tx_lat->bin_count; + +	pos += scnprintf(buf + pos, bufsz - pos, "\t\t\t%d", tid); +	/* make sure you don't divide in 0 */ +	if (tx_lat->counter) +		avg = tx_lat->sum / tx_lat->counter; + +	pos += scnprintf(buf + pos, bufsz - pos, "\t%d\t%d", +			  tx_lat->max, avg); + +	if (tx_lat_range->n_ranges && tx_lat->bins) +		for (j = 0; j < bin_count; j++) +			pos += scnprintf(buf + pos, bufsz - pos, +					  "\t%d", tx_lat->bins[j]); +	pos += scnprintf(buf + pos, bufsz - pos, "\n"); + +	return pos; +} + +/* + * Output Tx latency statistics station && restart all statistics information + */ +static ssize_t sta_tx_latency_stat_read(struct file *file, +					char __user *userbuf, +					size_t count, loff_t *ppos) +{ +	struct sta_info *sta = file->private_data; +	struct ieee80211_local *local = sta->local; +	struct ieee80211_tx_latency_bin_ranges *tx_latency; +	char *buf; +	int bufsz, ret, i; +	int pos = 0; + +	bufsz = 20 * IEEE80211_NUM_TIDS * +		sizeof(struct ieee80211_tx_latency_stat); +	buf = kzalloc(bufsz, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	rcu_read_lock(); + +	tx_latency = rcu_dereference(local->tx_latency); + +	if (!sta->tx_lat) { +		pos += scnprintf(buf + pos, bufsz - pos, +				 "Tx latency statistics are not enabled\n"); +		goto unlock; +	} + +	pos = sta_tx_latency_stat_header(tx_latency, buf, pos, bufsz); + +	pos += scnprintf(buf + pos, bufsz - pos, "%pM\n", sta->sta.addr); +	for (i = 0; i < IEEE80211_NUM_TIDS; i++) +		pos = sta_tx_latency_stat_table(tx_latency, &sta->tx_lat[i], +						buf, pos, bufsz, i); +unlock: +	rcu_read_unlock(); + +	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); +	kfree(buf); + +	return ret; +} +STA_OPS(tx_latency_stat); + +static ssize_t sta_tx_latency_stat_reset_write(struct file *file, +					       const char __user *userbuf, +					       size_t count, loff_t *ppos) +{ +	u32 *bins; +	int bin_count; +	struct sta_info *sta = file->private_data; +	int i; + +	if (!sta->tx_lat) +		return -EINVAL; + +	for (i = 0; i < IEEE80211_NUM_TIDS; i++) { +		bins = sta->tx_lat[i].bins; +		bin_count = sta->tx_lat[i].bin_count; + +		sta->tx_lat[i].max = 0; +		sta->tx_lat[i].sum = 0; +		sta->tx_lat[i].counter = 0; + +		if (bin_count) +			memset(bins, 0, bin_count * sizeof(u32)); +	} + +	return count; +} +STA_OPS_W(tx_latency_stat_reset); +  #define DEBUGFS_ADD(name) \  	debugfs_create_file(#name, 0400, \  		sta->debugfs.dir, sta, &sta_ ##name## _ops); @@ -297,6 +534,8 @@ STA_OPS(ht_capa);  void ieee80211_sta_debugfs_add(struct sta_info *sta)  { +	struct ieee80211_local *local = sta->local; +	struct ieee80211_sub_if_data *sdata = sta->sdata;  	struct dentry *stations_dir = sta->sdata->debugfs.subdir_stations;  	u8 mac[3*ETH_ALEN]; @@ -323,11 +562,19 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)  	DEBUGFS_ADD(flags);  	DEBUGFS_ADD(num_ps_buf_frames);  	DEBUGFS_ADD(inactive_ms); +	DEBUGFS_ADD(connected_time);  	DEBUGFS_ADD(last_seq_ctrl);  	DEBUGFS_ADD(agg_status);  	DEBUGFS_ADD(dev);  	DEBUGFS_ADD(last_signal); +	DEBUGFS_ADD(beacon_loss_count);  	DEBUGFS_ADD(ht_capa); +	DEBUGFS_ADD(vht_capa); +	DEBUGFS_ADD(last_ack_signal); +	DEBUGFS_ADD(current_tx_rate); +	DEBUGFS_ADD(last_rx_rate); +	DEBUGFS_ADD(tx_latency_stat); +	DEBUGFS_ADD(tx_latency_stat_reset);  	DEBUGFS_ADD_COUNTER(rx_packets, rx_packets);  	DEBUGFS_ADD_COUNTER(tx_packets, tx_packets); @@ -341,10 +588,25 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)  	DEBUGFS_ADD_COUNTER(tx_retry_failed, tx_retry_failed);  	DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count);  	DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count); + +	if (sizeof(sta->driver_buffered_tids) == sizeof(u32)) +		debugfs_create_x32("driver_buffered_tids", 0400, +				   sta->debugfs.dir, +				   (u32 *)&sta->driver_buffered_tids); +	else +		debugfs_create_x64("driver_buffered_tids", 0400, +				   sta->debugfs.dir, +				   (u64 *)&sta->driver_buffered_tids); + +	drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs.dir);  }  void ieee80211_sta_debugfs_remove(struct sta_info *sta)  { +	struct ieee80211_local *local = sta->local; +	struct ieee80211_sub_if_data *sdata = sta->sdata; + +	drv_sta_remove_debugfs(local, sdata, &sta->sta, sta->debugfs.dir);  	debugfs_remove_recursive(sta->debugfs.dir);  	sta->debugfs.dir = NULL;  } diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 4244554d218..bd782dcffcc 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -3,11 +3,67 @@  #include <net/mac80211.h>  #include "ieee80211_i.h" -#include "driver-trace.h" +#include "trace.h" -static inline int drv_tx(struct ieee80211_local *local, struct sk_buff *skb) +static inline bool check_sdata_in_driver(struct ieee80211_sub_if_data *sdata)  { -	return local->ops->tx(&local->hw, skb); +	return !WARN(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER), +		     "%s:  Failed check-sdata-in-driver check, flags: 0x%x\n", +		     sdata->dev ? sdata->dev->name : sdata->name, sdata->flags); +} + +static inline struct ieee80211_sub_if_data * +get_bss_sdata(struct ieee80211_sub_if_data *sdata) +{ +	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +		sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, +				     u.ap); + +	return sdata; +} + +static inline void drv_tx(struct ieee80211_local *local, +			  struct ieee80211_tx_control *control, +			  struct sk_buff *skb) +{ +	local->ops->tx(&local->hw, control, skb); +} + +static inline void drv_get_et_strings(struct ieee80211_sub_if_data *sdata, +				      u32 sset, u8 *data) +{ +	struct ieee80211_local *local = sdata->local; +	if (local->ops->get_et_strings) { +		trace_drv_get_et_strings(local, sset); +		local->ops->get_et_strings(&local->hw, &sdata->vif, sset, data); +		trace_drv_return_void(local); +	} +} + +static inline void drv_get_et_stats(struct ieee80211_sub_if_data *sdata, +				    struct ethtool_stats *stats, +				    u64 *data) +{ +	struct ieee80211_local *local = sdata->local; +	if (local->ops->get_et_stats) { +		trace_drv_get_et_stats(local); +		local->ops->get_et_stats(&local->hw, &sdata->vif, stats, data); +		trace_drv_return_void(local); +	} +} + +static inline int drv_get_et_sset_count(struct ieee80211_sub_if_data *sdata, +					int sset) +{ +	struct ieee80211_local *local = sdata->local; +	int rv = 0; +	if (local->ops->get_et_sset_count) { +		trace_drv_get_et_sset_count(local, sset); +		rv = local->ops->get_et_sset_count(&local->hw, &sdata->vif, +						   sset); +		trace_drv_return_int(local, rv); +	} +	return rv;  }  static inline int drv_start(struct ieee80211_local *local) @@ -41,16 +97,66 @@ static inline void drv_stop(struct ieee80211_local *local)  	local->started = false;  } +#ifdef CONFIG_PM +static inline int drv_suspend(struct ieee80211_local *local, +			      struct cfg80211_wowlan *wowlan) +{ +	int ret; + +	might_sleep(); + +	trace_drv_suspend(local); +	ret = local->ops->suspend(&local->hw, wowlan); +	trace_drv_return_int(local, ret); +	return ret; +} + +static inline int drv_resume(struct ieee80211_local *local) +{ +	int ret; + +	might_sleep(); + +	trace_drv_resume(local); +	ret = local->ops->resume(&local->hw); +	trace_drv_return_int(local, ret); +	return ret; +} + +static inline void drv_set_wakeup(struct ieee80211_local *local, +				  bool enabled) +{ +	might_sleep(); + +	if (!local->ops->set_wakeup) +		return; + +	trace_drv_set_wakeup(local, enabled); +	local->ops->set_wakeup(&local->hw, enabled); +	trace_drv_return_void(local); +} +#endif +  static inline int drv_add_interface(struct ieee80211_local *local, -				    struct ieee80211_vif *vif) +				    struct ieee80211_sub_if_data *sdata)  {  	int ret;  	might_sleep(); -	trace_drv_add_interface(local, vif_to_sdata(vif)); -	ret = local->ops->add_interface(&local->hw, vif); +	if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || +		    (sdata->vif.type == NL80211_IFTYPE_MONITOR && +		     !(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF) && +		     !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)))) +		return -EINVAL; + +	trace_drv_add_interface(local, sdata); +	ret = local->ops->add_interface(&local->hw, &sdata->vif);  	trace_drv_return_int(local, ret); + +	if (ret == 0) +		sdata->flags |= IEEE80211_SDATA_IN_DRIVER; +  	return ret;  } @@ -62,6 +168,9 @@ static inline int drv_change_interface(struct ieee80211_local *local,  	might_sleep(); +	if (!check_sdata_in_driver(sdata)) +		return -EIO; +  	trace_drv_change_interface(local, sdata, type, p2p);  	ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p);  	trace_drv_return_int(local, ret); @@ -69,12 +178,16 @@ static inline int drv_change_interface(struct ieee80211_local *local,  }  static inline void drv_remove_interface(struct ieee80211_local *local, -					struct ieee80211_vif *vif) +					struct ieee80211_sub_if_data *sdata)  {  	might_sleep(); -	trace_drv_remove_interface(local, vif_to_sdata(vif)); -	local->ops->remove_interface(&local->hw, vif); +	if (!check_sdata_in_driver(sdata)) +		return; + +	trace_drv_remove_interface(local, sdata); +	local->ops->remove_interface(&local->hw, &sdata->vif); +	sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;  	trace_drv_return_void(local);  } @@ -97,6 +210,20 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,  {  	might_sleep(); +	if (WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON | +				    BSS_CHANGED_BEACON_ENABLED) && +			 sdata->vif.type != NL80211_IFTYPE_AP && +			 sdata->vif.type != NL80211_IFTYPE_ADHOC && +			 sdata->vif.type != NL80211_IFTYPE_MESH_POINT)) +		return; + +	if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE || +			 sdata->vif.type == NL80211_IFTYPE_MONITOR)) +		return; + +	if (!check_sdata_in_driver(sdata)) +		return; +  	trace_drv_bss_info_changed(local, sdata, info, changed);  	if (local->ops->bss_info_changed)  		local->ops->bss_info_changed(&local->hw, &sdata->vif, info, changed); @@ -153,6 +280,10 @@ static inline int drv_set_key(struct ieee80211_local *local,  	might_sleep(); +	sdata = get_bss_sdata(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO; +  	trace_drv_set_key(local, cmd, sdata, sta, key);  	ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key);  	trace_drv_return_int(local, ret); @@ -170,6 +301,10 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local,  	if (sta)  		ista = &sta->sta; +	sdata = get_bss_sdata(sdata); +	if (!check_sdata_in_driver(sdata)) +		return; +  	trace_drv_update_tkip_key(local, sdata, conf, ista, iv32);  	if (local->ops->update_tkip_key)  		local->ops->update_tkip_key(&local->hw, &sdata->vif, conf, @@ -185,12 +320,65 @@ static inline int drv_hw_scan(struct ieee80211_local *local,  	might_sleep(); -	trace_drv_hw_scan(local, sdata, req); +	if (!check_sdata_in_driver(sdata)) +		return -EIO; + +	trace_drv_hw_scan(local, sdata);  	ret = local->ops->hw_scan(&local->hw, &sdata->vif, req);  	trace_drv_return_int(local, ret);  	return ret;  } +static inline void drv_cancel_hw_scan(struct ieee80211_local *local, +				      struct ieee80211_sub_if_data *sdata) +{ +	might_sleep(); + +	if (!check_sdata_in_driver(sdata)) +		return; + +	trace_drv_cancel_hw_scan(local, sdata); +	local->ops->cancel_hw_scan(&local->hw, &sdata->vif); +	trace_drv_return_void(local); +} + +static inline int +drv_sched_scan_start(struct ieee80211_local *local, +		     struct ieee80211_sub_if_data *sdata, +		     struct cfg80211_sched_scan_request *req, +		     struct ieee80211_sched_scan_ies *ies) +{ +	int ret; + +	might_sleep(); + +	if (!check_sdata_in_driver(sdata)) +		return -EIO; + +	trace_drv_sched_scan_start(local, sdata); +	ret = local->ops->sched_scan_start(&local->hw, &sdata->vif, +					      req, ies); +	trace_drv_return_int(local, ret); +	return ret; +} + +static inline int drv_sched_scan_stop(struct ieee80211_local *local, +				      struct ieee80211_sub_if_data *sdata) +{ +	int ret; + +	might_sleep(); + +	if (!check_sdata_in_driver(sdata)) +		return -EIO; + +	trace_drv_sched_scan_stop(local, sdata); +	ret = local->ops->sched_scan_stop(&local->hw, &sdata->vif); +	trace_drv_return_int(local, ret); + +	return ret; +} +  static inline void drv_sw_scan_start(struct ieee80211_local *local)  {  	might_sleep(); @@ -282,6 +470,10 @@ static inline void drv_sta_notify(struct ieee80211_local *local,  				  enum sta_notify_cmd cmd,  				  struct ieee80211_sta *sta)  { +	sdata = get_bss_sdata(sdata); +	if (!check_sdata_in_driver(sdata)) +		return; +  	trace_drv_sta_notify(local, sdata, cmd, sta);  	if (local->ops->sta_notify)  		local->ops->sta_notify(&local->hw, &sdata->vif, cmd, sta); @@ -296,6 +488,10 @@ static inline int drv_sta_add(struct ieee80211_local *local,  	might_sleep(); +	sdata = get_bss_sdata(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO; +  	trace_drv_sta_add(local, sdata, sta);  	if (local->ops->sta_add)  		ret = local->ops->sta_add(&local->hw, &sdata->vif, sta); @@ -311,6 +507,10 @@ static inline void drv_sta_remove(struct ieee80211_local *local,  {  	might_sleep(); +	sdata = get_bss_sdata(sdata); +	if (!check_sdata_in_driver(sdata)) +		return; +  	trace_drv_sta_remove(local, sdata, sta);  	if (local->ops->sta_remove)  		local->ops->sta_remove(&local->hw, &sdata->vif, sta); @@ -318,56 +518,176 @@ static inline void drv_sta_remove(struct ieee80211_local *local,  	trace_drv_return_void(local);  } -static inline int drv_conf_tx(struct ieee80211_local *local, u16 queue, +#ifdef CONFIG_MAC80211_DEBUGFS +static inline void drv_sta_add_debugfs(struct ieee80211_local *local, +				       struct ieee80211_sub_if_data *sdata, +				       struct ieee80211_sta *sta, +				       struct dentry *dir) +{ +	might_sleep(); + +	sdata = get_bss_sdata(sdata); +	if (!check_sdata_in_driver(sdata)) +		return; + +	if (local->ops->sta_add_debugfs) +		local->ops->sta_add_debugfs(&local->hw, &sdata->vif, +					    sta, dir); +} + +static inline void drv_sta_remove_debugfs(struct ieee80211_local *local, +					  struct ieee80211_sub_if_data *sdata, +					  struct ieee80211_sta *sta, +					  struct dentry *dir) +{ +	might_sleep(); + +	sdata = get_bss_sdata(sdata); +	check_sdata_in_driver(sdata); + +	if (local->ops->sta_remove_debugfs) +		local->ops->sta_remove_debugfs(&local->hw, &sdata->vif, +					       sta, dir); +} +#endif + +static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local, +					  struct ieee80211_sub_if_data *sdata, +					  struct sta_info *sta) +{ +	might_sleep(); + +	sdata = get_bss_sdata(sdata); +	if (!check_sdata_in_driver(sdata)) +		return; + +	trace_drv_sta_pre_rcu_remove(local, sdata, &sta->sta); +	if (local->ops->sta_pre_rcu_remove) +		local->ops->sta_pre_rcu_remove(&local->hw, &sdata->vif, +					       &sta->sta); +	trace_drv_return_void(local); +} + +static inline __must_check +int drv_sta_state(struct ieee80211_local *local, +		  struct ieee80211_sub_if_data *sdata, +		  struct sta_info *sta, +		  enum ieee80211_sta_state old_state, +		  enum ieee80211_sta_state new_state) +{ +	int ret = 0; + +	might_sleep(); + +	sdata = get_bss_sdata(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO; + +	trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state); +	if (local->ops->sta_state) { +		ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta, +					    old_state, new_state); +	} else if (old_state == IEEE80211_STA_AUTH && +		   new_state == IEEE80211_STA_ASSOC) { +		ret = drv_sta_add(local, sdata, &sta->sta); +		if (ret == 0) +			sta->uploaded = true; +	} else if (old_state == IEEE80211_STA_ASSOC && +		   new_state == IEEE80211_STA_AUTH) { +		drv_sta_remove(local, sdata, &sta->sta); +	} +	trace_drv_return_int(local, ret); +	return ret; +} + +static inline void drv_sta_rc_update(struct ieee80211_local *local, +				     struct ieee80211_sub_if_data *sdata, +				     struct ieee80211_sta *sta, u32 changed) +{ +	sdata = get_bss_sdata(sdata); +	if (!check_sdata_in_driver(sdata)) +		return; + +	WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && +		(sdata->vif.type != NL80211_IFTYPE_ADHOC && +		 sdata->vif.type != NL80211_IFTYPE_MESH_POINT)); + +	trace_drv_sta_rc_update(local, sdata, sta, changed); +	if (local->ops->sta_rc_update) +		local->ops->sta_rc_update(&local->hw, &sdata->vif, +					  sta, changed); + +	trace_drv_return_void(local); +} + +static inline int drv_conf_tx(struct ieee80211_local *local, +			      struct ieee80211_sub_if_data *sdata, u16 ac,  			      const struct ieee80211_tx_queue_params *params)  {  	int ret = -EOPNOTSUPP;  	might_sleep(); -	trace_drv_conf_tx(local, queue, params); +	if (!check_sdata_in_driver(sdata)) +		return -EIO; + +	trace_drv_conf_tx(local, sdata, ac, params);  	if (local->ops->conf_tx) -		ret = local->ops->conf_tx(&local->hw, queue, params); +		ret = local->ops->conf_tx(&local->hw, &sdata->vif, +					  ac, params);  	trace_drv_return_int(local, ret);  	return ret;  } -static inline u64 drv_get_tsf(struct ieee80211_local *local) +static inline u64 drv_get_tsf(struct ieee80211_local *local, +			      struct ieee80211_sub_if_data *sdata)  {  	u64 ret = -1ULL;  	might_sleep(); -	trace_drv_get_tsf(local); +	if (!check_sdata_in_driver(sdata)) +		return ret; + +	trace_drv_get_tsf(local, sdata);  	if (local->ops->get_tsf) -		ret = local->ops->get_tsf(&local->hw); +		ret = local->ops->get_tsf(&local->hw, &sdata->vif);  	trace_drv_return_u64(local, ret);  	return ret;  } -static inline void drv_set_tsf(struct ieee80211_local *local, u64 tsf) +static inline void drv_set_tsf(struct ieee80211_local *local, +			       struct ieee80211_sub_if_data *sdata, +			       u64 tsf)  {  	might_sleep(); -	trace_drv_set_tsf(local, tsf); +	if (!check_sdata_in_driver(sdata)) +		return; + +	trace_drv_set_tsf(local, sdata, tsf);  	if (local->ops->set_tsf) -		local->ops->set_tsf(&local->hw, tsf); +		local->ops->set_tsf(&local->hw, &sdata->vif, tsf);  	trace_drv_return_void(local);  } -static inline void drv_reset_tsf(struct ieee80211_local *local) +static inline void drv_reset_tsf(struct ieee80211_local *local, +				 struct ieee80211_sub_if_data *sdata)  {  	might_sleep(); -	trace_drv_reset_tsf(local); +	if (!check_sdata_in_driver(sdata)) +		return; + +	trace_drv_reset_tsf(local, sdata);  	if (local->ops->reset_tsf) -		local->ops->reset_tsf(&local->hw); +		local->ops->reset_tsf(&local->hw, &sdata->vif);  	trace_drv_return_void(local);  }  static inline int drv_tx_last_beacon(struct ieee80211_local *local)  { -	int ret = 1; +	int ret = 0; /* default unsupported op for less congestion */  	might_sleep(); @@ -382,17 +702,21 @@ static inline int drv_ampdu_action(struct ieee80211_local *local,  				   struct ieee80211_sub_if_data *sdata,  				   enum ieee80211_ampdu_mlme_action action,  				   struct ieee80211_sta *sta, u16 tid, -				   u16 *ssn) +				   u16 *ssn, u8 buf_size)  {  	int ret = -EOPNOTSUPP;  	might_sleep(); -	trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn); +	sdata = get_bss_sdata(sdata); +	if (!check_sdata_in_driver(sdata)) +		return -EIO; + +	trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size);  	if (local->ops->ampdu_action)  		ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action, -					       sta, tid, ssn); +					       sta, tid, ssn, buf_size);  	trace_drv_return_int(local, ret); @@ -422,13 +746,20 @@ static inline void drv_rfkill_poll(struct ieee80211_local *local)  		local->ops->rfkill_poll(&local->hw);  } -static inline void drv_flush(struct ieee80211_local *local, bool drop) +static inline void drv_flush(struct ieee80211_local *local, +			     struct ieee80211_sub_if_data *sdata, +			     u32 queues, bool drop)  { +	struct ieee80211_vif *vif = sdata ? &sdata->vif : NULL; +  	might_sleep(); -	trace_drv_flush(local, drop); +	if (sdata && !check_sdata_in_driver(sdata)) +		return; + +	trace_drv_flush(local, queues, drop);  	if (local->ops->flush) -		local->ops->flush(&local->hw, drop); +		local->ops->flush(&local->hw, vif, queues, drop);  	trace_drv_return_void(local);  } @@ -465,4 +796,430 @@ static inline int drv_get_antenna(struct ieee80211_local *local,  	return ret;  } +static inline int drv_remain_on_channel(struct ieee80211_local *local, +					struct ieee80211_sub_if_data *sdata, +					struct ieee80211_channel *chan, +					unsigned int duration, +					enum ieee80211_roc_type type) +{ +	int ret; + +	might_sleep(); + +	trace_drv_remain_on_channel(local, sdata, chan, duration, type); +	ret = local->ops->remain_on_channel(&local->hw, &sdata->vif, +					    chan, duration, type); +	trace_drv_return_int(local, ret); + +	return ret; +} + +static inline int drv_cancel_remain_on_channel(struct ieee80211_local *local) +{ +	int ret; + +	might_sleep(); + +	trace_drv_cancel_remain_on_channel(local); +	ret = local->ops->cancel_remain_on_channel(&local->hw); +	trace_drv_return_int(local, ret); + +	return ret; +} + +static inline int drv_set_ringparam(struct ieee80211_local *local, +				    u32 tx, u32 rx) +{ +	int ret = -ENOTSUPP; + +	might_sleep(); + +	trace_drv_set_ringparam(local, tx, rx); +	if (local->ops->set_ringparam) +		ret = local->ops->set_ringparam(&local->hw, tx, rx); +	trace_drv_return_int(local, ret); + +	return ret; +} + +static inline void drv_get_ringparam(struct ieee80211_local *local, +				     u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max) +{ +	might_sleep(); + +	trace_drv_get_ringparam(local, tx, tx_max, rx, rx_max); +	if (local->ops->get_ringparam) +		local->ops->get_ringparam(&local->hw, tx, tx_max, rx, rx_max); +	trace_drv_return_void(local); +} + +static inline bool drv_tx_frames_pending(struct ieee80211_local *local) +{ +	bool ret = false; + +	might_sleep(); + +	trace_drv_tx_frames_pending(local); +	if (local->ops->tx_frames_pending) +		ret = local->ops->tx_frames_pending(&local->hw); +	trace_drv_return_bool(local, ret); + +	return ret; +} + +static inline int drv_set_bitrate_mask(struct ieee80211_local *local, +				       struct ieee80211_sub_if_data *sdata, +				       const struct cfg80211_bitrate_mask *mask) +{ +	int ret = -EOPNOTSUPP; + +	might_sleep(); + +	if (!check_sdata_in_driver(sdata)) +		return -EIO; + +	trace_drv_set_bitrate_mask(local, sdata, mask); +	if (local->ops->set_bitrate_mask) +		ret = local->ops->set_bitrate_mask(&local->hw, +						   &sdata->vif, mask); +	trace_drv_return_int(local, ret); + +	return ret; +} + +static inline void drv_set_rekey_data(struct ieee80211_local *local, +				      struct ieee80211_sub_if_data *sdata, +				      struct cfg80211_gtk_rekey_data *data) +{ +	if (!check_sdata_in_driver(sdata)) +		return; + +	trace_drv_set_rekey_data(local, sdata, data); +	if (local->ops->set_rekey_data) +		local->ops->set_rekey_data(&local->hw, &sdata->vif, data); +	trace_drv_return_void(local); +} + +static inline void drv_rssi_callback(struct ieee80211_local *local, +				     struct ieee80211_sub_if_data *sdata, +				     const enum ieee80211_rssi_event event) +{ +	trace_drv_rssi_callback(local, sdata, event); +	if (local->ops->rssi_callback) +		local->ops->rssi_callback(&local->hw, &sdata->vif, event); +	trace_drv_return_void(local); +} + +static inline void +drv_release_buffered_frames(struct ieee80211_local *local, +			    struct sta_info *sta, u16 tids, int num_frames, +			    enum ieee80211_frame_release_type reason, +			    bool more_data) +{ +	trace_drv_release_buffered_frames(local, &sta->sta, tids, num_frames, +					  reason, more_data); +	if (local->ops->release_buffered_frames) +		local->ops->release_buffered_frames(&local->hw, &sta->sta, tids, +						    num_frames, reason, +						    more_data); +	trace_drv_return_void(local); +} + +static inline void +drv_allow_buffered_frames(struct ieee80211_local *local, +			  struct sta_info *sta, u16 tids, int num_frames, +			  enum ieee80211_frame_release_type reason, +			  bool more_data) +{ +	trace_drv_allow_buffered_frames(local, &sta->sta, tids, num_frames, +					reason, more_data); +	if (local->ops->allow_buffered_frames) +		local->ops->allow_buffered_frames(&local->hw, &sta->sta, +						  tids, num_frames, reason, +						  more_data); +	trace_drv_return_void(local); +} + +static inline int drv_get_rssi(struct ieee80211_local *local, +				struct ieee80211_sub_if_data *sdata, +				struct ieee80211_sta *sta, +				s8 *rssi_dbm) +{ +	int ret; + +	might_sleep(); + +	ret = local->ops->get_rssi(&local->hw, &sdata->vif, sta, rssi_dbm); +	trace_drv_get_rssi(local, sta, *rssi_dbm, ret); + +	return ret; +} + +static inline void drv_mgd_prepare_tx(struct ieee80211_local *local, +				      struct ieee80211_sub_if_data *sdata) +{ +	might_sleep(); + +	if (!check_sdata_in_driver(sdata)) +		return; +	WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION); + +	trace_drv_mgd_prepare_tx(local, sdata); +	if (local->ops->mgd_prepare_tx) +		local->ops->mgd_prepare_tx(&local->hw, &sdata->vif); +	trace_drv_return_void(local); +} + +static inline int drv_add_chanctx(struct ieee80211_local *local, +				  struct ieee80211_chanctx *ctx) +{ +	int ret = -EOPNOTSUPP; + +	trace_drv_add_chanctx(local, ctx); +	if (local->ops->add_chanctx) +		ret = local->ops->add_chanctx(&local->hw, &ctx->conf); +	trace_drv_return_int(local, ret); +	if (!ret) +		ctx->driver_present = true; + +	return ret; +} + +static inline void drv_remove_chanctx(struct ieee80211_local *local, +				      struct ieee80211_chanctx *ctx) +{ +	if (WARN_ON(!ctx->driver_present)) +		return; + +	trace_drv_remove_chanctx(local, ctx); +	if (local->ops->remove_chanctx) +		local->ops->remove_chanctx(&local->hw, &ctx->conf); +	trace_drv_return_void(local); +	ctx->driver_present = false; +} + +static inline void drv_change_chanctx(struct ieee80211_local *local, +				      struct ieee80211_chanctx *ctx, +				      u32 changed) +{ +	trace_drv_change_chanctx(local, ctx, changed); +	if (local->ops->change_chanctx) { +		WARN_ON_ONCE(!ctx->driver_present); +		local->ops->change_chanctx(&local->hw, &ctx->conf, changed); +	} +	trace_drv_return_void(local); +} + +static inline int drv_assign_vif_chanctx(struct ieee80211_local *local, +					 struct ieee80211_sub_if_data *sdata, +					 struct ieee80211_chanctx *ctx) +{ +	int ret = 0; + +	if (!check_sdata_in_driver(sdata)) +		return -EIO; + +	trace_drv_assign_vif_chanctx(local, sdata, ctx); +	if (local->ops->assign_vif_chanctx) { +		WARN_ON_ONCE(!ctx->driver_present); +		ret = local->ops->assign_vif_chanctx(&local->hw, +						     &sdata->vif, +						     &ctx->conf); +	} +	trace_drv_return_int(local, ret); + +	return ret; +} + +static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local, +					    struct ieee80211_sub_if_data *sdata, +					    struct ieee80211_chanctx *ctx) +{ +	if (!check_sdata_in_driver(sdata)) +		return; + +	trace_drv_unassign_vif_chanctx(local, sdata, ctx); +	if (local->ops->unassign_vif_chanctx) { +		WARN_ON_ONCE(!ctx->driver_present); +		local->ops->unassign_vif_chanctx(&local->hw, +						 &sdata->vif, +						 &ctx->conf); +	} +	trace_drv_return_void(local); +} + +static inline int +drv_switch_vif_chanctx(struct ieee80211_local *local, +		       struct ieee80211_vif_chanctx_switch *vifs, +		       int n_vifs, +		       enum ieee80211_chanctx_switch_mode mode) +{ +	int ret = 0; +	int i; + +	if (!local->ops->switch_vif_chanctx) +		return -EOPNOTSUPP; + +	for (i = 0; i < n_vifs; i++) { +		struct ieee80211_chanctx *new_ctx = +			container_of(vifs[i].new_ctx, +				     struct ieee80211_chanctx, +				     conf); +		struct ieee80211_chanctx *old_ctx = +			container_of(vifs[i].old_ctx, +				     struct ieee80211_chanctx, +				     conf); + +		WARN_ON_ONCE(!old_ctx->driver_present); +		WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS && +			      new_ctx->driver_present) || +			     (mode == CHANCTX_SWMODE_REASSIGN_VIF && +			      !new_ctx->driver_present)); +	} + +	trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode); +	ret = local->ops->switch_vif_chanctx(&local->hw, +					     vifs, n_vifs, mode); +	trace_drv_return_int(local, ret); + +	if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) { +		for (i = 0; i < n_vifs; i++) { +			struct ieee80211_chanctx *new_ctx = +				container_of(vifs[i].new_ctx, +					     struct ieee80211_chanctx, +					     conf); +			struct ieee80211_chanctx *old_ctx = +				container_of(vifs[i].old_ctx, +					     struct ieee80211_chanctx, +					     conf); + +			new_ctx->driver_present = true; +			old_ctx->driver_present = false; +		} +	} + +	return ret; +} + +static inline int drv_start_ap(struct ieee80211_local *local, +			       struct ieee80211_sub_if_data *sdata) +{ +	int ret = 0; + +	if (!check_sdata_in_driver(sdata)) +		return -EIO; + +	trace_drv_start_ap(local, sdata, &sdata->vif.bss_conf); +	if (local->ops->start_ap) +		ret = local->ops->start_ap(&local->hw, &sdata->vif); +	trace_drv_return_int(local, ret); +	return ret; +} + +static inline void drv_stop_ap(struct ieee80211_local *local, +			       struct ieee80211_sub_if_data *sdata) +{ +	if (!check_sdata_in_driver(sdata)) +		return; + +	trace_drv_stop_ap(local, sdata); +	if (local->ops->stop_ap) +		local->ops->stop_ap(&local->hw, &sdata->vif); +	trace_drv_return_void(local); +} + +static inline void drv_restart_complete(struct ieee80211_local *local) +{ +	might_sleep(); + +	trace_drv_restart_complete(local); +	if (local->ops->restart_complete) +		local->ops->restart_complete(&local->hw); +	trace_drv_return_void(local); +} + +static inline void +drv_set_default_unicast_key(struct ieee80211_local *local, +			    struct ieee80211_sub_if_data *sdata, +			    int key_idx) +{ +	if (!check_sdata_in_driver(sdata)) +		return; + +	WARN_ON_ONCE(key_idx < -1 || key_idx > 3); + +	trace_drv_set_default_unicast_key(local, sdata, key_idx); +	if (local->ops->set_default_unicast_key) +		local->ops->set_default_unicast_key(&local->hw, &sdata->vif, +						    key_idx); +	trace_drv_return_void(local); +} + +#if IS_ENABLED(CONFIG_IPV6) +static inline void drv_ipv6_addr_change(struct ieee80211_local *local, +					struct ieee80211_sub_if_data *sdata, +					struct inet6_dev *idev) +{ +	trace_drv_ipv6_addr_change(local, sdata); +	if (local->ops->ipv6_addr_change) +		local->ops->ipv6_addr_change(&local->hw, &sdata->vif, idev); +	trace_drv_return_void(local); +} +#endif + +static inline void +drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata, +			  struct cfg80211_chan_def *chandef) +{ +	struct ieee80211_local *local = sdata->local; + +	if (local->ops->channel_switch_beacon) { +		trace_drv_channel_switch_beacon(local, sdata, chandef); +		local->ops->channel_switch_beacon(&local->hw, &sdata->vif, +						  chandef); +	} +} + +static inline int drv_join_ibss(struct ieee80211_local *local, +				struct ieee80211_sub_if_data *sdata) +{ +	int ret = 0; + +	might_sleep(); +	if (!check_sdata_in_driver(sdata)) +		return -EIO; + +	trace_drv_join_ibss(local, sdata, &sdata->vif.bss_conf); +	if (local->ops->join_ibss) +		ret = local->ops->join_ibss(&local->hw, &sdata->vif); +	trace_drv_return_int(local, ret); +	return ret; +} + +static inline void drv_leave_ibss(struct ieee80211_local *local, +				  struct ieee80211_sub_if_data *sdata) +{ +	might_sleep(); +	if (!check_sdata_in_driver(sdata)) +		return; + +	trace_drv_leave_ibss(local, sdata); +	if (local->ops->leave_ibss) +		local->ops->leave_ibss(&local->hw, &sdata->vif); +	trace_drv_return_void(local); +} + +static inline u32 drv_get_expected_throughput(struct ieee80211_local *local, +					      struct ieee80211_sta *sta) +{ +	u32 ret = 0; + +	trace_drv_get_expected_throughput(sta); +	if (local->ops->get_expected_throughput) +		ret = local->ops->get_expected_throughput(sta); +	trace_drv_return_u32(local, ret); + +	return ret; +} +  #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/driver-trace.c b/net/mac80211/driver-trace.c deleted file mode 100644 index 8ed8711b1a6..00000000000 --- a/net/mac80211/driver-trace.c +++ /dev/null @@ -1,9 +0,0 @@ -/* bug in tracepoint.h, it should include this */ -#include <linux/module.h> - -/* sparse isn't too happy with all macros... */ -#ifndef __CHECKER__ -#include "driver-ops.h" -#define CREATE_TRACE_POINTS -#include "driver-trace.h" -#endif diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h deleted file mode 100644 index c2772f23ac9..00000000000 --- a/net/mac80211/driver-trace.h +++ /dev/null @@ -1,1231 +0,0 @@ -#if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ) -#define __MAC80211_DRIVER_TRACE - -#include <linux/tracepoint.h> -#include <net/mac80211.h> -#include "ieee80211_i.h" - -#if !defined(CONFIG_MAC80211_DRIVER_API_TRACER) || defined(__CHECKER__) -#undef TRACE_EVENT -#define TRACE_EVENT(name, proto, ...) \ -static inline void trace_ ## name(proto) {} -#endif - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM mac80211 - -#define MAXNAME		32 -#define LOCAL_ENTRY	__array(char, wiphy_name, 32) -#define LOCAL_ASSIGN	strlcpy(__entry->wiphy_name, wiphy_name(local->hw.wiphy), MAXNAME) -#define LOCAL_PR_FMT	"%s" -#define LOCAL_PR_ARG	__entry->wiphy_name - -#define STA_ENTRY	__array(char, sta_addr, ETH_ALEN) -#define STA_ASSIGN	(sta ? memcpy(__entry->sta_addr, sta->addr, ETH_ALEN) : memset(__entry->sta_addr, 0, ETH_ALEN)) -#define STA_PR_FMT	" sta:%pM" -#define STA_PR_ARG	__entry->sta_addr - -#define VIF_ENTRY	__field(enum nl80211_iftype, vif_type) __field(void *, sdata)	\ -			__field(bool, p2p)						\ -			__string(vif_name, sdata->dev ? sdata->dev->name : "<nodev>") -#define VIF_ASSIGN	__entry->vif_type = sdata->vif.type; __entry->sdata = sdata;	\ -			__entry->p2p = sdata->vif.p2p;					\ -			__assign_str(vif_name, sdata->dev ? sdata->dev->name : "<nodev>") -#define VIF_PR_FMT	" vif:%s(%d%s)" -#define VIF_PR_ARG	__get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" - -/* - * Tracing for driver callbacks. - */ - -TRACE_EVENT(drv_return_void, -	TP_PROTO(struct ieee80211_local *local), -	TP_ARGS(local), -	TP_STRUCT__entry( -		LOCAL_ENTRY -	), -	TP_fast_assign( -		LOCAL_ASSIGN; -	), -	TP_printk(LOCAL_PR_FMT, LOCAL_PR_ARG) -); - -TRACE_EVENT(drv_return_int, -	TP_PROTO(struct ieee80211_local *local, int ret), -	TP_ARGS(local, ret), -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(int, ret) -	), -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->ret = ret; -	), -	TP_printk(LOCAL_PR_FMT " - %d", LOCAL_PR_ARG, __entry->ret) -); - -TRACE_EVENT(drv_return_u64, -	TP_PROTO(struct ieee80211_local *local, u64 ret), -	TP_ARGS(local, ret), -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(u64, ret) -	), -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->ret = ret; -	), -	TP_printk(LOCAL_PR_FMT " - %llu", LOCAL_PR_ARG, __entry->ret) -); - -TRACE_EVENT(drv_start, -	TP_PROTO(struct ieee80211_local *local), - -	TP_ARGS(local), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -	), - -	TP_printk( -		LOCAL_PR_FMT, LOCAL_PR_ARG -	) -); - -TRACE_EVENT(drv_stop, -	TP_PROTO(struct ieee80211_local *local), - -	TP_ARGS(local), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -	), - -	TP_printk( -		LOCAL_PR_FMT, LOCAL_PR_ARG -	) -); - -TRACE_EVENT(drv_add_interface, -	TP_PROTO(struct ieee80211_local *local, -		 struct ieee80211_sub_if_data *sdata), - -	TP_ARGS(local, sdata), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		VIF_ENTRY -		__array(char, addr, 6) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		VIF_ASSIGN; -		memcpy(__entry->addr, sdata->vif.addr, 6); -	), - -	TP_printk( -		LOCAL_PR_FMT  VIF_PR_FMT " addr:%pM", -		LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr -	) -); - -TRACE_EVENT(drv_change_interface, -	TP_PROTO(struct ieee80211_local *local, -		 struct ieee80211_sub_if_data *sdata, -		 enum nl80211_iftype type, bool p2p), - -	TP_ARGS(local, sdata, type, p2p), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		VIF_ENTRY -		__field(u32, new_type) -		__field(bool, new_p2p) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		VIF_ASSIGN; -		__entry->new_type = type; -		__entry->new_p2p = p2p; -	), - -	TP_printk( -		LOCAL_PR_FMT  VIF_PR_FMT " new type:%d%s", -		LOCAL_PR_ARG, VIF_PR_ARG, __entry->new_type, -		__entry->new_p2p ? "/p2p" : "" -	) -); - -TRACE_EVENT(drv_remove_interface, -	TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), - -	TP_ARGS(local, sdata), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		VIF_ENTRY -		__array(char, addr, 6) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		VIF_ASSIGN; -		memcpy(__entry->addr, sdata->vif.addr, 6); -	), - -	TP_printk( -		LOCAL_PR_FMT  VIF_PR_FMT " addr:%pM", -		LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr -	) -); - -TRACE_EVENT(drv_config, -	TP_PROTO(struct ieee80211_local *local, -		 u32 changed), - -	TP_ARGS(local, changed), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(u32, changed) -		__field(u32, flags) -		__field(int, power_level) -		__field(int, dynamic_ps_timeout) -		__field(int, max_sleep_period) -		__field(u16, listen_interval) -		__field(u8, long_frame_max_tx_count) -		__field(u8, short_frame_max_tx_count) -		__field(int, center_freq) -		__field(int, channel_type) -		__field(int, smps) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->changed = changed; -		__entry->flags = local->hw.conf.flags; -		__entry->power_level = local->hw.conf.power_level; -		__entry->dynamic_ps_timeout = local->hw.conf.dynamic_ps_timeout; -		__entry->max_sleep_period = local->hw.conf.max_sleep_period; -		__entry->listen_interval = local->hw.conf.listen_interval; -		__entry->long_frame_max_tx_count = local->hw.conf.long_frame_max_tx_count; -		__entry->short_frame_max_tx_count = local->hw.conf.short_frame_max_tx_count; -		__entry->center_freq = local->hw.conf.channel->center_freq; -		__entry->channel_type = local->hw.conf.channel_type; -		__entry->smps = local->hw.conf.smps_mode; -	), - -	TP_printk( -		LOCAL_PR_FMT " ch:%#x freq:%d", -		LOCAL_PR_ARG, __entry->changed, __entry->center_freq -	) -); - -TRACE_EVENT(drv_bss_info_changed, -	TP_PROTO(struct ieee80211_local *local, -		 struct ieee80211_sub_if_data *sdata, -		 struct ieee80211_bss_conf *info, -		 u32 changed), - -	TP_ARGS(local, sdata, info, changed), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		VIF_ENTRY -		__field(bool, assoc) -		__field(u16, aid) -		__field(bool, cts) -		__field(bool, shortpre) -		__field(bool, shortslot) -		__field(u8, dtimper) -		__field(u16, bcnint) -		__field(u16, assoc_cap) -		__field(u64, timestamp) -		__field(u32, basic_rates) -		__field(u32, changed) -		__field(bool, enable_beacon) -		__field(u16, ht_operation_mode) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		VIF_ASSIGN; -		__entry->changed = changed; -		__entry->aid = info->aid; -		__entry->assoc = info->assoc; -		__entry->shortpre = info->use_short_preamble; -		__entry->cts = info->use_cts_prot; -		__entry->shortslot = info->use_short_slot; -		__entry->dtimper = info->dtim_period; -		__entry->bcnint = info->beacon_int; -		__entry->assoc_cap = info->assoc_capability; -		__entry->timestamp = info->timestamp; -		__entry->basic_rates = info->basic_rates; -		__entry->enable_beacon = info->enable_beacon; -		__entry->ht_operation_mode = info->ht_operation_mode; -	), - -	TP_printk( -		LOCAL_PR_FMT  VIF_PR_FMT " changed:%#x", -		LOCAL_PR_ARG, VIF_PR_ARG, __entry->changed -	) -); - -TRACE_EVENT(drv_prepare_multicast, -	TP_PROTO(struct ieee80211_local *local, int mc_count), - -	TP_ARGS(local, mc_count), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(int, mc_count) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->mc_count = mc_count; -	), - -	TP_printk( -		LOCAL_PR_FMT " prepare mc (%d)", -		LOCAL_PR_ARG, __entry->mc_count -	) -); - -TRACE_EVENT(drv_configure_filter, -	TP_PROTO(struct ieee80211_local *local, -		 unsigned int changed_flags, -		 unsigned int *total_flags, -		 u64 multicast), - -	TP_ARGS(local, changed_flags, total_flags, multicast), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(unsigned int, changed) -		__field(unsigned int, total) -		__field(u64, multicast) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->changed = changed_flags; -		__entry->total = *total_flags; -		__entry->multicast = multicast; -	), - -	TP_printk( -		LOCAL_PR_FMT " changed:%#x total:%#x", -		LOCAL_PR_ARG, __entry->changed, __entry->total -	) -); - -TRACE_EVENT(drv_set_tim, -	TP_PROTO(struct ieee80211_local *local, -		 struct ieee80211_sta *sta, bool set), - -	TP_ARGS(local, sta, set), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		STA_ENTRY -		__field(bool, set) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		STA_ASSIGN; -		__entry->set = set; -	), - -	TP_printk( -		LOCAL_PR_FMT STA_PR_FMT " set:%d", -		LOCAL_PR_ARG, STA_PR_FMT, __entry->set -	) -); - -TRACE_EVENT(drv_set_key, -	TP_PROTO(struct ieee80211_local *local, -		 enum set_key_cmd cmd, struct ieee80211_sub_if_data *sdata, -		 struct ieee80211_sta *sta, -		 struct ieee80211_key_conf *key), - -	TP_ARGS(local, cmd, sdata, sta, key), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		VIF_ENTRY -		STA_ENTRY -		__field(u32, cipher) -		__field(u8, hw_key_idx) -		__field(u8, flags) -		__field(s8, keyidx) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		VIF_ASSIGN; -		STA_ASSIGN; -		__entry->cipher = key->cipher; -		__entry->flags = key->flags; -		__entry->keyidx = key->keyidx; -		__entry->hw_key_idx = key->hw_key_idx; -	), - -	TP_printk( -		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT, -		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG -	) -); - -TRACE_EVENT(drv_update_tkip_key, -	TP_PROTO(struct ieee80211_local *local, -		 struct ieee80211_sub_if_data *sdata, -		 struct ieee80211_key_conf *conf, -		 struct ieee80211_sta *sta, u32 iv32), - -	TP_ARGS(local, sdata, conf, sta, iv32), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		VIF_ENTRY -		STA_ENTRY -		__field(u32, iv32) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		VIF_ASSIGN; -		STA_ASSIGN; -		__entry->iv32 = iv32; -	), - -	TP_printk( -		LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " iv32:%#x", -		LOCAL_PR_ARG,VIF_PR_ARG,STA_PR_ARG, __entry->iv32 -	) -); - -TRACE_EVENT(drv_hw_scan, -	TP_PROTO(struct ieee80211_local *local, -		 struct ieee80211_sub_if_data *sdata, -		 struct cfg80211_scan_request *req), - -	TP_ARGS(local, sdata, req), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		VIF_ENTRY -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		VIF_ASSIGN; -	), - -	TP_printk( -		LOCAL_PR_FMT VIF_PR_FMT, -		LOCAL_PR_ARG,VIF_PR_ARG -	) -); - -TRACE_EVENT(drv_sw_scan_start, -	TP_PROTO(struct ieee80211_local *local), - -	TP_ARGS(local), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -	), - -	TP_printk( -		LOCAL_PR_FMT, LOCAL_PR_ARG -	) -); - -TRACE_EVENT(drv_sw_scan_complete, -	TP_PROTO(struct ieee80211_local *local), - -	TP_ARGS(local), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -	), - -	TP_printk( -		LOCAL_PR_FMT, LOCAL_PR_ARG -	) -); - -TRACE_EVENT(drv_get_stats, -	TP_PROTO(struct ieee80211_local *local, -		 struct ieee80211_low_level_stats *stats, -		 int ret), - -	TP_ARGS(local, stats, ret), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(int, ret) -		__field(unsigned int, ackfail) -		__field(unsigned int, rtsfail) -		__field(unsigned int, fcserr) -		__field(unsigned int, rtssucc) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->ret = ret; -		__entry->ackfail = stats->dot11ACKFailureCount; -		__entry->rtsfail = stats->dot11RTSFailureCount; -		__entry->fcserr = stats->dot11FCSErrorCount; -		__entry->rtssucc = stats->dot11RTSSuccessCount; -	), - -	TP_printk( -		LOCAL_PR_FMT " ret:%d", -		LOCAL_PR_ARG, __entry->ret -	) -); - -TRACE_EVENT(drv_get_tkip_seq, -	TP_PROTO(struct ieee80211_local *local, -		 u8 hw_key_idx, u32 *iv32, u16 *iv16), - -	TP_ARGS(local, hw_key_idx, iv32, iv16), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(u8, hw_key_idx) -		__field(u32, iv32) -		__field(u16, iv16) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->hw_key_idx = hw_key_idx; -		__entry->iv32 = *iv32; -		__entry->iv16 = *iv16; -	), - -	TP_printk( -		LOCAL_PR_FMT, LOCAL_PR_ARG -	) -); - -TRACE_EVENT(drv_set_frag_threshold, -	TP_PROTO(struct ieee80211_local *local, u32 value), - -	TP_ARGS(local, value), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(u32, value) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->value = value; -	), - -	TP_printk( -		LOCAL_PR_FMT " value:%d", -		LOCAL_PR_ARG, __entry->value -	) -); - -TRACE_EVENT(drv_set_rts_threshold, -	TP_PROTO(struct ieee80211_local *local, u32 value), - -	TP_ARGS(local, value), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(u32, value) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->value = value; -	), - -	TP_printk( -		LOCAL_PR_FMT " value:%d", -		LOCAL_PR_ARG, __entry->value -	) -); - -TRACE_EVENT(drv_set_coverage_class, -	TP_PROTO(struct ieee80211_local *local, u8 value), - -	TP_ARGS(local, value), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(u8, value) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->value = value; -	), - -	TP_printk( -		LOCAL_PR_FMT " value:%d", -		LOCAL_PR_ARG, __entry->value -	) -); - -TRACE_EVENT(drv_sta_notify, -	TP_PROTO(struct ieee80211_local *local, -		 struct ieee80211_sub_if_data *sdata, -		 enum sta_notify_cmd cmd, -		 struct ieee80211_sta *sta), - -	TP_ARGS(local, sdata, cmd, sta), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		VIF_ENTRY -		STA_ENTRY -		__field(u32, cmd) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		VIF_ASSIGN; -		STA_ASSIGN; -		__entry->cmd = cmd; -	), - -	TP_printk( -		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT " cmd:%d", -		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->cmd -	) -); - -TRACE_EVENT(drv_sta_add, -	TP_PROTO(struct ieee80211_local *local, -		 struct ieee80211_sub_if_data *sdata, -		 struct ieee80211_sta *sta), - -	TP_ARGS(local, sdata, sta), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		VIF_ENTRY -		STA_ENTRY -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		VIF_ASSIGN; -		STA_ASSIGN; -	), - -	TP_printk( -		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT, -		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG -	) -); - -TRACE_EVENT(drv_sta_remove, -	TP_PROTO(struct ieee80211_local *local, -		 struct ieee80211_sub_if_data *sdata, -		 struct ieee80211_sta *sta), - -	TP_ARGS(local, sdata, sta), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		VIF_ENTRY -		STA_ENTRY -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		VIF_ASSIGN; -		STA_ASSIGN; -	), - -	TP_printk( -		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT, -		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG -	) -); - -TRACE_EVENT(drv_conf_tx, -	TP_PROTO(struct ieee80211_local *local, u16 queue, -		 const struct ieee80211_tx_queue_params *params), - -	TP_ARGS(local, queue, params), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(u16, queue) -		__field(u16, txop) -		__field(u16, cw_min) -		__field(u16, cw_max) -		__field(u8, aifs) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->queue = queue; -		__entry->txop = params->txop; -		__entry->cw_max = params->cw_max; -		__entry->cw_min = params->cw_min; -		__entry->aifs = params->aifs; -	), - -	TP_printk( -		LOCAL_PR_FMT " queue:%d", -		LOCAL_PR_ARG, __entry->queue -	) -); - -TRACE_EVENT(drv_get_tsf, -	TP_PROTO(struct ieee80211_local *local), - -	TP_ARGS(local), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -	), - -	TP_printk( -		LOCAL_PR_FMT, -		LOCAL_PR_ARG -	) -); - -TRACE_EVENT(drv_set_tsf, -	TP_PROTO(struct ieee80211_local *local, u64 tsf), - -	TP_ARGS(local, tsf), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(u64, tsf) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->tsf = tsf; -	), - -	TP_printk( -		LOCAL_PR_FMT " tsf:%llu", -		LOCAL_PR_ARG, (unsigned long long)__entry->tsf -	) -); - -TRACE_EVENT(drv_reset_tsf, -	TP_PROTO(struct ieee80211_local *local), - -	TP_ARGS(local), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -	), - -	TP_printk( -		LOCAL_PR_FMT, LOCAL_PR_ARG -	) -); - -TRACE_EVENT(drv_tx_last_beacon, -	TP_PROTO(struct ieee80211_local *local), - -	TP_ARGS(local), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -	), - -	TP_printk( -		LOCAL_PR_FMT, -		LOCAL_PR_ARG -	) -); - -TRACE_EVENT(drv_ampdu_action, -	TP_PROTO(struct ieee80211_local *local, -		 struct ieee80211_sub_if_data *sdata, -		 enum ieee80211_ampdu_mlme_action action, -		 struct ieee80211_sta *sta, u16 tid, -		 u16 *ssn), - -	TP_ARGS(local, sdata, action, sta, tid, ssn), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		STA_ENTRY -		__field(u32, action) -		__field(u16, tid) -		__field(u16, ssn) -		VIF_ENTRY -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		VIF_ASSIGN; -		STA_ASSIGN; -		__entry->action = action; -		__entry->tid = tid; -		__entry->ssn = ssn ? *ssn : 0; -	), - -	TP_printk( -		LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d", -		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action, __entry->tid -	) -); - -TRACE_EVENT(drv_get_survey, -	TP_PROTO(struct ieee80211_local *local, int idx, -		 struct survey_info *survey), - -	TP_ARGS(local, idx, survey), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(int, idx) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->idx = idx; -	), - -	TP_printk( -		LOCAL_PR_FMT " idx:%d", -		LOCAL_PR_ARG, __entry->idx -	) -); - -TRACE_EVENT(drv_flush, -	TP_PROTO(struct ieee80211_local *local, bool drop), - -	TP_ARGS(local, drop), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(bool, drop) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->drop = drop; -	), - -	TP_printk( -		LOCAL_PR_FMT " drop:%d", -		LOCAL_PR_ARG, __entry->drop -	) -); - -TRACE_EVENT(drv_channel_switch, -	TP_PROTO(struct ieee80211_local *local, -		 struct ieee80211_channel_switch *ch_switch), - -	TP_ARGS(local, ch_switch), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(u64, timestamp) -		__field(bool, block_tx) -		__field(u16, freq) -		__field(u8, count) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->timestamp = ch_switch->timestamp; -		__entry->block_tx = ch_switch->block_tx; -		__entry->freq = ch_switch->channel->center_freq; -		__entry->count = ch_switch->count; -	), - -	TP_printk( -		LOCAL_PR_FMT " new freq:%u count:%d", -		LOCAL_PR_ARG, __entry->freq, __entry->count -	) -); - -TRACE_EVENT(drv_set_antenna, -	TP_PROTO(struct ieee80211_local *local, u32 tx_ant, u32 rx_ant, int ret), - -	TP_ARGS(local, tx_ant, rx_ant, ret), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(u32, tx_ant) -		__field(u32, rx_ant) -		__field(int, ret) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->tx_ant = tx_ant; -		__entry->rx_ant = rx_ant; -		__entry->ret = ret; -	), - -	TP_printk( -		LOCAL_PR_FMT " tx_ant:%d rx_ant:%d ret:%d", -		LOCAL_PR_ARG, __entry->tx_ant, __entry->rx_ant, __entry->ret -	) -); - -TRACE_EVENT(drv_get_antenna, -	TP_PROTO(struct ieee80211_local *local, u32 tx_ant, u32 rx_ant, int ret), - -	TP_ARGS(local, tx_ant, rx_ant, ret), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(u32, tx_ant) -		__field(u32, rx_ant) -		__field(int, ret) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->tx_ant = tx_ant; -		__entry->rx_ant = rx_ant; -		__entry->ret = ret; -	), - -	TP_printk( -		LOCAL_PR_FMT " tx_ant:%d rx_ant:%d ret:%d", -		LOCAL_PR_ARG, __entry->tx_ant, __entry->rx_ant, __entry->ret -	) -); - -/* - * Tracing for API calls that drivers call. - */ - -TRACE_EVENT(api_start_tx_ba_session, -	TP_PROTO(struct ieee80211_sta *sta, u16 tid), - -	TP_ARGS(sta, tid), - -	TP_STRUCT__entry( -		STA_ENTRY -		__field(u16, tid) -	), - -	TP_fast_assign( -		STA_ASSIGN; -		__entry->tid = tid; -	), - -	TP_printk( -		STA_PR_FMT " tid:%d", -		STA_PR_ARG, __entry->tid -	) -); - -TRACE_EVENT(api_start_tx_ba_cb, -	TP_PROTO(struct ieee80211_sub_if_data *sdata, const u8 *ra, u16 tid), - -	TP_ARGS(sdata, ra, tid), - -	TP_STRUCT__entry( -		VIF_ENTRY -		__array(u8, ra, ETH_ALEN) -		__field(u16, tid) -	), - -	TP_fast_assign( -		VIF_ASSIGN; -		memcpy(__entry->ra, ra, ETH_ALEN); -		__entry->tid = tid; -	), - -	TP_printk( -		VIF_PR_FMT " ra:%pM tid:%d", -		VIF_PR_ARG, __entry->ra, __entry->tid -	) -); - -TRACE_EVENT(api_stop_tx_ba_session, -	TP_PROTO(struct ieee80211_sta *sta, u16 tid), - -	TP_ARGS(sta, tid), - -	TP_STRUCT__entry( -		STA_ENTRY -		__field(u16, tid) -	), - -	TP_fast_assign( -		STA_ASSIGN; -		__entry->tid = tid; -	), - -	TP_printk( -		STA_PR_FMT " tid:%d", -		STA_PR_ARG, __entry->tid -	) -); - -TRACE_EVENT(api_stop_tx_ba_cb, -	TP_PROTO(struct ieee80211_sub_if_data *sdata, const u8 *ra, u16 tid), - -	TP_ARGS(sdata, ra, tid), - -	TP_STRUCT__entry( -		VIF_ENTRY -		__array(u8, ra, ETH_ALEN) -		__field(u16, tid) -	), - -	TP_fast_assign( -		VIF_ASSIGN; -		memcpy(__entry->ra, ra, ETH_ALEN); -		__entry->tid = tid; -	), - -	TP_printk( -		VIF_PR_FMT " ra:%pM tid:%d", -		VIF_PR_ARG, __entry->ra, __entry->tid -	) -); - -TRACE_EVENT(api_restart_hw, -	TP_PROTO(struct ieee80211_local *local), - -	TP_ARGS(local), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -	), - -	TP_printk( -		LOCAL_PR_FMT, -		LOCAL_PR_ARG -	) -); - -TRACE_EVENT(api_beacon_loss, -	TP_PROTO(struct ieee80211_sub_if_data *sdata), - -	TP_ARGS(sdata), - -	TP_STRUCT__entry( -		VIF_ENTRY -	), - -	TP_fast_assign( -		VIF_ASSIGN; -	), - -	TP_printk( -		VIF_PR_FMT, -		VIF_PR_ARG -	) -); - -TRACE_EVENT(api_connection_loss, -	TP_PROTO(struct ieee80211_sub_if_data *sdata), - -	TP_ARGS(sdata), - -	TP_STRUCT__entry( -		VIF_ENTRY -	), - -	TP_fast_assign( -		VIF_ASSIGN; -	), - -	TP_printk( -		VIF_PR_FMT, -		VIF_PR_ARG -	) -); - -TRACE_EVENT(api_cqm_rssi_notify, -	TP_PROTO(struct ieee80211_sub_if_data *sdata, -		 enum nl80211_cqm_rssi_threshold_event rssi_event), - -	TP_ARGS(sdata, rssi_event), - -	TP_STRUCT__entry( -		VIF_ENTRY -		__field(u32, rssi_event) -	), - -	TP_fast_assign( -		VIF_ASSIGN; -		__entry->rssi_event = rssi_event; -	), - -	TP_printk( -		VIF_PR_FMT " event:%d", -		VIF_PR_ARG, __entry->rssi_event -	) -); - -TRACE_EVENT(api_scan_completed, -	TP_PROTO(struct ieee80211_local *local, bool aborted), - -	TP_ARGS(local, aborted), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(bool, aborted) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->aborted = aborted; -	), - -	TP_printk( -		LOCAL_PR_FMT " aborted:%d", -		LOCAL_PR_ARG, __entry->aborted -	) -); - -TRACE_EVENT(api_sta_block_awake, -	TP_PROTO(struct ieee80211_local *local, -		 struct ieee80211_sta *sta, bool block), - -	TP_ARGS(local, sta, block), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		STA_ENTRY -		__field(bool, block) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		STA_ASSIGN; -		__entry->block = block; -	), - -	TP_printk( -		LOCAL_PR_FMT STA_PR_FMT " block:%d", -		LOCAL_PR_ARG, STA_PR_FMT, __entry->block -	) -); - -TRACE_EVENT(api_chswitch_done, -	TP_PROTO(struct ieee80211_sub_if_data *sdata, bool success), - -	TP_ARGS(sdata, success), - -	TP_STRUCT__entry( -		VIF_ENTRY -		__field(bool, success) -	), - -	TP_fast_assign( -		VIF_ASSIGN; -		__entry->success = success; -	), - -	TP_printk( -		VIF_PR_FMT " success=%d", -		VIF_PR_ARG, __entry->success -	) -); - -/* - * Tracing for internal functions - * (which may also be called in response to driver calls) - */ - -TRACE_EVENT(wake_queue, -	TP_PROTO(struct ieee80211_local *local, u16 queue, -		 enum queue_stop_reason reason), - -	TP_ARGS(local, queue, reason), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(u16, queue) -		__field(u32, reason) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->queue = queue; -		__entry->reason = reason; -	), - -	TP_printk( -		LOCAL_PR_FMT " queue:%d, reason:%d", -		LOCAL_PR_ARG, __entry->queue, __entry->reason -	) -); - -TRACE_EVENT(stop_queue, -	TP_PROTO(struct ieee80211_local *local, u16 queue, -		 enum queue_stop_reason reason), - -	TP_ARGS(local, queue, reason), - -	TP_STRUCT__entry( -		LOCAL_ENTRY -		__field(u16, queue) -		__field(u32, reason) -	), - -	TP_fast_assign( -		LOCAL_ASSIGN; -		__entry->queue = queue; -		__entry->reason = reason; -	), - -	TP_printk( -		LOCAL_PR_FMT " queue:%d, reason:%d", -		LOCAL_PR_ARG, __entry->queue, __entry->reason -	) -); -#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */ - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_FILE driver-trace -#include <trace/define_trace.h> diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 75d679d75e6..15702ff64a4 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -14,25 +14,150 @@   */  #include <linux/ieee80211.h> +#include <linux/export.h>  #include <net/mac80211.h>  #include "ieee80211_i.h"  #include "rate.h" -void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, -				       struct ieee80211_ht_cap *ht_cap_ie, -				       struct ieee80211_sta_ht_cap *ht_cap) +static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa, +				  struct ieee80211_ht_cap *ht_capa_mask, +				  struct ieee80211_sta_ht_cap *ht_cap, +				  u16 flag)  { +	__le16 le_flag = cpu_to_le16(flag); +	if (ht_capa_mask->cap_info & le_flag) { +		if (!(ht_capa->cap_info & le_flag)) +			ht_cap->cap &= ~flag; +	} +} + +static void __check_htcap_enable(struct ieee80211_ht_cap *ht_capa, +				  struct ieee80211_ht_cap *ht_capa_mask, +				  struct ieee80211_sta_ht_cap *ht_cap, +				  u16 flag) +{ +	__le16 le_flag = cpu_to_le16(flag); + +	if ((ht_capa_mask->cap_info & le_flag) && +	    (ht_capa->cap_info & le_flag)) +		ht_cap->cap |= flag; +} + +void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, +				     struct ieee80211_sta_ht_cap *ht_cap) +{ +	struct ieee80211_ht_cap *ht_capa, *ht_capa_mask; +	u8 *scaps, *smask; +	int i; + +	if (!ht_cap->ht_supported) +		return; + +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_STATION: +		ht_capa = &sdata->u.mgd.ht_capa; +		ht_capa_mask = &sdata->u.mgd.ht_capa_mask; +		break; +	case NL80211_IFTYPE_ADHOC: +		ht_capa = &sdata->u.ibss.ht_capa; +		ht_capa_mask = &sdata->u.ibss.ht_capa_mask; +		break; +	default: +		WARN_ON_ONCE(1); +		return; +	} + +	scaps = (u8 *)(&ht_capa->mcs.rx_mask); +	smask = (u8 *)(&ht_capa_mask->mcs.rx_mask); + +	/* NOTE:  If you add more over-rides here, update register_hw +	 * ht_capa_mod_mask logic in main.c as well. +	 * And, if this method can ever change ht_cap.ht_supported, fix +	 * the check in ieee80211_add_ht_ie. +	 */ + +	/* check for HT over-rides, MCS rates first. */ +	for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { +		u8 m = smask[i]; +		ht_cap->mcs.rx_mask[i] &= ~m; /* turn off all masked bits */ +		/* Add back rates that are supported */ +		ht_cap->mcs.rx_mask[i] |= (m & scaps[i]); +	} + +	/* Force removal of HT-40 capabilities? */ +	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, +			      IEEE80211_HT_CAP_SUP_WIDTH_20_40); +	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, +			      IEEE80211_HT_CAP_SGI_40); + +	/* Allow user to disable SGI-20 (SGI-40 is handled above) */ +	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, +			      IEEE80211_HT_CAP_SGI_20); + +	/* Allow user to disable the max-AMSDU bit. */ +	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, +			      IEEE80211_HT_CAP_MAX_AMSDU); + +	/* Allow user to disable LDPC */ +	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap, +			      IEEE80211_HT_CAP_LDPC_CODING); + +	/* Allow user to enable 40 MHz intolerant bit. */ +	__check_htcap_enable(ht_capa, ht_capa_mask, ht_cap, +			     IEEE80211_HT_CAP_40MHZ_INTOLERANT); + +	/* Allow user to decrease AMPDU factor */ +	if (ht_capa_mask->ampdu_params_info & +	    IEEE80211_HT_AMPDU_PARM_FACTOR) { +		u8 n = ht_capa->ampdu_params_info & +		       IEEE80211_HT_AMPDU_PARM_FACTOR; +		if (n < ht_cap->ampdu_factor) +			ht_cap->ampdu_factor = n; +	} + +	/* Allow the user to increase AMPDU density. */ +	if (ht_capa_mask->ampdu_params_info & +	    IEEE80211_HT_AMPDU_PARM_DENSITY) { +		u8 n = (ht_capa->ampdu_params_info & +			IEEE80211_HT_AMPDU_PARM_DENSITY) +			>> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT; +		if (n > ht_cap->ampdu_density) +			ht_cap->ampdu_density = n; +	} +} + + +bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, +				       struct ieee80211_supported_band *sband, +				       const struct ieee80211_ht_cap *ht_cap_ie, +				       struct sta_info *sta) +{ +	struct ieee80211_sta_ht_cap ht_cap, own_cap;  	u8 ampdu_info, tx_mcs_set_cap;  	int i, max_tx_streams; +	bool changed; +	enum ieee80211_sta_rx_bandwidth bw; +	enum ieee80211_smps_mode smps_mode; -	BUG_ON(!ht_cap); - -	memset(ht_cap, 0, sizeof(*ht_cap)); +	memset(&ht_cap, 0, sizeof(ht_cap));  	if (!ht_cap_ie || !sband->ht_cap.ht_supported) -		return; +		goto apply; -	ht_cap->ht_supported = true; +	ht_cap.ht_supported = true; + +	own_cap = sband->ht_cap; + +	/* +	 * If user has specified capability over-rides, take care +	 * of that if the station we're setting up is the AP that +	 * we advertised a restricted capability set to. Override +	 * our own capabilities and then use those below. +	 */ +	if ((sdata->vif.type == NL80211_IFTYPE_STATION || +	     sdata->vif.type == NL80211_IFTYPE_ADHOC) && +	    !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) +		ieee80211_apply_htcap_overrides(sdata, &own_cap);  	/*  	 * The bits listed in this expression should be @@ -40,35 +165,38 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,  	 * advertises more then we can't use those thus  	 * we mask them out.  	 */ -	ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) & -		(sband->ht_cap.cap | -		 ~(IEEE80211_HT_CAP_LDPC_CODING | -		   IEEE80211_HT_CAP_SUP_WIDTH_20_40 | -		   IEEE80211_HT_CAP_GRN_FLD | -		   IEEE80211_HT_CAP_SGI_20 | -		   IEEE80211_HT_CAP_SGI_40 | -		   IEEE80211_HT_CAP_DSSSCCK40)); +	ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) & +		(own_cap.cap | ~(IEEE80211_HT_CAP_LDPC_CODING | +				 IEEE80211_HT_CAP_SUP_WIDTH_20_40 | +				 IEEE80211_HT_CAP_GRN_FLD | +				 IEEE80211_HT_CAP_SGI_20 | +				 IEEE80211_HT_CAP_SGI_40 | +				 IEEE80211_HT_CAP_DSSSCCK40)); +  	/*  	 * The STBC bits are asymmetric -- if we don't have  	 * TX then mask out the peer's RX and vice versa.  	 */ -	if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)) -		ht_cap->cap &= ~IEEE80211_HT_CAP_RX_STBC; -	if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)) -		ht_cap->cap &= ~IEEE80211_HT_CAP_TX_STBC; +	if (!(own_cap.cap & IEEE80211_HT_CAP_TX_STBC)) +		ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC; +	if (!(own_cap.cap & IEEE80211_HT_CAP_RX_STBC)) +		ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC;  	ampdu_info = ht_cap_ie->ampdu_params_info; -	ht_cap->ampdu_factor = +	ht_cap.ampdu_factor =  		ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; -	ht_cap->ampdu_density = +	ht_cap.ampdu_density =  		(ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;  	/* own MCS TX capabilities */ -	tx_mcs_set_cap = sband->ht_cap.mcs.tx_params; +	tx_mcs_set_cap = own_cap.mcs.tx_params; + +	/* Copy peer MCS TX capabilities, the driver might need them. */ +	ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params;  	/* can we TX with MCS rates? */  	if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) -		return; +		goto apply;  	/* Counting from 0, therefore +1 */  	if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) @@ -79,38 +207,91 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,  		max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS;  	/* -	 * 802.11n D5.0 20.3.5 / 20.6 says: +	 * 802.11n-2009 20.3.5 / 20.6 says:  	 * - indices 0 to 7 and 32 are single spatial stream  	 * - 8 to 31 are multiple spatial streams using equal modulation  	 *   [8..15 for two streams, 16..23 for three and 24..31 for four]  	 * - remainder are multiple spatial streams using unequal modulation  	 */  	for (i = 0; i < max_tx_streams; i++) -		ht_cap->mcs.rx_mask[i] = -			sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i]; +		ht_cap.mcs.rx_mask[i] = +			own_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];  	if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)  		for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;  		     i < IEEE80211_HT_MCS_MASK_LEN; i++) -			ht_cap->mcs.rx_mask[i] = -				sband->ht_cap.mcs.rx_mask[i] & +			ht_cap.mcs.rx_mask[i] = +				own_cap.mcs.rx_mask[i] &  					ht_cap_ie->mcs.rx_mask[i];  	/* handle MCS rate 32 too */ -	if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) -		ht_cap->mcs.rx_mask[32/8] |= 1; +	if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) +		ht_cap.mcs.rx_mask[32/8] |= 1; + + apply: +	changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap)); + +	memcpy(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap)); + +	switch (sdata->vif.bss_conf.chandef.width) { +	default: +		WARN_ON_ONCE(1); +		/* fall through */ +	case NL80211_CHAN_WIDTH_20_NOHT: +	case NL80211_CHAN_WIDTH_20: +		bw = IEEE80211_STA_RX_BW_20; +		break; +	case NL80211_CHAN_WIDTH_40: +	case NL80211_CHAN_WIDTH_80: +	case NL80211_CHAN_WIDTH_80P80: +	case NL80211_CHAN_WIDTH_160: +		bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? +				IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; +		break; +	} + +	if (bw != sta->sta.bandwidth) +		changed = true; +	sta->sta.bandwidth = bw; + +	sta->cur_max_bandwidth = +		ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? +				IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; + +	switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS) +			>> IEEE80211_HT_CAP_SM_PS_SHIFT) { +	case WLAN_HT_CAP_SM_PS_INVALID: +	case WLAN_HT_CAP_SM_PS_STATIC: +		smps_mode = IEEE80211_SMPS_STATIC; +		break; +	case WLAN_HT_CAP_SM_PS_DYNAMIC: +		smps_mode = IEEE80211_SMPS_DYNAMIC; +		break; +	case WLAN_HT_CAP_SM_PS_DISABLED: +		smps_mode = IEEE80211_SMPS_OFF; +		break; +	} + +	if (smps_mode != sta->sta.smps_mode) +		changed = true; +	sta->sta.smps_mode = smps_mode; + +	return changed;  } -void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx) +void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, +					 enum ieee80211_agg_stop_reason reason)  {  	int i;  	cancel_work_sync(&sta->ampdu_mlme.work); -	for (i = 0; i <  STA_TID_NUM; i++) { -		__ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR, tx); +	for (i = 0; i <  IEEE80211_NUM_TIDS; i++) { +		__ieee80211_stop_tx_ba_session(sta, i, reason);  		__ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT, -					       WLAN_REASON_QSTA_LEAVE_QBSS, tx); +					       WLAN_REASON_QSTA_LEAVE_QBSS, +					       reason != AGG_STOP_DESTROY_STA && +					       reason != AGG_STOP_PEER_REQUEST);  	}  } @@ -127,27 +308,49 @@ void ieee80211_ba_session_work(struct work_struct *work)  	 * down by the code that set the flag, so this  	 * need not run.  	 */ -	if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) +	if (test_sta_flag(sta, WLAN_STA_BLOCK_BA))  		return;  	mutex_lock(&sta->ampdu_mlme.mtx); -	for (tid = 0; tid < STA_TID_NUM; tid++) { +	for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {  		if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired))  			___ieee80211_stop_rx_ba_session(  				sta, tid, WLAN_BACK_RECIPIENT,  				WLAN_REASON_QSTA_TIMEOUT, true); -		tid_tx = sta->ampdu_mlme.tid_tx[tid]; -		if (!tid_tx) -			continue; +		if (test_and_clear_bit(tid, +				       sta->ampdu_mlme.tid_rx_stop_requested)) +			___ieee80211_stop_rx_ba_session( +				sta, tid, WLAN_BACK_RECIPIENT, +				WLAN_REASON_UNSPECIFIED, true); + +		spin_lock_bh(&sta->lock); + +		tid_tx = sta->ampdu_mlme.tid_start_tx[tid]; +		if (tid_tx) { +			/* +			 * Assign it over to the normal tid_tx array +			 * where it "goes live". +			 */ + +			sta->ampdu_mlme.tid_start_tx[tid] = NULL; +			/* could there be a race? */ +			if (sta->ampdu_mlme.tid_tx[tid]) +				kfree(tid_tx); +			else +				ieee80211_assign_tid_tx(sta, tid, tid_tx); +			spin_unlock_bh(&sta->lock); -		if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state))  			ieee80211_tx_ba_session_handle_start(sta, tid); -		else if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, -					    &tid_tx->state)) +			continue; +		} +		spin_unlock_bh(&sta->lock); + +		tid_tx = rcu_dereference_protected_tid_tx(sta, tid); +		if (tid_tx && test_and_clear_bit(HT_AGG_STATE_WANT_STOP, +						 &tid_tx->state))  			___ieee80211_stop_tx_ba_session(sta, tid, -							WLAN_BACK_INITIATOR, -							true); +							AGG_STOP_LOCAL_REQUEST);  	}  	mutex_unlock(&sta->ampdu_mlme.mtx);  } @@ -162,12 +365,8 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,  	u16 params;  	skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); - -	if (!skb) { -		printk(KERN_ERR "%s: failed to allocate buffer " -					"for delba frame\n", sdata->name); +	if (!skb)  		return; -	}  	skb_reserve(skb, local->hw.extra_tx_headroom);  	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); @@ -175,10 +374,13 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,  	memcpy(mgmt->da, da, ETH_ALEN);  	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);  	if (sdata->vif.type == NL80211_IFTYPE_AP || -	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN || +	    sdata->vif.type == NL80211_IFTYPE_MESH_POINT)  		memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);  	else if (sdata->vif.type == NL80211_IFTYPE_STATION)  		memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); +	else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) +		memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN);  	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |  					  IEEE80211_STYPE_ACTION); @@ -207,19 +409,16 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,  	tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12;  	initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11; -#ifdef CONFIG_MAC80211_HT_DEBUG -	if (net_ratelimit()) -		printk(KERN_DEBUG "delba from %pM (%s) tid %d reason code %d\n", -			mgmt->sa, initiator ? "initiator" : "recipient", tid, -			le16_to_cpu(mgmt->u.action.u.delba.reason_code)); -#endif /* CONFIG_MAC80211_HT_DEBUG */ +	ht_dbg_ratelimited(sdata, "delba from %pM (%s) tid %d reason code %d\n", +			   mgmt->sa, initiator ? "initiator" : "recipient", +			   tid, +			   le16_to_cpu(mgmt->u.action.u.delba.reason_code));  	if (initiator == WLAN_BACK_INITIATOR)  		__ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0,  					       true);  	else -		__ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, -					       true); +		__ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_PEER_REQUEST);  }  int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, @@ -269,15 +468,28 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,  	return 0;  } -void ieee80211_request_smps_work(struct work_struct *work) +void ieee80211_request_smps_mgd_work(struct work_struct *work)  {  	struct ieee80211_sub_if_data *sdata =  		container_of(work, struct ieee80211_sub_if_data,  			     u.mgd.request_smps_work); -	mutex_lock(&sdata->u.mgd.mtx); -	__ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode); -	mutex_unlock(&sdata->u.mgd.mtx); +	sdata_lock(sdata); +	__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode); +	sdata_unlock(sdata); +} + +void ieee80211_request_smps_ap_work(struct work_struct *work) +{ +	struct ieee80211_sub_if_data *sdata = +		container_of(work, struct ieee80211_sub_if_data, +			     u.ap.request_smps_work); + +	sdata_lock(sdata); +	if (sdata_dereference(sdata->u.ap.beacon, sdata)) +		__ieee80211_request_smps_ap(sdata, +					    sdata->u.ap.driver_smps_mode); +	sdata_unlock(sdata);  }  void ieee80211_request_smps(struct ieee80211_vif *vif, @@ -285,16 +497,26 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,  {  	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); -	if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) +	if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION && +			 vif->type != NL80211_IFTYPE_AP))  		return; -	if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF)) -		smps_mode = IEEE80211_SMPS_AUTOMATIC; - -	sdata->u.mgd.driver_smps_mode = smps_mode; - -	ieee80211_queue_work(&sdata->local->hw, -			     &sdata->u.mgd.request_smps_work); +	if (vif->type == NL80211_IFTYPE_STATION) { +		if (sdata->u.mgd.driver_smps_mode == smps_mode) +			return; +		sdata->u.mgd.driver_smps_mode = smps_mode; +		ieee80211_queue_work(&sdata->local->hw, +				     &sdata->u.mgd.request_smps_work); +	} else { +		/* AUTOMATIC is meaningless in AP mode */ +		if (WARN_ON_ONCE(smps_mode == IEEE80211_SMPS_AUTOMATIC)) +			return; +		if (sdata->u.ap.driver_smps_mode == smps_mode) +			return; +		sdata->u.ap.driver_smps_mode = smps_mode; +		ieee80211_queue_work(&sdata->local->hw, +				     &sdata->u.ap.request_smps_work); +	}  }  /* this might change ... don't want non-open drivers using it */  EXPORT_SYMBOL_GPL(ieee80211_request_smps); diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 410d104b134..18ee0a256b1 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -20,161 +20,176 @@  #include <linux/etherdevice.h>  #include <linux/rtnetlink.h>  #include <net/mac80211.h> -#include <asm/unaligned.h>  #include "ieee80211_i.h"  #include "driver-ops.h"  #include "rate.h"  #define IEEE80211_SCAN_INTERVAL (2 * HZ) -#define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ)  #define IEEE80211_IBSS_JOIN_TIMEOUT (7 * HZ)  #define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ) -#define IEEE80211_IBSS_MERGE_DELAY 0x400000  #define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ) +#define IEEE80211_IBSS_RSN_INACTIVITY_LIMIT (10 * HZ)  #define IEEE80211_IBSS_MAX_STA_ENTRIES 128 - -static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, -					struct ieee80211_mgmt *mgmt, -					size_t len) -{ -	u16 auth_alg, auth_transaction, status_code; - -	lockdep_assert_held(&sdata->u.ibss.mtx); - -	if (len < 24 + 6) -		return; - -	auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); -	auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); -	status_code = le16_to_cpu(mgmt->u.auth.status_code); - -	/* -	 * IEEE 802.11 standard does not require authentication in IBSS -	 * networks and most implementations do not seem to use it. -	 * However, try to reply to authentication attempts if someone -	 * has actually implemented this. -	 */ -	if (auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) -		ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, NULL, 0, -				    sdata->u.ibss.bssid, NULL, 0, 0); -} - -static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, -				      const u8 *bssid, const int beacon_int, -				      struct ieee80211_channel *chan, -				      const u32 basic_rates, -				      const u16 capability, u64 tsf) +static struct beacon_data * +ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, +			   const int beacon_int, const u32 basic_rates, +			   const u16 capability, u64 tsf, +			   struct cfg80211_chan_def *chandef, +			   bool *have_higher_than_11mbit, +			   struct cfg80211_csa_settings *csa_settings)  {  	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;  	struct ieee80211_local *local = sdata->local; -	int rates, i; -	struct sk_buff *skb; +	int rates_n = 0, i, ri;  	struct ieee80211_mgmt *mgmt;  	u8 *pos;  	struct ieee80211_supported_band *sband; -	struct cfg80211_bss *bss; -	u32 bss_change; -	u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; - -	lockdep_assert_held(&ifibss->mtx); - -	/* Reset own TSF to allow time synchronization work. */ -	drv_reset_tsf(local); - -	skb = ifibss->skb; -	rcu_assign_pointer(ifibss->presp, NULL); -	synchronize_rcu(); -	skb->data = skb->head; -	skb->len = 0; -	skb_reset_tail_pointer(skb); -	skb_reserve(skb, sdata->local->hw.extra_tx_headroom); - -	if (memcmp(ifibss->bssid, bssid, ETH_ALEN)) -		sta_info_flush(sdata->local, sdata); - -	/* if merging, indicate to driver that we leave the old IBSS */ -	if (sdata->vif.bss_conf.ibss_joined) { -		sdata->vif.bss_conf.ibss_joined = false; -		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS); -	} +	u32 rate_flags, rates = 0, rates_added = 0; +	struct beacon_data *presp; +	int frame_len; +	int shift; -	memcpy(ifibss->bssid, bssid, ETH_ALEN); - -	sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; - -	local->oper_channel = chan; -	WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); -	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); - -	sband = local->hw.wiphy->bands[chan->band]; +	/* Build IBSS probe response */ +	frame_len = sizeof(struct ieee80211_hdr_3addr) + +		    12 /* struct ieee80211_mgmt.u.beacon */ + +		    2 + IEEE80211_MAX_SSID_LEN /* max SSID */ + +		    2 + 8 /* max Supported Rates */ + +		    3 /* max DS params */ + +		    4 /* IBSS params */ + +		    5 /* Channel Switch Announcement */ + +		    2 + (IEEE80211_MAX_SUPP_RATES - 8) + +		    2 + sizeof(struct ieee80211_ht_cap) + +		    2 + sizeof(struct ieee80211_ht_operation) + +		    ifibss->ie_len; +	presp = kzalloc(sizeof(*presp) + frame_len, GFP_KERNEL); +	if (!presp) +		return NULL; -	/* build supported rates array */ -	pos = supp_rates; -	for (i = 0; i < sband->n_bitrates; i++) { -		int rate = sband->bitrates[i].bitrate; -		u8 basic = 0; -		if (basic_rates & BIT(i)) -			basic = 0x80; -		*pos++ = basic | (u8) (rate / 5); -	} +	presp->head = (void *)(presp + 1); -	/* Build IBSS probe response */ -	mgmt = (void *) skb_put(skb, 24 + sizeof(mgmt->u.beacon)); -	memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); +	mgmt = (void *) presp->head;  	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |  					  IEEE80211_STYPE_PROBE_RESP); -	memset(mgmt->da, 0xff, ETH_ALEN); +	eth_broadcast_addr(mgmt->da);  	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);  	memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);  	mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_int);  	mgmt->u.beacon.timestamp = cpu_to_le64(tsf);  	mgmt->u.beacon.capab_info = cpu_to_le16(capability); -	pos = skb_put(skb, 2 + ifibss->ssid_len); +	pos = (u8 *)mgmt + offsetof(struct ieee80211_mgmt, u.beacon.variable); +  	*pos++ = WLAN_EID_SSID;  	*pos++ = ifibss->ssid_len;  	memcpy(pos, ifibss->ssid, ifibss->ssid_len); +	pos += ifibss->ssid_len; + +	sband = local->hw.wiphy->bands[chandef->chan->band]; +	rate_flags = ieee80211_chandef_rate_flags(chandef); +	shift = ieee80211_chandef_get_shift(chandef); +	rates_n = 0; +	if (have_higher_than_11mbit) +		*have_higher_than_11mbit = false; + +	for (i = 0; i < sband->n_bitrates; i++) { +		if ((rate_flags & sband->bitrates[i].flags) != rate_flags) +			continue; +		if (sband->bitrates[i].bitrate > 110 && +		    have_higher_than_11mbit) +			*have_higher_than_11mbit = true; + +		rates |= BIT(i); +		rates_n++; +	} -	rates = sband->n_bitrates; -	if (rates > 8) -		rates = 8; -	pos = skb_put(skb, 2 + rates);  	*pos++ = WLAN_EID_SUPP_RATES; -	*pos++ = rates; -	memcpy(pos, supp_rates, rates); +	*pos++ = min_t(int, 8, rates_n); +	for (ri = 0; ri < sband->n_bitrates; ri++) { +		int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate, +					5 * (1 << shift)); +		u8 basic = 0; +		if (!(rates & BIT(ri))) +			continue; + +		if (basic_rates & BIT(ri)) +			basic = 0x80; +		*pos++ = basic | (u8) rate; +		if (++rates_added == 8) { +			ri++; /* continue at next rate for EXT_SUPP_RATES */ +			break; +		} +	}  	if (sband->band == IEEE80211_BAND_2GHZ) { -		pos = skb_put(skb, 2 + 1);  		*pos++ = WLAN_EID_DS_PARAMS;  		*pos++ = 1; -		*pos++ = ieee80211_frequency_to_channel(chan->center_freq); +		*pos++ = ieee80211_frequency_to_channel( +				chandef->chan->center_freq);  	} -	pos = skb_put(skb, 2 + 2);  	*pos++ = WLAN_EID_IBSS_PARAMS;  	*pos++ = 2;  	/* FIX: set ATIM window based on scan results */  	*pos++ = 0;  	*pos++ = 0; -	if (sband->n_bitrates > 8) { -		rates = sband->n_bitrates - 8; -		pos = skb_put(skb, 2 + rates); +	if (csa_settings) { +		*pos++ = WLAN_EID_CHANNEL_SWITCH; +		*pos++ = 3; +		*pos++ = csa_settings->block_tx ? 1 : 0; +		*pos++ = ieee80211_frequency_to_channel( +				csa_settings->chandef.chan->center_freq); +		sdata->csa_counter_offset_beacon[0] = (pos - presp->head); +		*pos++ = csa_settings->count; +	} + +	/* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */ +	if (rates_n > 8) {  		*pos++ = WLAN_EID_EXT_SUPP_RATES; -		*pos++ = rates; -		memcpy(pos, &supp_rates[8], rates); +		*pos++ = rates_n - 8; +		for (; ri < sband->n_bitrates; ri++) { +			int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate, +						5 * (1 << shift)); +			u8 basic = 0; +			if (!(rates & BIT(ri))) +				continue; + +			if (basic_rates & BIT(ri)) +				basic = 0x80; +			*pos++ = basic | (u8) rate; +		}  	} -	if (ifibss->ie_len) -		memcpy(skb_put(skb, ifibss->ie_len), -		       ifibss->ie, ifibss->ie_len); +	if (ifibss->ie_len) { +		memcpy(pos, ifibss->ie, ifibss->ie_len); +		pos += ifibss->ie_len; +	} -	if (local->hw.queues >= 4) { -		pos = skb_put(skb, 9); +	/* add HT capability and information IEs */ +	if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT && +	    chandef->width != NL80211_CHAN_WIDTH_5 && +	    chandef->width != NL80211_CHAN_WIDTH_10 && +	    sband->ht_cap.ht_supported) { +		struct ieee80211_sta_ht_cap ht_cap; + +		memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); +		ieee80211_apply_htcap_overrides(sdata, &ht_cap); + +		pos = ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap); +		/* +		 * Note: According to 802.11n-2009 9.13.3.1, HT Protection +		 * field and RIFS Mode are reserved in IBSS mode, therefore +		 * keep them at 0 +		 */ +		pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap, +						 chandef, 0); +	} + +	if (local->hw.queues >= IEEE80211_NUM_ACS) {  		*pos++ = WLAN_EID_VENDOR_SPECIFIC;  		*pos++ = 7; /* len */  		*pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ @@ -186,30 +201,194 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  		*pos++ = 0; /* U-APSD no in use */  	} -	rcu_assign_pointer(ifibss->presp, skb); +	presp->head_len = pos - presp->head; +	if (WARN_ON(presp->head_len > frame_len)) +		goto error; + +	return presp; +error: +	kfree(presp); +	return NULL; +} + +static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, +				      const u8 *bssid, const int beacon_int, +				      struct cfg80211_chan_def *req_chandef, +				      const u32 basic_rates, +				      const u16 capability, u64 tsf, +				      bool creator) +{ +	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_mgmt *mgmt; +	struct cfg80211_bss *bss; +	u32 bss_change; +	struct cfg80211_chan_def chandef; +	struct ieee80211_channel *chan; +	struct beacon_data *presp; +	enum nl80211_bss_scan_width scan_width; +	bool have_higher_than_11mbit; +	bool radar_required; +	int err; + +	sdata_assert_lock(sdata); + +	/* Reset own TSF to allow time synchronization work. */ +	drv_reset_tsf(local, sdata); + +	if (!ether_addr_equal(ifibss->bssid, bssid)) +		sta_info_flush(sdata); + +	/* if merging, indicate to driver that we leave the old IBSS */ +	if (sdata->vif.bss_conf.ibss_joined) { +		sdata->vif.bss_conf.ibss_joined = false; +		sdata->vif.bss_conf.ibss_creator = false; +		sdata->vif.bss_conf.enable_beacon = false; +		netif_carrier_off(sdata->dev); +		ieee80211_bss_info_change_notify(sdata, +						 BSS_CHANGED_IBSS | +						 BSS_CHANGED_BEACON_ENABLED); +		drv_leave_ibss(local, sdata); +	} + +	presp = rcu_dereference_protected(ifibss->presp, +					  lockdep_is_held(&sdata->wdev.mtx)); +	RCU_INIT_POINTER(ifibss->presp, NULL); +	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; +	if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef, +				     NL80211_IFTYPE_ADHOC)) { +		if (chandef.width == NL80211_CHAN_WIDTH_5 || +		    chandef.width == NL80211_CHAN_WIDTH_10 || +		    chandef.width == NL80211_CHAN_WIDTH_20_NOHT || +		    chandef.width == NL80211_CHAN_WIDTH_20) { +			sdata_info(sdata, +				   "Failed to join IBSS, beacons forbidden\n"); +			return; +		} +		chandef.width = NL80211_CHAN_WIDTH_20; +		chandef.center_freq1 = chan->center_freq; +		/* check again for downgraded chandef */ +		if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef, +					     NL80211_IFTYPE_ADHOC)) { +			sdata_info(sdata, +				   "Failed to join IBSS, beacons forbidden\n"); +			return; +		} +	} + +	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, +					    &chandef, NL80211_IFTYPE_ADHOC); +	if (err < 0) { +		sdata_info(sdata, +			   "Failed to join IBSS, invalid chandef\n"); +		return; +	} +	if (err > 0 && !ifibss->userspace_handles_dfs) { +		sdata_info(sdata, +			   "Failed to join IBSS, DFS channel without control program\n"); +		return; +	} + +	radar_required = err; +	mutex_lock(&local->mtx); +	if (ieee80211_vif_use_channel(sdata, &chandef, +				      ifibss->fixed_channel ? +					IEEE80211_CHANCTX_SHARED : +					IEEE80211_CHANCTX_EXCLUSIVE)) { +		sdata_info(sdata, "Failed to join IBSS, no channel context\n"); +		mutex_unlock(&local->mtx); +		return; +	} +	sdata->radar_required = radar_required; +	mutex_unlock(&local->mtx); + +	memcpy(ifibss->bssid, bssid, ETH_ALEN); + +	presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates, +					   capability, tsf, &chandef, +					   &have_higher_than_11mbit, NULL); +	if (!presp) +		return; + +	rcu_assign_pointer(ifibss->presp, presp); +	mgmt = (void *)presp->head; + +	sdata->vif.bss_conf.enable_beacon = true;  	sdata->vif.bss_conf.beacon_int = beacon_int;  	sdata->vif.bss_conf.basic_rates = basic_rates; +	sdata->vif.bss_conf.ssid_len = ifibss->ssid_len; +	memcpy(sdata->vif.bss_conf.ssid, ifibss->ssid, ifibss->ssid_len);  	bss_change = BSS_CHANGED_BEACON_INT;  	bss_change |= ieee80211_reset_erp_info(sdata);  	bss_change |= BSS_CHANGED_BSSID;  	bss_change |= BSS_CHANGED_BEACON;  	bss_change |= BSS_CHANGED_BEACON_ENABLED;  	bss_change |= BSS_CHANGED_BASIC_RATES; +	bss_change |= BSS_CHANGED_HT;  	bss_change |= BSS_CHANGED_IBSS; +	bss_change |= BSS_CHANGED_SSID; + +	/* +	 * In 5 GHz/802.11a, we can always use short slot time. +	 * (IEEE 802.11-2012 18.3.8.7) +	 * +	 * In 2.4GHz, we must always use long slots in IBSS for compatibility +	 * reasons. +	 * (IEEE 802.11-2012 19.4.5) +	 * +	 * HT follows these specifications (IEEE 802.11-2012 20.3.18) +	 */ +	sdata->vif.bss_conf.use_short_slot = chan->band == IEEE80211_BAND_5GHZ; +	bss_change |= BSS_CHANGED_ERP_SLOT; + +	/* cf. IEEE 802.11 9.2.12 */ +	if (chan->band == IEEE80211_BAND_2GHZ && have_higher_than_11mbit) +		sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; +	else +		sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; + +	ieee80211_set_wmm_default(sdata, true); +  	sdata->vif.bss_conf.ibss_joined = true; -	ieee80211_bss_info_change_notify(sdata, bss_change); +	sdata->vif.bss_conf.ibss_creator = creator; -	ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates); +	err = drv_join_ibss(local, sdata); +	if (err) { +		sdata->vif.bss_conf.ibss_joined = false; +		sdata->vif.bss_conf.ibss_creator = false; +		sdata->vif.bss_conf.enable_beacon = false; +		sdata->vif.bss_conf.ssid_len = 0; +		RCU_INIT_POINTER(ifibss->presp, NULL); +		kfree_rcu(presp, rcu_head); +		mutex_lock(&local->mtx); +		ieee80211_vif_release_channel(sdata); +		mutex_unlock(&local->mtx); +		sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n", +			   err); +		return; +	} + +	ieee80211_bss_info_change_notify(sdata, bss_change);  	ifibss->state = IEEE80211_IBSS_MLME_JOINED;  	mod_timer(&ifibss->timer,  		  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); -	bss = cfg80211_inform_bss_frame(local->hw.wiphy, local->hw.conf.channel, -					mgmt, skb->len, 0, GFP_KERNEL); -	cfg80211_put_bss(bss); -	cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL); +	scan_width = cfg80211_chandef_to_scan_width(&chandef); +	bss = cfg80211_inform_bss_width_frame(local->hw.wiphy, chan, +					      scan_width, mgmt, +					      presp->head_len, 0, GFP_KERNEL); +	cfg80211_put_bss(local->hw.wiphy, bss); +	netif_carrier_on(sdata->dev); +	cfg80211_ibss_joined(sdata->dev, ifibss->bssid, chan, GFP_KERNEL);  }  static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, @@ -218,25 +397,60 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  	struct cfg80211_bss *cbss =  		container_of((void *)bss, struct cfg80211_bss, priv);  	struct ieee80211_supported_band *sband; +	struct cfg80211_chan_def chandef;  	u32 basic_rates;  	int i, j;  	u16 beacon_int = cbss->beacon_interval; +	const struct cfg80211_bss_ies *ies; +	enum nl80211_channel_type chan_type; +	u64 tsf; +	u32 rate_flags; +	int shift; -	lockdep_assert_held(&sdata->u.ibss.mtx); +	sdata_assert_lock(sdata);  	if (beacon_int < 10)  		beacon_int = 10; +	switch (sdata->u.ibss.chandef.width) { +	case NL80211_CHAN_WIDTH_20_NOHT: +	case NL80211_CHAN_WIDTH_20: +	case NL80211_CHAN_WIDTH_40: +		chan_type = cfg80211_get_chandef_type(&sdata->u.ibss.chandef); +		cfg80211_chandef_create(&chandef, cbss->channel, chan_type); +		break; +	case NL80211_CHAN_WIDTH_5: +	case NL80211_CHAN_WIDTH_10: +		cfg80211_chandef_create(&chandef, cbss->channel, +					NL80211_CHAN_WIDTH_20_NOHT); +		chandef.width = sdata->u.ibss.chandef.width; +		break; +	default: +		/* fall back to 20 MHz for unsupported modes */ +		cfg80211_chandef_create(&chandef, cbss->channel, +					NL80211_CHAN_WIDTH_20_NOHT); +		break; +	} +  	sband = sdata->local->hw.wiphy->bands[cbss->channel->band]; +	rate_flags = ieee80211_chandef_rate_flags(&sdata->u.ibss.chandef); +	shift = ieee80211_vif_get_shift(&sdata->vif);  	basic_rates = 0;  	for (i = 0; i < bss->supp_rates_len; i++) { -		int rate = (bss->supp_rates[i] & 0x7f) * 5; +		int rate = bss->supp_rates[i] & 0x7f;  		bool is_basic = !!(bss->supp_rates[i] & 0x80);  		for (j = 0; j < sband->n_bitrates; j++) { -			if (sband->bitrates[j].bitrate == rate) { +			int brate; +			if ((rate_flags & sband->bitrates[j].flags) +			    != rate_flags) +				continue; + +			brate = DIV_ROUND_UP(sband->bitrates[j].bitrate, +					     5 * (1 << shift)); +			if (brate == rate) {  				if (is_basic)  					basic_rates |= BIT(j);  				break; @@ -244,23 +458,542 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,  		}  	} +	rcu_read_lock(); +	ies = rcu_dereference(cbss->ies); +	tsf = ies->tsf; +	rcu_read_unlock(); +  	__ieee80211_sta_join_ibss(sdata, cbss->bssid,  				  beacon_int, -				  cbss->channel, +				  &chandef,  				  basic_rates,  				  cbss->capability, -				  cbss->tsf); +				  tsf, false);  } -static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, -				  struct ieee80211_mgmt *mgmt, -				  size_t len, -				  struct ieee80211_rx_status *rx_status, +int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, +			      struct cfg80211_csa_settings *csa_settings) +{ +	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +	struct beacon_data *presp, *old_presp; +	struct cfg80211_bss *cbss; +	const struct cfg80211_bss_ies *ies; +	u16 capability; +	u64 tsf; +	int ret = 0; + +	sdata_assert_lock(sdata); + +	capability = WLAN_CAPABILITY_IBSS; + +	if (ifibss->privacy) +		capability |= WLAN_CAPABILITY_PRIVACY; + +	cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan, +				ifibss->bssid, ifibss->ssid, +				ifibss->ssid_len, WLAN_CAPABILITY_IBSS | +				WLAN_CAPABILITY_PRIVACY, +				capability); + +	if (WARN_ON(!cbss)) { +		ret = -EINVAL; +		goto out; +	} + +	rcu_read_lock(); +	ies = rcu_dereference(cbss->ies); +	tsf = ies->tsf; +	rcu_read_unlock(); +	cfg80211_put_bss(sdata->local->hw.wiphy, cbss); + +	old_presp = rcu_dereference_protected(ifibss->presp, +					  lockdep_is_held(&sdata->wdev.mtx)); + +	presp = ieee80211_ibss_build_presp(sdata, +					   sdata->vif.bss_conf.beacon_int, +					   sdata->vif.bss_conf.basic_rates, +					   capability, tsf, &ifibss->chandef, +					   NULL, csa_settings); +	if (!presp) { +		ret = -ENOMEM; +		goto out; +	} + +	rcu_assign_pointer(ifibss->presp, presp); +	if (old_presp) +		kfree_rcu(old_presp, rcu_head); + +	return BSS_CHANGED_BEACON; + out: +	return ret; +} + +int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +	struct cfg80211_bss *cbss; +	int err, changed = 0; +	u16 capability; + +	sdata_assert_lock(sdata); + +	/* update cfg80211 bss information with the new channel */ +	if (!is_zero_ether_addr(ifibss->bssid)) { +		capability = WLAN_CAPABILITY_IBSS; + +		if (ifibss->privacy) +			capability |= WLAN_CAPABILITY_PRIVACY; + +		cbss = cfg80211_get_bss(sdata->local->hw.wiphy, +					ifibss->chandef.chan, +					ifibss->bssid, ifibss->ssid, +					ifibss->ssid_len, WLAN_CAPABILITY_IBSS | +					WLAN_CAPABILITY_PRIVACY, +					capability); +		/* XXX: should not really modify cfg80211 data */ +		if (cbss) { +			cbss->channel = sdata->csa_chandef.chan; +			cfg80211_put_bss(sdata->local->hw.wiphy, cbss); +		} +	} + +	ifibss->chandef = sdata->csa_chandef; + +	/* generate the beacon */ +	err = ieee80211_ibss_csa_beacon(sdata, NULL); +	if (err < 0) +		return err; + +	changed |= err; + +	return changed; +} + +void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + +	cancel_work_sync(&ifibss->csa_connection_drop_work); +} + +static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta) +	__acquires(RCU) +{ +	struct ieee80211_sub_if_data *sdata = sta->sdata; +	u8 addr[ETH_ALEN]; + +	memcpy(addr, sta->sta.addr, ETH_ALEN); + +	ibss_dbg(sdata, "Adding new IBSS station %pM\n", addr); + +	sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); +	sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); +	/* authorize the station only if the network is not RSN protected. If +	 * not wait for the userspace to authorize it */ +	if (!sta->sdata->u.ibss.control_port) +		sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); + +	rate_control_rate_init(sta); + +	/* If it fails, maybe we raced another insertion? */ +	if (sta_info_insert_rcu(sta)) +		return sta_info_get(sdata, addr); +	return sta; +} + +static struct sta_info * +ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid, +		       const u8 *addr, u32 supp_rates) +	__acquires(RCU) +{ +	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +	struct ieee80211_local *local = sdata->local; +	struct sta_info *sta; +	struct ieee80211_chanctx_conf *chanctx_conf; +	struct ieee80211_supported_band *sband; +	enum nl80211_bss_scan_width scan_width; +	int band; + +	/* +	 * XXX: Consider removing the least recently used entry and +	 * 	allow new one to be added. +	 */ +	if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { +		net_info_ratelimited("%s: No room for a new IBSS STA entry %pM\n", +				    sdata->name, addr); +		rcu_read_lock(); +		return NULL; +	} + +	if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH) { +		rcu_read_lock(); +		return NULL; +	} + +	if (!ether_addr_equal(bssid, sdata->u.ibss.bssid)) { +		rcu_read_lock(); +		return NULL; +	} + +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	if (WARN_ON_ONCE(!chanctx_conf)) +		return NULL; +	band = chanctx_conf->def.chan->band; +	scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def); +	rcu_read_unlock(); + +	sta = sta_info_alloc(sdata, addr, GFP_KERNEL); +	if (!sta) { +		rcu_read_lock(); +		return NULL; +	} + +	sta->last_rx = jiffies; + +	/* make sure mandatory rates are always added */ +	sband = local->hw.wiphy->bands[band]; +	sta->sta.supp_rates[band] = supp_rates | +			ieee80211_mandatory_rates(sband, scan_width); + +	return ieee80211_ibss_finish_sta(sta); +} + +static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local; +	int active = 0; +	struct sta_info *sta; + +	sdata_assert_lock(sdata); + +	rcu_read_lock(); + +	list_for_each_entry_rcu(sta, &local->sta_list, list) { +		if (sta->sdata == sdata && +		    time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, +			       jiffies)) { +			active++; +			break; +		} +	} + +	rcu_read_unlock(); + +	return active; +} + +static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +	struct ieee80211_local *local = sdata->local; +	struct cfg80211_bss *cbss; +	struct beacon_data *presp; +	struct sta_info *sta; +	u16 capability; + +	if (!is_zero_ether_addr(ifibss->bssid)) { +		capability = WLAN_CAPABILITY_IBSS; + +		if (ifibss->privacy) +			capability |= WLAN_CAPABILITY_PRIVACY; + +		cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan, +					ifibss->bssid, ifibss->ssid, +					ifibss->ssid_len, WLAN_CAPABILITY_IBSS | +					WLAN_CAPABILITY_PRIVACY, +					capability); + +		if (cbss) { +			cfg80211_unlink_bss(local->hw.wiphy, cbss); +			cfg80211_put_bss(sdata->local->hw.wiphy, cbss); +		} +	} + +	ifibss->state = IEEE80211_IBSS_MLME_SEARCH; + +	sta_info_flush(sdata); + +	spin_lock_bh(&ifibss->incomplete_lock); +	while (!list_empty(&ifibss->incomplete_stations)) { +		sta = list_first_entry(&ifibss->incomplete_stations, +				       struct sta_info, list); +		list_del(&sta->list); +		spin_unlock_bh(&ifibss->incomplete_lock); + +		sta_info_free(local, sta); +		spin_lock_bh(&ifibss->incomplete_lock); +	} +	spin_unlock_bh(&ifibss->incomplete_lock); + +	netif_carrier_off(sdata->dev); + +	sdata->vif.bss_conf.ibss_joined = false; +	sdata->vif.bss_conf.ibss_creator = false; +	sdata->vif.bss_conf.enable_beacon = false; +	sdata->vif.bss_conf.ssid_len = 0; + +	/* remove beacon */ +	presp = rcu_dereference_protected(ifibss->presp, +					  lockdep_is_held(&sdata->wdev.mtx)); +	RCU_INIT_POINTER(sdata->u.ibss.presp, NULL); +	if (presp) +		kfree_rcu(presp, rcu_head); + +	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); +	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | +						BSS_CHANGED_IBSS); +	drv_leave_ibss(local, sdata); +	mutex_lock(&local->mtx); +	ieee80211_vif_release_channel(sdata); +	mutex_unlock(&local->mtx); +} + +static void ieee80211_csa_connection_drop_work(struct work_struct *work) +{ +	struct ieee80211_sub_if_data *sdata = +		container_of(work, struct ieee80211_sub_if_data, +			     u.ibss.csa_connection_drop_work); + +	sdata_lock(sdata); + +	ieee80211_ibss_disconnect(sdata); +	synchronize_rcu(); +	skb_queue_purge(&sdata->skb_queue); + +	/* trigger a scan to find another IBSS network to join */ +	ieee80211_queue_work(&sdata->local->hw, &sdata->work); + +	sdata_unlock(sdata); +} + +static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +	int err; + +	/* if the current channel is a DFS channel, mark the channel as +	 * unavailable. +	 */ +	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, +					    &ifibss->chandef, +					    NL80211_IFTYPE_ADHOC); +	if (err > 0) +		cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef, +				     GFP_ATOMIC); +} + +static bool +ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,  				  struct ieee802_11_elems *elems,  				  bool beacon)  { +	struct cfg80211_csa_settings params; +	struct ieee80211_csa_ie csa_ie; +	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +	enum nl80211_channel_type ch_type; +	int err; +	u32 sta_flags; + +	sdata_assert_lock(sdata); + +	sta_flags = IEEE80211_STA_DISABLE_VHT; +	switch (ifibss->chandef.width) { +	case NL80211_CHAN_WIDTH_5: +	case NL80211_CHAN_WIDTH_10: +	case NL80211_CHAN_WIDTH_20_NOHT: +		sta_flags |= IEEE80211_STA_DISABLE_HT; +		/* fall through */ +	case NL80211_CHAN_WIDTH_20: +		sta_flags |= IEEE80211_STA_DISABLE_40MHZ; +		break; +	default: +		break; +	} + +	memset(¶ms, 0, sizeof(params)); +	memset(&csa_ie, 0, sizeof(csa_ie)); +	err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, +					   ifibss->chandef.chan->band, +					   sta_flags, ifibss->bssid, &csa_ie); +	/* can't switch to destination channel, fail */ +	if (err < 0) +		goto disconnect; + +	/* did not contain a CSA */ +	if (err) +		return false; + +	/* channel switch is not supported, disconnect */ +	if (!(sdata->local->hw.wiphy->flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)) +		goto disconnect; + +	params.count = csa_ie.count; +	params.chandef = csa_ie.chandef; + +	switch (ifibss->chandef.width) { +	case NL80211_CHAN_WIDTH_20_NOHT: +	case NL80211_CHAN_WIDTH_20: +	case NL80211_CHAN_WIDTH_40: +		/* keep our current HT mode (HT20/HT40+/HT40-), even if +		 * another mode  has been announced. The mode is not adopted +		 * within the beacon while doing CSA and we should therefore +		 * keep the mode which we announce. +		 */ +		ch_type = cfg80211_get_chandef_type(&ifibss->chandef); +		cfg80211_chandef_create(¶ms.chandef, params.chandef.chan, +					ch_type); +		break; +	case NL80211_CHAN_WIDTH_5: +	case NL80211_CHAN_WIDTH_10: +		if (params.chandef.width != ifibss->chandef.width) { +			sdata_info(sdata, +				   "IBSS %pM received channel switch from incompatible channel width (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", +				   ifibss->bssid, +				   params.chandef.chan->center_freq, +				   params.chandef.width, +				   params.chandef.center_freq1, +				   params.chandef.center_freq2); +			goto disconnect; +		} +		break; +	default: +		/* should not happen, sta_flags should prevent VHT modes. */ +		WARN_ON(1); +		goto disconnect; +	} + +	if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, ¶ms.chandef, +				     NL80211_IFTYPE_ADHOC)) { +		sdata_info(sdata, +			   "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", +			   ifibss->bssid, +			   params.chandef.chan->center_freq, +			   params.chandef.width, +			   params.chandef.center_freq1, +			   params.chandef.center_freq2); +		goto disconnect; +	} + +	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, +					    ¶ms.chandef, +					    NL80211_IFTYPE_ADHOC); +	if (err < 0) +		goto disconnect; +	if (err > 0 && !ifibss->userspace_handles_dfs) { +		/* IBSS-DFS only allowed with a control program */ +		goto disconnect; +	} + +	params.radar_required = err; + +	if (cfg80211_chandef_identical(¶ms.chandef, +				       &sdata->vif.bss_conf.chandef)) { +		ibss_dbg(sdata, +			 "received csa with an identical chandef, ignoring\n"); +		return true; +	} + +	/* all checks done, now perform the channel switch. */ +	ibss_dbg(sdata, +		 "received channel switch announcement to go to channel %d MHz\n", +		 params.chandef.chan->center_freq); + +	params.block_tx = !!csa_ie.mode; + +	if (ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev, +				     ¶ms)) +		goto disconnect; + +	ieee80211_ibss_csa_mark_radar(sdata); + +	return true; +disconnect: +	ibss_dbg(sdata, "Can't handle channel switch, disconnect\n"); +	ieee80211_queue_work(&sdata->local->hw, +			     &ifibss->csa_connection_drop_work); + +	ieee80211_ibss_csa_mark_radar(sdata); + +	return true; +} + +static void +ieee80211_rx_mgmt_spectrum_mgmt(struct ieee80211_sub_if_data *sdata, +				struct ieee80211_mgmt *mgmt, size_t len, +				struct ieee80211_rx_status *rx_status, +				struct ieee802_11_elems *elems) +{ +	int required_len; + +	if (len < IEEE80211_MIN_ACTION_SIZE + 1) +		return; + +	/* CSA is the only action we handle for now */ +	if (mgmt->u.action.u.measurement.action_code != +	    WLAN_ACTION_SPCT_CHL_SWITCH) +		return; + +	required_len = IEEE80211_MIN_ACTION_SIZE + +		       sizeof(mgmt->u.action.u.chan_switch); +	if (len < required_len) +		return; + +	if (!sdata->vif.csa_active) +		ieee80211_ibss_process_chanswitch(sdata, elems, false); +} + +static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata, +					  struct ieee80211_mgmt *mgmt, +					  size_t len) +{ +	u16 reason = le16_to_cpu(mgmt->u.deauth.reason_code); + +	if (len < IEEE80211_DEAUTH_FRAME_LEN) +		return; + +	ibss_dbg(sdata, "RX DeAuth SA=%pM DA=%pM BSSID=%pM (reason: %d)\n", +		 mgmt->sa, mgmt->da, mgmt->bssid, reason); +	sta_info_destroy_addr(sdata, mgmt->sa); +} + +static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, +					struct ieee80211_mgmt *mgmt, +					size_t len) +{ +	u16 auth_alg, auth_transaction; + +	sdata_assert_lock(sdata); + +	if (len < 24 + 6) +		return; + +	auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); +	auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); + +	ibss_dbg(sdata, +		 "RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n", +		 mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction); + +	if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) +		return; + +	/* +	 * IEEE 802.11 standard does not require authentication in IBSS +	 * networks and most implementations do not seem to use it. +	 * However, try to reply to authentication attempts if someone +	 * has actually implemented this. +	 */ +	ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, 0, NULL, 0, +			    mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0, 0); +} + +static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, +				  struct ieee80211_mgmt *mgmt, size_t len, +				  struct ieee80211_rx_status *rx_status, +				  struct ieee802_11_elems *elems) +{  	struct ieee80211_local *local = sdata->local; -	int freq;  	struct cfg80211_bss *cbss;  	struct ieee80211_bss *bss;  	struct sta_info *sta; @@ -268,80 +1001,109 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  	u64 beacon_timestamp, rx_timestamp;  	u32 supp_rates = 0;  	enum ieee80211_band band = rx_status->band; +	enum nl80211_bss_scan_width scan_width; +	struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; +	bool rates_updated = false; -	if (elems->ds_params && elems->ds_params_len == 1) -		freq = ieee80211_channel_to_frequency(elems->ds_params[0]); -	else -		freq = rx_status->freq; - -	channel = ieee80211_get_channel(local->hw.wiphy, freq); - -	if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) +	channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq); +	if (!channel)  		return;  	if (sdata->vif.type == NL80211_IFTYPE_ADHOC && -	    memcmp(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN) == 0) { +	    ether_addr_equal(mgmt->bssid, sdata->u.ibss.bssid)) {  		rcu_read_lock();  		sta = sta_info_get(sdata, mgmt->sa);  		if (elems->supp_rates) { -			supp_rates = ieee80211_sta_get_rates(local, elems, -							     band); +			supp_rates = ieee80211_sta_get_rates(sdata, elems, +							     band, NULL);  			if (sta) {  				u32 prev_rates;  				prev_rates = sta->sta.supp_rates[band];  				/* make sure mandatory rates are always added */ -				sta->sta.supp_rates[band] = supp_rates | -					ieee80211_mandatory_rates(local, band); +				scan_width = NL80211_BSS_CHAN_WIDTH_20; +				if (rx_status->flag & RX_FLAG_5MHZ) +					scan_width = NL80211_BSS_CHAN_WIDTH_5; +				if (rx_status->flag & RX_FLAG_10MHZ) +					scan_width = NL80211_BSS_CHAN_WIDTH_10; +				sta->sta.supp_rates[band] = supp_rates | +					ieee80211_mandatory_rates(sband, +								  scan_width);  				if (sta->sta.supp_rates[band] != prev_rates) { -#ifdef CONFIG_MAC80211_IBSS_DEBUG -					printk(KERN_DEBUG -						"%s: updated supp_rates set " -						"for %pM based on beacon" -						"/probe_resp (0x%x -> 0x%x)\n", -						sdata->name, sta->sta.addr, -						prev_rates, -						sta->sta.supp_rates[band]); -#endif -					rate_control_rate_init(sta); +					ibss_dbg(sdata, +						 "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n", +						 sta->sta.addr, prev_rates, +						 sta->sta.supp_rates[band]); +					rates_updated = true;  				} -			} else +			} else { +				rcu_read_unlock();  				sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, -						mgmt->sa, supp_rates, -						GFP_ATOMIC); +						mgmt->sa, supp_rates); +			}  		}  		if (sta && elems->wmm_info) -			set_sta_flags(sta, WLAN_STA_WME); +			set_sta_flag(sta, WLAN_STA_WME); + +		if (sta && elems->ht_operation && elems->ht_cap_elem && +		    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT && +		    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 && +		    sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) { +			/* we both use HT */ +			struct ieee80211_ht_cap htcap_ie; +			struct cfg80211_chan_def chandef; + +			ieee80211_ht_oper_to_chandef(channel, +						     elems->ht_operation, +						     &chandef); + +			memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie)); + +			/* +			 * fall back to HT20 if we don't use or use +			 * the other extension channel +			 */ +			if (chandef.center_freq1 != +			    sdata->u.ibss.chandef.center_freq1) +				htcap_ie.cap_info &= +					cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40); + +			rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap( +						sdata, sband, &htcap_ie, sta); +		} + +		if (sta && rates_updated) { +			drv_sta_rc_update(local, sdata, &sta->sta, +					  IEEE80211_RC_SUPP_RATES_CHANGED); +			rate_control_rate_init(sta); +		}  		rcu_read_unlock();  	}  	bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, -					channel, beacon); +					channel);  	if (!bss)  		return;  	cbss = container_of((void *)bss, struct cfg80211_bss, priv); -	/* was just updated in ieee80211_bss_info_update */ -	beacon_timestamp = cbss->tsf; +	/* same for beacon and probe response */ +	beacon_timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);  	/* check if we need to merge IBSS */ -	/* we use a fixed BSSID */ -	if (sdata->u.ibss.fixed_bssid) -		goto put_bss; -  	/* not an IBSS */  	if (!(cbss->capability & WLAN_CAPABILITY_IBSS))  		goto put_bss;  	/* different channel */ -	if (cbss->channel != local->oper_channel) +	if (sdata->u.ibss.fixed_channel && +	    sdata->u.ibss.chandef.chan != cbss->channel)  		goto put_bss;  	/* different SSID */ @@ -350,149 +1112,135 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  				sdata->u.ibss.ssid_len))  		goto put_bss; -	/* same BSSID */ -	if (memcmp(cbss->bssid, sdata->u.ibss.bssid, ETH_ALEN) == 0) +	/* process channel switch */ +	if (sdata->vif.csa_active || +	    ieee80211_ibss_process_chanswitch(sdata, elems, true))  		goto put_bss; -	if (rx_status->flag & RX_FLAG_TSFT) { -		/* -		 * For correct IBSS merging we need mactime; since mactime is -		 * defined as the time the first data symbol of the frame hits -		 * the PHY, and the timestamp of the beacon is defined as "the -		 * time that the data symbol containing the first bit of the -		 * timestamp is transmitted to the PHY plus the transmitting -		 * STA's delays through its local PHY from the MAC-PHY -		 * interface to its interface with the WM" (802.11 11.1.2) -		 * - equals the time this bit arrives at the receiver - we have -		 * to take into account the offset between the two. -		 * -		 * E.g. at 1 MBit that means mactime is 192 usec earlier -		 * (=24 bytes * 8 usecs/byte) than the beacon timestamp. -		 */ -		int rate; +	/* same BSSID */ +	if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid)) +		goto put_bss; -		if (rx_status->flag & RX_FLAG_HT) -			rate = 65; /* TODO: HT rates */ -		else -			rate = local->hw.wiphy->bands[band]-> -				bitrates[rx_status->rate_idx].bitrate; +	/* we use a fixed BSSID */ +	if (sdata->u.ibss.fixed_bssid) +		goto put_bss; -		rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate); +	if (ieee80211_have_rx_timestamp(rx_status)) { +		/* time when timestamp field was received */ +		rx_timestamp = +			ieee80211_calculate_rx_timestamp(local, rx_status, +							 len + FCS_LEN, 24);  	} else {  		/*  		 * second best option: get current TSF  		 * (will return -1 if not supported)  		 */ -		rx_timestamp = drv_get_tsf(local); +		rx_timestamp = drv_get_tsf(local, sdata);  	} -#ifdef CONFIG_MAC80211_IBSS_DEBUG -	printk(KERN_DEBUG "RX beacon SA=%pM BSSID=" -	       "%pM TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n", -	       mgmt->sa, mgmt->bssid, -	       (unsigned long long)rx_timestamp, -	       (unsigned long long)beacon_timestamp, -	       (unsigned long long)(rx_timestamp - beacon_timestamp), -	       jiffies); -#endif - -	/* give slow hardware some time to do the TSF sync */ -	if (rx_timestamp < IEEE80211_IBSS_MERGE_DELAY) -		goto put_bss; +	ibss_dbg(sdata, +		 "RX beacon SA=%pM BSSID=%pM TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n", +		 mgmt->sa, mgmt->bssid, +		 (unsigned long long)rx_timestamp, +		 (unsigned long long)beacon_timestamp, +		 (unsigned long long)(rx_timestamp - beacon_timestamp), +		 jiffies);  	if (beacon_timestamp > rx_timestamp) { -#ifdef CONFIG_MAC80211_IBSS_DEBUG -		printk(KERN_DEBUG "%s: beacon TSF higher than " -		       "local TSF - IBSS merge with BSSID %pM\n", -		       sdata->name, mgmt->bssid); -#endif +		ibss_dbg(sdata, +			 "beacon TSF higher than local TSF - IBSS merge with BSSID %pM\n", +			 mgmt->bssid);  		ieee80211_sta_join_ibss(sdata, bss); -		supp_rates = ieee80211_sta_get_rates(local, elems, band); +		supp_rates = ieee80211_sta_get_rates(sdata, elems, band, NULL);  		ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, -				       supp_rates, GFP_KERNEL); +				       supp_rates); +		rcu_read_unlock();  	}   put_bss:  	ieee80211_rx_bss_put(local, bss);  } -/* - * Add a new IBSS station, will also be called by the RX code when, - * in IBSS mode, receiving a frame from a yet-unknown station, hence - * must be callable in atomic context. - */ -struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, -					u8 *bssid,u8 *addr, u32 supp_rates, -					gfp_t gfp) +void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, +			      const u8 *bssid, const u8 *addr, +			      u32 supp_rates)  {  	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;  	struct ieee80211_local *local = sdata->local;  	struct sta_info *sta; -	int band = local->hw.conf.channel->band; +	struct ieee80211_chanctx_conf *chanctx_conf; +	struct ieee80211_supported_band *sband; +	enum nl80211_bss_scan_width scan_width; +	int band;  	/*  	 * XXX: Consider removing the least recently used entry and  	 * 	allow new one to be added.  	 */  	if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { -		if (net_ratelimit()) -			printk(KERN_DEBUG "%s: No room for a new IBSS STA entry %pM\n", -			       sdata->name, addr); -		return NULL; +		net_info_ratelimited("%s: No room for a new IBSS STA entry %pM\n", +				    sdata->name, addr); +		return;  	}  	if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH) -		return NULL; +		return; -	if (compare_ether_addr(bssid, sdata->u.ibss.bssid)) -		return NULL; +	if (!ether_addr_equal(bssid, sdata->u.ibss.bssid)) +		return; -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -	wiphy_debug(local->hw.wiphy, "Adding new IBSS station %pM (dev=%s)\n", -		    addr, sdata->name); -#endif +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	if (WARN_ON_ONCE(!chanctx_conf)) { +		rcu_read_unlock(); +		return; +	} +	band = chanctx_conf->def.chan->band; +	scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def); +	rcu_read_unlock(); -	sta = sta_info_alloc(sdata, addr, gfp); +	sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);  	if (!sta) -		return NULL; +		return;  	sta->last_rx = jiffies; -	set_sta_flags(sta, WLAN_STA_AUTHORIZED);  	/* make sure mandatory rates are always added */ +	sband = local->hw.wiphy->bands[band];  	sta->sta.supp_rates[band] = supp_rates | -			ieee80211_mandatory_rates(local, band); - -	rate_control_rate_init(sta); +			ieee80211_mandatory_rates(sband, scan_width); -	/* If it fails, maybe we raced another insertion? */ -	if (sta_info_insert(sta)) -		return sta_info_get(sdata, addr); -	return sta; +	spin_lock(&ifibss->incomplete_lock); +	list_add(&sta->list, &ifibss->incomplete_stations); +	spin_unlock(&ifibss->incomplete_lock); +	ieee80211_queue_work(&local->hw, &sdata->work);  } -static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) +static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_local *local = sdata->local; -	int active = 0; -	struct sta_info *sta; +	struct sta_info *sta, *tmp; +	unsigned long exp_time = IEEE80211_IBSS_INACTIVITY_LIMIT; +	unsigned long exp_rsn_time = IEEE80211_IBSS_RSN_INACTIVITY_LIMIT; -	lockdep_assert_held(&sdata->u.ibss.mtx); +	mutex_lock(&local->sta_mtx); -	rcu_read_lock(); +	list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { +		if (sdata != sta->sdata) +			continue; -	list_for_each_entry_rcu(sta, &local->sta_list, list) { -		if (sta->sdata == sdata && -		    time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, -			       jiffies)) { -			active++; -			break; +		if (time_after(jiffies, sta->last_rx + exp_time) || +		    (time_after(jiffies, sta->last_rx + exp_rsn_time) && +		     sta->sta_state != IEEE80211_STA_AUTHORIZED)) { +			sta_dbg(sta->sdata, "expiring inactive %sSTA %pM\n", +				sta->sta_state != IEEE80211_STA_AUTHORIZED ? +				"not authorized " : "", sta->sta.addr); + +			WARN_ON(__sta_info_destroy(sta));  		}  	} -	rcu_read_unlock(); - -	return active; +	mutex_unlock(&local->sta_mtx);  }  /* @@ -502,13 +1250,14 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)  static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +	enum nl80211_bss_scan_width scan_width; -	lockdep_assert_held(&ifibss->mtx); +	sdata_assert_lock(sdata);  	mod_timer(&ifibss->timer,  		  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); -	ieee80211_sta_expire(sdata, IEEE80211_IBSS_INACTIVITY_LIMIT); +	ieee80211_ibss_sta_expire(sdata);  	if (time_before(jiffies, ifibss->last_scan_completed +  		       IEEE80211_IBSS_MERGE_INTERVAL)) @@ -520,24 +1269,22 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)  	if (ifibss->fixed_channel)  		return; -	printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other " -	       "IBSS networks with same SSID (merge)\n", sdata->name); +	sdata_info(sdata, +		   "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n"); -	ieee80211_request_internal_scan(sdata, -			ifibss->ssid, ifibss->ssid_len, -			ifibss->fixed_channel ? ifibss->channel : NULL); +	scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef); +	ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len, +				    NULL, scan_width);  }  static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; -	struct ieee80211_local *local = sdata->local; -	struct ieee80211_supported_band *sband;  	u8 bssid[ETH_ALEN];  	u16 capability;  	int i; -	lockdep_assert_held(&ifibss->mtx); +	sdata_assert_lock(sdata);  	if (ifibss->fixed_bssid) {  		memcpy(bssid, ifibss->bssid, ETH_ALEN); @@ -552,10 +1299,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)  		bssid[0] |= 0x02;  	} -	printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %pM\n", -	       sdata->name, bssid); - -	sband = local->hw.wiphy->bands[ifibss->channel->band]; +	sdata_info(sdata, "Creating new IBSS network, BSSID %pM\n", bssid);  	capability = WLAN_CAPABILITY_IBSS; @@ -565,8 +1309,8 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)  		sdata->drop_unencrypted = 0;  	__ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int, -				  ifibss->channel, ifibss->basic_rates, -				  capability, 0); +				  &ifibss->chandef, ifibss->basic_rates, +				  capability, 0, true);  }  /* @@ -580,16 +1324,14 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)  	struct cfg80211_bss *cbss;  	struct ieee80211_channel *chan = NULL;  	const u8 *bssid = NULL; +	enum nl80211_bss_scan_width scan_width;  	int active_ibss;  	u16 capability; -	lockdep_assert_held(&ifibss->mtx); +	sdata_assert_lock(sdata);  	active_ibss = ieee80211_sta_active_ibss(sdata); -#ifdef CONFIG_MAC80211_IBSS_DEBUG -	printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n", -	       sdata->name, active_ibss); -#endif /* CONFIG_MAC80211_IBSS_DEBUG */ +	ibss_dbg(sdata, "sta_find_ibss (active_ibss=%d)\n", active_ibss);  	if (active_ibss)  		return; @@ -600,7 +1342,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)  	if (ifibss->fixed_bssid)  		bssid = ifibss->bssid;  	if (ifibss->fixed_channel) -		chan = ifibss->channel; +		chan = ifibss->chandef.chan;  	if (!is_zero_ether_addr(ifibss->bssid))  		bssid = ifibss->bssid;  	cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid, @@ -612,50 +1354,46 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)  		struct ieee80211_bss *bss;  		bss = (void *)cbss->priv; -#ifdef CONFIG_MAC80211_IBSS_DEBUG -		printk(KERN_DEBUG "   sta_find_ibss: selected %pM current " -		       "%pM\n", cbss->bssid, ifibss->bssid); -#endif /* CONFIG_MAC80211_IBSS_DEBUG */ - -		printk(KERN_DEBUG "%s: Selected IBSS BSSID %pM" -		       " based on configured SSID\n", -		       sdata->name, cbss->bssid); +		ibss_dbg(sdata, +			 "sta_find_ibss: selected %pM current %pM\n", +			 cbss->bssid, ifibss->bssid); +		sdata_info(sdata, +			   "Selected IBSS BSSID %pM based on configured SSID\n", +			   cbss->bssid);  		ieee80211_sta_join_ibss(sdata, bss);  		ieee80211_rx_bss_put(local, bss);  		return;  	} -#ifdef CONFIG_MAC80211_IBSS_DEBUG -	printk(KERN_DEBUG "   did not try to join ibss\n"); -#endif /* CONFIG_MAC80211_IBSS_DEBUG */ +	/* if a fixed bssid and a fixed freq have been provided create the IBSS +	 * directly and do not waste time scanning +	 */ +	if (ifibss->fixed_bssid && ifibss->fixed_channel) { +		sdata_info(sdata, "Created IBSS using preconfigured BSSID %pM\n", +			   bssid); +		ieee80211_sta_create_ibss(sdata); +		return; +	} + + +	ibss_dbg(sdata, "sta_find_ibss: did not try to join ibss\n");  	/* Selected IBSS not found in current scan results - try to scan */  	if (time_after(jiffies, ifibss->last_scan_completed +  					IEEE80211_SCAN_INTERVAL)) { -		printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to " -		       "join\n", sdata->name); +		sdata_info(sdata, "Trigger new scan to find an IBSS to join\n"); -		ieee80211_request_internal_scan(sdata, -				ifibss->ssid, ifibss->ssid_len, -				ifibss->fixed_channel ? ifibss->channel : NULL); +		scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef); +		ieee80211_request_ibss_scan(sdata, ifibss->ssid, +					    ifibss->ssid_len, chan, +					    scan_width);  	} else {  		int interval = IEEE80211_SCAN_INTERVAL;  		if (time_after(jiffies, ifibss->ibss_join_req + -			       IEEE80211_IBSS_JOIN_TIMEOUT)) { -			if (!(local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS)) { -				ieee80211_sta_create_ibss(sdata); -				return; -			} -			printk(KERN_DEBUG "%s: IBSS not allowed on" -			       " %d MHz\n", sdata->name, -			       local->hw.conf.channel->center_freq); - -			/* No IBSS found - decrease scan interval and continue -			 * scanning. */ -			interval = IEEE80211_SCAN_INTERVAL_SLOW; -		} +			       IEEE80211_IBSS_JOIN_TIMEOUT)) +			ieee80211_sta_create_ibss(sdata);  		mod_timer(&ifibss->timer,  			  round_jiffies(jiffies + interval)); @@ -663,47 +1401,44 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)  }  static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, -					struct ieee80211_mgmt *mgmt, -					size_t len) +					struct sk_buff *req)  { +	struct ieee80211_mgmt *mgmt = (void *)req->data;  	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;  	struct ieee80211_local *local = sdata->local; -	int tx_last_beacon; +	int tx_last_beacon, len = req->len;  	struct sk_buff *skb; -	struct ieee80211_mgmt *resp; +	struct beacon_data *presp;  	u8 *pos, *end; -	lockdep_assert_held(&ifibss->mtx); +	sdata_assert_lock(sdata); + +	presp = rcu_dereference_protected(ifibss->presp, +					  lockdep_is_held(&sdata->wdev.mtx));  	if (ifibss->state != IEEE80211_IBSS_MLME_JOINED || -	    len < 24 + 2 || !ifibss->presp) +	    len < 24 + 2 || !presp)  		return;  	tx_last_beacon = drv_tx_last_beacon(local); -#ifdef CONFIG_MAC80211_IBSS_DEBUG -	printk(KERN_DEBUG "%s: RX ProbeReq SA=%pM DA=%pM BSSID=%pM" -	       " (tx_last_beacon=%d)\n", -	       sdata->name, mgmt->sa, mgmt->da, -	       mgmt->bssid, tx_last_beacon); -#endif /* CONFIG_MAC80211_IBSS_DEBUG */ +	ibss_dbg(sdata, +		 "RX ProbeReq SA=%pM DA=%pM BSSID=%pM (tx_last_beacon=%d)\n", +		 mgmt->sa, mgmt->da, mgmt->bssid, tx_last_beacon); -	if (!tx_last_beacon) +	if (!tx_last_beacon && is_multicast_ether_addr(mgmt->da))  		return; -	if (memcmp(mgmt->bssid, ifibss->bssid, ETH_ALEN) != 0 && -	    memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) +	if (!ether_addr_equal(mgmt->bssid, ifibss->bssid) && +	    !is_broadcast_ether_addr(mgmt->bssid))  		return;  	end = ((u8 *) mgmt) + len;  	pos = mgmt->u.probe_req.variable;  	if (pos[0] != WLAN_EID_SSID ||  	    pos + 2 + pos[1] > end) { -#ifdef CONFIG_MAC80211_IBSS_DEBUG -		printk(KERN_DEBUG "%s: Invalid SSID IE in ProbeReq " -		       "from %pM\n", -		       sdata->name, mgmt->sa); -#endif +		ibss_dbg(sdata, "Invalid SSID IE in ProbeReq from %pM\n", +			 mgmt->sa);  		return;  	}  	if (pos[1] != 0 && @@ -714,57 +1449,47 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,  	}  	/* Reply with ProbeResp */ -	skb = skb_copy(ifibss->presp, GFP_KERNEL); +	skb = dev_alloc_skb(local->tx_headroom + presp->head_len);  	if (!skb)  		return; -	resp = (struct ieee80211_mgmt *) skb->data; -	memcpy(resp->da, mgmt->sa, ETH_ALEN); -#ifdef CONFIG_MAC80211_IBSS_DEBUG -	printk(KERN_DEBUG "%s: Sending ProbeResp to %pM\n", -	       sdata->name, resp->da); -#endif /* CONFIG_MAC80211_IBSS_DEBUG */ +	skb_reserve(skb, local->tx_headroom); +	memcpy(skb_put(skb, presp->head_len), presp->head, presp->head_len); + +	memcpy(((struct ieee80211_mgmt *) skb->data)->da, mgmt->sa, ETH_ALEN); +	ibss_dbg(sdata, "Sending ProbeResp to %pM\n", mgmt->sa);  	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + +	/* avoid excessive retries for probe request to wildcard SSIDs */ +	if (pos[1] == 0) +		IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_NO_ACK; +  	ieee80211_tx_skb(sdata, skb);  } -static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, -					 struct ieee80211_mgmt *mgmt, -					 size_t len, -					 struct ieee80211_rx_status *rx_status) +static +void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata, +				    struct ieee80211_mgmt *mgmt, size_t len, +				    struct ieee80211_rx_status *rx_status)  {  	size_t baselen;  	struct ieee802_11_elems elems; -	if (memcmp(mgmt->da, sdata->vif.addr, ETH_ALEN)) -		return; /* ignore ProbeResp to foreign address */ +	BUILD_BUG_ON(offsetof(typeof(mgmt->u.probe_resp), variable) != +		     offsetof(typeof(mgmt->u.beacon), variable)); +	/* +	 * either beacon or probe_resp but the variable field is at the +	 * same offset +	 */  	baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;  	if (baselen > len)  		return;  	ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, -				&elems); +			       false, &elems); -	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); -} - -static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, -				     struct ieee80211_mgmt *mgmt, -				     size_t len, -				     struct ieee80211_rx_status *rx_status) -{ -	size_t baselen; -	struct ieee802_11_elems elems; - -	/* Process beacon from the current BSS */ -	baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; -	if (baselen > len) -		return; - -	ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems); - -	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true); +	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);  }  void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, @@ -773,38 +1498,66 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_rx_status *rx_status;  	struct ieee80211_mgmt *mgmt;  	u16 fc; +	struct ieee802_11_elems elems; +	int ies_len;  	rx_status = IEEE80211_SKB_RXCB(skb);  	mgmt = (struct ieee80211_mgmt *) skb->data;  	fc = le16_to_cpu(mgmt->frame_control); -	mutex_lock(&sdata->u.ibss.mtx); +	sdata_lock(sdata); + +	if (!sdata->u.ibss.ssid_len) +		goto mgmt_out; /* not ready to merge yet */  	switch (fc & IEEE80211_FCTL_STYPE) {  	case IEEE80211_STYPE_PROBE_REQ: -		ieee80211_rx_mgmt_probe_req(sdata, mgmt, skb->len); +		ieee80211_rx_mgmt_probe_req(sdata, skb);  		break;  	case IEEE80211_STYPE_PROBE_RESP: -		ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len, -					     rx_status); -		break;  	case IEEE80211_STYPE_BEACON: -		ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, -					 rx_status); +		ieee80211_rx_mgmt_probe_beacon(sdata, mgmt, skb->len, +					       rx_status);  		break;  	case IEEE80211_STYPE_AUTH:  		ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len);  		break; +	case IEEE80211_STYPE_DEAUTH: +		ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len); +		break; +	case IEEE80211_STYPE_ACTION: +		switch (mgmt->u.action.category) { +		case WLAN_CATEGORY_SPECTRUM_MGMT: +			ies_len = skb->len - +				  offsetof(struct ieee80211_mgmt, +					   u.action.u.chan_switch.variable); + +			if (ies_len < 0) +				break; + +			ieee802_11_parse_elems( +				mgmt->u.action.u.chan_switch.variable, +				ies_len, true, &elems); + +			if (elems.parse_error) +				break; + +			ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt, skb->len, +							rx_status, &elems); +			break; +		}  	} -	mutex_unlock(&sdata->u.ibss.mtx); + mgmt_out: +	sdata_unlock(sdata);  }  void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +	struct sta_info *sta; -	mutex_lock(&ifibss->mtx); +	sdata_lock(sdata);  	/*  	 * Work could be scheduled after scan or similar @@ -814,6 +1567,19 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)  	if (!ifibss->ssid_len)  		goto out; +	spin_lock_bh(&ifibss->incomplete_lock); +	while (!list_empty(&ifibss->incomplete_stations)) { +		sta = list_first_entry(&ifibss->incomplete_stations, +				       struct sta_info, list); +		list_del(&sta->list); +		spin_unlock_bh(&ifibss->incomplete_lock); + +		ieee80211_ibss_finish_sta(sta); +		rcu_read_unlock(); +		spin_lock_bh(&ifibss->incomplete_lock); +	} +	spin_unlock_bh(&ifibss->incomplete_lock); +  	switch (ifibss->state) {  	case IEEE80211_IBSS_MLME_SEARCH:  		ieee80211_sta_find_ibss(sdata); @@ -827,43 +1593,16 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)  	}   out: -	mutex_unlock(&ifibss->mtx); +	sdata_unlock(sdata);  }  static void ieee80211_ibss_timer(unsigned long data)  {  	struct ieee80211_sub_if_data *sdata =  		(struct ieee80211_sub_if_data *) data; -	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; -	struct ieee80211_local *local = sdata->local; - -	if (local->quiescing) { -		ifibss->timer_running = true; -		return; -	} - -	ieee80211_queue_work(&local->hw, &sdata->work); -} - -#ifdef CONFIG_PM -void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; - -	if (del_timer_sync(&ifibss->timer)) -		ifibss->timer_running = true; -} - -void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; -	if (ifibss->timer_running) { -		add_timer(&ifibss->timer); -		ifibss->timer_running = false; -	} +	ieee80211_queue_work(&sdata->local->hw, &sdata->work);  } -#endif  void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)  { @@ -871,7 +1610,10 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)  	setup_timer(&ifibss->timer, ieee80211_ibss_timer,  		    (unsigned long) sdata); -	mutex_init(&ifibss->mtx); +	INIT_LIST_HEAD(&ifibss->incomplete_stations); +	spin_lock_init(&ifibss->incomplete_lock); +	INIT_WORK(&ifibss->csa_connection_drop_work, +		  ieee80211_csa_connection_drop_work);  }  /* scan finished notification */ @@ -894,18 +1636,36 @@ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local)  int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  			struct cfg80211_ibss_params *params)  { -	struct sk_buff *skb; +	u32 changed = 0; +	u32 rate_flags; +	struct ieee80211_supported_band *sband; +	enum ieee80211_chanctx_mode chanmode; +	struct ieee80211_local *local = sdata->local; +	int radar_detect_width = 0; +	int i; +	int ret; + +	ret = cfg80211_chandef_dfs_required(local->hw.wiphy, +					    ¶ms->chandef, +					    sdata->wdev.iftype); +	if (ret < 0) +		return ret; + +	if (ret > 0) { +		if (!params->userspace_handles_dfs) +			return -EINVAL; +		radar_detect_width = BIT(params->chandef.width); +	} -	skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom + -			    36 /* bitrates */ + -			    34 /* SSID */ + -			    3  /* DS params */ + -			    4  /* IBSS params */ + -			    params->ie_len); -	if (!skb) -		return -ENOMEM; +	chanmode = (params->channel_fixed && !ret) ? +		IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE; -	mutex_lock(&sdata->u.ibss.mtx); +	mutex_lock(&local->chanctx_mtx); +	ret = ieee80211_check_combinations(sdata, ¶ms->chandef, chanmode, +					   radar_detect_width); +	mutex_unlock(&local->chanctx_mtx); +	if (ret < 0) +		return ret;  	if (params->bssid) {  		memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN); @@ -914,22 +1674,26 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  		sdata->u.ibss.fixed_bssid = false;  	sdata->u.ibss.privacy = params->privacy; +	sdata->u.ibss.control_port = params->control_port; +	sdata->u.ibss.userspace_handles_dfs = params->userspace_handles_dfs;  	sdata->u.ibss.basic_rates = params->basic_rates; +	sdata->u.ibss.last_scan_completed = jiffies; + +	/* fix basic_rates if channel does not support these rates */ +	rate_flags = ieee80211_chandef_rate_flags(¶ms->chandef); +	sband = local->hw.wiphy->bands[params->chandef.chan->band]; +	for (i = 0; i < sband->n_bitrates; i++) { +		if ((rate_flags & sband->bitrates[i].flags) != rate_flags) +			sdata->u.ibss.basic_rates &= ~BIT(i); +	}  	memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate,  	       sizeof(params->mcast_rate));  	sdata->vif.bss_conf.beacon_int = params->beacon_interval; -	sdata->u.ibss.channel = params->channel; +	sdata->u.ibss.chandef = params->chandef;  	sdata->u.ibss.fixed_channel = params->channel_fixed; -	/* fix ourselves to that channel now already */ -	if (params->channel_fixed) { -		sdata->local->oper_channel = params->channel; -		WARN_ON(!ieee80211_set_channel_type(sdata->local, sdata, -						    NL80211_CHAN_NO_HT)); -	} -  	if (params->ie) {  		sdata->u.ibss.ie = kmemdup(params->ie, params->ie_len,  					   GFP_KERNEL); @@ -937,78 +1701,62 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  			sdata->u.ibss.ie_len = params->ie_len;  	} -	sdata->u.ibss.skb = skb;  	sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH;  	sdata->u.ibss.ibss_join_req = jiffies; -	memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN); +	memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len);  	sdata->u.ibss.ssid_len = params->ssid_len; -	mutex_unlock(&sdata->u.ibss.mtx); +	memcpy(&sdata->u.ibss.ht_capa, ¶ms->ht_capa, +	       sizeof(sdata->u.ibss.ht_capa)); +	memcpy(&sdata->u.ibss.ht_capa_mask, ¶ms->ht_capa_mask, +	       sizeof(sdata->u.ibss.ht_capa_mask)); -	mutex_lock(&sdata->local->mtx); -	ieee80211_recalc_idle(sdata->local); -	mutex_unlock(&sdata->local->mtx); +	/* +	 * 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is +	 * reserved, but an HT STA shall protect HT transmissions as though +	 * the HT Protection field were set to non-HT mixed mode. +	 * +	 * In an IBSS, the RIFS Mode field of the HT Operation element is +	 * also reserved, but an HT STA shall operate as though this field +	 * were set to 1. +	 */ -	ieee80211_queue_work(&sdata->local->hw, &sdata->work); +	sdata->vif.bss_conf.ht_operation_mode |= +		  IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED +		| IEEE80211_HT_PARAM_RIFS_MODE; + +	changed |= BSS_CHANGED_HT; +	ieee80211_bss_info_change_notify(sdata, changed); + +	sdata->smps_mode = IEEE80211_SMPS_OFF; +	sdata->needed_rx_chains = local->rx_chains; + +	ieee80211_queue_work(&local->hw, &sdata->work);  	return 0;  }  int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)  { -	struct sk_buff *skb;  	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; -	struct ieee80211_local *local = sdata->local; -	struct cfg80211_bss *cbss; -	u16 capability; -	int active_ibss; - -	mutex_lock(&sdata->u.ibss.mtx); -	active_ibss = ieee80211_sta_active_ibss(sdata); - -	if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) { -		capability = WLAN_CAPABILITY_IBSS; - -		if (ifibss->privacy) -			capability |= WLAN_CAPABILITY_PRIVACY; - -		cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->channel, -					ifibss->bssid, ifibss->ssid, -					ifibss->ssid_len, WLAN_CAPABILITY_IBSS | -					WLAN_CAPABILITY_PRIVACY, -					capability); - -		if (cbss) { -			cfg80211_unlink_bss(local->hw.wiphy, cbss); -			cfg80211_put_bss(cbss); -		} -	} - -	sta_info_flush(sdata->local, sdata); +	ieee80211_ibss_disconnect(sdata); +	ifibss->ssid_len = 0; +	memset(ifibss->bssid, 0, ETH_ALEN);  	/* remove beacon */  	kfree(sdata->u.ibss.ie); -	skb = sdata->u.ibss.presp; -	rcu_assign_pointer(sdata->u.ibss.presp, NULL); -	sdata->vif.bss_conf.ibss_joined = false; -	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | -						BSS_CHANGED_IBSS); + +	/* on the next join, re-program HT parameters */ +	memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa)); +	memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask)); +  	synchronize_rcu(); -	kfree_skb(skb);  	skb_queue_purge(&sdata->skb_queue); -	memset(sdata->u.ibss.bssid, 0, ETH_ALEN); -	sdata->u.ibss.ssid_len = 0;  	del_timer_sync(&sdata->u.ibss.timer); -	mutex_unlock(&sdata->u.ibss.mtx); - -	mutex_lock(&local->mtx); -	ieee80211_recalc_idle(sdata->local); -	mutex_unlock(&local->mtx); -  	return 0;  } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 5bc0745368f..ac9836e0aab 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -23,11 +23,14 @@  #include <linux/types.h>  #include <linux/spinlock.h>  #include <linux/etherdevice.h> +#include <linux/leds.h> +#include <linux/idr.h>  #include <net/ieee80211_radiotap.h>  #include <net/cfg80211.h>  #include <net/mac80211.h>  #include "key.h"  #include "sta_info.h" +#include "debug.h"  struct ieee80211_local; @@ -50,17 +53,23 @@ struct ieee80211_local;   * increased memory use (about 2 kB of RAM per entry). */  #define IEEE80211_FRAGMENT_MAX 4 -#define TU_TO_EXP_TIME(x)	(jiffies + usecs_to_jiffies((x) * 1024)) +/* power level hasn't been configured (or set to automatic) */ +#define IEEE80211_UNSET_POWER_LEVEL	INT_MIN +/* + * Some APs experience problems when working with U-APSD. Decrease the + * probability of that happening by using legacy mode for all ACs but VO. + * The AP that caused us trouble was a Cisco 4410N. It ignores our + * setting, and always treats non-VO ACs as legacy. + */  #define IEEE80211_DEFAULT_UAPSD_QUEUES \ -	(IEEE80211_WMM_IE_STA_QOSINFO_AC_BK |	\ -	 IEEE80211_WMM_IE_STA_QOSINFO_AC_BE |	\ -	 IEEE80211_WMM_IE_STA_QOSINFO_AC_VI |	\ -	 IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) +	IEEE80211_WMM_IE_STA_QOSINFO_AC_VO  #define IEEE80211_DEFAULT_MAX_SP_LEN		\  	IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL +#define IEEE80211_DEAUTH_FRAME_LEN	(24 /* hdr */ + 2 /* reason */) +  struct ieee80211_fragment_entry {  	unsigned long first_frag_time;  	unsigned int seq; @@ -74,81 +83,79 @@ struct ieee80211_fragment_entry {  struct ieee80211_bss { -	/* don't want to look up all the time */ -	size_t ssid_len; -	u8 ssid[IEEE80211_MAX_SSID_LEN]; - -	u8 dtim_period; +	u32 device_ts_beacon, device_ts_presp;  	bool wmm_used;  	bool uapsd_supported; -	unsigned long last_probe_resp; - -#ifdef CONFIG_MAC80211_MESH -	u8 *mesh_id; -	size_t mesh_id_len; -	u8 *mesh_cfg; -#endif -  #define IEEE80211_MAX_SUPP_RATES 32  	u8 supp_rates[IEEE80211_MAX_SUPP_RATES];  	size_t supp_rates_len; +	struct ieee80211_rate *beacon_rate;  	/* -	 * During assocation, we save an ERP value from a probe response so +	 * During association, we save an ERP value from a probe response so  	 * that we can feed ERP info to the driver when handling the  	 * association completes. these fields probably won't be up-to-date  	 * otherwise, you probably don't want to use them.  	 */  	bool has_erp_value;  	u8 erp_value; -}; -static inline u8 *bss_mesh_cfg(struct ieee80211_bss *bss) -{ -#ifdef CONFIG_MAC80211_MESH -	return bss->mesh_cfg; -#endif -	return NULL; -} +	/* Keep track of the corruption of the last beacon/probe response. */ +	u8 corrupt_data; -static inline u8 *bss_mesh_id(struct ieee80211_bss *bss) -{ -#ifdef CONFIG_MAC80211_MESH -	return bss->mesh_id; -#endif -	return NULL; -} +	/* Keep track of what bits of information we have valid info for. */ +	u8 valid_data; +}; -static inline u8 bss_mesh_id_len(struct ieee80211_bss *bss) -{ -#ifdef CONFIG_MAC80211_MESH -	return bss->mesh_id_len; -#endif -	return 0; -} +/** + * enum ieee80211_corrupt_data_flags - BSS data corruption flags + * @IEEE80211_BSS_CORRUPT_BEACON: last beacon frame received was corrupted + * @IEEE80211_BSS_CORRUPT_PROBE_RESP: last probe response received was corrupted + * + * These are bss flags that are attached to a bss in the + * @corrupt_data field of &struct ieee80211_bss. + */ +enum ieee80211_bss_corrupt_data_flags { +	IEEE80211_BSS_CORRUPT_BEACON		= BIT(0), +	IEEE80211_BSS_CORRUPT_PROBE_RESP	= BIT(1) +}; +/** + * enum ieee80211_valid_data_flags - BSS valid data flags + * @IEEE80211_BSS_VALID_WMM: WMM/UAPSD data was gathered from non-corrupt IE + * @IEEE80211_BSS_VALID_RATES: Supported rates were gathered from non-corrupt IE + * @IEEE80211_BSS_VALID_ERP: ERP flag was gathered from non-corrupt IE + * + * These are bss flags that are attached to a bss in the + * @valid_data field of &struct ieee80211_bss.  They show which parts + * of the data structure were recieved as a result of an un-corrupted + * beacon/probe response. + */ +enum ieee80211_bss_valid_data_flags { +	IEEE80211_BSS_VALID_WMM			= BIT(1), +	IEEE80211_BSS_VALID_RATES		= BIT(2), +	IEEE80211_BSS_VALID_ERP			= BIT(3) +};  typedef unsigned __bitwise__ ieee80211_tx_result;  #define TX_CONTINUE	((__force ieee80211_tx_result) 0u)  #define TX_DROP		((__force ieee80211_tx_result) 1u)  #define TX_QUEUED	((__force ieee80211_tx_result) 2u) -#define IEEE80211_TX_FRAGMENTED		BIT(0)  #define IEEE80211_TX_UNICAST		BIT(1)  #define IEEE80211_TX_PS_BUFFERED	BIT(2)  struct ieee80211_tx_data {  	struct sk_buff *skb; +	struct sk_buff_head skbs;  	struct ieee80211_local *local;  	struct ieee80211_sub_if_data *sdata;  	struct sta_info *sta;  	struct ieee80211_key *key; +	struct ieee80211_tx_rate rate; -	struct ieee80211_channel *channel; - -	u16 ethertype;  	unsigned int flags;  }; @@ -163,32 +170,35 @@ typedef unsigned __bitwise__ ieee80211_rx_result;   * enum ieee80211_packet_rx_flags - packet RX flags   * @IEEE80211_RX_RA_MATCH: frame is destined to interface currently processed   *	(incl. multicast frames) - * @IEEE80211_RX_IN_SCAN: received while scanning   * @IEEE80211_RX_FRAGMENTED: fragmented frame   * @IEEE80211_RX_AMSDU: a-MSDU packet   * @IEEE80211_RX_MALFORMED_ACTION_FRM: action frame is malformed + * @IEEE80211_RX_DEFERRED_RELEASE: frame was subjected to receive reordering   *   * These are per-frame flags that are attached to a frame in the   * @rx_flags field of &struct ieee80211_rx_status.   */  enum ieee80211_packet_rx_flags { -	IEEE80211_RX_IN_SCAN			= BIT(0),  	IEEE80211_RX_RA_MATCH			= BIT(1),  	IEEE80211_RX_FRAGMENTED			= BIT(2),  	IEEE80211_RX_AMSDU			= BIT(3),  	IEEE80211_RX_MALFORMED_ACTION_FRM	= BIT(4), +	IEEE80211_RX_DEFERRED_RELEASE		= BIT(5),  };  /**   * enum ieee80211_rx_flags - RX data flags   *   * @IEEE80211_RX_CMNTR: received on cooked monitor already + * @IEEE80211_RX_BEACON_REPORTED: This frame was already reported + *	to cfg80211_report_obss_beacon().   *   * These flags are used across handling multiple interfaces   * for a single frame.   */  enum ieee80211_rx_flags {  	IEEE80211_RX_CMNTR		= BIT(0), +	IEEE80211_RX_BEACON_REPORTED	= BIT(1),  };  struct ieee80211_rx_data { @@ -199,7 +209,22 @@ struct ieee80211_rx_data {  	struct ieee80211_key *key;  	unsigned int flags; -	int queue; + +	/* +	 * Index into sequence numbers array, 0..16 +	 * since the last (16) is used for non-QoS, +	 * will be 16 on non-QoS frames. +	 */ +	int seqno_idx; + +	/* +	 * Index into the security IV/PN arrays, 0..16 +	 * since the last (16) is used for CCMP-encrypted +	 * management frames, will be set to 16 on mgmt +	 * frames and 0 on non-QoS frames. +	 */ +	int security_idx; +  	u32 tkip_iv32;  	u16 tkip_iv16;  }; @@ -207,21 +232,42 @@ struct ieee80211_rx_data {  struct beacon_data {  	u8 *head, *tail;  	int head_len, tail_len; -	int dtim_period; +	struct ieee80211_meshconf_ie *meshconf; +	struct rcu_head rcu_head;  }; -struct ieee80211_if_ap { -	struct beacon_data *beacon; - -	struct list_head vlans; +struct probe_resp { +	struct rcu_head rcu_head; +	int len; +	u8 data[0]; +}; +struct ps_data {  	/* yes, this looks ugly, but guarantees that we can later use  	 * bitmap_empty :)  	 * NB: don't touch this bitmap, use sta_info_{set,clear}_tim_bit */ -	u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)]; -	struct sk_buff_head ps_bc_buf; +	u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)] +			__aligned(__alignof__(unsigned long)); +	struct sk_buff_head bc_buf;  	atomic_t num_sta_ps; /* number of stations in PS mode */  	int dtim_count; +	bool dtim_bc_mc; +}; + +struct ieee80211_if_ap { +	struct beacon_data __rcu *beacon; +	struct probe_resp __rcu *probe_resp; + +	/* to be used after channel switch. */ +	struct cfg80211_beacon_data *next_beacon; +	struct list_head vlans; /* write-protected with RTNL and local->mtx */ + +	struct ps_data ps; +	atomic_t num_mcast_sta; /* number of stations receiving multicast */ +	enum ieee80211_smps_mode req_smps, /* requested smps mode */ +			 driver_smps_mode; /* smps mode request */ + +	struct work_struct request_smps_work;  };  struct ieee80211_if_wds { @@ -230,10 +276,10 @@ struct ieee80211_if_wds {  };  struct ieee80211_if_vlan { -	struct list_head list; +	struct list_head list; /* write-protected with RTNL and local->mtx */  	/* used for all tx if the VLAN is configured to 4-addr mode */ -	struct sta_info *sta; +	struct sta_info __rcu *sta;  };  struct mesh_stats { @@ -242,7 +288,7 @@ struct mesh_stats {  	__u32 fwded_frames;		/* Mesh total forwarded frames */  	__u32 dropped_frames_ttl;	/* Not transmitted since mesh_ttl == 0*/  	__u32 dropped_frames_no_route;	/* Not transmitted, no route found */ -	atomic_t estab_plinks; +	__u32 dropped_frames_congestion;/* Not forwarded due to congestion */  };  #define PREQ_Q_F_START		0x1 @@ -253,91 +299,90 @@ struct mesh_preq_queue {  	u8 flags;  }; -enum ieee80211_work_type { -	IEEE80211_WORK_ABORT, -	IEEE80211_WORK_DIRECT_PROBE, -	IEEE80211_WORK_AUTH, -	IEEE80211_WORK_ASSOC_BEACON_WAIT, -	IEEE80211_WORK_ASSOC, -	IEEE80211_WORK_REMAIN_ON_CHANNEL, -}; - -/** - * enum work_done_result - indicates what to do after work was done - * - * @WORK_DONE_DESTROY: This work item is no longer needed, destroy. - * @WORK_DONE_REQUEUE: This work item was reset to be reused, and - *	should be requeued. - */ -enum work_done_result { -	WORK_DONE_DESTROY, -	WORK_DONE_REQUEUE, -}; +#if HZ/100 == 0 +#define IEEE80211_ROC_MIN_LEFT	1 +#else +#define IEEE80211_ROC_MIN_LEFT	(HZ/100) +#endif -struct ieee80211_work { +struct ieee80211_roc_work {  	struct list_head list; +	struct list_head dependents; -	struct rcu_head rcu_head; +	struct delayed_work work;  	struct ieee80211_sub_if_data *sdata; -	enum work_done_result (*done)(struct ieee80211_work *wk, -				      struct sk_buff *skb); -  	struct ieee80211_channel *chan; -	enum nl80211_channel_type chan_type; -	unsigned long timeout; -	enum ieee80211_work_type type; +	bool started, abort, hw_begun, notified; +	bool to_be_freed; +	bool on_channel; -	u8 filter_ta[ETH_ALEN]; +	unsigned long hw_start_time; -	bool started; - -	union { -		struct { -			int tries; -			u16 algorithm, transaction; -			u8 ssid[IEEE80211_MAX_SSID_LEN]; -			u8 ssid_len; -			u8 key[WLAN_KEY_LEN_WEP104]; -			u8 key_len, key_idx; -			bool privacy; -		} probe_auth; -		struct { -			struct cfg80211_bss *bss; -			const u8 *supp_rates; -			const u8 *ht_information_ie; -			enum ieee80211_smps_mode smps; -			int tries; -			u16 capability; -			u8 prev_bssid[ETH_ALEN]; -			u8 ssid[IEEE80211_MAX_SSID_LEN]; -			u8 ssid_len; -			u8 supp_rates_len; -			bool wmm_used, use_11n, uapsd_used; -		} assoc; -		struct { -			u32 duration; -		} remain; -	}; - -	int ie_len; -	/* must be last */ -	u8 ie[0]; +	u32 duration, req_duration; +	struct sk_buff *frame; +	u64 cookie, mgmt_tx_cookie; +	enum ieee80211_roc_type type;  };  /* flags used in struct ieee80211_if_managed.flags */  enum ieee80211_sta_flags { -	IEEE80211_STA_BEACON_POLL	= BIT(0),  	IEEE80211_STA_CONNECTION_POLL	= BIT(1),  	IEEE80211_STA_CONTROL_PORT	= BIT(2), -	IEEE80211_STA_DISABLE_11N	= BIT(4), +	IEEE80211_STA_DISABLE_HT	= BIT(4),  	IEEE80211_STA_CSA_RECEIVED	= BIT(5),  	IEEE80211_STA_MFP_ENABLED	= BIT(6),  	IEEE80211_STA_UAPSD_ENABLED	= BIT(7),  	IEEE80211_STA_NULLFUNC_ACKED	= BIT(8),  	IEEE80211_STA_RESET_SIGNAL_AVE	= BIT(9), +	IEEE80211_STA_DISABLE_40MHZ	= BIT(10), +	IEEE80211_STA_DISABLE_VHT	= BIT(11), +	IEEE80211_STA_DISABLE_80P80MHZ	= BIT(12), +	IEEE80211_STA_DISABLE_160MHZ	= BIT(13), +	IEEE80211_STA_DISABLE_WMM	= BIT(14), +}; + +struct ieee80211_mgd_auth_data { +	struct cfg80211_bss *bss; +	unsigned long timeout; +	int tries; +	u16 algorithm, expected_transaction; + +	u8 key[WLAN_KEY_LEN_WEP104]; +	u8 key_len, key_idx; +	bool done; +	bool timeout_started; + +	u16 sae_trans, sae_status; +	size_t data_len; +	u8 data[]; +}; + +struct ieee80211_mgd_assoc_data { +	struct cfg80211_bss *bss; +	const u8 *supp_rates; + +	unsigned long timeout; +	int tries; + +	u16 capability; +	u8 prev_bssid[ETH_ALEN]; +	u8 ssid[IEEE80211_MAX_SSID_LEN]; +	u8 ssid_len; +	u8 supp_rates_len; +	bool wmm, uapsd; +	bool need_beacon; +	bool synced; +	bool timeout_started; + +	u8 ap_ht_param; + +	struct ieee80211_vht_cap ap_vht_cap; + +	size_t ie_len; +	u8 ie[];  };  struct ieee80211_if_managed { @@ -348,22 +393,27 @@ struct ieee80211_if_managed {  	struct work_struct monitor_work;  	struct work_struct chswitch_work;  	struct work_struct beacon_connection_loss_work; +	struct work_struct csa_connection_drop_work;  	unsigned long beacon_timeout;  	unsigned long probe_timeout;  	int probe_send_count; +	bool nullfunc_failed; +	bool connection_loss; -	struct mutex mtx;  	struct cfg80211_bss *associated; +	struct ieee80211_mgd_auth_data *auth_data; +	struct ieee80211_mgd_assoc_data *assoc_data;  	u8 bssid[ETH_ALEN];  	u16 aid; -	unsigned long timers_running; /* used for quiesce/restart */  	bool powersave; /* powersave requested for this iface */ +	bool broken_ap; /* AP is broken -- turn off powersave */ +	bool have_beacon; +	u8 dtim_period;  	enum ieee80211_smps_mode req_smps, /* requested smps mode */ -				 ap_smps, /* smps mode AP thinks we're in */  				 driver_smps_mode; /* smps mode request */  	struct work_struct request_smps_work; @@ -373,16 +423,36 @@ struct ieee80211_if_managed {  	bool beacon_crc_valid;  	u32 beacon_crc; +	bool status_acked; +	bool status_received; +	__le16 status_fc; +  	enum {  		IEEE80211_MFP_DISABLED,  		IEEE80211_MFP_OPTIONAL,  		IEEE80211_MFP_REQUIRED  	} mfp; /* management frame protection */ +	/* +	 * Bitmask of enabled u-apsd queues, +	 * IEEE80211_WMM_IE_STA_QOSINFO_AC_BE & co. Needs a new association +	 * to take effect. +	 */ +	unsigned int uapsd_queues; + +	/* +	 * Maximum number of buffered frames AP can deliver during a +	 * service period, IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL or similar. +	 * Needs a new association to take effect. +	 */ +	unsigned int uapsd_max_sp_len; +  	int wmm_last_param_set;  	u8 use_4addr; +	s16 p2p_noa_index; +  	/* Signal strength from the last Beacon frame in the current BSS. */  	int last_beacon_signal; @@ -407,32 +477,51 @@ struct ieee80211_if_managed {  	 * generated for the current association.  	 */  	int last_cqm_event_signal; + +	/* +	 * State variables for keeping track of RSSI of the AP currently +	 * connected to and informing driver when RSSI has gone +	 * below/above a certain threshold. +	 */ +	int rssi_min_thold, rssi_max_thold; +	int last_ave_beacon_signal; + +	struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */ +	struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */ +	struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */ +	struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */  };  struct ieee80211_if_ibss {  	struct timer_list timer; - -	struct mutex mtx; +	struct work_struct csa_connection_drop_work;  	unsigned long last_scan_completed;  	u32 basic_rates; -	bool timer_running; -  	bool fixed_bssid;  	bool fixed_channel;  	bool privacy; -	u8 bssid[ETH_ALEN]; +	bool control_port; +	bool userspace_handles_dfs; + +	u8 bssid[ETH_ALEN] __aligned(2);  	u8 ssid[IEEE80211_MAX_SSID_LEN];  	u8 ssid_len, ie_len;  	u8 *ie; -	struct ieee80211_channel *channel; +	struct cfg80211_chan_def chandef;  	unsigned long ibss_join_req;  	/* probe response/beacon for IBSS */ -	struct sk_buff *presp, *skb; +	struct beacon_data __rcu *presp; + +	struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */ +	struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */ + +	spinlock_t incomplete_lock; +	struct list_head incomplete_stations;  	enum {  		IEEE80211_IBSS_MLME_SEARCH, @@ -440,14 +529,39 @@ struct ieee80211_if_ibss {  	} state;  }; +/** + * struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface + * + * these declarations define the interface, which enables + * vendor-specific mesh synchronization + * + */ +struct ieee802_11_elems; +struct ieee80211_mesh_sync_ops { +	void (*rx_bcn_presp)(struct ieee80211_sub_if_data *sdata, +			     u16 stype, +			     struct ieee80211_mgmt *mgmt, +			     struct ieee802_11_elems *elems, +			     struct ieee80211_rx_status *rx_status); + +	/* should be called with beacon_data under RCU read lock */ +	void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata, +			    struct beacon_data *beacon); +	/* add other framework functions here */ +}; + +struct mesh_csa_settings { +	struct rcu_head rcu_head; +	struct cfg80211_csa_settings settings; +}; +  struct ieee80211_if_mesh {  	struct timer_list housekeeping_timer;  	struct timer_list mesh_path_timer;  	struct timer_list mesh_path_root_timer; -	unsigned long timers_running; -  	unsigned long wrkq_flags; +	unsigned long mbss_changed;  	u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];  	size_t mesh_id_len; @@ -468,7 +582,9 @@ struct ieee80211_if_mesh {  	atomic_t mpaths;  	/* Timestamp of last SN update */  	unsigned long last_sn_update; -	/* Timestamp of last SN sent */ +	/* Time when it's ok to send next PERR */ +	unsigned long next_perr; +	/* Timestamp of last PREQ sent */  	unsigned long last_preq;  	struct mesh_rmc *rmc;  	spinlock_t mesh_preq_queue_lock; @@ -476,8 +592,41 @@ struct ieee80211_if_mesh {  	int preq_queue_len;  	struct mesh_stats mshstats;  	struct mesh_config mshcfg; +	atomic_t estab_plinks;  	u32 mesh_seqnum;  	bool accepting_plinks; +	int num_gates; +	struct beacon_data __rcu *beacon; +	const u8 *ie; +	u8 ie_len; +	enum { +		IEEE80211_MESH_SEC_NONE = 0x0, +		IEEE80211_MESH_SEC_AUTHED = 0x1, +		IEEE80211_MESH_SEC_SECURED = 0x2, +	} security; +	bool user_mpm; +	/* Extensible Synchronization Framework */ +	const struct ieee80211_mesh_sync_ops *sync_ops; +	s64 sync_offset_clockdrift_max; +	spinlock_t sync_offset_lock; +	bool adjusting_tbtt; +	/* mesh power save */ +	enum nl80211_mesh_power_mode nonpeer_pm; +	int ps_peers_light_sleep; +	int ps_peers_deep_sleep; +	struct ps_data ps; +	/* Channel Switching Support */ +	struct mesh_csa_settings __rcu *csa; +	enum { +		IEEE80211_MESH_CSA_ROLE_NONE, +		IEEE80211_MESH_CSA_ROLE_INIT, +		IEEE80211_MESH_CSA_ROLE_REPEATER, +	} csa_role; +	u8 chsw_ttl; +	u16 pre_value; + +	/* offset from skb->data while building IE */ +	int meshconf_offset;  };  #ifdef CONFIG_MAC80211_MESH @@ -497,12 +646,16 @@ struct ieee80211_if_mesh {   * @IEEE80211_SDATA_DONT_BRIDGE_PACKETS: bridge packets between   *	associated stations and deliver multicast frames both   *	back to wireless media and to the local net stack. + * @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume. + * @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver   */  enum ieee80211_sub_if_data_flags {  	IEEE80211_SDATA_ALLMULTI		= BIT(0),  	IEEE80211_SDATA_PROMISC			= BIT(1),  	IEEE80211_SDATA_OPERATING_GMODE		= BIT(2),  	IEEE80211_SDATA_DONT_BRIDGE_PACKETS	= BIT(3), +	IEEE80211_SDATA_DISCONNECT_RESUME	= BIT(4), +	IEEE80211_SDATA_IN_DRIVER		= BIT(5),  };  /** @@ -512,10 +665,45 @@ enum ieee80211_sub_if_data_flags {   *	change handling while the interface is up   * @SDATA_STATE_OFFCHANNEL: This interface is currently in offchannel   *	mode, so queues are stopped + * @SDATA_STATE_OFFCHANNEL_BEACON_STOPPED: Beaconing was stopped due + *	to offchannel, reset when offchannel returns   */  enum ieee80211_sdata_state_bits {  	SDATA_STATE_RUNNING,  	SDATA_STATE_OFFCHANNEL, +	SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, +}; + +/** + * enum ieee80211_chanctx_mode - channel context configuration mode + * + * @IEEE80211_CHANCTX_SHARED: channel context may be used by + *	multiple interfaces + * @IEEE80211_CHANCTX_EXCLUSIVE: channel context can be used + *	only by a single interface. This can be used for example for + *	non-fixed channel IBSS. + */ +enum ieee80211_chanctx_mode { +	IEEE80211_CHANCTX_SHARED, +	IEEE80211_CHANCTX_EXCLUSIVE +}; + +struct ieee80211_chanctx { +	struct list_head list; +	struct rcu_head rcu_head; + +	struct list_head assigned_vifs; +	struct list_head reserved_vifs; + +	enum ieee80211_chanctx_mode mode; +	bool driver_present; + +	struct ieee80211_chanctx_conf conf; +}; + +struct mac80211_qos_map { +	struct cfg80211_qos_map qos_map; +	struct rcu_head rcu_head;  };  struct ieee80211_sub_if_data { @@ -526,6 +714,11 @@ struct ieee80211_sub_if_data {  	/* keys */  	struct list_head key_list; +	/* count for keys needing tailroom space allocation */ +	int crypto_tx_tailroom_needed_cnt; +	int crypto_tx_tailroom_pending_dec; +	struct delayed_work dec_tailroom_needed_wk; +  	struct net_device *dev;  	struct ieee80211_local *local; @@ -537,31 +730,59 @@ struct ieee80211_sub_if_data {  	char name[IFNAMSIZ]; -	/* -	 * keep track of whether the HT opmode (stored in -	 * vif.bss_info.ht_operation_mode) is valid. -	 */ -	bool ht_opmode_valid; - -	/* to detect idle changes */ -	bool old_idle; -  	/* Fragment table for host-based reassembly */  	struct ieee80211_fragment_entry	fragments[IEEE80211_FRAGMENT_MAX];  	unsigned int fragment_next; -	struct ieee80211_key *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; -	struct ieee80211_key *default_key; -	struct ieee80211_key *default_mgmt_key; +	/* TID bitmap for NoAck policy */ +	u16 noack_map; + +	/* bit field of ACM bits (BIT(802.1D tag)) */ +	u8 wmm_acm; + +	struct ieee80211_key __rcu *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; +	struct ieee80211_key __rcu *default_unicast_key; +	struct ieee80211_key __rcu *default_multicast_key; +	struct ieee80211_key __rcu *default_mgmt_key;  	u16 sequence_number;  	__be16 control_port_protocol;  	bool control_port_no_encrypt; +	int encrypt_headroom; + +	struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; +	struct mac80211_qos_map __rcu *qos_map; + +	struct work_struct csa_finalize_work; +	u16 csa_counter_offset_beacon[IEEE80211_MAX_CSA_COUNTERS_NUM]; +	u16 csa_counter_offset_presp[IEEE80211_MAX_CSA_COUNTERS_NUM]; +	bool csa_radar_required; +	bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */ +	struct cfg80211_chan_def csa_chandef; + +	struct list_head assigned_chanctx_list; /* protected by chanctx_mtx */ +	struct list_head reserved_chanctx_list; /* protected by chanctx_mtx */ + +	/* context reservation -- protected with chanctx_mtx */ +	struct ieee80211_chanctx *reserved_chanctx; +	struct cfg80211_chan_def reserved_chandef; +	bool reserved_radar_required; +	u8 csa_current_counter; + +	/* used to reconfigure hardware SM PS */ +	struct work_struct recalc_smps;  	struct work_struct work;  	struct sk_buff_head skb_queue; -	bool arp_filter_state; +	u8 needed_rx_chains; +	enum ieee80211_smps_mode smps_mode; + +	int user_power_level; /* in dBm */ +	int ap_power_level; /* in dBm */ + +	bool radar_required; +	struct delayed_work dfs_cac_timer_work;  	/*  	 * AP this belongs to: self in AP mode and @@ -573,26 +794,28 @@ struct ieee80211_sub_if_data {  	/* bitmap of allowed (non-MCS) rate indexes for rate control */  	u32 rc_rateidx_mask[IEEE80211_NUM_BANDS]; +	bool rc_has_mcs_mask[IEEE80211_NUM_BANDS]; +	u8  rc_rateidx_mcs_mask[IEEE80211_NUM_BANDS][IEEE80211_HT_MCS_MASK_LEN]; +  	union {  		struct ieee80211_if_ap ap;  		struct ieee80211_if_wds wds;  		struct ieee80211_if_vlan vlan;  		struct ieee80211_if_managed mgd;  		struct ieee80211_if_ibss ibss; -#ifdef CONFIG_MAC80211_MESH  		struct ieee80211_if_mesh mesh; -#endif  		u32 mntr_flags;  	} u;  #ifdef CONFIG_MAC80211_DEBUGFS  	struct { -		struct dentry *dir;  		struct dentry *subdir_stations; -		struct dentry *default_key; +		struct dentry *default_unicast_key; +		struct dentry *default_multicast_key;  		struct dentry *default_mgmt_key;  	} debugfs;  #endif +  	/* must be last, dynamically sized area in this! */  	struct ieee80211_vif vif;  }; @@ -603,17 +826,70 @@ struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p)  	return container_of(p, struct ieee80211_sub_if_data, vif);  } +static inline void sdata_lock(struct ieee80211_sub_if_data *sdata) +	__acquires(&sdata->wdev.mtx) +{ +	mutex_lock(&sdata->wdev.mtx); +	__acquire(&sdata->wdev.mtx); +} + +static inline void sdata_unlock(struct ieee80211_sub_if_data *sdata) +	__releases(&sdata->wdev.mtx) +{ +	mutex_unlock(&sdata->wdev.mtx); +	__release(&sdata->wdev.mtx); +} + +#define sdata_dereference(p, sdata) \ +	rcu_dereference_protected(p, lockdep_is_held(&sdata->wdev.mtx)) +  static inline void -ieee80211_sdata_set_mesh_id(struct ieee80211_sub_if_data *sdata, -			    u8 mesh_id_len, u8 *mesh_id) +sdata_assert_lock(struct ieee80211_sub_if_data *sdata)  { -#ifdef CONFIG_MAC80211_MESH -	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; -	ifmsh->mesh_id_len = mesh_id_len; -	memcpy(ifmsh->mesh_id, mesh_id, mesh_id_len); -#else -	WARN_ON(1); -#endif +	lockdep_assert_held(&sdata->wdev.mtx); +} + +static inline enum ieee80211_band +ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata) +{ +	enum ieee80211_band band = IEEE80211_BAND_2GHZ; +	struct ieee80211_chanctx_conf *chanctx_conf; + +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	if (!WARN_ON(!chanctx_conf)) +		band = chanctx_conf->def.chan->band; +	rcu_read_unlock(); + +	return band; +} + +static inline int +ieee80211_chandef_get_shift(struct cfg80211_chan_def *chandef) +{ +	switch (chandef->width) { +	case NL80211_CHAN_WIDTH_5: +		return 2; +	case NL80211_CHAN_WIDTH_10: +		return 1; +	default: +		return 0; +	} +} + +static inline int +ieee80211_vif_get_shift(struct ieee80211_vif *vif) +{ +	struct ieee80211_chanctx_conf *chanctx_conf; +	int shift = 0; + +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(vif->chanctx_conf); +	if (chanctx_conf) +		shift = ieee80211_chandef_get_shift(&chanctx_conf->def); +	rcu_read_unlock(); + +	return shift;  }  enum sdata_queue_type { @@ -634,6 +910,40 @@ enum queue_stop_reason {  	IEEE80211_QUEUE_STOP_REASON_AGGREGATION,  	IEEE80211_QUEUE_STOP_REASON_SUSPEND,  	IEEE80211_QUEUE_STOP_REASON_SKB_ADD, +	IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, +	IEEE80211_QUEUE_STOP_REASON_FLUSH, +}; + +#ifdef CONFIG_MAC80211_LEDS +struct tpt_led_trigger { +	struct led_trigger trig; +	char name[32]; +	const struct ieee80211_tpt_blink *blink_table; +	unsigned int blink_table_len; +	struct timer_list timer; +	unsigned long prev_traffic; +	unsigned long tx_bytes, rx_bytes; +	unsigned int active, want; +	bool running; +}; +#endif + +/* + * struct ieee80211_tx_latency_bin_ranges - Tx latency statistics bins ranges + * + * Measuring Tx latency statistics. Counts how many Tx frames transmitted in a + * certain latency range (in Milliseconds). Each station that uses these + * ranges will have bins to count the amount of frames received in that range. + * The user can configure the ranges via debugfs. + * If ranges is NULL then Tx latency statistics bins are disabled for all + * stations. + * + * @n_ranges: number of ranges that are taken in account + * @ranges: the ranges that the user requested or NULL if disabled. + */ +struct ieee80211_tx_latency_bin_ranges { +	int n_ranges; +	u32 ranges[];  };  /** @@ -643,19 +953,22 @@ enum queue_stop_reason {   *	well be on the operating channel   * @SCAN_HW_SCANNING: The hardware is scanning for us, we have no way to   *	determine if we are on the operating channel or not - * @SCAN_OFF_CHANNEL: We're off our operating channel for scanning, - *	gets only set in conjunction with SCAN_SW_SCANNING + * @SCAN_ONCHANNEL_SCANNING:  Do a software scan on only the current operating + *	channel. This should not interrupt normal traffic.   * @SCAN_COMPLETED: Set for our scan work function when the driver reported   *	that the scan completed.   * @SCAN_ABORTED: Set for our scan work function when the driver reported   *	a scan complete for an aborted scan. + * @SCAN_HW_CANCELLED: Set for our scan work function when the scan is being + *	cancelled.   */  enum {  	SCAN_SW_SCANNING,  	SCAN_HW_SCANNING, -	SCAN_OFF_CHANNEL, +	SCAN_ONCHANNEL_SCANNING,  	SCAN_COMPLETED,  	SCAN_ABORTED, +	SCAN_HW_CANCELLED,  };  /** @@ -666,17 +979,18 @@ enum {   *	operating channel   * @SCAN_SET_CHANNEL: Set the next channel to be scanned   * @SCAN_SEND_PROBE: Send probe requests and wait for probe responses - * @SCAN_LEAVE_OPER_CHANNEL: Leave the operating channel, notify the AP - *	about us leaving the channel and stop all associated STA interfaces - * @SCAN_ENTER_OPER_CHANNEL: Enter the operating channel again, notify the - *	AP about us being back and restart all associated STA interfaces + * @SCAN_SUSPEND: Suspend the scan and go back to operating channel to + *	send out data + * @SCAN_RESUME: Resume the scan and scan the next channel + * @SCAN_ABORT: Abort the scan and go back to operating channel   */  enum mac80211_scan_state {  	SCAN_DECISION,  	SCAN_SET_CHANNEL,  	SCAN_SEND_PROBE, -	SCAN_LEAVE_OPER_CHANNEL, -	SCAN_ENTER_OPER_CHANNEL, +	SCAN_SUSPEND, +	SCAN_RESUME, +	SCAN_ABORT,  };  struct ieee80211_local { @@ -688,14 +1002,6 @@ struct ieee80211_local {  	const struct ieee80211_ops *ops;  	/* -	 * work stuff, potentially off-channel (in the future) -	 */ -	struct list_head work_list; -	struct timer_list work_timer; -	struct work_struct work_work; -	struct sk_buff_head work_skb_queue; - -	/*  	 * private workqueue to mac80211. mac80211 makes this accessible  	 * via ieee80211_queue_work()  	 */ @@ -715,15 +1021,14 @@ struct ieee80211_local {  	bool wiphy_ciphers_allocated; +	bool use_chanctx; +  	/* protects the aggregated multicast list and filter calls */  	spinlock_t filter_lock;  	/* used for uploading changed mc list */  	struct work_struct reconfig_filter; -	/* used to reconfigure hardware SM PS */ -	struct work_struct recalc_smps; -  	/* aggregated multicast list */  	struct netdev_hw_addr_list mc_list; @@ -754,6 +1059,19 @@ struct ieee80211_local {  	/* device is started */  	bool started; +	/* device is during a HW reconfig */ +	bool in_reconfig; + +	/* wowlan is enabled -- don't reconfig on resume */ +	bool wowlan; + +	/* DFS/radar detection is enabled */ +	bool radar_detect_enabled; +	struct work_struct radar_detected_work; + +	/* number of RX chains the hardware has */ +	u8 rx_chains; +  	int tx_headroom; /* required headroom for hardware/radiotap */  	/* Tasklet and skb queue to process calls from IRQ mode. All frames @@ -765,22 +1083,27 @@ struct ieee80211_local {  	struct sk_buff_head skb_queue;  	struct sk_buff_head skb_queue_unreliable; +	spinlock_t rx_path_lock; +  	/* Station data */  	/* -	 * The mutex only protects the list and counter, -	 * reads are done in RCU. -	 * Additionally, the lock protects the hash table, -	 * the pending list and each BSS's TIM bitmap. +	 * The mutex only protects the list, hash table and +	 * counter, reads are done with RCU.  	 */  	struct mutex sta_mtx; -	spinlock_t sta_lock; +	spinlock_t tim_lock;  	unsigned long num_sta; -	struct list_head sta_list, sta_pending_list; -	struct sta_info *sta_hash[STA_HASH_SIZE]; +	struct list_head sta_list; +	struct sta_info __rcu *sta_hash[STA_HASH_SIZE];  	struct timer_list sta_cleanup; -	struct work_struct sta_finish_work;  	int sta_generation; +	/* +	 * Tx latency statistics parameters for all stations. +	 * Can enable via debugfs (NULL when disabled). +	 */ +	struct ieee80211_tx_latency_bin_ranges __rcu *tx_latency; +  	struct sk_buff_head pending[IEEE80211_MAX_QUEUES];  	struct tasklet_struct tx_pending_tasklet; @@ -791,8 +1114,8 @@ struct ieee80211_local {  	struct rate_control_ref *rate_ctrl; -	struct crypto_blkcipher *wep_tx_tfm; -	struct crypto_blkcipher *wep_rx_tfm; +	struct crypto_cipher *wep_tx_tfm; +	struct crypto_cipher *wep_rx_tfm;  	u32 wep_iv;  	/* see iface.c */ @@ -813,21 +1136,29 @@ struct ieee80211_local {  	struct cfg80211_ssid scan_ssid;  	struct cfg80211_scan_request *int_scan_req;  	struct cfg80211_scan_request *scan_req, *hw_scan_req; -	struct ieee80211_channel *scan_channel; +	struct cfg80211_chan_def scan_chandef;  	enum ieee80211_band hw_scan_band;  	int scan_channel_idx;  	int scan_ies_len; +	int hw_scan_ies_bufsize; + +	struct work_struct sched_scan_stopped_work; +	struct ieee80211_sub_if_data __rcu *sched_scan_sdata; +	struct cfg80211_sched_scan_request *sched_scan_req;  	unsigned long leave_oper_channel_time;  	enum mac80211_scan_state next_scan_state;  	struct delayed_work scan_work; -	struct ieee80211_sub_if_data *scan_sdata; -	enum nl80211_channel_type _oper_channel_type; -	struct ieee80211_channel *oper_channel, *csa_channel; +	struct ieee80211_sub_if_data __rcu *scan_sdata; +	/* For backward compatibility only -- do not use */ +	struct cfg80211_chan_def _oper_chandef;  	/* Temporary remain-on-channel for off-channel operations */  	struct ieee80211_channel *tmp_channel; -	enum nl80211_channel_type tmp_channel_type; + +	/* channel contexts */ +	struct list_head chanctx_list; +	struct mutex chanctx_mtx;  	/* SNMP counters */  	/* dot11CountersTable */ @@ -842,8 +1173,8 @@ struct ieee80211_local {  	u32 dot11TransmittedFrameCount;  #ifdef CONFIG_MAC80211_LEDS -	int tx_led_counter, rx_led_counter;  	struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led; +	struct tpt_led_trigger *tpt_led_trigger;  	char tx_led_name[32], rx_led_name[32],  	     assoc_led_name[32], radio_led_name[32];  #endif @@ -862,7 +1193,6 @@ struct ieee80211_local {  	unsigned int rx_handlers_drop_nullfunc;  	unsigned int rx_handlers_drop_defrag;  	unsigned int rx_handlers_drop_short; -	unsigned int rx_handlers_drop_passive_scan;  	unsigned int tx_expand_skb_head;  	unsigned int tx_expand_skb_head_cloned;  	unsigned int rx_expand_skb_head; @@ -878,22 +1208,6 @@ struct ieee80211_local {  	int total_ps_buffered; /* total number of all buffered unicast and  				* multicast packets for power saving stations  				*/ -	int wifi_wme_noack_test; -	unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */ - -	/* -	 * Bitmask of enabled u-apsd queues, -	 * IEEE80211_WMM_IE_STA_QOSINFO_AC_BE & co. Needs a new association -	 * to take effect. -	 */ -	unsigned int uapsd_queues; - -	/* -	 * Maximum number of buffered frames AP can deliver during a -	 * service period, IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL or similar. -	 * Needs a new association to take effect. -	 */ -	unsigned int uapsd_max_sp_len;  	bool pspolling;  	bool offchannel_ps_enabled; @@ -907,17 +1221,15 @@ struct ieee80211_local {  	struct timer_list dynamic_ps_timer;  	struct notifier_block network_latency_notifier;  	struct notifier_block ifa_notifier; +	struct notifier_block ifa6_notifier;  	/*  	 * The dynamic ps timeout configured from user space via WEXT -  	 * this will override whatever chosen by mac80211 internally.  	 */  	int dynamic_ps_forced_timeout; -	int dynamic_ps_user_timeout; -	bool disable_dynamic_ps; -	int user_power_level; /* in dBm */ -	int power_constr_level; /* in dBm */ +	int user_power_level; /* in dBm, for all interfaces */  	enum ieee80211_smps_mode smps_mode; @@ -930,10 +1242,24 @@ struct ieee80211_local {  	} debugfs;  #endif -	/* dummy netdev for use w/ NAPI */ -	struct net_device napi_dev; +	/* +	 * Remain-on-channel support +	 */ +	struct list_head roc_list; +	struct work_struct hw_roc_start, hw_roc_done; +	unsigned long hw_roc_start_time; +	u64 roc_cookie_counter; + +	struct idr ack_status_frames; +	spinlock_t ack_status_lock; + +	struct ieee80211_sub_if_data __rcu *p2p_sdata; -	struct napi_struct napi; +	struct napi_struct *napi; + +	/* virtual monitor interface */ +	struct ieee80211_sub_if_data __rcu *monitor_sdata; +	struct cfg80211_chan_def monitor_chandef;  };  static inline struct ieee80211_sub_if_data * @@ -942,73 +1268,83 @@ IEEE80211_DEV_TO_SUB_IF(struct net_device *dev)  	return netdev_priv(dev);  } +static inline struct ieee80211_sub_if_data * +IEEE80211_WDEV_TO_SUB_IF(struct wireless_dev *wdev) +{ +	return container_of(wdev, struct ieee80211_sub_if_data, wdev); +} +  /* this struct represents 802.11n's RA/TID combination */  struct ieee80211_ra_tid {  	u8 ra[ETH_ALEN];  	u16 tid;  }; +/* this struct holds the value parsing from channel switch IE  */ +struct ieee80211_csa_ie { +	struct cfg80211_chan_def chandef; +	u8 mode; +	u8 count; +	u8 ttl; +	u16 pre_value; +}; +  /* Parsed Information Elements */  struct ieee802_11_elems { -	u8 *ie_start; +	const u8 *ie_start;  	size_t total_len;  	/* pointers to IEs */ -	u8 *ssid; -	u8 *supp_rates; -	u8 *fh_params; -	u8 *ds_params; -	u8 *cf_params; -	struct ieee80211_tim_ie *tim; -	u8 *ibss_params; -	u8 *challenge; -	u8 *wpa; -	u8 *rsn; -	u8 *erp_info; -	u8 *ext_supp_rates; -	u8 *wmm_info; -	u8 *wmm_param; -	struct ieee80211_ht_cap *ht_cap_elem; -	struct ieee80211_ht_info *ht_info_elem; -	struct ieee80211_meshconf_ie *mesh_config; -	u8 *mesh_id; -	u8 *peer_link; -	u8 *preq; -	u8 *prep; -	u8 *perr; -	struct ieee80211_rann_ie *rann; -	u8 *ch_switch_elem; -	u8 *country_elem; -	u8 *pwr_constr_elem; -	u8 *quiet_elem; 	/* first quite element */ -	u8 *timeout_int; +	const u8 *ssid; +	const u8 *supp_rates; +	const u8 *ds_params; +	const struct ieee80211_tim_ie *tim; +	const u8 *challenge; +	const u8 *rsn; +	const u8 *erp_info; +	const u8 *ext_supp_rates; +	const u8 *wmm_info; +	const u8 *wmm_param; +	const struct ieee80211_ht_cap *ht_cap_elem; +	const struct ieee80211_ht_operation *ht_operation; +	const struct ieee80211_vht_cap *vht_cap_elem; +	const struct ieee80211_vht_operation *vht_operation; +	const struct ieee80211_meshconf_ie *mesh_config; +	const u8 *mesh_id; +	const u8 *peering; +	const __le16 *awake_window; +	const u8 *preq; +	const u8 *prep; +	const u8 *perr; +	const struct ieee80211_rann_ie *rann; +	const struct ieee80211_channel_sw_ie *ch_switch_ie; +	const struct ieee80211_ext_chansw_ie *ext_chansw_ie; +	const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; +	const u8 *country_elem; +	const u8 *pwr_constr_elem; +	const struct ieee80211_timeout_interval_ie *timeout_int; +	const u8 *opmode_notif; +	const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; +	const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie;  	/* length of them, respectively */  	u8 ssid_len;  	u8 supp_rates_len; -	u8 fh_params_len; -	u8 ds_params_len; -	u8 cf_params_len;  	u8 tim_len; -	u8 ibss_params_len;  	u8 challenge_len; -	u8 wpa_len;  	u8 rsn_len; -	u8 erp_info_len;  	u8 ext_supp_rates_len;  	u8 wmm_info_len;  	u8 wmm_param_len;  	u8 mesh_id_len; -	u8 peer_link_len; +	u8 peering_len;  	u8 preq_len;  	u8 prep_len;  	u8 perr_len; -	u8 ch_switch_elem_len;  	u8 country_elem_len; -	u8 pwr_constr_elem_len; -	u8 quiet_elem_len; -	u8 num_of_quiet_elem;	/* can be more the one */ -	u8 timeout_int_len; + +	/* whether a parse error occurred while retrieving these elements */ +	bool parse_error;  };  static inline struct ieee80211_local *hw_to_local( @@ -1017,20 +1353,25 @@ static inline struct ieee80211_local *hw_to_local(  	return container_of(hw, struct ieee80211_local, hw);  } -static inline struct ieee80211_hw *local_to_hw( -	struct ieee80211_local *local) -{ -	return &local->hw; -} -  static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)  { -	return compare_ether_addr(raddr, addr) == 0 || +	return ether_addr_equal(raddr, addr) ||  	       is_broadcast_ether_addr(raddr);  } +static inline bool +ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status) +{ +	WARN_ON_ONCE(status->flag & RX_FLAG_MACTIME_START && +		     status->flag & RX_FLAG_MACTIME_END); +	return status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END); +} +u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, +				     struct ieee80211_rx_status *status, +				     unsigned int mpdu_len, +				     unsigned int mpdu_offset);  int ieee80211_hw_config(struct ieee80211_local *local, u32 changed);  void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);  void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, @@ -1038,8 +1379,6 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,  void ieee80211_configure_filter(struct ieee80211_local *local);  u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata); -extern bool ieee80211_disable_40mhz_24ghz; -  /* STA code */  void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata);  int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, @@ -1047,59 +1386,62 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,  int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  			struct cfg80211_assoc_request *req);  int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, -			 struct cfg80211_deauth_request *req, -			 void *cookie); +			 struct cfg80211_deauth_request *req);  int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, -			   struct cfg80211_disassoc_request *req, -			   void *cookie); +			   struct cfg80211_disassoc_request *req);  void ieee80211_send_pspoll(struct ieee80211_local *local,  			   struct ieee80211_sub_if_data *sdata);  void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency); +void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata);  int ieee80211_max_network_latency(struct notifier_block *nb,  				  unsigned long data, void *dummy);  int ieee80211_set_arp_filter(struct ieee80211_sub_if_data *sdata); -void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, -				      struct ieee80211_channel_sw_ie *sw_elem, -				      struct ieee80211_bss *bss, -				      u64 timestamp); -void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata); -void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);  void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata);  void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  				  struct sk_buff *skb);  void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata);  void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata); +void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata); +void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata, +				  __le16 fc, bool acked); +void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata); +void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);  /* IBSS code */  void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);  void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata); -struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, -					u8 *bssid, u8 *addr, u32 supp_rates, -					gfp_t gfp); +void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, +			      const u8 *bssid, const u8 *addr, u32 supp_rates);  int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,  			struct cfg80211_ibss_params *params);  int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata); -void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata); -void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata);  void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata);  void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  				   struct sk_buff *skb); +int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, +			      struct cfg80211_csa_settings *csa_settings); +int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata); +void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);  /* mesh code */  void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);  void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  				   struct sk_buff *skb); +int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, +			      struct cfg80211_csa_settings *csa_settings); +int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);  /* scan/BSS handling */  void ieee80211_scan_work(struct work_struct *work); -int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, -				    const u8 *ssid, u8 ssid_len, -				    struct ieee80211_channel *chan); +int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, +				const u8 *ssid, u8 ssid_len, +				struct ieee80211_channel *chan, +				enum nl80211_bss_scan_width scan_width);  int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,  			   struct cfg80211_scan_request *req);  void ieee80211_scan_cancel(struct ieee80211_local *local); -ieee80211_rx_result -ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); +void ieee80211_run_deferred_scan(struct ieee80211_local *local); +void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb);  void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local);  struct ieee80211_bss * @@ -1108,34 +1450,58 @@ ieee80211_bss_info_update(struct ieee80211_local *local,  			  struct ieee80211_mgmt *mgmt,  			  size_t len,  			  struct ieee802_11_elems *elems, -			  struct ieee80211_channel *channel, -			  bool beacon); -struct ieee80211_bss * -ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, -		     u8 *ssid, u8 ssid_len); +			  struct ieee80211_channel *channel);  void ieee80211_rx_bss_put(struct ieee80211_local *local,  			  struct ieee80211_bss *bss); +/* scheduled scan handling */ +int +__ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, +				     struct cfg80211_sched_scan_request *req); +int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, +				       struct cfg80211_sched_scan_request *req); +int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata); +void ieee80211_sched_scan_end(struct ieee80211_local *local); +void ieee80211_sched_scan_stopped_work(struct work_struct *work); +  /* off-channel helpers */ -void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local); -void ieee80211_offchannel_stop_station(struct ieee80211_local *local); -void ieee80211_offchannel_return(struct ieee80211_local *local, -				 bool enable_beaconing); +void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local); +void ieee80211_offchannel_return(struct ieee80211_local *local); +void ieee80211_roc_setup(struct ieee80211_local *local); +void ieee80211_start_next_roc(struct ieee80211_local *local); +void ieee80211_roc_purge(struct ieee80211_local *local, +			 struct ieee80211_sub_if_data *sdata); +void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free); +void ieee80211_sw_roc_work(struct work_struct *work); +void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); + +/* channel switch handling */ +bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local); +void ieee80211_csa_finalize_work(struct work_struct *work); +int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, +			     struct cfg80211_csa_settings *params);  /* interface handling */  int ieee80211_iface_init(void);  void ieee80211_iface_exit(void);  int ieee80211_if_add(struct ieee80211_local *local, const char *name, -		     struct net_device **new_dev, enum nl80211_iftype type, +		     struct wireless_dev **new_wdev, enum nl80211_iftype type,  		     struct vif_params *params);  int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,  			     enum nl80211_iftype type);  void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata);  void ieee80211_remove_interfaces(struct ieee80211_local *local); -u32 __ieee80211_recalc_idle(struct ieee80211_local *local); +u32 ieee80211_idle_off(struct ieee80211_local *local);  void ieee80211_recalc_idle(struct ieee80211_local *local);  void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,  				    const int offset); +int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up); +void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata); +int ieee80211_add_virtual_monitor(struct ieee80211_local *local); +void ieee80211_del_virtual_monitor(struct ieee80211_local *local); + +bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); +void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);  static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)  { @@ -1149,37 +1515,33 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,  					 struct net_device *dev);  netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  				       struct net_device *dev); - -/* - * radiotap header for status frames - */ -struct ieee80211_tx_status_rtap_hdr { -	struct ieee80211_radiotap_header hdr; -	u8 rate; -	u8 padding_for_rate; -	__le16 tx_flags; -	u8 data_retries; -} __packed; - +void ieee80211_purge_tx_queue(struct ieee80211_hw *hw, +			      struct sk_buff_head *skbs);  /* HT */ -void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, -				       struct ieee80211_ht_cap *ht_cap_ie, -				       struct ieee80211_sta_ht_cap *ht_cap); -void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn); +void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, +				     struct ieee80211_sta_ht_cap *ht_cap); +bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, +				       struct ieee80211_supported_band *sband, +				       const struct ieee80211_ht_cap *ht_cap_ie, +				       struct sta_info *sta);  void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,  			  const u8 *da, u16 tid,  			  u16 initiator, u16 reason_code);  int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,  			       enum ieee80211_smps_mode smps, const u8 *da,  			       const u8 *bssid); -void ieee80211_request_smps_work(struct work_struct *work); +void ieee80211_request_smps_ap_work(struct work_struct *work); +void ieee80211_request_smps_mgd_work(struct work_struct *work); +bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, +				   enum ieee80211_smps_mode smps_mode_new);  void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,  				     u16 initiator, u16 reason, bool stop);  void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,  				    u16 initiator, u16 reason, bool stop); -void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx); +void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, +					 enum ieee80211_agg_stop_reason reason);  void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,  			     struct sta_info *sta,  			     struct ieee80211_mgmt *mgmt, size_t len); @@ -1193,28 +1555,67 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,  				     size_t len);  int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, -				   enum ieee80211_back_parties initiator, -				   bool tx); +				   enum ieee80211_agg_stop_reason reason);  int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, -				    enum ieee80211_back_parties initiator, -				    bool tx); +				    enum ieee80211_agg_stop_reason reason);  void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid);  void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid);  void ieee80211_ba_session_work(struct work_struct *work);  void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid);  void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid); +u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs); + +/* VHT */ +void +ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, +				    struct ieee80211_supported_band *sband, +				    const struct ieee80211_vht_cap *vht_cap_ie, +				    struct sta_info *sta); +enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta); +void ieee80211_sta_set_rx_nss(struct sta_info *sta); +u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, +                                  struct sta_info *sta, u8 opmode, +                                  enum ieee80211_band band, bool nss_only); +void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, +				 struct sta_info *sta, u8 opmode, +				 enum ieee80211_band band, bool nss_only); +void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata, +				      struct ieee80211_sta_vht_cap *vht_cap); +  /* Spectrum management */  void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,  				       struct ieee80211_mgmt *mgmt,  				       size_t len); +/** + * ieee80211_parse_ch_switch_ie - parses channel switch IEs + * @sdata: the sdata of the interface which has received the frame + * @elems: parsed 802.11 elements received with the frame + * @beacon: indicates if the frame was a beacon or probe response + * @current_band: indicates the current band + * @sta_flags: contains information about own capabilities and restrictions + *	to decide which channel switch announcements can be accepted. Only the + *	following subset of &enum ieee80211_sta_flags are evaluated: + *	%IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT, + *	%IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ, + *	%IEEE80211_STA_DISABLE_160MHZ. + * @bssid: the currently connected bssid (for reporting) + * @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl. +	All of them will be filled with if success only. + * Return: 0 on success, <0 on error and >0 if there is nothing to parse. + */ +int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, +				 struct ieee802_11_elems *elems, bool beacon, +				 enum ieee80211_band current_band, +				 u32 sta_flags, u8 *bssid, +				 struct ieee80211_csa_ie *csa_ie);  /* Suspend/resume and hw reconfiguration */  int ieee80211_reconfig(struct ieee80211_local *local);  void ieee80211_stop_device(struct ieee80211_local *local); -#ifdef CONFIG_PM -int __ieee80211_suspend(struct ieee80211_hw *hw); +int __ieee80211_suspend(struct ieee80211_hw *hw, +			struct cfg80211_wowlan *wowlan);  static inline int __ieee80211_resume(struct ieee80211_hw *hw)  { @@ -1226,36 +1627,70 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)  	return ieee80211_reconfig(hw_to_local(hw));  } -#else -static inline int __ieee80211_suspend(struct ieee80211_hw *hw) -{ -	return 0; -} - -static inline int __ieee80211_resume(struct ieee80211_hw *hw) -{ -	return 0; -} -#endif  /* utility functions/constants */ -extern void *mac80211_wiphy_privid; /* for wiphy privid */ +extern const void *const mac80211_wiphy_privid; /* for wiphy privid */  u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,  			enum nl80211_iftype type); -int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, -			     int rate, int erp, int short_preamble); +int ieee80211_frame_duration(enum ieee80211_band band, size_t len, +			     int rate, int erp, int short_preamble, +			     int shift);  void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,  				     struct ieee80211_hdr *hdr, const u8 *tsc,  				     gfp_t gfp); -void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata); -void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); -void ieee802_11_parse_elems(u8 *start, size_t len, -			    struct ieee802_11_elems *elems); -u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, +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, +		    enum ieee80211_band band); + +void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, +				 struct sk_buff *skb, int tid, +				 enum ieee80211_band band); + +static inline void +ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, +			  struct sk_buff *skb, int tid, +			  enum ieee80211_band band) +{ +	rcu_read_lock(); +	__ieee80211_tx_skb_tid_band(sdata, skb, tid, band); +	rcu_read_unlock(); +} + +static inline void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, +					struct sk_buff *skb, int tid) +{ +	struct ieee80211_chanctx_conf *chanctx_conf; + +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	if (WARN_ON(!chanctx_conf)) { +		rcu_read_unlock(); +		kfree_skb(skb); +		return; +	} + +	__ieee80211_tx_skb_tid_band(sdata, skb, tid, +				    chanctx_conf->def.chan->band); +	rcu_read_unlock(); +} + +static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, +				    struct sk_buff *skb) +{ +	/* Send all internal mgmt frames on VO. Accordingly set TID to 7. */ +	ieee80211_tx_skb_tid(sdata, skb, 7); +} + +u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,  			       struct ieee802_11_elems *elems,  			       u64 filter, u32 crc); -u32 ieee80211_mandatory_rates(struct ieee80211_local *local, -			      enum ieee80211_band band); +static inline void ieee802_11_parse_elems(const u8 *start, size_t len, +					  bool action, +					  struct ieee802_11_elems *elems) +{ +	ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0); +}  void ieee80211_dynamic_ps_enable_work(struct work_struct *work);  void ieee80211_dynamic_ps_disable_work(struct work_struct *work); @@ -1266,82 +1701,151 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,  void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,  			     struct ieee80211_hdr *hdr);  void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, -			     struct ieee80211_hdr *hdr); -void ieee80211_beacon_connection_loss_work(struct work_struct *work); +			     struct ieee80211_hdr *hdr, bool ack);  void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, +				     unsigned long queues,  				     enum queue_stop_reason reason);  void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, +				     unsigned long queues,  				     enum queue_stop_reason reason);  void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,  				    enum queue_stop_reason reason);  void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,  				    enum queue_stop_reason reason); +void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue);  void ieee80211_add_pending_skb(struct ieee80211_local *local,  			       struct sk_buff *skb); -int ieee80211_add_pending_skbs(struct ieee80211_local *local, -			       struct sk_buff_head *skbs); -int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, -				  struct sk_buff_head *skbs, -				  void (*fn)(void *data), void *data); +void ieee80211_add_pending_skbs(struct ieee80211_local *local, +				struct sk_buff_head *skbs); +void ieee80211_flush_queues(struct ieee80211_local *local, +			    struct ieee80211_sub_if_data *sdata);  void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, -			 u16 transaction, u16 auth_alg, -			 u8 *extra, size_t extra_len, const u8 *bssid, -			 const u8 *key, u8 key_len, u8 key_idx); +			 u16 transaction, u16 auth_alg, u16 status, +			 const u8 *extra, size_t extra_len, const u8 *bssid, +			 const u8 *da, const u8 *key, u8 key_len, u8 key_idx, +			 u32 tx_flags); +void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, +				    const u8 *bssid, u16 stype, u16 reason, +				    bool send_frame, u8 *frame_buf);  int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, -			     const u8 *ie, size_t ie_len, +			     size_t buffer_len, const u8 *ie, size_t ie_len,  			     enum ieee80211_band band, u32 rate_mask, -			     u8 channel); +			     struct cfg80211_chan_def *chandef);  struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, -					  u8 *dst, +					  u8 *dst, u32 ratemask, +					  struct ieee80211_channel *chan,  					  const u8 *ssid, size_t ssid_len, -					  const u8 *ie, size_t ie_len); +					  const u8 *ie, size_t ie_len, +					  bool directed);  void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,  			      const u8 *ssid, size_t ssid_len, -			      const u8 *ie, size_t ie_len); +			      const u8 *ie, size_t ie_len, +			      u32 ratemask, bool directed, u32 tx_flags, +			      struct ieee80211_channel *channel, bool scan); -void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, -				  const size_t supp_rates_len, -				  const u8 *supp_rates); -u32 ieee80211_sta_get_rates(struct ieee80211_local *local, +u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,  			    struct ieee802_11_elems *elems, -			    enum ieee80211_band band); -int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, -			     enum ieee80211_smps_mode smps_mode); -void ieee80211_recalc_smps(struct ieee80211_local *local); +			    enum ieee80211_band band, u32 *basic_rates); +int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, +				 enum ieee80211_smps_mode smps_mode); +int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, +				enum ieee80211_smps_mode smps_mode); +void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); +void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata);  size_t ieee80211_ie_split(const u8 *ies, size_t ielen,  			  const u8 *ids, int n_ids, size_t offset);  size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset); - -/* internal work items */ -void ieee80211_work_init(struct ieee80211_local *local); -void ieee80211_add_work(struct ieee80211_work *wk); -void free_work(struct ieee80211_work *wk); -void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata); -ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata, -					   struct sk_buff *skb); -int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, -				   struct ieee80211_channel *chan, -				   enum nl80211_channel_type channel_type, -				   unsigned int duration, u64 *cookie); -int ieee80211_wk_cancel_remain_on_channel( -	struct ieee80211_sub_if_data *sdata, u64 cookie); +u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, +			      u16 cap); +u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, +			       const struct cfg80211_chan_def *chandef, +			       u16 prot_mode); +u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, +			       u32 cap); +int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef, +			     const struct ieee80211_supported_band *sband, +			     const u8 *srates, int srates_len, u32 *rates); +int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, +			    struct sk_buff *skb, bool need_basic, +			    enum ieee80211_band band); +int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, +				struct sk_buff *skb, bool need_basic, +				enum ieee80211_band band);  /* channel management */ -enum ieee80211_chan_mode { -	CHAN_MODE_UNDEFINED, -	CHAN_MODE_HOPPING, -	CHAN_MODE_FIXED, -}; +void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, +				  const struct ieee80211_ht_operation *ht_oper, +				  struct cfg80211_chan_def *chandef); +u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c); + +int __must_check +ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, +			  const struct cfg80211_chan_def *chandef, +			  enum ieee80211_chanctx_mode mode); +int __must_check +ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, +			      const struct cfg80211_chan_def *chandef, +			      enum ieee80211_chanctx_mode mode, +			      bool radar_required); +int __must_check +ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata, +				   u32 *changed); +int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata); + +int __must_check +ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, +			       const struct cfg80211_chan_def *chandef, +			       u32 *changed); +/* NOTE: only use ieee80211_vif_change_channel() for channel switch */ +int __must_check +ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, +			     u32 *changed); +void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); +void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata); +void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, +					 bool clear); +int ieee80211_chanctx_refcount(struct ieee80211_local *local, +			       struct ieee80211_chanctx *ctx); + +void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, +				   struct ieee80211_chanctx *chanctx); +void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, +				      struct ieee80211_chanctx *ctx); + +void ieee80211_dfs_cac_timer(unsigned long data); +void ieee80211_dfs_cac_timer_work(struct work_struct *work); +void ieee80211_dfs_cac_cancel(struct ieee80211_local *local); +void ieee80211_dfs_radar_detected_work(struct work_struct *work); +int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, +			      struct cfg80211_csa_settings *csa_settings); + +bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs); +bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n); +const struct ieee80211_cipher_scheme * +ieee80211_cs_get(struct ieee80211_local *local, u32 cipher, +		 enum nl80211_iftype iftype); +int ieee80211_cs_headroom(struct ieee80211_local *local, +			  struct cfg80211_crypto_settings *crypto, +			  enum nl80211_iftype iftype); +void ieee80211_recalc_dtim(struct ieee80211_local *local, +			   struct ieee80211_sub_if_data *sdata); +int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, +				 const struct cfg80211_chan_def *chandef, +				 enum ieee80211_chanctx_mode chanmode, +				 u8 radar_detect); +int ieee80211_max_num_channels(struct ieee80211_local *local); + +/* TDLS */ +int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, +			const u8 *peer, u8 action_code, u8 dialog_token, +			u16 status_code, u32 peer_capability, +			const u8 *extra_ies, size_t extra_ies_len); +int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, +			const u8 *peer, enum nl80211_tdls_operation oper); -enum ieee80211_chan_mode -ieee80211_get_channel_mode(struct ieee80211_local *local, -			   struct ieee80211_sub_if_data *ignore); -bool ieee80211_set_channel_type(struct ieee80211_local *local, -				struct ieee80211_sub_if_data *sdata, -				enum nl80211_channel_type chantype);  #ifdef CONFIG_MAC80211_NOINLINE  #define debug_noinline noinline diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 7aa85591dbe..388b863e821 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1,5 +1,5 @@  /* - * Interface handling (except master interface) + * Interface handling   *   * Copyright 2002-2005, Instant802 Networks, Inc.   * Copyright 2005-2006, Devicescape Software, Inc. @@ -42,37 +42,185 @@   * by either the RTNL, the iflist_mtx or RCU.   */ +bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_chanctx_conf *chanctx_conf; +	int power; + +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	if (!chanctx_conf) { +		rcu_read_unlock(); +		return false; +	} -static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) +	power = ieee80211_chandef_max_power(&chanctx_conf->def); +	rcu_read_unlock(); + +	if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL) +		power = min(power, sdata->user_power_level); + +	if (sdata->ap_power_level != IEEE80211_UNSET_POWER_LEVEL) +		power = min(power, sdata->ap_power_level); + +	if (power != sdata->vif.bss_conf.txpower) { +		sdata->vif.bss_conf.txpower = power; +		ieee80211_hw_config(sdata->local, 0); +		return true; +	} + +	return false; +} + +void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)  { -	int meshhdrlen; -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	if (__ieee80211_recalc_txpower(sdata)) +		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER); +} + +static u32 __ieee80211_idle_off(struct ieee80211_local *local) +{ +	if (!(local->hw.conf.flags & IEEE80211_CONF_IDLE)) +		return 0; + +	local->hw.conf.flags &= ~IEEE80211_CONF_IDLE; +	return IEEE80211_CONF_CHANGE_IDLE; +} + +static u32 __ieee80211_idle_on(struct ieee80211_local *local) +{ +	if (local->hw.conf.flags & IEEE80211_CONF_IDLE) +		return 0; + +	ieee80211_flush_queues(local, NULL); + +	local->hw.conf.flags |= IEEE80211_CONF_IDLE; +	return IEEE80211_CONF_CHANGE_IDLE; +} + +static u32 __ieee80211_recalc_idle(struct ieee80211_local *local, +				   bool force_active) +{ +	bool working, scanning, active; +	unsigned int led_trig_start = 0, led_trig_stop = 0; + +	lockdep_assert_held(&local->mtx); + +	active = force_active || +		 !list_empty(&local->chanctx_list) || +		 local->monitors; + +	working = !local->ops->remain_on_channel && +		  !list_empty(&local->roc_list); + +	scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) || +		   test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning); + +	if (working || scanning) +		led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_WORK; +	else +		led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_WORK; + +	if (active) +		led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_CONNECTED; +	else +		led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_CONNECTED; + +	ieee80211_mod_tpt_led_trig(local, led_trig_start, led_trig_stop); + +	if (working || scanning || active) +		return __ieee80211_idle_off(local); +	return __ieee80211_idle_on(local); +} + +u32 ieee80211_idle_off(struct ieee80211_local *local) +{ +	return __ieee80211_recalc_idle(local, true); +} -	meshhdrlen = (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ? 5 : 0; +void ieee80211_recalc_idle(struct ieee80211_local *local) +{ +	u32 change = __ieee80211_recalc_idle(local, false); +	if (change) +		ieee80211_hw_config(local, change); +} -	/* FIX: what would be proper limits for MTU? -	 * This interface uses 802.3 frames. */ -	if (new_mtu < 256 || -	    new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6 - meshhdrlen) { +static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) +{ +	if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN)  		return -EINVAL; -	} -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -	printk(KERN_DEBUG "%s: setting MTU %d\n", dev->name, new_mtu); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */  	dev->mtu = new_mtu;  	return 0;  } +static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr, +				bool check_dup) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_sub_if_data *iter; +	u64 new, mask, tmp; +	u8 *m; +	int ret = 0; + +	if (is_zero_ether_addr(local->hw.wiphy->addr_mask)) +		return 0; + +	m = addr; +	new =	((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) | +		((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) | +		((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); + +	m = local->hw.wiphy->addr_mask; +	mask =	((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) | +		((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) | +		((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); + +	if (!check_dup) +		return ret; + +	mutex_lock(&local->iflist_mtx); +	list_for_each_entry(iter, &local->interfaces, list) { +		if (iter == sdata) +			continue; + +		if (iter->vif.type == NL80211_IFTYPE_MONITOR && +		    !(iter->u.mntr_flags & MONITOR_FLAG_ACTIVE)) +			continue; + +		m = iter->vif.addr; +		tmp =	((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) | +			((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) | +			((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); + +		if ((new & ~mask) != (tmp & ~mask)) { +			ret = -EINVAL; +			break; +		} +	} +	mutex_unlock(&local->iflist_mtx); + +	return ret; +} +  static int ieee80211_change_mac(struct net_device *dev, void *addr)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct sockaddr *sa = addr; +	bool check_dup = true;  	int ret;  	if (ieee80211_sdata_running(sdata))  		return -EBUSY; +	if (sdata->vif.type == NL80211_IFTYPE_MONITOR && +	    !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) +		check_dup = false; + +	ret = ieee80211_verify_mac(sdata, sa->sa_data, check_dup); +	if (ret) +		return ret; +  	ret = eth_mac_addr(dev, sa);  	if (ret == 0) @@ -85,6 +233,8 @@ static inline int identical_mac_addr_allowed(int type1, int type2)  {  	return type1 == NL80211_IFTYPE_MONITOR ||  		type2 == NL80211_IFTYPE_MONITOR || +		type1 == NL80211_IFTYPE_P2P_DEVICE || +		type2 == NL80211_IFTYPE_P2P_DEVICE ||  		(type1 == NL80211_IFTYPE_AP && type2 == NL80211_IFTYPE_WDS) ||  		(type1 == NL80211_IFTYPE_WDS &&  			(type2 == NL80211_IFTYPE_WDS || @@ -100,15 +250,13 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,  {  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_sub_if_data *nsdata; -	struct net_device *dev = sdata->dev; +	int ret;  	ASSERT_RTNL();  	/* we hold the RTNL here so can safely walk the list */  	list_for_each_entry(nsdata, &local->interfaces, list) { -		struct net_device *ndev = nsdata->dev; - -		if (ndev != dev && ieee80211_sdata_running(nsdata)) { +		if (nsdata != sdata && ieee80211_sdata_running(nsdata)) {  			/*  			 * Allow only a single IBSS interface to be up at any  			 * time. This is restricted because beacon distribution @@ -122,12 +270,19 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,  			if (iftype == NL80211_IFTYPE_ADHOC &&  			    nsdata->vif.type == NL80211_IFTYPE_ADHOC)  				return -EBUSY; +			/* +			 * will not add another interface while any channel +			 * switch is active. +			 */ +			if (nsdata->vif.csa_active) +				return -EBUSY;  			/*  			 * The remaining checks are only performed for interfaces  			 * with the same MAC address.  			 */ -			if (compare_ether_addr(dev->dev_addr, ndev->dev_addr)) +			if (!ether_addr_equal(sdata->vif.addr, +					      nsdata->vif.addr))  				continue;  			/* @@ -146,6 +301,43 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,  		}  	} +	mutex_lock(&local->chanctx_mtx); +	ret = ieee80211_check_combinations(sdata, NULL, 0, 0); +	mutex_unlock(&local->chanctx_mtx); +	return ret; +} + +static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata, +				  enum nl80211_iftype iftype) +{ +	int n_queues = sdata->local->hw.queues; +	int i; + +	if (iftype != NL80211_IFTYPE_P2P_DEVICE) { +		for (i = 0; i < IEEE80211_NUM_ACS; i++) { +			if (WARN_ON_ONCE(sdata->vif.hw_queue[i] == +					 IEEE80211_INVAL_HW_QUEUE)) +				return -EINVAL; +			if (WARN_ON_ONCE(sdata->vif.hw_queue[i] >= +					 n_queues)) +				return -EINVAL; +		} +	} + +	if ((iftype != NL80211_IFTYPE_AP && +	     iftype != NL80211_IFTYPE_P2P_GO && +	     iftype != NL80211_IFTYPE_MESH_POINT) || +	    !(sdata->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)) { +		sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE; +		return 0; +	} + +	if (WARN_ON_ONCE(sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE)) +		return -EINVAL; + +	if (WARN_ON_ONCE(sdata->vif.cab_queue >= n_queues)) +		return -EINVAL; +  	return 0;  } @@ -169,14 +361,125 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,  #undef ADJUST  } +static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local; +	int i; + +	for (i = 0; i < IEEE80211_NUM_ACS; i++) { +		if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) +			sdata->vif.hw_queue[i] = IEEE80211_INVAL_HW_QUEUE; +		else if (local->hw.queues >= IEEE80211_NUM_ACS) +			sdata->vif.hw_queue[i] = i; +		else +			sdata->vif.hw_queue[i] = 0; +	} +	sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE; +} + +int ieee80211_add_virtual_monitor(struct ieee80211_local *local) +{ +	struct ieee80211_sub_if_data *sdata; +	int ret; + +	if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF)) +		return 0; + +	ASSERT_RTNL(); + +	if (local->monitor_sdata) +		return 0; + +	sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL); +	if (!sdata) +		return -ENOMEM; + +	/* set up data */ +	sdata->local = local; +	sdata->vif.type = NL80211_IFTYPE_MONITOR; +	snprintf(sdata->name, IFNAMSIZ, "%s-monitor", +		 wiphy_name(local->hw.wiphy)); +	sdata->wdev.iftype = NL80211_IFTYPE_MONITOR; + +	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; + +	ieee80211_set_default_queues(sdata); + +	ret = drv_add_interface(local, sdata); +	if (WARN_ON(ret)) { +		/* ok .. stupid driver, it asked for this! */ +		kfree(sdata); +		return ret; +	} + +	ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR); +	if (ret) { +		kfree(sdata); +		return ret; +	} + +	mutex_lock(&local->iflist_mtx); +	rcu_assign_pointer(local->monitor_sdata, sdata); +	mutex_unlock(&local->iflist_mtx); + +	mutex_lock(&local->mtx); +	ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef, +					IEEE80211_CHANCTX_EXCLUSIVE); +	mutex_unlock(&local->mtx); +	if (ret) { +		mutex_lock(&local->iflist_mtx); +		RCU_INIT_POINTER(local->monitor_sdata, NULL); +		mutex_unlock(&local->iflist_mtx); +		synchronize_net(); +		drv_remove_interface(local, sdata); +		kfree(sdata); +		return ret; +	} + +	return 0; +} + +void ieee80211_del_virtual_monitor(struct ieee80211_local *local) +{ +	struct ieee80211_sub_if_data *sdata; + +	if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF)) +		return; + +	ASSERT_RTNL(); + +	mutex_lock(&local->iflist_mtx); + +	sdata = rcu_dereference_protected(local->monitor_sdata, +					  lockdep_is_held(&local->iflist_mtx)); +	if (!sdata) { +		mutex_unlock(&local->iflist_mtx); +		return; +	} + +	RCU_INIT_POINTER(local->monitor_sdata, NULL); +	mutex_unlock(&local->iflist_mtx); + +	synchronize_net(); + +	mutex_lock(&local->mtx); +	ieee80211_vif_release_channel(sdata); +	mutex_unlock(&local->mtx); + +	drv_remove_interface(local, sdata); + +	kfree(sdata); +} +  /*   * NOTE: Be very careful when changing this function, it must NOT return   * an error on interface type changes that have been pre-checked, so most   * checks should be in ieee80211_check_concurrent_iface.   */ -static int ieee80211_do_open(struct net_device *dev, bool coming_up) +int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)  { -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); +	struct net_device *dev = wdev->netdev;  	struct ieee80211_local *local = sdata->local;  	struct sta_info *sta;  	u32 changed = 0; @@ -188,23 +491,35 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)  		if (!is_valid_ether_addr(sdata->u.wds.remote_addr))  			return -ENOLINK;  		break; -	case NL80211_IFTYPE_AP_VLAN: +	case NL80211_IFTYPE_AP_VLAN: { +		struct ieee80211_sub_if_data *master; +  		if (!sdata->bss)  			return -ENOLINK; + +		mutex_lock(&local->mtx);  		list_add(&sdata->u.vlan.list, &sdata->bss->vlans); +		mutex_unlock(&local->mtx); + +		master = container_of(sdata->bss, +				      struct ieee80211_sub_if_data, u.ap); +		sdata->control_port_protocol = +			master->control_port_protocol; +		sdata->control_port_no_encrypt = +			master->control_port_no_encrypt; +		sdata->vif.cab_queue = master->vif.cab_queue; +		memcpy(sdata->vif.hw_queue, master->vif.hw_queue, +		       sizeof(sdata->vif.hw_queue));  		break; +		}  	case NL80211_IFTYPE_AP:  		sdata->bss = &sdata->u.ap;  		break;  	case NL80211_IFTYPE_MESH_POINT: -		if (!ieee80211_vif_is_mesh(&sdata->vif)) -			break; -		/* mesh ifaces must set allmulti to forward mcast traffic */ -		atomic_inc(&local->iff_allmultis); -		break;  	case NL80211_IFTYPE_STATION:  	case NL80211_IFTYPE_MONITOR:  	case NL80211_IFTYPE_ADHOC: +	case NL80211_IFTYPE_P2P_DEVICE:  		/* no special treatment */  		break;  	case NL80211_IFTYPE_UNSPECIFIED: @@ -220,33 +535,38 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)  		res = drv_start(local);  		if (res)  			goto err_del_bss; -		if (local->ops->napi_poll) -			napi_enable(&local->napi);  		/* we're brought up, everything changes */  		hw_reconf_flags = ~0;  		ieee80211_led_radio(local, true); +		ieee80211_mod_tpt_led_trig(local, +					   IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);  	}  	/*  	 * Copy the hopefully now-present MAC address to  	 * this interface, if it has the special null one.  	 */ -	if (is_zero_ether_addr(dev->dev_addr)) { +	if (dev && is_zero_ether_addr(dev->dev_addr)) {  		memcpy(dev->dev_addr,  		       local->hw.wiphy->perm_addr,  		       ETH_ALEN);  		memcpy(dev->perm_addr, dev->dev_addr, ETH_ALEN);  		if (!is_valid_ether_addr(dev->dev_addr)) { -			if (!local->open_count) -				drv_stop(local); -			return -EADDRNOTAVAIL; +			res = -EADDRNOTAVAIL; +			goto err_stop;  		}  	}  	switch (sdata->vif.type) {  	case NL80211_IFTYPE_AP_VLAN: -		/* no need to tell driver */ +		/* no need to tell driver, but set carrier and chanctx */ +		if (rtnl_dereference(sdata->bss->beacon)) { +			ieee80211_vif_vlan_copy_chanctx(sdata); +			netif_carrier_on(dev); +		} else { +			netif_carrier_off(dev); +		}  		break;  	case NL80211_IFTYPE_MONITOR:  		if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) { @@ -254,6 +574,16 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)  			break;  		} +		if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) { +			res = drv_add_interface(local, sdata); +			if (res) +				goto err_stop; +		} else if (local->monitors == 0 && local->open_count == 0) { +			res = ieee80211_add_virtual_monitor(local); +			if (res) +				goto err_stop; +		} +  		/* must be before the call to ieee80211_configure_filter */  		local->monitors++;  		if (local->monitors == 1) { @@ -263,22 +593,26 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)  		ieee80211_adjust_monitor_flags(sdata, 1);  		ieee80211_configure_filter(local); +		mutex_lock(&local->mtx); +		ieee80211_recalc_idle(local); +		mutex_unlock(&local->mtx);  		netif_carrier_on(dev);  		break;  	default:  		if (coming_up) { -			res = drv_add_interface(local, &sdata->vif); +			ieee80211_del_virtual_monitor(local); + +			res = drv_add_interface(local, sdata);  			if (res)  				goto err_stop; +			res = ieee80211_check_queues(sdata, +				ieee80211_vif_type_p2p(&sdata->vif)); +			if (res) +				goto err_del_interface;  		} -		if (ieee80211_vif_is_mesh(&sdata->vif)) { -			local->fif_other_bss++; -			ieee80211_configure_filter(local); - -			ieee80211_start_mesh(sdata); -		} else if (sdata->vif.type == NL80211_IFTYPE_AP) { +		if (sdata->vif.type == NL80211_IFTYPE_AP) {  			local->fif_pspoll++;  			local->fif_probe_req++; @@ -287,13 +621,31 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)  			local->fif_probe_req++;  		} -		changed |= ieee80211_reset_erp_info(sdata); +		if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) +			changed |= ieee80211_reset_erp_info(sdata);  		ieee80211_bss_info_change_notify(sdata, changed); -		if (sdata->vif.type == NL80211_IFTYPE_STATION) +		switch (sdata->vif.type) { +		case NL80211_IFTYPE_STATION: +		case NL80211_IFTYPE_ADHOC: +		case NL80211_IFTYPE_AP: +		case NL80211_IFTYPE_MESH_POINT:  			netif_carrier_off(dev); -		else -			netif_carrier_on(dev); +			break; +		case NL80211_IFTYPE_WDS: +		case NL80211_IFTYPE_P2P_DEVICE: +			break; +		default: +			/* not reached */ +			WARN_ON(1); +		} + +		/* +		 * set default queue parameters so drivers don't +		 * need to initialise the hardware if the hardware +		 * doesn't start up with sane defaults +		 */ +		ieee80211_set_wmm_default(sdata, true);  	}  	set_bit(SDATA_STATE_RUNNING, &sdata->state); @@ -307,8 +659,9 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)  			goto err_del_interface;  		} -		/* no locking required since STA is not live yet */ -		sta->flags |= WLAN_STA_AUTHORIZED; +		sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); +		sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); +		sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);  		res = sta_info_insert(sta);  		if (res) { @@ -317,6 +670,9 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)  		}  		rate_control_rate_init(sta); +		netif_carrier_on(dev); +	} else if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) { +		rcu_assign_pointer(local->p2p_sdata, sdata);  	}  	/* @@ -330,37 +686,55 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)  	if (sdata->flags & IEEE80211_SDATA_PROMISC)  		atomic_inc(&local->iff_promiscs); -	mutex_lock(&local->mtx); -	hw_reconf_flags |= __ieee80211_recalc_idle(local); -	mutex_unlock(&local->mtx); -  	if (coming_up)  		local->open_count++; -	if (hw_reconf_flags) { +	if (hw_reconf_flags)  		ieee80211_hw_config(local, hw_reconf_flags); -		/* -		 * set default queue parameters so drivers don't -		 * need to initialise the hardware if the hardware -		 * doesn't start up with sane defaults -		 */ -		ieee80211_set_wmm_default(sdata); -	}  	ieee80211_recalc_ps(local, -1); -	netif_tx_start_all_queues(dev); +	if (sdata->vif.type == NL80211_IFTYPE_MONITOR || +	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { +		/* XXX: for AP_VLAN, actually track AP queues */ +		netif_tx_start_all_queues(dev); +	} else if (dev) { +		unsigned long flags; +		int n_acs = IEEE80211_NUM_ACS; +		int ac; + +		if (local->hw.queues < IEEE80211_NUM_ACS) +			n_acs = 1; + +		spin_lock_irqsave(&local->queue_stop_reason_lock, flags); +		if (sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE || +		    (local->queue_stop_reasons[sdata->vif.cab_queue] == 0 && +		     skb_queue_empty(&local->pending[sdata->vif.cab_queue]))) { +			for (ac = 0; ac < n_acs; ac++) { +				int ac_queue = sdata->vif.hw_queue[ac]; + +				if (local->queue_stop_reasons[ac_queue] == 0 && +				    skb_queue_empty(&local->pending[ac_queue])) +					netif_start_subqueue(dev, ac); +			} +		} +		spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); +	}  	return 0;   err_del_interface: -	drv_remove_interface(local, &sdata->vif); +	drv_remove_interface(local, sdata);   err_stop:  	if (!local->open_count)  		drv_stop(local);   err_del_bss:  	sdata->bss = NULL; -	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { +		mutex_lock(&local->mtx);  		list_del(&sdata->u.vlan.list); +		mutex_unlock(&local->mtx); +	} +	/* might already be clear but that doesn't matter */  	clear_bit(SDATA_STATE_RUNNING, &sdata->state);  	return res;  } @@ -371,15 +745,14 @@ static int ieee80211_open(struct net_device *dev)  	int err;  	/* fail early if user set an invalid address */ -	if (!is_zero_ether_addr(dev->dev_addr) && -	    !is_valid_ether_addr(dev->dev_addr)) +	if (!is_valid_ether_addr(dev->dev_addr))  		return -EADDRNOTAVAIL;  	err = ieee80211_check_concurrent_iface(sdata, sdata->vif.type);  	if (err)  		return err; -	return ieee80211_do_open(dev, true); +	return ieee80211_do_open(&sdata->wdev, true);  }  static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, @@ -389,22 +762,36 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  	unsigned long flags;  	struct sk_buff *skb, *tmp;  	u32 hw_reconf_flags = 0; -	int i; - -	if (local->scan_sdata == sdata) -		ieee80211_scan_cancel(local); +	int i, flushed; +	struct ps_data *ps; +	struct cfg80211_chan_def chandef;  	clear_bit(SDATA_STATE_RUNNING, &sdata->state); +	if (rcu_access_pointer(local->scan_sdata) == sdata) +		ieee80211_scan_cancel(local); +  	/*  	 * Stop TX on this interface first.  	 */ -	netif_tx_stop_all_queues(sdata->dev); +	if (sdata->dev) +		netif_tx_stop_all_queues(sdata->dev); -	/* -	 * Purge work for this interface. -	 */ -	ieee80211_work_purge(sdata); +	ieee80211_roc_purge(local, sdata); + +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_STATION: +		ieee80211_mgd_stop(sdata); +		break; +	case NL80211_IFTYPE_ADHOC: +		ieee80211_ibss_stop(sdata); +		break; +	case NL80211_IFTYPE_AP: +		cancel_work_sync(&sdata->u.ap.request_smps_work); +		break; +	default: +		break; +	}  	/*  	 * Remove all stations associated with this interface. @@ -416,18 +803,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  	 * (because if we remove a STA after ops->remove_interface()  	 * the driver will have removed the vif info already!)  	 * -	 * This is relevant only in AP, WDS and mesh modes, since in -	 * all other modes we've already removed all stations when -	 * disconnecting etc. +	 * This is relevant only in WDS mode, in all other modes we've +	 * already removed all stations when disconnecting or similar, +	 * so warn otherwise.  	 */ -	sta_info_flush(local, sdata); +	flushed = sta_info_flush(sdata); +	WARN_ON_ONCE((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) || +		     (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)); -	/* -	 * Don't count this interface for promisc/allmulti while it -	 * is down. dev_mc_unsync() will invoke set_multicast_list -	 * on the master interface which will sync these down to the -	 * hardware as filter flags. -	 */ +	/* don't count this interface for promisc/allmulti while it is down */  	if (sdata->flags & IEEE80211_SDATA_ALLMULTI)  		atomic_dec(&local->iff_allmultis); @@ -441,43 +825,66 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  		local->fif_probe_req--;  	} -	netif_addr_lock_bh(sdata->dev); -	spin_lock_bh(&local->filter_lock); -	__hw_addr_unsync(&local->mc_list, &sdata->dev->mc, -			 sdata->dev->addr_len); -	spin_unlock_bh(&local->filter_lock); -	netif_addr_unlock_bh(sdata->dev); - -	ieee80211_configure_filter(local); +	if (sdata->dev) { +		netif_addr_lock_bh(sdata->dev); +		spin_lock_bh(&local->filter_lock); +		__hw_addr_unsync(&local->mc_list, &sdata->dev->mc, +				 sdata->dev->addr_len); +		spin_unlock_bh(&local->filter_lock); +		netif_addr_unlock_bh(sdata->dev); +	}  	del_timer_sync(&local->dynamic_ps_timer);  	cancel_work_sync(&local->dynamic_ps_enable_work); -	/* APs need special treatment */ -	if (sdata->vif.type == NL80211_IFTYPE_AP) { -		struct ieee80211_sub_if_data *vlan, *tmpsdata; -		struct beacon_data *old_beacon = sdata->u.ap.beacon; +	cancel_work_sync(&sdata->recalc_smps); +	sdata_lock(sdata); +	mutex_lock(&local->mtx); +	sdata->vif.csa_active = false; +	if (!ieee80211_csa_needs_block_tx(local)) +		ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA); +	mutex_unlock(&local->mtx); +	sdata_unlock(sdata); -		/* sdata_running will return false, so this will disable */ -		ieee80211_bss_info_change_notify(sdata, -						 BSS_CHANGED_BEACON_ENABLED); +	cancel_work_sync(&sdata->csa_finalize_work); -		/* remove beacon */ -		rcu_assign_pointer(sdata->u.ap.beacon, NULL); -		synchronize_rcu(); -		kfree(old_beacon); +	cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); -		/* free all potentially still buffered bcast frames */ -		while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) { -			local->total_ps_buffered--; -			dev_kfree_skb(skb); -		} +	if (sdata->wdev.cac_started) { +		chandef = sdata->vif.bss_conf.chandef; +		WARN_ON(local->suspended); +		mutex_lock(&local->mtx); +		ieee80211_vif_release_channel(sdata); +		mutex_unlock(&local->mtx); +		cfg80211_cac_event(sdata->dev, &chandef, +				   NL80211_RADAR_CAC_ABORTED, +				   GFP_KERNEL); +	} + +	/* APs need special treatment */ +	if (sdata->vif.type == NL80211_IFTYPE_AP) { +		struct ieee80211_sub_if_data *vlan, *tmpsdata;  		/* down all dependent devices, that is VLANs */  		list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,  					 u.vlan.list)  			dev_close(vlan->dev);  		WARN_ON(!list_empty(&sdata->u.ap.vlans)); +	} else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { +		/* remove all packets in parent bc_buf pointing to this dev */ +		ps = &sdata->bss->ps; + +		spin_lock_irqsave(&ps->bc_buf.lock, flags); +		skb_queue_walk_safe(&ps->bc_buf, skb, tmp) { +			if (skb->dev == sdata->dev) { +				__skb_unlink(skb, &ps->bc_buf); +				local->total_ps_buffered--; +				ieee80211_free_txskb(&local->hw, skb); +			} +		} +		spin_unlock_irqrestore(&ps->bc_buf.lock, flags);  	}  	if (going_down) @@ -485,7 +892,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  	switch (sdata->vif.type) {  	case NL80211_IFTYPE_AP_VLAN: +		mutex_lock(&local->mtx);  		list_del(&sdata->u.vlan.list); +		mutex_unlock(&local->mtx); +		RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);  		/* no need to tell driver */  		break;  	case NL80211_IFTYPE_MONITOR: @@ -501,82 +911,97 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  		}  		ieee80211_adjust_monitor_flags(sdata, -1); -		ieee80211_configure_filter(local);  		break; -	case NL80211_IFTYPE_MESH_POINT: -		if (ieee80211_vif_is_mesh(&sdata->vif)) { -			/* other_bss and allmulti are always set on mesh -			 * ifaces */ -			local->fif_other_bss--; -			atomic_dec(&local->iff_allmultis); - -			ieee80211_configure_filter(local); - -			ieee80211_stop_mesh(sdata); -		} +	case NL80211_IFTYPE_P2P_DEVICE: +		/* relies on synchronize_rcu() below */ +		RCU_INIT_POINTER(local->p2p_sdata, NULL);  		/* fall through */  	default: -		flush_work(&sdata->work); +		cancel_work_sync(&sdata->work);  		/*  		 * When we get here, the interface is marked down. -		 * Call synchronize_rcu() to wait for the RX path -		 * should it be using the interface and enqueuing -		 * frames at this very time on another CPU. +		 * Free the remaining keys, if there are any +		 * (shouldn't be, except maybe in WDS mode?) +		 * +		 * Force the key freeing to always synchronize_net() +		 * to wait for the RX path in case it is using this +		 * interface enqueuing frames * at this very time on +		 * another CPU.  		 */ -		synchronize_rcu(); +		ieee80211_free_keys(sdata, true); + +		/* fall through */ +	case NL80211_IFTYPE_AP:  		skb_queue_purge(&sdata->skb_queue); +	} -		/* -		 * Disable beaconing here for mesh only, AP and IBSS -		 * are already taken care of. -		 */ -		if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) -			ieee80211_bss_info_change_notify(sdata, -				BSS_CHANGED_BEACON_ENABLED); +	sdata->bss = NULL; -		/* -		 * Free all remaining keys, there shouldn't be any, -		 * except maybe group keys in AP more or WDS? -		 */ -		ieee80211_free_keys(sdata); +	spin_lock_irqsave(&local->queue_stop_reason_lock, flags); +	for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { +		skb_queue_walk_safe(&local->pending[i], skb, tmp) { +			struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +			if (info->control.vif == &sdata->vif) { +				__skb_unlink(skb, &local->pending[i]); +				ieee80211_free_txskb(&local->hw, skb); +			} +		} +	} +	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); -		if (going_down) -			drv_remove_interface(local, &sdata->vif); +	if (local->open_count == 0) +		ieee80211_clear_tx_pending(local); + +	/* +	 * If the interface goes down while suspended, presumably because +	 * the device was unplugged and that happens before our resume, +	 * then the driver is already unconfigured and the remainder of +	 * this function isn't needed. +	 * XXX: what about WoWLAN? If the device has software state, e.g. +	 *	memory allocated, it might expect teardown commands from +	 *	mac80211 here? +	 */ +	if (local->suspended) { +		WARN_ON(local->wowlan); +		WARN_ON(rtnl_dereference(local->monitor_sdata)); +		return;  	} -	sdata->bss = NULL; +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_AP_VLAN: +		break; +	case NL80211_IFTYPE_MONITOR: +		if (local->monitors == 0) +			ieee80211_del_virtual_monitor(local); -	mutex_lock(&local->mtx); -	hw_reconf_flags |= __ieee80211_recalc_idle(local); -	mutex_unlock(&local->mtx); +		mutex_lock(&local->mtx); +		ieee80211_recalc_idle(local); +		mutex_unlock(&local->mtx); + +		if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) +			break; + +		/* fall through */ +	default: +		if (going_down) +			drv_remove_interface(local, sdata); +	}  	ieee80211_recalc_ps(local, -1);  	if (local->open_count == 0) { -		if (local->ops->napi_poll) -			napi_disable(&local->napi); -		ieee80211_clear_tx_pending(local);  		ieee80211_stop_device(local);  		/* no reconfiguring after stop! */ -		hw_reconf_flags = 0; +		return;  	}  	/* do after stop to avoid reconfiguring when we stop anyway */ -	if (hw_reconf_flags) -		ieee80211_hw_config(local, hw_reconf_flags); +	ieee80211_configure_filter(local); +	ieee80211_hw_config(local, hw_reconf_flags); -	spin_lock_irqsave(&local->queue_stop_reason_lock, flags); -	for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { -		skb_queue_walk_safe(&local->pending[i], skb, tmp) { -			struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); -			if (info->control.vif == &sdata->vif) { -				__skb_unlink(skb, &local->pending[i]); -				dev_kfree_skb_irq(skb); -			} -		} -	} -	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); +	if (local->monitors == local->open_count) +		ieee80211_add_virtual_monitor(local);  }  static int ieee80211_stop(struct net_device *dev) @@ -624,15 +1049,12 @@ static void ieee80211_set_multicast_list(struct net_device *dev)   * Called when the netdev is removed or, by the code below, before   * the interface type changes.   */ -static void ieee80211_teardown_sdata(struct net_device *dev) +static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)  { -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); -	struct ieee80211_local *local = sdata->local; -	int flushed;  	int i;  	/* free extra data */ -	ieee80211_free_keys(sdata); +	ieee80211_free_keys(sdata, false);  	ieee80211_debugfs_remove_netdev(sdata); @@ -642,13 +1064,17 @@ static void ieee80211_teardown_sdata(struct net_device *dev)  	if (ieee80211_vif_is_mesh(&sdata->vif))  		mesh_rmc_free(sdata); +} -	flushed = sta_info_flush(local, sdata); -	WARN_ON(flushed); +static void ieee80211_uninit(struct net_device *dev) +{ +	ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev));  }  static u16 ieee80211_netdev_select_queue(struct net_device *dev, -					 struct sk_buff *skb) +					 struct sk_buff *skb, +					 void *accel_priv, +					 select_queue_fallback_t fallback)  {  	return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb);  } @@ -656,24 +1082,25 @@ static u16 ieee80211_netdev_select_queue(struct net_device *dev,  static const struct net_device_ops ieee80211_dataif_ops = {  	.ndo_open		= ieee80211_open,  	.ndo_stop		= ieee80211_stop, -	.ndo_uninit		= ieee80211_teardown_sdata, +	.ndo_uninit		= ieee80211_uninit,  	.ndo_start_xmit		= ieee80211_subif_start_xmit, -	.ndo_set_multicast_list = ieee80211_set_multicast_list, +	.ndo_set_rx_mode	= ieee80211_set_multicast_list,  	.ndo_change_mtu 	= ieee80211_change_mtu,  	.ndo_set_mac_address 	= ieee80211_change_mac,  	.ndo_select_queue	= ieee80211_netdev_select_queue,  };  static u16 ieee80211_monitor_select_queue(struct net_device *dev, -					  struct sk_buff *skb) +					  struct sk_buff *skb, +					  void *accel_priv, +					  select_queue_fallback_t fallback)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_hdr *hdr;  	struct ieee80211_radiotap_header *rtap = (void *)skb->data; -	u8 *p; -	if (local->hw.queues < 4) +	if (local->hw.queues < IEEE80211_NUM_ACS)  		return 0;  	if (skb->len < 4 || @@ -682,35 +1109,24 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev,  	hdr = (void *)((u8 *)skb->data + le16_to_cpu(rtap->it_len)); -	if (!ieee80211_is_data(hdr->frame_control)) { -		skb->priority = 7; -		return ieee802_1d_to_ac[skb->priority]; -	} -	if (!ieee80211_is_data_qos(hdr->frame_control)) { -		skb->priority = 0; -		return ieee802_1d_to_ac[skb->priority]; -	} - -	p = ieee80211_get_qos_ctl(hdr); -	skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK; - -	return ieee80211_downgrade_queue(local, skb); +	return ieee80211_select_queue_80211(sdata, skb, hdr);  }  static const struct net_device_ops ieee80211_monitorif_ops = {  	.ndo_open		= ieee80211_open,  	.ndo_stop		= ieee80211_stop, -	.ndo_uninit		= ieee80211_teardown_sdata, +	.ndo_uninit		= ieee80211_uninit,  	.ndo_start_xmit		= ieee80211_monitor_start_xmit, -	.ndo_set_multicast_list = ieee80211_set_multicast_list, +	.ndo_set_rx_mode	= ieee80211_set_multicast_list,  	.ndo_change_mtu 	= ieee80211_change_mtu, -	.ndo_set_mac_address 	= eth_mac_addr, +	.ndo_set_mac_address 	= ieee80211_change_mac,  	.ndo_select_queue	= ieee80211_monitor_select_queue,  };  static void ieee80211_if_setup(struct net_device *dev)  {  	ether_setup(dev); +	dev->priv_flags &= ~IFF_TX_SKB_SHARING;  	dev->netdev_ops = &ieee80211_dataif_ops;  	dev->destructor = free_netdev;  } @@ -845,6 +1261,13 @@ static void ieee80211_iface_work(struct work_struct *work)  	}  } +static void ieee80211_recalc_smps_work(struct work_struct *work) +{ +	struct ieee80211_sub_if_data *sdata = +		container_of(work, struct ieee80211_sub_if_data, recalc_smps); + +	ieee80211_recalc_smps(sdata); +}  /*   * Helper function to initialise an interface to a specific type. @@ -858,17 +1281,27 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,  	/* and set some type-dependent values */  	sdata->vif.type = type;  	sdata->vif.p2p = false; -	sdata->dev->netdev_ops = &ieee80211_dataif_ops;  	sdata->wdev.iftype = type;  	sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE);  	sdata->control_port_no_encrypt = false; +	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; +	sdata->vif.bss_conf.idle = true; -	/* only monitor differs */ -	sdata->dev->type = ARPHRD_ETHER; +	sdata->noack_map = 0; + +	/* only monitor/p2p-device differ */ +	if (sdata->dev) { +		sdata->dev->netdev_ops = &ieee80211_dataif_ops; +		sdata->dev->type = ARPHRD_ETHER; +	}  	skb_queue_head_init(&sdata->skb_queue);  	INIT_WORK(&sdata->work, ieee80211_iface_work); +	INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work); +	INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work); +	INIT_LIST_HEAD(&sdata->assigned_chanctx_list); +	INIT_LIST_HEAD(&sdata->reserved_chanctx_list);  	switch (type) {  	case NL80211_IFTYPE_P2P_GO: @@ -877,8 +1310,12 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,  		sdata->vif.p2p = true;  		/* fall through */  	case NL80211_IFTYPE_AP: -		skb_queue_head_init(&sdata->u.ap.ps_bc_buf); +		skb_queue_head_init(&sdata->u.ap.ps.bc_buf);  		INIT_LIST_HEAD(&sdata->u.ap.vlans); +		INIT_WORK(&sdata->u.ap.request_smps_work, +			  ieee80211_request_smps_ap_work); +		sdata->vif.bss_conf.bssid = sdata->vif.addr; +		sdata->u.ap.req_smps = IEEE80211_SMPS_OFF;  		break;  	case NL80211_IFTYPE_P2P_CLIENT:  		type = NL80211_IFTYPE_STATION; @@ -886,9 +1323,11 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,  		sdata->vif.p2p = true;  		/* fall through */  	case NL80211_IFTYPE_STATION: +		sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;  		ieee80211_sta_setup_sdata(sdata);  		break;  	case NL80211_IFTYPE_ADHOC: +		sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;  		ieee80211_ibss_setup_sdata(sdata);  		break;  	case NL80211_IFTYPE_MESH_POINT: @@ -902,7 +1341,11 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,  				      MONITOR_FLAG_OTHER_BSS;  		break;  	case NL80211_IFTYPE_WDS: +		sdata->vif.bss_conf.bssid = NULL; +		break;  	case NL80211_IFTYPE_AP_VLAN: +	case NL80211_IFTYPE_P2P_DEVICE: +		sdata->vif.bss_conf.bssid = sdata->vif.addr;  		break;  	case NL80211_IFTYPE_UNSPECIFIED:  	case NUM_NL80211_IFTYPES: @@ -970,15 +1413,22 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,  	ieee80211_do_stop(sdata, false); -	ieee80211_teardown_sdata(sdata->dev); +	ieee80211_teardown_sdata(sdata);  	ret = drv_change_interface(local, sdata, internal_type, p2p);  	if (ret) -		type = sdata->vif.type; +		type = ieee80211_vif_type_p2p(&sdata->vif); + +	/* +	 * Ignore return value here, there's not much we can do since +	 * the driver changed the interface type internally already. +	 * The warnings will hopefully make driver authors fix it :-) +	 */ +	ieee80211_check_queues(sdata, type);  	ieee80211_setup_sdata(sdata, type); -	err = ieee80211_do_open(sdata->dev, false); +	err = ieee80211_do_open(&sdata->wdev, false);  	WARN(err, "type change: do_open returned %d", err);  	return ret; @@ -994,25 +1444,17 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,  	if (type == ieee80211_vif_type_p2p(&sdata->vif))  		return 0; -	/* Setting ad-hoc mode on non-IBSS channel is not supported. */ -	if (sdata->local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS && -	    type == NL80211_IFTYPE_ADHOC) -		return -EOPNOTSUPP; -  	if (ieee80211_sdata_running(sdata)) {  		ret = ieee80211_runtime_change_iftype(sdata, type);  		if (ret)  			return ret;  	} else {  		/* Purge and reset type-dependent state. */ -		ieee80211_teardown_sdata(sdata->dev); +		ieee80211_teardown_sdata(sdata);  		ieee80211_setup_sdata(sdata, type);  	}  	/* reset some values that shouldn't be kept across type changes */ -	sdata->vif.bss_conf.basic_rates = -		ieee80211_mandatory_rates(sdata->local, -			sdata->local->hw.conf.channel->band);  	sdata->drop_unencrypted = 0;  	if (type == NL80211_IFTYPE_STATION)  		sdata->u.mgd.use_4addr = false; @@ -1021,8 +1463,7 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,  }  static void ieee80211_assign_perm_addr(struct ieee80211_local *local, -				       struct net_device *dev, -				       enum nl80211_iftype type) +				       u8 *perm_addr, enum nl80211_iftype type)  {  	struct ieee80211_sub_if_data *sdata;  	u64 mask, start, addr, val, inc; @@ -1031,13 +1472,12 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,  	int i;  	/* default ... something at least */ -	memcpy(dev->perm_addr, local->hw.wiphy->perm_addr, ETH_ALEN); +	memcpy(perm_addr, local->hw.wiphy->perm_addr, ETH_ALEN);  	if (is_zero_ether_addr(local->hw.wiphy->addr_mask) &&  	    local->hw.wiphy->n_addresses <= 1)  		return; -  	mutex_lock(&local->iflist_mtx);  	switch (type) { @@ -1050,26 +1490,39 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,  		list_for_each_entry(sdata, &local->interfaces, list) {  			if (sdata->vif.type != NL80211_IFTYPE_AP)  				continue; -			memcpy(dev->perm_addr, sdata->vif.addr, ETH_ALEN); +			memcpy(perm_addr, sdata->vif.addr, ETH_ALEN);  			break;  		}  		/* keep default if no AP interface present */  		break; +	case NL80211_IFTYPE_P2P_CLIENT: +	case NL80211_IFTYPE_P2P_GO: +		if (local->hw.flags & IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF) { +			list_for_each_entry(sdata, &local->interfaces, list) { +				if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) +					continue; +				if (!ieee80211_sdata_running(sdata)) +					continue; +				memcpy(perm_addr, sdata->vif.addr, ETH_ALEN); +				goto out_unlock; +			} +		} +		/* otherwise fall through */  	default:  		/* assign a new address if possible -- try n_addresses first */  		for (i = 0; i < local->hw.wiphy->n_addresses; i++) {  			bool used = false;  			list_for_each_entry(sdata, &local->interfaces, list) { -				if (memcmp(local->hw.wiphy->addresses[i].addr, -					   sdata->vif.addr, ETH_ALEN) == 0) { +				if (ether_addr_equal(local->hw.wiphy->addresses[i].addr, +						     sdata->vif.addr)) {  					used = true;  					break;  				}  			}  			if (!used) { -				memcpy(dev->perm_addr, +				memcpy(perm_addr,  				       local->hw.wiphy->addresses[i].addr,  				       ETH_ALEN);  				break; @@ -1087,11 +1540,21 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,  		if (__ffs64(mask) + hweight64(mask) != fls64(mask)) {  			/* not a contiguous mask ... not handled now! */ -			printk(KERN_DEBUG "not contiguous\n"); +			pr_info("not contiguous\n");  			break;  		} +		/* +		 * Pick address of existing interface in case user changed +		 * MAC address manually, default to perm_addr. +		 */  		m = local->hw.wiphy->perm_addr; +		list_for_each_entry(sdata, &local->interfaces, list) { +			if (sdata->vif.type == NL80211_IFTYPE_MONITOR) +				continue; +			m = sdata->vif.addr; +			break; +		}  		start = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |  			((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |  			((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); @@ -1112,15 +1575,14 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,  			val += inc;  			list_for_each_entry(sdata, &local->interfaces, list) { -				if (memcmp(tmp_addr, sdata->vif.addr, -							ETH_ALEN) == 0) { +				if (ether_addr_equal(tmp_addr, sdata->vif.addr)) {  					used = true;  					break;  				}  			}  			if (!used) { -				memcpy(dev->perm_addr, tmp_addr, ETH_ALEN); +				memcpy(perm_addr, tmp_addr, ETH_ALEN);  				break;  			}  			addr = (start & ~mask) | (val & mask); @@ -1129,99 +1591,135 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,  		break;  	} + out_unlock:  	mutex_unlock(&local->iflist_mtx);  }  int ieee80211_if_add(struct ieee80211_local *local, const char *name, -		     struct net_device **new_dev, enum nl80211_iftype type, +		     struct wireless_dev **new_wdev, enum nl80211_iftype type,  		     struct vif_params *params)  { -	struct net_device *ndev; +	struct net_device *ndev = NULL;  	struct ieee80211_sub_if_data *sdata = NULL;  	int ret, i; +	int txqs = 1;  	ASSERT_RTNL(); -	ndev = alloc_netdev_mq(sizeof(*sdata) + local->hw.vif_data_size, -			       name, ieee80211_if_setup, local->hw.queues); -	if (!ndev) -		return -ENOMEM; -	dev_net_set(ndev, wiphy_net(local->hw.wiphy)); - -	ndev->needed_headroom = local->tx_headroom + -				4*6 /* four MAC addresses */ -				+ 2 + 2 + 2 + 2 /* ctl, dur, seq, qos */ -				+ 6 /* mesh */ -				+ 8 /* rfc1042/bridge tunnel */ -				- ETH_HLEN /* ethernet hard_header_len */ -				+ IEEE80211_ENCRYPT_HEADROOM; -	ndev->needed_tailroom = IEEE80211_ENCRYPT_TAILROOM; - -	ret = dev_alloc_name(ndev, ndev->name); -	if (ret < 0) -		goto fail; - -	ieee80211_assign_perm_addr(local, ndev, type); -	memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN); -	SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy)); - -	/* don't use IEEE80211_DEV_TO_SUB_IF because it checks too much */ -	sdata = netdev_priv(ndev); -	ndev->ieee80211_ptr = &sdata->wdev; -	memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN); -	memcpy(sdata->name, ndev->name, IFNAMSIZ); +	if (type == NL80211_IFTYPE_P2P_DEVICE) { +		struct wireless_dev *wdev; + +		sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, +				GFP_KERNEL); +		if (!sdata) +			return -ENOMEM; +		wdev = &sdata->wdev; + +		sdata->dev = NULL; +		strlcpy(sdata->name, name, IFNAMSIZ); +		ieee80211_assign_perm_addr(local, wdev->address, type); +		memcpy(sdata->vif.addr, wdev->address, ETH_ALEN); +	} else { +		if (local->hw.queues >= IEEE80211_NUM_ACS) +			txqs = IEEE80211_NUM_ACS; + +		ndev = alloc_netdev_mqs(sizeof(*sdata) + +					local->hw.vif_data_size, +					name, ieee80211_if_setup, txqs, 1); +		if (!ndev) +			return -ENOMEM; +		dev_net_set(ndev, wiphy_net(local->hw.wiphy)); + +		ndev->needed_headroom = local->tx_headroom + +					4*6 /* four MAC addresses */ +					+ 2 + 2 + 2 + 2 /* ctl, dur, seq, qos */ +					+ 6 /* mesh */ +					+ 8 /* rfc1042/bridge tunnel */ +					- ETH_HLEN /* ethernet hard_header_len */ +					+ IEEE80211_ENCRYPT_HEADROOM; +		ndev->needed_tailroom = IEEE80211_ENCRYPT_TAILROOM; + +		ret = dev_alloc_name(ndev, ndev->name); +		if (ret < 0) { +			free_netdev(ndev); +			return ret; +		} + +		ieee80211_assign_perm_addr(local, ndev->perm_addr, type); +		memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN); +		SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy)); + +		/* don't use IEEE80211_DEV_TO_SUB_IF -- it checks too much */ +		sdata = netdev_priv(ndev); +		ndev->ieee80211_ptr = &sdata->wdev; +		memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN); +		memcpy(sdata->name, ndev->name, IFNAMSIZ); + +		sdata->dev = ndev; +	}  	/* initialise type-independent data */  	sdata->wdev.wiphy = local->hw.wiphy;  	sdata->local = local; -	sdata->dev = ndev; -#ifdef CONFIG_INET -	sdata->arp_filter_state = true; -#endif  	for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)  		skb_queue_head_init(&sdata->fragments[i].skb_list);  	INIT_LIST_HEAD(&sdata->key_list); +	INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work, +			  ieee80211_dfs_cac_timer_work); +	INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk, +			  ieee80211_delayed_tailroom_dec); +  	for (i = 0; i < IEEE80211_NUM_BANDS; i++) {  		struct ieee80211_supported_band *sband;  		sband = local->hw.wiphy->bands[i];  		sdata->rc_rateidx_mask[i] =  			sband ? (1 << sband->n_bitrates) - 1 : 0; +		if (sband) +			memcpy(sdata->rc_rateidx_mcs_mask[i], +			       sband->ht_cap.mcs.rx_mask, +			       sizeof(sdata->rc_rateidx_mcs_mask[i])); +		else +			memset(sdata->rc_rateidx_mcs_mask[i], 0, +			       sizeof(sdata->rc_rateidx_mcs_mask[i]));  	} +	ieee80211_set_default_queues(sdata); + +	sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL; +	sdata->user_power_level = local->user_power_level; + +	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; +  	/* setup type-dependent data */  	ieee80211_setup_sdata(sdata, type); -	if (params) { -		ndev->ieee80211_ptr->use_4addr = params->use_4addr; -		if (type == NL80211_IFTYPE_STATION) -			sdata->u.mgd.use_4addr = params->use_4addr; -	} +	if (ndev) { +		if (params) { +			ndev->ieee80211_ptr->use_4addr = params->use_4addr; +			if (type == NL80211_IFTYPE_STATION) +				sdata->u.mgd.use_4addr = params->use_4addr; +		} -	ret = register_netdevice(ndev); -	if (ret) -		goto fail; +		ndev->features |= local->hw.netdev_features; -	if (ieee80211_vif_is_mesh(&sdata->vif) && -	    params && params->mesh_id_len) -		ieee80211_sdata_set_mesh_id(sdata, -					    params->mesh_id_len, -					    params->mesh_id); +		ret = register_netdevice(ndev); +		if (ret) { +			free_netdev(ndev); +			return ret; +		} +	}  	mutex_lock(&local->iflist_mtx);  	list_add_tail_rcu(&sdata->list, &local->interfaces);  	mutex_unlock(&local->iflist_mtx); -	if (new_dev) -		*new_dev = ndev; +	if (new_wdev) +		*new_wdev = &sdata->wdev;  	return 0; - - fail: -	free_netdev(ndev); -	return ret;  }  void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata) @@ -1233,7 +1731,21 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata)  	mutex_unlock(&sdata->local->iflist_mtx);  	synchronize_rcu(); -	unregister_netdevice(sdata->dev); + +	if (sdata->dev) { +		unregister_netdevice(sdata->dev); +	} else { +		cfg80211_unregister_wdev(&sdata->wdev); +		kfree(sdata); +	} +} + +void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata) +{ +	if (WARN_ON_ONCE(!test_bit(SDATA_STATE_RUNNING, &sdata->state))) +		return; +	ieee80211_do_stop(sdata, true); +	ieee80211_teardown_sdata(sdata);  }  /* @@ -1244,148 +1756,58 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local)  {  	struct ieee80211_sub_if_data *sdata, *tmp;  	LIST_HEAD(unreg_list); +	LIST_HEAD(wdev_list);  	ASSERT_RTNL(); +	/* +	 * Close all AP_VLAN interfaces first, as otherwise they +	 * might be closed while the AP interface they belong to +	 * is closed, causing unregister_netdevice_many() to crash. +	 */ +	list_for_each_entry(sdata, &local->interfaces, list) +		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +			dev_close(sdata->dev); +  	mutex_lock(&local->iflist_mtx);  	list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {  		list_del(&sdata->list); -		unregister_netdevice_queue(sdata->dev, &unreg_list); +		if (sdata->dev) +			unregister_netdevice_queue(sdata->dev, &unreg_list); +		else +			list_add(&sdata->list, &wdev_list);  	}  	mutex_unlock(&local->iflist_mtx);  	unregister_netdevice_many(&unreg_list); -} - -static u32 ieee80211_idle_off(struct ieee80211_local *local, -			      const char *reason) -{ -	if (!(local->hw.conf.flags & IEEE80211_CONF_IDLE)) -		return 0; - -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -	wiphy_debug(local->hw.wiphy, "device no longer idle - %s\n", reason); -#endif - -	local->hw.conf.flags &= ~IEEE80211_CONF_IDLE; -	return IEEE80211_CONF_CHANGE_IDLE; -} - -static u32 ieee80211_idle_on(struct ieee80211_local *local) -{ -	if (local->hw.conf.flags & IEEE80211_CONF_IDLE) -		return 0; - -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -	wiphy_debug(local->hw.wiphy, "device now idle\n"); -#endif - -	drv_flush(local, false); - -	local->hw.conf.flags |= IEEE80211_CONF_IDLE; -	return IEEE80211_CONF_CHANGE_IDLE; -} - -u32 __ieee80211_recalc_idle(struct ieee80211_local *local) -{ -	struct ieee80211_sub_if_data *sdata; -	int count = 0; -	bool working = false, scanning = false; -	struct ieee80211_work *wk; - -#ifdef CONFIG_PROVE_LOCKING -	WARN_ON(debug_locks && !lockdep_rtnl_is_held() && -		!lockdep_is_held(&local->iflist_mtx)); -#endif -	lockdep_assert_held(&local->mtx); - -	list_for_each_entry(sdata, &local->interfaces, list) { -		if (!ieee80211_sdata_running(sdata)) { -			sdata->vif.bss_conf.idle = true; -			continue; -		} -		sdata->old_idle = sdata->vif.bss_conf.idle; - -		/* do not count disabled managed interfaces */ -		if (sdata->vif.type == NL80211_IFTYPE_STATION && -		    !sdata->u.mgd.associated) { -			sdata->vif.bss_conf.idle = true; -			continue; -		} -		/* do not count unused IBSS interfaces */ -		if (sdata->vif.type == NL80211_IFTYPE_ADHOC && -		    !sdata->u.ibss.ssid_len) { -			sdata->vif.bss_conf.idle = true; -			continue; -		} -		/* count everything else */ -		count++; -	} - -	list_for_each_entry(wk, &local->work_list, list) { -		working = true; -		wk->sdata->vif.bss_conf.idle = false; -	} - -	if (local->scan_sdata) { -		scanning = true; -		local->scan_sdata->vif.bss_conf.idle = false; -	} - -	list_for_each_entry(sdata, &local->interfaces, list) { -		if (sdata->old_idle == sdata->vif.bss_conf.idle) -			continue; -		if (!ieee80211_sdata_running(sdata)) -			continue; -		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); +	list_for_each_entry_safe(sdata, tmp, &wdev_list, list) { +		list_del(&sdata->list); +		cfg80211_unregister_wdev(&sdata->wdev); +		kfree(sdata);  	} - -	if (working) -		return ieee80211_idle_off(local, "working"); -	if (scanning) -		return ieee80211_idle_off(local, "scanning"); -	if (!count) -		return ieee80211_idle_on(local); -	else -		return ieee80211_idle_off(local, "in use"); - -	return 0; -} - -void ieee80211_recalc_idle(struct ieee80211_local *local) -{ -	u32 chg; - -	mutex_lock(&local->iflist_mtx); -	chg = __ieee80211_recalc_idle(local); -	mutex_unlock(&local->iflist_mtx); -	if (chg) -		ieee80211_hw_config(local, chg);  }  static int netdev_notify(struct notifier_block *nb, -			 unsigned long state, -			 void *ndev) +			 unsigned long state, void *ptr)  { -	struct net_device *dev = ndev; +	struct net_device *dev = netdev_notifier_info_to_dev(ptr);  	struct ieee80211_sub_if_data *sdata;  	if (state != NETDEV_CHANGENAME) -		return 0; +		return NOTIFY_DONE;  	if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy) -		return 0; +		return NOTIFY_DONE;  	if (dev->ieee80211_ptr->wiphy->privid != mac80211_wiphy_privid) -		return 0; +		return NOTIFY_DONE;  	sdata = IEEE80211_DEV_TO_SUB_IF(dev); -  	memcpy(sdata->name, dev->name, IFNAMSIZ); -  	ieee80211_debugfs_rename_netdev(sdata); -	return 0; + +	return NOTIFY_OK;  }  static struct notifier_block mac80211_netdev_notifier = { diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 72df1ca7299..16d97f044a2 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -15,7 +15,9 @@  #include <linux/rcupdate.h>  #include <linux/rtnetlink.h>  #include <linux/slab.h> +#include <linux/export.h>  #include <net/mac80211.h> +#include <asm/unaligned.h>  #include "ieee80211_i.h"  #include "driver-ops.h"  #include "debugfs_key.h" @@ -30,19 +32,20 @@   * keys and per-station keys. Since each station belongs to an interface,   * each station key also belongs to that interface.   * - * Hardware acceleration is done on a best-effort basis, for each key - * that is eligible the hardware is asked to enable that key but if - * it cannot do that they key is simply kept for software encryption. - * There is currently no way of knowing this except by looking into - * debugfs. + * Hardware acceleration is done on a best-effort basis for algorithms + * that are implemented in software,  for each key the hardware is asked + * to enable that key for offloading but if it cannot do that the key is + * simply kept for software encryption (unless it is for an algorithm + * that isn't implemented in software). + * There is currently no way of knowing whether a key is handled in SW + * or HW except by looking into debugfs.   * - * All key operations are protected internally. - * - * Within mac80211, key references are, just as STA structure references, - * protected by RCU. Note, however, that some things are unprotected, - * namely the key->sta dereferences within the hardware acceleration - * functions. This means that sta_info_destroy() must remove the key - * which waits for an RCU grace period. + * All key management is internally protected by a mutex. Within all + * other parts of mac80211, key references are, just as STA structure + * references, protected by RCU. Note, however, that some things are + * unprotected, namely the key->sta dereferences within the hardware + * acceleration functions. This means that sta_info_destroy() must + * remove the key which waits for an RCU grace period.   */  static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; @@ -52,28 +55,53 @@ static void assert_key_lock(struct ieee80211_local *local)  	lockdep_assert_held(&local->key_mtx);  } -static struct ieee80211_sta *get_sta_for_key(struct ieee80211_key *key) +static void increment_tailroom_need_count(struct ieee80211_sub_if_data *sdata)  { -	if (key->sta) -		return &key->sta->sta; +	/* +	 * When this count is zero, SKB resizing for allocating tailroom +	 * for IV or MMIC is skipped. But, this check has created two race +	 * cases in xmit path while transiting from zero count to one: +	 * +	 * 1. SKB resize was skipped because no key was added but just before +	 * the xmit key is added and SW encryption kicks off. +	 * +	 * 2. SKB resize was skipped because all the keys were hw planted but +	 * just before xmit one of the key is deleted and SW encryption kicks +	 * off. +	 * +	 * In both the above case SW encryption will find not enough space for +	 * tailroom and exits with WARN_ON. (See WARN_ONs at wpa.c) +	 * +	 * Solution has been explained at +	 * http://mid.gmane.org/1308590980.4322.19.camel@jlt3.sipsolutions.net +	 */ -	return NULL; +	if (!sdata->crypto_tx_tailroom_needed_cnt++) { +		/* +		 * Flush all XMIT packets currently using HW encryption or no +		 * encryption at all if the count transition is from 0 -> 1. +		 */ +		synchronize_net(); +	}  }  static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)  {  	struct ieee80211_sub_if_data *sdata; -	struct ieee80211_sta *sta; +	struct sta_info *sta;  	int ret;  	might_sleep(); +	if (key->flags & KEY_FLAG_TAINTED) +		return -EINVAL; +  	if (!key->local->ops->set_key)  		goto out_unsupported;  	assert_key_lock(key->local); -	sta = get_sta_for_key(key); +	sta = key->sta;  	/*  	 * If this is a per-STA GTK, check if it @@ -83,6 +111,9 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)  	    !(key->local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK))  		goto out_unsupported; +	if (sta && !sta->uploaded) +		goto out_unsupported; +  	sdata = key->sdata;  	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {  		/* @@ -91,22 +122,30 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)  		 */  		if (!(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE))  			goto out_unsupported; -		sdata = container_of(sdata->bss, -				     struct ieee80211_sub_if_data, -				     u.ap);  	} -	ret = drv_set_key(key->local, SET_KEY, sdata, sta, &key->conf); +	ret = drv_set_key(key->local, SET_KEY, sdata, +			  sta ? &sta->sta : NULL, &key->conf);  	if (!ret) {  		key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; + +		if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || +		      (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || +		      (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) +			sdata->crypto_tx_tailroom_needed_cnt--; + +		WARN_ON((key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) && +			(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)); +  		return 0;  	}  	if (ret != -ENOSPC && ret != -EOPNOTSUPP) -		wiphy_err(key->local->hw.wiphy, +		sdata_err(sdata,  			  "failed to set key (%d, %pM) to hardware (%d)\n", -			  key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); +			  key->conf.keyidx, +			  sta ? sta->sta.addr : bcast_addr, ret);   out_unsupported:  	switch (key->conf.cipher) { @@ -125,7 +164,7 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)  static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)  {  	struct ieee80211_sub_if_data *sdata; -	struct ieee80211_sta *sta; +	struct sta_info *sta;  	int ret;  	might_sleep(); @@ -138,67 +177,52 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)  	if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))  		return; -	sta = get_sta_for_key(key); +	sta = key->sta;  	sdata = key->sdata; -	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) -		sdata = container_of(sdata->bss, -				     struct ieee80211_sub_if_data, -				     u.ap); +	if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || +	      (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || +	      (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) +		increment_tailroom_need_count(sdata);  	ret = drv_set_key(key->local, DISABLE_KEY, sdata, -			  sta, &key->conf); +			  sta ? &sta->sta : NULL, &key->conf);  	if (ret) -		wiphy_err(key->local->hw.wiphy, +		sdata_err(sdata,  			  "failed to remove key (%d, %pM) from hardware (%d)\n", -			  key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); +			  key->conf.keyidx, +			  sta ? sta->sta.addr : bcast_addr, ret);  	key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;  } -void ieee80211_key_removed(struct ieee80211_key_conf *key_conf) -{ -	struct ieee80211_key *key; - -	key = container_of(key_conf, struct ieee80211_key, conf); - -	might_sleep(); -	assert_key_lock(key->local); - -	key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; - -	/* -	 * Flush TX path to avoid attempts to use this key -	 * after this function returns. Until then, drivers -	 * must be prepared to handle the key. -	 */ -	synchronize_rcu(); -} -EXPORT_SYMBOL_GPL(ieee80211_key_removed); -  static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, -					int idx) +					int idx, bool uni, bool multi)  {  	struct ieee80211_key *key = NULL;  	assert_key_lock(sdata->local);  	if (idx >= 0 && idx < NUM_DEFAULT_KEYS) -		key = sdata->keys[idx]; +		key = key_mtx_dereference(sdata->local, sdata->keys[idx]); -	rcu_assign_pointer(sdata->default_key, key); - -	if (key) { -		ieee80211_debugfs_key_remove_default(key->sdata); -		ieee80211_debugfs_key_add_default(key->sdata); +	if (uni) { +		rcu_assign_pointer(sdata->default_unicast_key, key); +		drv_set_default_unicast_key(sdata->local, sdata, idx);  	} + +	if (multi) +		rcu_assign_pointer(sdata->default_multicast_key, key); + +	ieee80211_debugfs_key_update_default(sdata);  } -void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx) +void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx, +			       bool uni, bool multi)  {  	mutex_lock(&sdata->local->key_mtx); -	__ieee80211_set_default_key(sdata, idx); +	__ieee80211_set_default_key(sdata, idx, uni, multi);  	mutex_unlock(&sdata->local->key_mtx);  } @@ -211,14 +235,11 @@ __ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, int idx)  	if (idx >= NUM_DEFAULT_KEYS &&  	    idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) -		key = sdata->keys[idx]; +		key = key_mtx_dereference(sdata->local, sdata->keys[idx]);  	rcu_assign_pointer(sdata->default_mgmt_key, key); -	if (key) { -		ieee80211_debugfs_key_remove_mgmt_default(key->sdata); -		ieee80211_debugfs_key_add_mgmt_default(key->sdata); -	} +	ieee80211_debugfs_key_update_default(sdata);  }  void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, @@ -230,66 +251,82 @@ void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,  } -static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, -				    struct sta_info *sta, -				    bool pairwise, -				    struct ieee80211_key *old, -				    struct ieee80211_key *new) +static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, +				  struct sta_info *sta, +				  bool pairwise, +				  struct ieee80211_key *old, +				  struct ieee80211_key *new)  { -	int idx, defkey, defmgmtkey; +	int idx; +	bool defunikey, defmultikey, defmgmtkey; -	if (new) -		list_add(&new->list, &sdata->key_list); +	/* caller must provide at least one old/new */ +	if (WARN_ON(!new && !old)) +		return; -	if (sta && pairwise) { -		rcu_assign_pointer(sta->ptk, new); -	} else if (sta) { -		if (old) -			idx = old->conf.keyidx; -		else -			idx = new->conf.keyidx; -		rcu_assign_pointer(sta->gtk[idx], new); -	} else { -		WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx); +	if (new) +		list_add_tail(&new->list, &sdata->key_list); -		if (old) -			idx = old->conf.keyidx; -		else -			idx = new->conf.keyidx; +	WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx); -		defkey = old && sdata->default_key == old; -		defmgmtkey = old && sdata->default_mgmt_key == old; +	if (old) +		idx = old->conf.keyidx; +	else +		idx = new->conf.keyidx; -		if (defkey && !new) -			__ieee80211_set_default_key(sdata, -1); +	if (sta) { +		if (pairwise) { +			rcu_assign_pointer(sta->ptk[idx], new); +			sta->ptk_idx = idx; +		} else { +			rcu_assign_pointer(sta->gtk[idx], new); +			sta->gtk_idx = idx; +		} +	} else { +		defunikey = old && +			old == key_mtx_dereference(sdata->local, +						sdata->default_unicast_key); +		defmultikey = old && +			old == key_mtx_dereference(sdata->local, +						sdata->default_multicast_key); +		defmgmtkey = old && +			old == key_mtx_dereference(sdata->local, +						sdata->default_mgmt_key); + +		if (defunikey && !new) +			__ieee80211_set_default_key(sdata, -1, true, false); +		if (defmultikey && !new) +			__ieee80211_set_default_key(sdata, -1, false, true);  		if (defmgmtkey && !new)  			__ieee80211_set_default_mgmt_key(sdata, -1);  		rcu_assign_pointer(sdata->keys[idx], new); -		if (defkey && new) -			__ieee80211_set_default_key(sdata, new->conf.keyidx); +		if (defunikey && new) +			__ieee80211_set_default_key(sdata, new->conf.keyidx, +						    true, false); +		if (defmultikey && new) +			__ieee80211_set_default_key(sdata, new->conf.keyidx, +						    false, true);  		if (defmgmtkey && new)  			__ieee80211_set_default_mgmt_key(sdata,  							 new->conf.keyidx);  	} -	if (old) { -		/* -		 * We'll use an empty list to indicate that the key -		 * has already been removed. -		 */ -		list_del_init(&old->list); -	} +	if (old) +		list_del(&old->list);  } -struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, -					  const u8 *key_data, -					  size_t seq_len, const u8 *seq) +struct ieee80211_key * +ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, +		    const u8 *key_data, +		    size_t seq_len, const u8 *seq, +		    const struct ieee80211_cipher_scheme *cs)  {  	struct ieee80211_key *key;  	int i, j, err; -	BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS); +	if (WARN_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)) +		return ERR_PTR(-EINVAL);  	key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL);  	if (!key) @@ -308,29 +345,30 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,  	switch (cipher) {  	case WLAN_CIPHER_SUITE_WEP40:  	case WLAN_CIPHER_SUITE_WEP104: -		key->conf.iv_len = WEP_IV_LEN; -		key->conf.icv_len = WEP_ICV_LEN; +		key->conf.iv_len = IEEE80211_WEP_IV_LEN; +		key->conf.icv_len = IEEE80211_WEP_ICV_LEN;  		break;  	case WLAN_CIPHER_SUITE_TKIP: -		key->conf.iv_len = TKIP_IV_LEN; -		key->conf.icv_len = TKIP_ICV_LEN; +		key->conf.iv_len = IEEE80211_TKIP_IV_LEN; +		key->conf.icv_len = IEEE80211_TKIP_ICV_LEN;  		if (seq) { -			for (i = 0; i < NUM_RX_DATA_QUEUES; i++) { +			for (i = 0; i < IEEE80211_NUM_TIDS; i++) {  				key->u.tkip.rx[i].iv32 =  					get_unaligned_le32(&seq[2]);  				key->u.tkip.rx[i].iv16 =  					get_unaligned_le16(seq);  			}  		} +		spin_lock_init(&key->u.tkip.txlock);  		break;  	case WLAN_CIPHER_SUITE_CCMP: -		key->conf.iv_len = CCMP_HDR_LEN; -		key->conf.icv_len = CCMP_MIC_LEN; +		key->conf.iv_len = IEEE80211_CCMP_HDR_LEN; +		key->conf.icv_len = IEEE80211_CCMP_MIC_LEN;  		if (seq) { -			for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++) -				for (j = 0; j < CCMP_PN_LEN; j++) +			for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) +				for (j = 0; j < IEEE80211_CCMP_PN_LEN; j++)  					key->u.ccmp.rx_pn[i][j] = -						seq[CCMP_PN_LEN - j - 1]; +						seq[IEEE80211_CCMP_PN_LEN - j - 1];  		}  		/*  		 * Initialize AES key state here as an optimization so that @@ -340,15 +378,16 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,  		if (IS_ERR(key->u.ccmp.tfm)) {  			err = PTR_ERR(key->u.ccmp.tfm);  			kfree(key); -			key = ERR_PTR(err); +			return ERR_PTR(err);  		}  		break;  	case WLAN_CIPHER_SUITE_AES_CMAC:  		key->conf.iv_len = 0;  		key->conf.icv_len = sizeof(struct ieee80211_mmie);  		if (seq) -			for (j = 0; j < 6; j++) -				key->u.aes_cmac.rx_pn[j] = seq[6 - j - 1]; +			for (j = 0; j < IEEE80211_CMAC_PN_LEN; j++) +				key->u.aes_cmac.rx_pn[j] = +					seq[IEEE80211_CMAC_PN_LEN - j - 1];  		/*  		 * Initialize AES key state here as an optimization so that  		 * it does not need to be initialized for every packet. @@ -358,9 +397,21 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,  		if (IS_ERR(key->u.aes_cmac.tfm)) {  			err = PTR_ERR(key->u.aes_cmac.tfm);  			kfree(key); -			key = ERR_PTR(err); +			return ERR_PTR(err);  		}  		break; +	default: +		if (cs) { +			size_t len = (seq_len > MAX_PN_LEN) ? +						MAX_PN_LEN : seq_len; + +			key->conf.iv_len = cs->hdr_len; +			key->conf.icv_len = cs->mic_len; +			for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) +				for (j = 0; j < len; j++) +					key->u.gen.rx_pn[i][j] = +							seq[len - j - 1]; +		}  	}  	memcpy(key->conf.key, key_data, key_len);  	INIT_LIST_HEAD(&key->list); @@ -368,108 +419,120 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,  	return key;  } -static void __ieee80211_key_destroy(struct ieee80211_key *key) +static void ieee80211_key_free_common(struct ieee80211_key *key)  { -	if (!key) -		return; - -	if (key->local) -		ieee80211_key_disable_hw_accel(key); -  	if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP)  		ieee80211_aes_key_free(key->u.ccmp.tfm);  	if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC)  		ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); +	kfree(key); +} + +static void __ieee80211_key_destroy(struct ieee80211_key *key, +				    bool delay_tailroom) +{  	if (key->local) +		ieee80211_key_disable_hw_accel(key); + +	if (key->local) { +		struct ieee80211_sub_if_data *sdata = key->sdata; +  		ieee80211_debugfs_key_remove(key); -	kfree(key); +		if (delay_tailroom) { +			/* see ieee80211_delayed_tailroom_dec */ +			sdata->crypto_tx_tailroom_pending_dec++; +			schedule_delayed_work(&sdata->dec_tailroom_needed_wk, +					      HZ/2); +		} else { +			sdata->crypto_tx_tailroom_needed_cnt--; +		} +	} + +	ieee80211_key_free_common(key); +} + +static void ieee80211_key_destroy(struct ieee80211_key *key, +				  bool delay_tailroom) +{ +	if (!key) +		return; + +	/* +	 * Synchronize so the TX path can no longer be using +	 * this key before we free/remove it. +	 */ +	synchronize_net(); + +	__ieee80211_key_destroy(key, delay_tailroom); +} + +void ieee80211_key_free_unused(struct ieee80211_key *key) +{ +	WARN_ON(key->sdata || key->local); +	ieee80211_key_free_common(key);  }  int ieee80211_key_link(struct ieee80211_key *key,  		       struct ieee80211_sub_if_data *sdata,  		       struct sta_info *sta)  { +	struct ieee80211_local *local = sdata->local;  	struct ieee80211_key *old_key;  	int idx, ret; -	bool pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE; +	bool pairwise; -	BUG_ON(!sdata); -	BUG_ON(!key); +	if (WARN_ON(!sdata || !key)) +		return -EINVAL; +	pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;  	idx = key->conf.keyidx;  	key->local = sdata->local;  	key->sdata = sdata;  	key->sta = sta; -	if (sta) { -		/* -		 * some hardware cannot handle TKIP with QoS, so -		 * we indicate whether QoS could be in use. -		 */ -		if (test_sta_flags(sta, WLAN_STA_WME)) -			key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; -	} else { -		if (sdata->vif.type == NL80211_IFTYPE_STATION) { -			struct sta_info *ap; - -			/* -			 * We're getting a sta pointer in, -			 * so must be under RCU read lock. -			 */ - -			/* same here, the AP could be using QoS */ -			ap = sta_info_get(key->sdata, key->sdata->u.mgd.bssid); -			if (ap) { -				if (test_sta_flags(ap, WLAN_STA_WME)) -					key->conf.flags |= -						IEEE80211_KEY_FLAG_WMM_STA; -			} -		} -	} -  	mutex_lock(&sdata->local->key_mtx);  	if (sta && pairwise) -		old_key = sta->ptk; +		old_key = key_mtx_dereference(sdata->local, sta->ptk[idx]);  	else if (sta) -		old_key = sta->gtk[idx]; +		old_key = key_mtx_dereference(sdata->local, sta->gtk[idx]);  	else -		old_key = sdata->keys[idx]; +		old_key = key_mtx_dereference(sdata->local, sdata->keys[idx]); + +	increment_tailroom_need_count(sdata); -	__ieee80211_key_replace(sdata, sta, pairwise, old_key, key); -	__ieee80211_key_destroy(old_key); +	ieee80211_key_replace(sdata, sta, pairwise, old_key, key); +	ieee80211_key_destroy(old_key, true);  	ieee80211_debugfs_key_add(key); -	ret = ieee80211_key_enable_hw_accel(key); +	if (!local->wowlan) { +		ret = ieee80211_key_enable_hw_accel(key); +		if (ret) +			ieee80211_key_free(key, true); +	} else { +		ret = 0; +	}  	mutex_unlock(&sdata->local->key_mtx);  	return ret;  } -static void __ieee80211_key_free(struct ieee80211_key *key) +void ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom)  { +	if (!key) +		return; +  	/*  	 * Replace key with nothingness if it was ever used.  	 */  	if (key->sdata) -		__ieee80211_key_replace(key->sdata, key->sta, +		ieee80211_key_replace(key->sdata, key->sta,  				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,  				key, NULL); -	__ieee80211_key_destroy(key); -} - -void ieee80211_key_free(struct ieee80211_local *local, -			struct ieee80211_key *key) -{ -	if (!key) -		return; - -	mutex_lock(&local->key_mtx); -	__ieee80211_key_free(key); -	mutex_unlock(&local->key_mtx); +	ieee80211_key_destroy(key, delay_tailroom);  }  void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata) @@ -483,37 +546,379 @@ void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata)  	mutex_lock(&sdata->local->key_mtx); -	list_for_each_entry(key, &sdata->key_list, list) +	sdata->crypto_tx_tailroom_needed_cnt = 0; + +	list_for_each_entry(key, &sdata->key_list, list) { +		increment_tailroom_need_count(sdata);  		ieee80211_key_enable_hw_accel(key); +	}  	mutex_unlock(&sdata->local->key_mtx);  } -void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata) +void ieee80211_iter_keys(struct ieee80211_hw *hw, +			 struct ieee80211_vif *vif, +			 void (*iter)(struct ieee80211_hw *hw, +				      struct ieee80211_vif *vif, +				      struct ieee80211_sta *sta, +				      struct ieee80211_key_conf *key, +				      void *data), +			 void *iter_data)  { -	struct ieee80211_key *key; +	struct ieee80211_local *local = hw_to_local(hw); +	struct ieee80211_key *key, *tmp; +	struct ieee80211_sub_if_data *sdata;  	ASSERT_RTNL(); -	mutex_lock(&sdata->local->key_mtx); +	mutex_lock(&local->key_mtx); +	if (vif) { +		sdata = vif_to_sdata(vif); +		list_for_each_entry_safe(key, tmp, &sdata->key_list, list) +			iter(hw, &sdata->vif, +			     key->sta ? &key->sta->sta : NULL, +			     &key->conf, iter_data); +	} else { +		list_for_each_entry(sdata, &local->interfaces, list) +			list_for_each_entry_safe(key, tmp, +						 &sdata->key_list, list) +				iter(hw, &sdata->vif, +				     key->sta ? &key->sta->sta : NULL, +				     &key->conf, iter_data); +	} +	mutex_unlock(&local->key_mtx); +} +EXPORT_SYMBOL(ieee80211_iter_keys); -	list_for_each_entry(key, &sdata->key_list, list) -		ieee80211_key_disable_hw_accel(key); +static void ieee80211_free_keys_iface(struct ieee80211_sub_if_data *sdata, +				      struct list_head *keys) +{ +	struct ieee80211_key *key, *tmp; -	mutex_unlock(&sdata->local->key_mtx); +	sdata->crypto_tx_tailroom_needed_cnt -= +		sdata->crypto_tx_tailroom_pending_dec; +	sdata->crypto_tx_tailroom_pending_dec = 0; + +	ieee80211_debugfs_key_remove_mgmt_default(sdata); + +	list_for_each_entry_safe(key, tmp, &sdata->key_list, list) { +		ieee80211_key_replace(key->sdata, key->sta, +				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, +				key, NULL); +		list_add_tail(&key->list, keys); +	} + +	ieee80211_debugfs_key_update_default(sdata);  } -void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) +void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata, +			 bool force_synchronize)  { +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_sub_if_data *vlan;  	struct ieee80211_key *key, *tmp; +	LIST_HEAD(keys); -	mutex_lock(&sdata->local->key_mtx); +	cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk); -	ieee80211_debugfs_key_remove_default(sdata); -	ieee80211_debugfs_key_remove_mgmt_default(sdata); +	mutex_lock(&local->key_mtx); -	list_for_each_entry_safe(key, tmp, &sdata->key_list, list) -		__ieee80211_key_free(key); +	ieee80211_free_keys_iface(sdata, &keys); +	if (sdata->vif.type == NL80211_IFTYPE_AP) { +		list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) +			ieee80211_free_keys_iface(vlan, &keys); +	} + +	if (!list_empty(&keys) || force_synchronize) +		synchronize_net(); +	list_for_each_entry_safe(key, tmp, &keys, list) +		__ieee80211_key_destroy(key, false); + +	WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt || +		     sdata->crypto_tx_tailroom_pending_dec); +	if (sdata->vif.type == NL80211_IFTYPE_AP) { +		list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) +			WARN_ON_ONCE(vlan->crypto_tx_tailroom_needed_cnt || +				     vlan->crypto_tx_tailroom_pending_dec); +	} + +	mutex_unlock(&local->key_mtx); +} + +void ieee80211_free_sta_keys(struct ieee80211_local *local, +			     struct sta_info *sta) +{ +	struct ieee80211_key *key; +	int i; + +	mutex_lock(&local->key_mtx); +	for (i = 0; i < NUM_DEFAULT_KEYS; i++) { +		key = key_mtx_dereference(local, sta->gtk[i]); +		if (!key) +			continue; +		ieee80211_key_replace(key->sdata, key->sta, +				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, +				key, NULL); +		__ieee80211_key_destroy(key, true); +	} + +	for (i = 0; i < NUM_DEFAULT_KEYS; i++) { +		key = key_mtx_dereference(local, sta->ptk[i]); +		if (!key) +			continue; +		ieee80211_key_replace(key->sdata, key->sta, +				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, +				key, NULL); +		__ieee80211_key_destroy(key, true); +	} + +	mutex_unlock(&local->key_mtx); +} + +void ieee80211_delayed_tailroom_dec(struct work_struct *wk) +{ +	struct ieee80211_sub_if_data *sdata; + +	sdata = container_of(wk, struct ieee80211_sub_if_data, +			     dec_tailroom_needed_wk.work); + +	/* +	 * The reason for the delayed tailroom needed decrementing is to +	 * make roaming faster: during roaming, all keys are first deleted +	 * and then new keys are installed. The first new key causes the +	 * crypto_tx_tailroom_needed_cnt to go from 0 to 1, which invokes +	 * the cost of synchronize_net() (which can be slow). Avoid this +	 * by deferring the crypto_tx_tailroom_needed_cnt decrementing on +	 * key removal for a while, so if we roam the value is larger than +	 * zero and no 0->1 transition happens. +	 * +	 * The cost is that if the AP switching was from an AP with keys +	 * to one without, we still allocate tailroom while it would no +	 * longer be needed. However, in the typical (fast) roaming case +	 * within an ESS this usually won't happen. +	 */ + +	mutex_lock(&sdata->local->key_mtx); +	sdata->crypto_tx_tailroom_needed_cnt -= +		sdata->crypto_tx_tailroom_pending_dec; +	sdata->crypto_tx_tailroom_pending_dec = 0;  	mutex_unlock(&sdata->local->key_mtx);  } + +void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid, +				const u8 *replay_ctr, gfp_t gfp) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + +	trace_api_gtk_rekey_notify(sdata, bssid, replay_ctr); + +	cfg80211_gtk_rekey_notify(sdata->dev, bssid, replay_ctr, gfp); +} +EXPORT_SYMBOL_GPL(ieee80211_gtk_rekey_notify); + +void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf, +			      struct ieee80211_key_seq *seq) +{ +	struct ieee80211_key *key; +	u64 pn64; + +	if (WARN_ON(!(keyconf->flags & IEEE80211_KEY_FLAG_GENERATE_IV))) +		return; + +	key = container_of(keyconf, struct ieee80211_key, conf); + +	switch (key->conf.cipher) { +	case WLAN_CIPHER_SUITE_TKIP: +		seq->tkip.iv32 = key->u.tkip.tx.iv32; +		seq->tkip.iv16 = key->u.tkip.tx.iv16; +		break; +	case WLAN_CIPHER_SUITE_CCMP: +		pn64 = atomic64_read(&key->u.ccmp.tx_pn); +		seq->ccmp.pn[5] = pn64; +		seq->ccmp.pn[4] = pn64 >> 8; +		seq->ccmp.pn[3] = pn64 >> 16; +		seq->ccmp.pn[2] = pn64 >> 24; +		seq->ccmp.pn[1] = pn64 >> 32; +		seq->ccmp.pn[0] = pn64 >> 40; +		break; +	case WLAN_CIPHER_SUITE_AES_CMAC: +		pn64 = atomic64_read(&key->u.aes_cmac.tx_pn); +		seq->ccmp.pn[5] = pn64; +		seq->ccmp.pn[4] = pn64 >> 8; +		seq->ccmp.pn[3] = pn64 >> 16; +		seq->ccmp.pn[2] = pn64 >> 24; +		seq->ccmp.pn[1] = pn64 >> 32; +		seq->ccmp.pn[0] = pn64 >> 40; +		break; +	default: +		WARN_ON(1); +	} +} +EXPORT_SYMBOL(ieee80211_get_key_tx_seq); + +void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, +			      int tid, struct ieee80211_key_seq *seq) +{ +	struct ieee80211_key *key; +	const u8 *pn; + +	key = container_of(keyconf, struct ieee80211_key, conf); + +	switch (key->conf.cipher) { +	case WLAN_CIPHER_SUITE_TKIP: +		if (WARN_ON(tid < 0 || tid >= IEEE80211_NUM_TIDS)) +			return; +		seq->tkip.iv32 = key->u.tkip.rx[tid].iv32; +		seq->tkip.iv16 = key->u.tkip.rx[tid].iv16; +		break; +	case WLAN_CIPHER_SUITE_CCMP: +		if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS)) +			return; +		if (tid < 0) +			pn = key->u.ccmp.rx_pn[IEEE80211_NUM_TIDS]; +		else +			pn = key->u.ccmp.rx_pn[tid]; +		memcpy(seq->ccmp.pn, pn, IEEE80211_CCMP_PN_LEN); +		break; +	case WLAN_CIPHER_SUITE_AES_CMAC: +		if (WARN_ON(tid != 0)) +			return; +		pn = key->u.aes_cmac.rx_pn; +		memcpy(seq->aes_cmac.pn, pn, IEEE80211_CMAC_PN_LEN); +		break; +	} +} +EXPORT_SYMBOL(ieee80211_get_key_rx_seq); + +void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf, +			      struct ieee80211_key_seq *seq) +{ +	struct ieee80211_key *key; +	u64 pn64; + +	key = container_of(keyconf, struct ieee80211_key, conf); + +	switch (key->conf.cipher) { +	case WLAN_CIPHER_SUITE_TKIP: +		key->u.tkip.tx.iv32 = seq->tkip.iv32; +		key->u.tkip.tx.iv16 = seq->tkip.iv16; +		break; +	case WLAN_CIPHER_SUITE_CCMP: +		pn64 = (u64)seq->ccmp.pn[5] | +		       ((u64)seq->ccmp.pn[4] << 8) | +		       ((u64)seq->ccmp.pn[3] << 16) | +		       ((u64)seq->ccmp.pn[2] << 24) | +		       ((u64)seq->ccmp.pn[1] << 32) | +		       ((u64)seq->ccmp.pn[0] << 40); +		atomic64_set(&key->u.ccmp.tx_pn, pn64); +		break; +	case WLAN_CIPHER_SUITE_AES_CMAC: +		pn64 = (u64)seq->aes_cmac.pn[5] | +		       ((u64)seq->aes_cmac.pn[4] << 8) | +		       ((u64)seq->aes_cmac.pn[3] << 16) | +		       ((u64)seq->aes_cmac.pn[2] << 24) | +		       ((u64)seq->aes_cmac.pn[1] << 32) | +		       ((u64)seq->aes_cmac.pn[0] << 40); +		atomic64_set(&key->u.aes_cmac.tx_pn, pn64); +		break; +	default: +		WARN_ON(1); +		break; +	} +} +EXPORT_SYMBOL_GPL(ieee80211_set_key_tx_seq); + +void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf, +			      int tid, struct ieee80211_key_seq *seq) +{ +	struct ieee80211_key *key; +	u8 *pn; + +	key = container_of(keyconf, struct ieee80211_key, conf); + +	switch (key->conf.cipher) { +	case WLAN_CIPHER_SUITE_TKIP: +		if (WARN_ON(tid < 0 || tid >= IEEE80211_NUM_TIDS)) +			return; +		key->u.tkip.rx[tid].iv32 = seq->tkip.iv32; +		key->u.tkip.rx[tid].iv16 = seq->tkip.iv16; +		break; +	case WLAN_CIPHER_SUITE_CCMP: +		if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS)) +			return; +		if (tid < 0) +			pn = key->u.ccmp.rx_pn[IEEE80211_NUM_TIDS]; +		else +			pn = key->u.ccmp.rx_pn[tid]; +		memcpy(pn, seq->ccmp.pn, IEEE80211_CCMP_PN_LEN); +		break; +	case WLAN_CIPHER_SUITE_AES_CMAC: +		if (WARN_ON(tid != 0)) +			return; +		pn = key->u.aes_cmac.rx_pn; +		memcpy(pn, seq->aes_cmac.pn, IEEE80211_CMAC_PN_LEN); +		break; +	default: +		WARN_ON(1); +		break; +	} +} +EXPORT_SYMBOL_GPL(ieee80211_set_key_rx_seq); + +void ieee80211_remove_key(struct ieee80211_key_conf *keyconf) +{ +	struct ieee80211_key *key; + +	key = container_of(keyconf, struct ieee80211_key, conf); + +	assert_key_lock(key->local); + +	/* +	 * if key was uploaded, we assume the driver will/has remove(d) +	 * it, so adjust bookkeeping accordingly +	 */ +	if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) { +		key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; + +		if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || +		      (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || +		      (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) +			increment_tailroom_need_count(key->sdata); +	} + +	ieee80211_key_free(key, false); +} +EXPORT_SYMBOL_GPL(ieee80211_remove_key); + +struct ieee80211_key_conf * +ieee80211_gtk_rekey_add(struct ieee80211_vif *vif, +			struct ieee80211_key_conf *keyconf) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_key *key; +	int err; + +	if (WARN_ON(!local->wowlan)) +		return ERR_PTR(-EINVAL); + +	if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) +		return ERR_PTR(-EINVAL); + +	key = ieee80211_key_alloc(keyconf->cipher, keyconf->keyidx, +				  keyconf->keylen, keyconf->key, +				  0, NULL, NULL); +	if (IS_ERR(key)) +		return ERR_CAST(key); + +	if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED) +		key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; + +	err = ieee80211_key_link(key, sdata, NULL); +	if (err) +		return ERR_PTR(err); + +	return &key->conf; +} +EXPORT_SYMBOL_GPL(ieee80211_gtk_rekey_add); diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 0db1c0f5f69..19db68663d7 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -18,19 +18,7 @@  #define NUM_DEFAULT_KEYS 4  #define NUM_DEFAULT_MGMT_KEYS 2 - -#define WEP_IV_LEN		4 -#define WEP_ICV_LEN		4 -#define ALG_TKIP_KEY_LEN	32 -#define ALG_CCMP_KEY_LEN	16 -#define CCMP_HDR_LEN		8 -#define CCMP_MIC_LEN		8 -#define CCMP_TK_LEN		16 -#define CCMP_PN_LEN		6 -#define TKIP_IV_LEN		8 -#define TKIP_ICV_LEN		4 - -#define NUM_RX_DATA_QUEUES	17 +#define MAX_PN_LEN 16  struct ieee80211_local;  struct ieee80211_sub_if_data; @@ -41,9 +29,11 @@ struct sta_info;   *   * @KEY_FLAG_UPLOADED_TO_HARDWARE: Indicates that this key is present   *	in the hardware for TX crypto hardware acceleration. + * @KEY_FLAG_TAINTED: Key is tainted and packets should be dropped.   */  enum ieee80211_internal_key_flags {  	KEY_FLAG_UPLOADED_TO_HARDWARE	= BIT(0), +	KEY_FLAG_TAINTED		= BIT(1),  };  enum ieee80211_internal_tkip_state { @@ -53,9 +43,10 @@ enum ieee80211_internal_tkip_state {  };  struct tkip_ctx { -	u32 iv32; -	u16 iv16; -	u16 p1k[5]; +	u32 iv32;	/* current iv32 */ +	u16 iv16;	/* current iv16 */ +	u16 p1k[5];	/* p1k cache */ +	u32 p1k_iv32;	/* iv32 for which p1k computed */  	enum ieee80211_internal_tkip_state state;  }; @@ -72,40 +63,41 @@ struct ieee80211_key {  	union {  		struct { +			/* protects tx context */ +			spinlock_t txlock; +  			/* last used TSC */  			struct tkip_ctx tx;  			/* last received RSC */ -			struct tkip_ctx rx[NUM_RX_DATA_QUEUES]; +			struct tkip_ctx rx[IEEE80211_NUM_TIDS]; + +			/* number of mic failures */ +			u32 mic_failures;  		} tkip;  		struct { -			u8 tx_pn[6]; +			atomic64_t tx_pn;  			/*  			 * Last received packet number. The first -			 * NUM_RX_DATA_QUEUES counters are used with Data +			 * IEEE80211_NUM_TIDS counters are used with Data  			 * frames and the last counter is used with Robust  			 * Management frames.  			 */ -			u8 rx_pn[NUM_RX_DATA_QUEUES + 1][6]; -			struct crypto_cipher *tfm; +			u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; +			struct crypto_aead *tfm;  			u32 replays; /* dot11RSNAStatsCCMPReplays */ -			/* scratch buffers for virt_to_page() (crypto API) */ -#ifndef AES_BLOCK_LEN -#define AES_BLOCK_LEN 16 -#endif -			u8 tx_crypto_buf[6 * AES_BLOCK_LEN]; -			u8 rx_crypto_buf[6 * AES_BLOCK_LEN];  		} ccmp;  		struct { -			u8 tx_pn[6]; -			u8 rx_pn[6]; +			atomic64_t tx_pn; +			u8 rx_pn[IEEE80211_CMAC_PN_LEN];  			struct crypto_cipher *tfm;  			u32 replays; /* dot11RSNAStatsCMACReplays */  			u32 icverrors; /* dot11RSNAStatsCMACICVErrors */ -			/* scratch buffers for virt_to_page() (crypto API) */ -			u8 tx_crypto_buf[2 * AES_BLOCK_LEN]; -			u8 rx_crypto_buf[2 * AES_BLOCK_LEN];  		} aes_cmac; +		struct { +			/* generic cipher scheme */ +			u8 rx_pn[IEEE80211_NUM_TIDS + 1][MAX_PN_LEN]; +		} gen;  	} u;  	/* number of times this key has been used */ @@ -126,23 +118,33 @@ struct ieee80211_key {  	struct ieee80211_key_conf conf;  }; -struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, -					  const u8 *key_data, -					  size_t seq_len, const u8 *seq); +struct ieee80211_key * +ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, +		    const u8 *key_data, +		    size_t seq_len, const u8 *seq, +		    const struct ieee80211_cipher_scheme *cs);  /*   * Insert a key into data structures (sdata, sta if necessary) - * to make it used, free old key. + * to make it used, free old key. On failure, also free the new key.   */ -int __must_check ieee80211_key_link(struct ieee80211_key *key, -				    struct ieee80211_sub_if_data *sdata, -				    struct sta_info *sta); -void ieee80211_key_free(struct ieee80211_local *local, -			struct ieee80211_key *key); -void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx); +int ieee80211_key_link(struct ieee80211_key *key, +		       struct ieee80211_sub_if_data *sdata, +		       struct sta_info *sta); +void ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom); +void ieee80211_key_free_unused(struct ieee80211_key *key); +void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx, +			       bool uni, bool multi);  void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,  				    int idx); -void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata); +void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata, +			 bool force_synchronize); +void ieee80211_free_sta_keys(struct ieee80211_local *local, +			     struct sta_info *sta);  void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata); -void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata); + +#define key_mtx_dereference(local, ref) \ +	rcu_dereference_protected(ref, lockdep_is_held(&((local)->key_mtx))) + +void ieee80211_delayed_tailroom_dec(struct work_struct *wk);  #endif /* IEEE80211_KEY_H */ diff --git a/net/mac80211/led.c b/net/mac80211/led.c index 063aad94424..e2b836446af 100644 --- a/net/mac80211/led.c +++ b/net/mac80211/led.c @@ -9,29 +9,25 @@  /* just for IFNAMSIZ */  #include <linux/if.h>  #include <linux/slab.h> +#include <linux/export.h>  #include "led.h" +#define MAC80211_BLINK_DELAY 50 /* ms */ +  void ieee80211_led_rx(struct ieee80211_local *local)  { +	unsigned long led_delay = MAC80211_BLINK_DELAY;  	if (unlikely(!local->rx_led))  		return; -	if (local->rx_led_counter++ % 2 == 0) -		led_trigger_event(local->rx_led, LED_OFF); -	else -		led_trigger_event(local->rx_led, LED_FULL); +	led_trigger_blink_oneshot(local->rx_led, &led_delay, &led_delay, 0);  } -/* q is 1 if a packet was enqueued, 0 if it has been transmitted */ -void ieee80211_led_tx(struct ieee80211_local *local, int q) +void ieee80211_led_tx(struct ieee80211_local *local)  { +	unsigned long led_delay = MAC80211_BLINK_DELAY;  	if (unlikely(!local->tx_led))  		return; -	/* not sure how this is supposed to work ... */ -	local->tx_led_counter += 2*q-1; -	if (local->tx_led_counter % 2 == 0) -		led_trigger_event(local->tx_led, LED_OFF); -	else -		led_trigger_event(local->tx_led, LED_FULL); +	led_trigger_blink_oneshot(local->tx_led, &led_delay, &led_delay, 0);  }  void ieee80211_led_assoc(struct ieee80211_local *local, bool associated) @@ -54,12 +50,22 @@ void ieee80211_led_radio(struct ieee80211_local *local, bool enabled)  		led_trigger_event(local->radio_led, LED_OFF);  } +void ieee80211_led_names(struct ieee80211_local *local) +{ +	snprintf(local->rx_led_name, sizeof(local->rx_led_name), +		 "%srx", wiphy_name(local->hw.wiphy)); +	snprintf(local->tx_led_name, sizeof(local->tx_led_name), +		 "%stx", wiphy_name(local->hw.wiphy)); +	snprintf(local->assoc_led_name, sizeof(local->assoc_led_name), +		 "%sassoc", wiphy_name(local->hw.wiphy)); +	snprintf(local->radio_led_name, sizeof(local->radio_led_name), +		 "%sradio", wiphy_name(local->hw.wiphy)); +} +  void ieee80211_led_init(struct ieee80211_local *local)  {  	local->rx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);  	if (local->rx_led) { -		snprintf(local->rx_led_name, sizeof(local->rx_led_name), -			 "%srx", wiphy_name(local->hw.wiphy));  		local->rx_led->name = local->rx_led_name;  		if (led_trigger_register(local->rx_led)) {  			kfree(local->rx_led); @@ -69,8 +75,6 @@ void ieee80211_led_init(struct ieee80211_local *local)  	local->tx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);  	if (local->tx_led) { -		snprintf(local->tx_led_name, sizeof(local->tx_led_name), -			 "%stx", wiphy_name(local->hw.wiphy));  		local->tx_led->name = local->tx_led_name;  		if (led_trigger_register(local->tx_led)) {  			kfree(local->tx_led); @@ -80,8 +84,6 @@ void ieee80211_led_init(struct ieee80211_local *local)  	local->assoc_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);  	if (local->assoc_led) { -		snprintf(local->assoc_led_name, sizeof(local->assoc_led_name), -			 "%sassoc", wiphy_name(local->hw.wiphy));  		local->assoc_led->name = local->assoc_led_name;  		if (led_trigger_register(local->assoc_led)) {  			kfree(local->assoc_led); @@ -91,14 +93,19 @@ void ieee80211_led_init(struct ieee80211_local *local)  	local->radio_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);  	if (local->radio_led) { -		snprintf(local->radio_led_name, sizeof(local->radio_led_name), -			 "%sradio", wiphy_name(local->hw.wiphy));  		local->radio_led->name = local->radio_led_name;  		if (led_trigger_register(local->radio_led)) {  			kfree(local->radio_led);  			local->radio_led = NULL;  		}  	} + +	if (local->tpt_led_trigger) { +		if (led_trigger_register(&local->tpt_led_trigger->trig)) { +			kfree(local->tpt_led_trigger); +			local->tpt_led_trigger = NULL; +		} +	}  }  void ieee80211_led_exit(struct ieee80211_local *local) @@ -119,15 +126,18 @@ void ieee80211_led_exit(struct ieee80211_local *local)  		led_trigger_unregister(local->rx_led);  		kfree(local->rx_led);  	} + +	if (local->tpt_led_trigger) { +		led_trigger_unregister(&local->tpt_led_trigger->trig); +		kfree(local->tpt_led_trigger); +	}  }  char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)  {  	struct ieee80211_local *local = hw_to_local(hw); -	if (local->radio_led) -		return local->radio_led_name; -	return NULL; +	return local->radio_led_name;  }  EXPORT_SYMBOL(__ieee80211_get_radio_led_name); @@ -135,9 +145,7 @@ char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw)  {  	struct ieee80211_local *local = hw_to_local(hw); -	if (local->assoc_led) -		return local->assoc_led_name; -	return NULL; +	return local->assoc_led_name;  }  EXPORT_SYMBOL(__ieee80211_get_assoc_led_name); @@ -145,9 +153,7 @@ char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw)  {  	struct ieee80211_local *local = hw_to_local(hw); -	if (local->tx_led) -		return local->tx_led_name; -	return NULL; +	return local->tx_led_name;  }  EXPORT_SYMBOL(__ieee80211_get_tx_led_name); @@ -155,8 +161,144 @@ char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw)  {  	struct ieee80211_local *local = hw_to_local(hw); -	if (local->rx_led) -		return local->rx_led_name; -	return NULL; +	return local->rx_led_name;  }  EXPORT_SYMBOL(__ieee80211_get_rx_led_name); + +static unsigned long tpt_trig_traffic(struct ieee80211_local *local, +				      struct tpt_led_trigger *tpt_trig) +{ +	unsigned long traffic, delta; + +	traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes; + +	delta = traffic - tpt_trig->prev_traffic; +	tpt_trig->prev_traffic = traffic; +	return DIV_ROUND_UP(delta, 1024 / 8); +} + +static void tpt_trig_timer(unsigned long data) +{ +	struct ieee80211_local *local = (void *)data; +	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; +	struct led_classdev *led_cdev; +	unsigned long on, off, tpt; +	int i; + +	if (!tpt_trig->running) +		return; + +	mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ)); + +	tpt = tpt_trig_traffic(local, tpt_trig); + +	/* default to just solid on */ +	on = 1; +	off = 0; + +	for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) { +		if (tpt_trig->blink_table[i].throughput < 0 || +		    tpt > tpt_trig->blink_table[i].throughput) { +			off = tpt_trig->blink_table[i].blink_time / 2; +			on = tpt_trig->blink_table[i].blink_time - off; +			break; +		} +	} + +	read_lock(&tpt_trig->trig.leddev_list_lock); +	list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list) +		led_blink_set(led_cdev, &on, &off); +	read_unlock(&tpt_trig->trig.leddev_list_lock); +} + +char *__ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, +				unsigned int flags, +				const struct ieee80211_tpt_blink *blink_table, +				unsigned int blink_table_len) +{ +	struct ieee80211_local *local = hw_to_local(hw); +	struct tpt_led_trigger *tpt_trig; + +	if (WARN_ON(local->tpt_led_trigger)) +		return NULL; + +	tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL); +	if (!tpt_trig) +		return NULL; + +	snprintf(tpt_trig->name, sizeof(tpt_trig->name), +		 "%stpt", wiphy_name(local->hw.wiphy)); + +	tpt_trig->trig.name = tpt_trig->name; + +	tpt_trig->blink_table = blink_table; +	tpt_trig->blink_table_len = blink_table_len; +	tpt_trig->want = flags; + +	setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local); + +	local->tpt_led_trigger = tpt_trig; + +	return tpt_trig->name; +} +EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger); + +static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local) +{ +	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; + +	if (tpt_trig->running) +		return; + +	/* reset traffic */ +	tpt_trig_traffic(local, tpt_trig); +	tpt_trig->running = true; + +	tpt_trig_timer((unsigned long)local); +	mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ)); +} + +static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local) +{ +	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; +	struct led_classdev *led_cdev; + +	if (!tpt_trig->running) +		return; + +	tpt_trig->running = false; +	del_timer_sync(&tpt_trig->timer); + +	read_lock(&tpt_trig->trig.leddev_list_lock); +	list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list) +		led_set_brightness(led_cdev, LED_OFF); +	read_unlock(&tpt_trig->trig.leddev_list_lock); +} + +void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local, +				unsigned int types_on, unsigned int types_off) +{ +	struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; +	bool allowed; + +	WARN_ON(types_on & types_off); + +	if (!tpt_trig) +		return; + +	tpt_trig->active &= ~types_off; +	tpt_trig->active |= types_on; + +	/* +	 * Regardless of wanted state, we shouldn't blink when +	 * the radio is disabled -- this can happen due to some +	 * code ordering issues with __ieee80211_recalc_idle() +	 * being called before the radio is started. +	 */ +	allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO; + +	if (!allowed || !(tpt_trig->active & tpt_trig->want)) +		ieee80211_stop_tpt_led_trig(local); +	else +		ieee80211_start_tpt_led_trig(local); +} diff --git a/net/mac80211/led.h b/net/mac80211/led.h index 77b1e1ba603..89f4344f13b 100644 --- a/net/mac80211/led.h +++ b/net/mac80211/led.h @@ -12,19 +12,22 @@  #include "ieee80211_i.h"  #ifdef CONFIG_MAC80211_LEDS -extern void ieee80211_led_rx(struct ieee80211_local *local); -extern void ieee80211_led_tx(struct ieee80211_local *local, int q); -extern void ieee80211_led_assoc(struct ieee80211_local *local, -				bool associated); -extern void ieee80211_led_radio(struct ieee80211_local *local, -				bool enabled); -extern void ieee80211_led_init(struct ieee80211_local *local); -extern void ieee80211_led_exit(struct ieee80211_local *local); +void ieee80211_led_rx(struct ieee80211_local *local); +void ieee80211_led_tx(struct ieee80211_local *local); +void ieee80211_led_assoc(struct ieee80211_local *local, +			 bool associated); +void ieee80211_led_radio(struct ieee80211_local *local, +			 bool enabled); +void ieee80211_led_names(struct ieee80211_local *local); +void ieee80211_led_init(struct ieee80211_local *local); +void ieee80211_led_exit(struct ieee80211_local *local); +void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local, +				unsigned int types_on, unsigned int types_off);  #else  static inline void ieee80211_led_rx(struct ieee80211_local *local)  {  } -static inline void ieee80211_led_tx(struct ieee80211_local *local, int q) +static inline void ieee80211_led_tx(struct ieee80211_local *local)  {  }  static inline void ieee80211_led_assoc(struct ieee80211_local *local, @@ -35,10 +38,36 @@ static inline void ieee80211_led_radio(struct ieee80211_local *local,  				       bool enabled)  {  } +static inline void ieee80211_led_names(struct ieee80211_local *local) +{ +}  static inline void ieee80211_led_init(struct ieee80211_local *local)  {  }  static inline void ieee80211_led_exit(struct ieee80211_local *local)  {  } +static inline void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local, +					      unsigned int types_on, +					      unsigned int types_off) +{ +} +#endif + +static inline void +ieee80211_tpt_led_trig_tx(struct ieee80211_local *local, __le16 fc, int bytes) +{ +#ifdef CONFIG_MAC80211_LEDS +	if (local->tpt_led_trigger && ieee80211_is_data(fc)) +		local->tpt_led_trigger->tx_bytes += bytes; +#endif +} + +static inline void +ieee80211_tpt_led_trig_rx(struct ieee80211_local *local, __le16 fc, int bytes) +{ +#ifdef CONFIG_MAC80211_LEDS +	if (local->tpt_led_trigger && ieee80211_is_data(fc)) +		local->tpt_led_trigger->rx_bytes += bytes;  #endif +} diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 107a0cbe52a..d17c26d6e36 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -19,10 +19,11 @@  #include <linux/if_arp.h>  #include <linux/rtnetlink.h>  #include <linux/bitmap.h> -#include <linux/pm_qos_params.h> +#include <linux/pm_qos.h>  #include <linux/inetdevice.h>  #include <net/net_namespace.h>  #include <net/cfg80211.h> +#include <net/addrconf.h>  #include "ieee80211_i.h"  #include "driver-ops.h" @@ -33,12 +34,6 @@  #include "cfg.h"  #include "debugfs.h" - -bool ieee80211_disable_40mhz_24ghz; -module_param(ieee80211_disable_40mhz_24ghz, bool, 0644); -MODULE_PARM_DESC(ieee80211_disable_40mhz_24ghz, -		 "Disable 40MHz support in the 2.4GHz band"); -  void ieee80211_configure_filter(struct ieee80211_local *local)  {  	u64 mc; @@ -51,7 +46,8 @@ void ieee80211_configure_filter(struct ieee80211_local *local)  	if (atomic_read(&local->iff_allmultis))  		new_flags |= FIF_ALLMULTI; -	if (local->monitors || local->scanning) +	if (local->monitors || test_bit(SCAN_SW_SCANNING, &local->scanning) || +	    test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning))  		new_flags |= FIF_BCN_PRBRESP_PROMISC;  	if (local->fif_probe_req || local->probe_req_reg) @@ -96,39 +92,41 @@ static void ieee80211_reconfig_filter(struct work_struct *work)  	ieee80211_configure_filter(local);  } -int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) +static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)  { -	struct ieee80211_channel *chan, *scan_chan; -	int ret = 0; +	struct ieee80211_sub_if_data *sdata; +	struct cfg80211_chan_def chandef = {}; +	u32 changed = 0;  	int power; -	enum nl80211_channel_type channel_type;  	u32 offchannel_flag; -	might_sleep(); - -	scan_chan = local->scan_channel; -  	offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; -	if (scan_chan) { -		chan = scan_chan; -		channel_type = NL80211_CHAN_NO_HT; -		local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL; -	} else if (local->tmp_channel && -		   local->oper_channel != local->tmp_channel) { -		chan = scan_chan = local->tmp_channel; -		channel_type = local->tmp_channel_type; + +	if (local->scan_chandef.chan) { +		chandef = local->scan_chandef; +	} else if (local->tmp_channel) { +		chandef.chan = local->tmp_channel; +		chandef.width = NL80211_CHAN_WIDTH_20_NOHT; +		chandef.center_freq1 = chandef.chan->center_freq; +	} else +		chandef = local->_oper_chandef; + +	WARN(!cfg80211_chandef_valid(&chandef), +	     "control:%d MHz width:%d center: %d/%d MHz", +	     chandef.chan->center_freq, chandef.width, +	     chandef.center_freq1, chandef.center_freq2); + +	if (!cfg80211_chandef_identical(&chandef, &local->_oper_chandef))  		local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL; -	} else { -		chan = local->oper_channel; -		channel_type = local->_oper_channel_type; +	else  		local->hw.conf.flags &= ~IEEE80211_CONF_OFFCHANNEL; -	} +  	offchannel_flag ^= local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; -	if (offchannel_flag || chan != local->hw.conf.channel || -	    channel_type != local->hw.conf.channel_type) { -		local->hw.conf.channel = chan; -		local->hw.conf.channel_type = channel_type; +	if (offchannel_flag || +	    !cfg80211_chandef_identical(&local->hw.conf.chandef, +					&local->_oper_chandef)) { +		local->hw.conf.chandef = chandef;  		changed |= IEEE80211_CONF_CHANGE_CHANNEL;  	} @@ -144,21 +142,38 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)  		changed |= IEEE80211_CONF_CHANGE_SMPS;  	} -	if (scan_chan) -		power = chan->max_power; -	else -		power = local->power_constr_level ? -			(chan->max_power - local->power_constr_level) : -			chan->max_power; +	power = ieee80211_chandef_max_power(&chandef); -	if (local->user_power_level >= 0) -		power = min(power, local->user_power_level); +	rcu_read_lock(); +	list_for_each_entry_rcu(sdata, &local->interfaces, list) { +		if (!rcu_access_pointer(sdata->vif.chanctx_conf)) +			continue; +		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +			continue; +		power = min(power, sdata->vif.bss_conf.txpower); +	} +	rcu_read_unlock();  	if (local->hw.conf.power_level != power) {  		changed |= IEEE80211_CONF_CHANGE_POWER;  		local->hw.conf.power_level = power;  	} +	return changed; +} + +int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) +{ +	int ret = 0; + +	might_sleep(); + +	if (!local->use_chanctx) +		changed |= ieee80211_hw_conf_chan(local); +	else +		changed &= ~(IEEE80211_CONF_CHANGE_CHANNEL | +			     IEEE80211_CONF_CHANGE_POWER); +  	if (changed && local->open_count) {  		ret = drv_config(local, changed);  		/* @@ -185,76 +200,9 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,  				      u32 changed)  {  	struct ieee80211_local *local = sdata->local; -	static const u8 zero[ETH_ALEN] = { 0 }; - -	if (!changed) -		return; -	if (sdata->vif.type == NL80211_IFTYPE_STATION) { -		/* -		 * While not associated, claim a BSSID of all-zeroes -		 * so that drivers don't do any weird things with the -		 * BSSID at that time. -		 */ -		if (sdata->vif.bss_conf.assoc) -			sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid; -		else -			sdata->vif.bss_conf.bssid = zero; -	} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) -		sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid; -	else if (sdata->vif.type == NL80211_IFTYPE_AP) -		sdata->vif.bss_conf.bssid = sdata->vif.addr; -	else if (sdata->vif.type == NL80211_IFTYPE_WDS) -		sdata->vif.bss_conf.bssid = NULL; -	else if (ieee80211_vif_is_mesh(&sdata->vif)) { -		sdata->vif.bss_conf.bssid = zero; -	} else { -		WARN_ON(1); +	if (!changed || sdata->vif.type == NL80211_IFTYPE_AP_VLAN)  		return; -	} - -	switch (sdata->vif.type) { -	case NL80211_IFTYPE_AP: -	case NL80211_IFTYPE_ADHOC: -	case NL80211_IFTYPE_WDS: -	case NL80211_IFTYPE_MESH_POINT: -		break; -	default: -		/* do not warn to simplify caller in scan.c */ -		changed &= ~BSS_CHANGED_BEACON_ENABLED; -		if (WARN_ON(changed & BSS_CHANGED_BEACON)) -			return; -		break; -	} - -	if (changed & BSS_CHANGED_BEACON_ENABLED) { -		if (local->quiescing || !ieee80211_sdata_running(sdata) || -		    test_bit(SCAN_SW_SCANNING, &local->scanning)) { -			sdata->vif.bss_conf.enable_beacon = false; -		} else { -			/* -			 * Beacon should be enabled, but AP mode must -			 * check whether there is a beacon configured. -			 */ -			switch (sdata->vif.type) { -			case NL80211_IFTYPE_AP: -				sdata->vif.bss_conf.enable_beacon = -					!!sdata->u.ap.beacon; -				break; -			case NL80211_IFTYPE_ADHOC: -				sdata->vif.bss_conf.enable_beacon = -					!!sdata->u.ibss.presp; -				break; -			case NL80211_IFTYPE_MESH_POINT: -				sdata->vif.bss_conf.enable_beacon = true; -				break; -			default: -				/* not reached */ -				WARN_ON(1); -				break; -			} -		} -	}  	drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed);  } @@ -281,11 +229,11 @@ static void ieee80211_tasklet_handler(unsigned long data)  			/* Clear skb->pkt_type in order to not confuse kernel  			 * netstack. */  			skb->pkt_type = 0; -			ieee80211_rx(local_to_hw(local), skb); +			ieee80211_rx(&local->hw, skb);  			break;  		case IEEE80211_TX_STATUS_MSG:  			skb->pkt_type = 0; -			ieee80211_tx_status(local_to_hw(local), skb); +			ieee80211_tx_status(&local->hw, skb);  			break;  		default:  			WARN(1, "mac80211: Packet is of unknown type %d\n", @@ -304,10 +252,8 @@ static void ieee80211_restart_work(struct work_struct *work)  	/* wait for scan work complete */  	flush_workqueue(local->workqueue); -	mutex_lock(&local->mtx);  	WARN(test_bit(SCAN_HW_SCANNING, &local->scanning), -		"%s called with hardware scan in progress\n", __func__); -	mutex_unlock(&local->mtx); +	     "%s called with hardware scan in progress\n", __func__);  	rtnl_lock();  	ieee80211_scan_cancel(local); @@ -321,24 +267,24 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)  	trace_api_restart_hw(local); +	wiphy_info(hw->wiphy, +		   "Hardware restart was requested\n"); +  	/* use this reason, ieee80211_reconfig will unblock it */ -	ieee80211_stop_queues_by_reason(hw, -		IEEE80211_QUEUE_STOP_REASON_SUSPEND); +	ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_SUSPEND); + +	/* +	 * Stop all Rx during the reconfig. We don't want state changes +	 * or driver callbacks while this is in progress. +	 */ +	local->in_reconfig = true; +	barrier();  	schedule_work(&local->restart_work);  }  EXPORT_SYMBOL(ieee80211_restart_hw); -static void ieee80211_recalc_smps_work(struct work_struct *work) -{ -	struct ieee80211_local *local = -		container_of(work, struct ieee80211_local, recalc_smps); - -	mutex_lock(&local->iflist_mtx); -	ieee80211_recalc_smps(local); -	mutex_unlock(&local->iflist_mtx); -} -  #ifdef CONFIG_INET  static int ieee80211_ifa_changed(struct notifier_block *nb,  				 unsigned long data, void *arg) @@ -365,9 +311,6 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,  	sdata = IEEE80211_DEV_TO_SUB_IF(ndev);  	bss_conf = &sdata->vif.bss_conf; -	if (!ieee80211_sdata_running(sdata)) -		return NOTIFY_DONE; -  	/* ARP filtering is only supported in managed mode */  	if (sdata->vif.type != NL80211_IFTYPE_STATION)  		return NOTIFY_DONE; @@ -377,68 +320,70 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,  		return NOTIFY_DONE;  	ifmgd = &sdata->u.mgd; -	mutex_lock(&ifmgd->mtx); +	sdata_lock(sdata);  	/* Copy the addresses to the bss_conf list */  	ifa = idev->ifa_list; -	while (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN && ifa) { -		bss_conf->arp_addr_list[c] = ifa->ifa_address; +	while (ifa) { +		if (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN) +			bss_conf->arp_addr_list[c] = ifa->ifa_address;  		ifa = ifa->ifa_next;  		c++;  	} -	/* If not all addresses fit the list, disable filtering */ -	if (ifa) { -		sdata->arp_filter_state = false; -		c = 0; -	} else { -		sdata->arp_filter_state = true; -	}  	bss_conf->arp_addr_cnt = c; -	/* Configure driver only if associated */ -	if (ifmgd->associated) { -		bss_conf->arp_filter_enabled = sdata->arp_filter_state; +	/* Configure driver only if associated (which also implies it is up) */ +	if (ifmgd->associated)  		ieee80211_bss_info_change_notify(sdata,  						 BSS_CHANGED_ARP_FILTER); -	} -	mutex_unlock(&ifmgd->mtx); +	sdata_unlock(sdata); -	return NOTIFY_DONE; +	return NOTIFY_OK;  }  #endif -static int ieee80211_napi_poll(struct napi_struct *napi, int budget) +#if IS_ENABLED(CONFIG_IPV6) +static int ieee80211_ifa6_changed(struct notifier_block *nb, +				  unsigned long data, void *arg)  { +	struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)arg; +	struct inet6_dev *idev = ifa->idev; +	struct net_device *ndev = ifa->idev->dev;  	struct ieee80211_local *local = -		container_of(napi, struct ieee80211_local, napi); +		container_of(nb, struct ieee80211_local, ifa6_notifier); +	struct wireless_dev *wdev = ndev->ieee80211_ptr; +	struct ieee80211_sub_if_data *sdata; -	return local->ops->napi_poll(&local->hw, budget); -} +	/* Make sure it's our interface that got changed */ +	if (!wdev || wdev->wiphy != local->hw.wiphy) +		return NOTIFY_DONE; -void ieee80211_napi_schedule(struct ieee80211_hw *hw) -{ -	struct ieee80211_local *local = hw_to_local(hw); +	sdata = IEEE80211_DEV_TO_SUB_IF(ndev); -	napi_schedule(&local->napi); -} -EXPORT_SYMBOL(ieee80211_napi_schedule); +	/* +	 * For now only support station mode. This is mostly because +	 * doing AP would have to handle AP_VLAN in some way ... +	 */ +	if (sdata->vif.type != NL80211_IFTYPE_STATION) +		return NOTIFY_DONE; -void ieee80211_napi_complete(struct ieee80211_hw *hw) -{ -	struct ieee80211_local *local = hw_to_local(hw); +	drv_ipv6_addr_change(local, sdata, idev); -	napi_complete(&local->napi); +	return NOTIFY_OK;  } -EXPORT_SYMBOL(ieee80211_napi_complete); +#endif  /* There isn't a lot of sense in it, but you can transmit anything you like */  static const struct ieee80211_txrx_stypes  ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {  	[NL80211_IFTYPE_ADHOC] = {  		.tx = 0xffff, -		.rx = BIT(IEEE80211_STYPE_ACTION >> 4), +		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) | +			BIT(IEEE80211_STYPE_AUTH >> 4) | +			BIT(IEEE80211_STYPE_DEAUTH >> 4) | +			BIT(IEEE80211_STYPE_PROBE_REQ >> 4),  	},  	[NL80211_IFTYPE_STATION] = {  		.tx = 0xffff, @@ -481,6 +426,59 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {  			BIT(IEEE80211_STYPE_DEAUTH >> 4) |  			BIT(IEEE80211_STYPE_ACTION >> 4),  	}, +	[NL80211_IFTYPE_MESH_POINT] = { +		.tx = 0xffff, +		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) | +			BIT(IEEE80211_STYPE_AUTH >> 4) | +			BIT(IEEE80211_STYPE_DEAUTH >> 4), +	}, +	[NL80211_IFTYPE_P2P_DEVICE] = { +		.tx = 0xffff, +		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) | +			BIT(IEEE80211_STYPE_PROBE_REQ >> 4), +	}, +}; + +static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = { +	.ampdu_params_info = IEEE80211_HT_AMPDU_PARM_FACTOR | +			     IEEE80211_HT_AMPDU_PARM_DENSITY, + +	.cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 | +				IEEE80211_HT_CAP_MAX_AMSDU | +				IEEE80211_HT_CAP_SGI_20 | +				IEEE80211_HT_CAP_SGI_40 | +				IEEE80211_HT_CAP_LDPC_CODING | +				IEEE80211_HT_CAP_40MHZ_INTOLERANT), +	.mcs = { +		.rx_mask = { 0xff, 0xff, 0xff, 0xff, 0xff, +			     0xff, 0xff, 0xff, 0xff, 0xff, }, +	}, +}; + +static const struct ieee80211_vht_cap mac80211_vht_capa_mod_mask = { +	.vht_cap_info = +		cpu_to_le32(IEEE80211_VHT_CAP_RXLDPC | +			    IEEE80211_VHT_CAP_SHORT_GI_80 | +			    IEEE80211_VHT_CAP_SHORT_GI_160 | +			    IEEE80211_VHT_CAP_RXSTBC_1 | +			    IEEE80211_VHT_CAP_RXSTBC_2 | +			    IEEE80211_VHT_CAP_RXSTBC_3 | +			    IEEE80211_VHT_CAP_RXSTBC_4 | +			    IEEE80211_VHT_CAP_TXSTBC | +			    IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | +			    IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | +			    IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN | +			    IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | +			    IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK), +	.supp_mcs = { +		.rx_mcs_map = cpu_to_le16(~0), +		.tx_mcs_map = cpu_to_le16(~0), +	}, +}; + +static const u8 extended_capabilities[] = { +	0, 0, 0, 0, 0, 0, 0, +	WLAN_EXT_CAPA8_OPMODE_NOTIF,  };  struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, @@ -489,6 +487,23 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,  	struct ieee80211_local *local;  	int priv_size, i;  	struct wiphy *wiphy; +	bool use_chanctx; + +	if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config || +		    !ops->add_interface || !ops->remove_interface || +		    !ops->configure_filter)) +		return NULL; + +	if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove))) +		return NULL; + +	/* check all or no channel context operations exist */ +	i = !!ops->add_chanctx + !!ops->remove_chanctx + +	    !!ops->change_chanctx + !!ops->assign_vif_chanctx + +	    !!ops->unassign_vif_chanctx; +	if (WARN_ON(i != 0 && i != 5)) +		return NULL; +	use_chanctx = i == 5;  	/* Ensure 32-byte alignment of our private data and hw private data.  	 * We use the wiphy priv data for both our ieee80211_local and for @@ -514,10 +529,34 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,  	wiphy->mgmt_stypes = ieee80211_default_mgmt_stypes; +	wiphy->privid = mac80211_wiphy_privid; +  	wiphy->flags |= WIPHY_FLAG_NETNS_OK |  			WIPHY_FLAG_4ADDR_AP | -			WIPHY_FLAG_4ADDR_STATION; -	wiphy->privid = mac80211_wiphy_privid; +			WIPHY_FLAG_4ADDR_STATION | +			WIPHY_FLAG_REPORTS_OBSS | +			WIPHY_FLAG_OFFCHAN_TX; + +	wiphy->extended_capabilities = extended_capabilities; +	wiphy->extended_capabilities_mask = extended_capabilities; +	wiphy->extended_capabilities_len = ARRAY_SIZE(extended_capabilities); + +	if (ops->remain_on_channel) +		wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + +	wiphy->features |= NL80211_FEATURE_SK_TX_STATUS | +			   NL80211_FEATURE_SAE | +			   NL80211_FEATURE_HT_IBSS | +			   NL80211_FEATURE_VIF_TXPOWER | +			   NL80211_FEATURE_USERSPACE_MPM; + +	if (!ops->hw_scan) +		wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN | +				   NL80211_FEATURE_AP_SCAN; + + +	if (!ops->set_key) +		wiphy->flags |= WIPHY_FLAG_IBSS_RSN;  	wiphy->bss_priv_size = sizeof(struct ieee80211_bss); @@ -527,24 +566,28 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,  	local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); -	BUG_ON(!ops->tx); -	BUG_ON(!ops->start); -	BUG_ON(!ops->stop); -	BUG_ON(!ops->config); -	BUG_ON(!ops->add_interface); -	BUG_ON(!ops->remove_interface); -	BUG_ON(!ops->configure_filter);  	local->ops = ops; +	local->use_chanctx = use_chanctx;  	/* set up some defaults */  	local->hw.queues = 1;  	local->hw.max_rates = 1;  	local->hw.max_report_rates = 0; +	local->hw.max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; +	local->hw.max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF; +	local->hw.offchannel_tx_hw_queue = IEEE80211_INVAL_HW_QUEUE;  	local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;  	local->hw.conf.short_frame_max_tx_count = wiphy->retry_short; -	local->user_power_level = -1; -	local->uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES; -	local->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN; +	local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS | +					 IEEE80211_RADIOTAP_MCS_HAVE_GI | +					 IEEE80211_RADIOTAP_MCS_HAVE_BW; +	local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI | +					 IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH; +	local->hw.uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES; +	local->hw.uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN; +	local->user_power_level = IEEE80211_UNSET_POWER_LEVEL; +	wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; +	wiphy->vht_capa_mod_mask = &mac80211_vht_capa_mod_mask;  	INIT_LIST_HEAD(&local->interfaces); @@ -555,16 +598,20 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,  	mutex_init(&local->key_mtx);  	spin_lock_init(&local->filter_lock); +	spin_lock_init(&local->rx_path_lock);  	spin_lock_init(&local->queue_stop_reason_lock); -	INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work); +	INIT_LIST_HEAD(&local->chanctx_list); +	mutex_init(&local->chanctx_mtx); -	ieee80211_work_init(local); +	INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);  	INIT_WORK(&local->restart_work, ieee80211_restart_work); +	INIT_WORK(&local->radar_detected_work, +		  ieee80211_dfs_radar_detected_work); +  	INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); -	INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work);  	local->smps_mode = IEEE80211_SMPS_OFF;  	INIT_WORK(&local->dynamic_ps_enable_work, @@ -574,6 +621,12 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,  	setup_timer(&local->dynamic_ps_timer,  		    ieee80211_dynamic_ps_timer, (unsigned long) local); +	INIT_WORK(&local->sched_scan_stopped_work, +		  ieee80211_sched_scan_stopped_work); + +	spin_lock_init(&local->ack_status_lock); +	idr_init(&local->ack_status_frames); +  	sta_info_init(local);  	for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { @@ -590,20 +643,22 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,  	skb_queue_head_init(&local->skb_queue);  	skb_queue_head_init(&local->skb_queue_unreliable); -	/* init dummy netdev for use w/ NAPI */ -	init_dummy_netdev(&local->napi_dev); +	ieee80211_led_names(local); + +	ieee80211_roc_setup(local); -	return local_to_hw(local); +	return &local->hw;  }  EXPORT_SYMBOL(ieee80211_alloc_hw); -int ieee80211_register_hw(struct ieee80211_hw *hw) +static int ieee80211_init_cipher_suites(struct ieee80211_local *local)  { -	struct ieee80211_local *local = hw_to_local(hw); -	int result; -	enum ieee80211_band band; -	int channels, max_bitrates; -	bool supp_ht; +	bool have_wep = !(IS_ERR(local->wep_tx_tfm) || +			  IS_ERR(local->wep_rx_tfm)); +	bool have_mfp = local->hw.flags & IEEE80211_HW_MFP_CAPABLE; +	const struct ieee80211_cipher_scheme *cs = local->hw.cipher_schemes; +	int n_suites = 0, r = 0, w = 0; +	u32 *suites;  	static const u32 cipher_suites[] = {  		/* keep WEP first, it may be removed below */  		WLAN_CIPHER_SUITE_WEP40, @@ -615,9 +670,143 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  		WLAN_CIPHER_SUITE_AES_CMAC  	}; +	/* Driver specifies the ciphers, we have nothing to do... */ +	if (local->hw.wiphy->cipher_suites && have_wep) +		return 0; + +	/* Set up cipher suites if driver relies on mac80211 cipher defs */ +	if (!local->hw.wiphy->cipher_suites && !cs) { +		local->hw.wiphy->cipher_suites = cipher_suites; +		local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + +		if (!have_mfp) +			local->hw.wiphy->n_cipher_suites--; + +		if (!have_wep) { +			local->hw.wiphy->cipher_suites += 2; +			local->hw.wiphy->n_cipher_suites -= 2; +		} + +		return 0; +	} + +	if (!local->hw.wiphy->cipher_suites) { +		/* +		 * Driver specifies cipher schemes only +		 * We start counting ciphers defined by schemes, TKIP and CCMP +		 */ +		n_suites = local->hw.n_cipher_schemes + 2; + +		/* check if we have WEP40 and WEP104 */ +		if (have_wep) +			n_suites += 2; + +		/* check if we have AES_CMAC */ +		if (have_mfp) +			n_suites++; + +		suites = kmalloc(sizeof(u32) * n_suites, GFP_KERNEL); +		if (!suites) +			return -ENOMEM; + +		suites[w++] = WLAN_CIPHER_SUITE_CCMP; +		suites[w++] = WLAN_CIPHER_SUITE_TKIP; + +		if (have_wep) { +			suites[w++] = WLAN_CIPHER_SUITE_WEP40; +			suites[w++] = WLAN_CIPHER_SUITE_WEP104; +		} + +		if (have_mfp) +			suites[w++] = WLAN_CIPHER_SUITE_AES_CMAC; + +		for (r = 0; r < local->hw.n_cipher_schemes; r++) +			suites[w++] = cs[r].cipher; +	} else { +		/* Driver provides cipher suites, but we need to exclude WEP */ +		suites = kmemdup(local->hw.wiphy->cipher_suites, +				 sizeof(u32) * local->hw.wiphy->n_cipher_suites, +				 GFP_KERNEL); +		if (!suites) +			return -ENOMEM; + +		for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) { +			u32 suite = local->hw.wiphy->cipher_suites[r]; + +			if (suite == WLAN_CIPHER_SUITE_WEP40 || +			    suite == WLAN_CIPHER_SUITE_WEP104) +				continue; +			suites[w++] = suite; +		} +	} + +	local->hw.wiphy->cipher_suites = suites; +	local->hw.wiphy->n_cipher_suites = w; +	local->wiphy_ciphers_allocated = true; + +	return 0; +} + +int ieee80211_register_hw(struct ieee80211_hw *hw) +{ +	struct ieee80211_local *local = hw_to_local(hw); +	int result, i; +	enum ieee80211_band band; +	int channels, max_bitrates; +	bool supp_ht, supp_vht; +	netdev_features_t feature_whitelist; +	struct cfg80211_chan_def dflt_chandef = {}; + +	if (hw->flags & IEEE80211_HW_QUEUE_CONTROL && +	    (local->hw.offchannel_tx_hw_queue == IEEE80211_INVAL_HW_QUEUE || +	     local->hw.offchannel_tx_hw_queue >= local->hw.queues)) +		return -EINVAL; + +#ifdef CONFIG_PM +	if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume)) +		return -EINVAL; +#endif + +	if (!local->use_chanctx) { +		for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) { +			const struct ieee80211_iface_combination *comb; + +			comb = &local->hw.wiphy->iface_combinations[i]; + +			if (comb->num_different_channels > 1) +				return -EINVAL; +		} +	} else { +		/* +		 * WDS is currently prohibited when channel contexts are used +		 * because there's no clear definition of which channel WDS +		 * type interfaces use +		 */ +		if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS)) +			return -EINVAL; + +		/* DFS currently not supported with channel context drivers */ +		for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) { +			const struct ieee80211_iface_combination *comb; + +			comb = &local->hw.wiphy->iface_combinations[i]; + +			if (comb->radar_detect_widths) +				return -EINVAL; +		} +	} + +	/* Only HW csum features are currently compatible with mac80211 */ +	feature_whitelist = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | +			    NETIF_F_HW_CSUM; +	if (WARN_ON(hw->netdev_features & ~feature_whitelist)) +		return -EINVAL; +  	if (hw->max_report_rates == 0)  		hw->max_report_rates = hw->max_rates; +	local->rx_chains = 1; +  	/*  	 * generic code guarantees at least one band,  	 * set this very early because much code assumes @@ -626,23 +815,62 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  	channels = 0;  	max_bitrates = 0;  	supp_ht = false; +	supp_vht = false;  	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {  		struct ieee80211_supported_band *sband;  		sband = local->hw.wiphy->bands[band];  		if (!sband)  			continue; -		if (!local->oper_channel) { + +		if (!dflt_chandef.chan) { +			cfg80211_chandef_create(&dflt_chandef, +						&sband->channels[0], +						NL80211_CHAN_NO_HT);  			/* init channel we're on */ -			local->hw.conf.channel = -			local->oper_channel = &sband->channels[0]; -			local->hw.conf.channel_type = NL80211_CHAN_NO_HT; +			if (!local->use_chanctx && !local->_oper_chandef.chan) { +				local->hw.conf.chandef = dflt_chandef; +				local->_oper_chandef = dflt_chandef; +			} +			local->monitor_chandef = dflt_chandef;  		} +  		channels += sband->n_channels;  		if (max_bitrates < sband->n_bitrates)  			max_bitrates = sband->n_bitrates;  		supp_ht = supp_ht || sband->ht_cap.ht_supported; +		supp_vht = supp_vht || sband->vht_cap.vht_supported; + +		if (sband->ht_cap.ht_supported) +			local->rx_chains = +				max(ieee80211_mcs_to_chains(&sband->ht_cap.mcs), +				    local->rx_chains); + +		/* TODO: consider VHT for RX chains, hopefully it's the same */ +	} + +	/* if low-level driver supports AP, we also support VLAN */ +	if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { +		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); +		hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN); +	} + +	/* mac80211 always supports monitor */ +	hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); +	hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR); + +	/* mac80211 doesn't support more than one IBSS interface right now */ +	for (i = 0; i < hw->wiphy->n_iface_combinations; i++) { +		const struct ieee80211_iface_combination *c; +		int j; + +		c = &hw->wiphy->iface_combinations[i]; + +		for (j = 0; j < c->n_limits; j++) +			if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) && +			    c->limits[j].max > 1) +				return -EINVAL;  	}  	local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + @@ -650,25 +878,34 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  	if (!local->int_scan_req)  		return -ENOMEM; -	/* if low-level driver supports AP, we also support VLAN */ -	if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) -		local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); - -	/* mac80211 always supports monitor */ -	local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); +	for (band = 0; band < IEEE80211_NUM_BANDS; band++) { +		if (!local->hw.wiphy->bands[band]) +			continue; +		local->int_scan_req->rates[band] = (u32) -1; +	}  #ifndef CONFIG_MAC80211_MESH  	/* mesh depends on Kconfig, but drivers should set it if they want */  	local->hw.wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MESH_POINT);  #endif +	/* if the underlying driver supports mesh, mac80211 will (at least) +	 * provide routing of mesh authentication frames to userspace */ +	if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_MESH_POINT)) +		local->hw.wiphy->flags |= WIPHY_FLAG_MESH_AUTH; +  	/* mac80211 supports control port protocol changing */  	local->hw.wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL; -	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) +	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {  		local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; -	else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) +	} else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) {  		local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; +		if (hw->max_signal <= 0) { +			result = -EINVAL; +			goto fail_wiphy_register; +		} +	}  	WARN((local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)  	     && (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK), @@ -685,6 +922,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  	if (supp_ht)  		local->scan_ies_len += 2 + sizeof(struct ieee80211_ht_cap); +	if (supp_vht) +		local->scan_ies_len += +			2 + sizeof(struct ieee80211_vht_cap); +  	if (!local->ops->hw_scan) {  		/* For hw_scan, driver needs to set these up. */  		local->hw.wiphy->max_scan_ssids = 4; @@ -701,41 +942,21 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  	if (local->hw.wiphy->max_scan_ie_len)  		local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len; -	/* Set up cipher suites unless driver already did */ -	if (!local->hw.wiphy->cipher_suites) { -		local->hw.wiphy->cipher_suites = cipher_suites; -		local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); -		if (!(local->hw.flags & IEEE80211_HW_MFP_CAPABLE)) -			local->hw.wiphy->n_cipher_suites--; -	} -	if (IS_ERR(local->wep_tx_tfm) || IS_ERR(local->wep_rx_tfm)) { -		if (local->hw.wiphy->cipher_suites == cipher_suites) { -			local->hw.wiphy->cipher_suites += 2; -			local->hw.wiphy->n_cipher_suites -= 2; -		} else { -			u32 *suites; -			int r, w = 0; - -			/* Filter out WEP */ - -			suites = kmemdup( -				local->hw.wiphy->cipher_suites, -				sizeof(u32) * local->hw.wiphy->n_cipher_suites, -				GFP_KERNEL); -			if (!suites) -				return -ENOMEM; -			for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) { -				u32 suite = local->hw.wiphy->cipher_suites[r]; -				if (suite == WLAN_CIPHER_SUITE_WEP40 || -				    suite == WLAN_CIPHER_SUITE_WEP104) -					continue; -				suites[w++] = suite; -			} -			local->hw.wiphy->cipher_suites = suites; -			local->hw.wiphy->n_cipher_suites = w; -			local->wiphy_ciphers_allocated = true; -		} -	} +	WARN_ON(!ieee80211_cs_list_valid(local->hw.cipher_schemes, +					 local->hw.n_cipher_schemes)); + +	result = ieee80211_init_cipher_suites(local); +	if (result < 0) +		goto fail_wiphy_register; + +	if (!local->ops->remain_on_channel) +		local->hw.wiphy->max_remain_on_channel_duration = 5000; + +	/* mac80211 based drivers don't support internal TDLS setup */ +	if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) +		local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP; + +	local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM;  	result = wiphy_register(local->hw.wiphy);  	if (result < 0) @@ -749,7 +970,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  		hw->queues = IEEE80211_MAX_QUEUES;  	local->workqueue = -		alloc_ordered_workqueue(wiphy_name(local->hw.wiphy), 0); +		alloc_ordered_workqueue("%s", 0, wiphy_name(local->hw.wiphy));  	if (!local->workqueue) {  		result = -ENOMEM;  		goto fail_workqueue; @@ -760,10 +981,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  	 * and we need some headroom for passing the frame to monitor  	 * interfaces, but never both at the same time.  	 */ -	BUILD_BUG_ON(IEEE80211_TX_STATUS_HEADROOM != -			sizeof(struct ieee80211_tx_status_rtap_hdr));  	local->tx_headroom = max_t(unsigned int , local->hw.extra_tx_headroom, -				   sizeof(struct ieee80211_tx_status_rtap_hdr)); +				   IEEE80211_TX_STATUS_HEADROOM);  	debugfs_hw_add(local); @@ -778,15 +997,15 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  	local->dynamic_ps_forced_timeout = -1; -	result = sta_info_start(local); -	if (result < 0) -		goto fail_sta_info; -  	result = ieee80211_wep_init(local);  	if (result < 0)  		wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n",  			    result); +	local->hw.conf.flags = IEEE80211_CONF_IDLE; + +	ieee80211_led_init(local); +  	rtnl_lock();  	result = ieee80211_init_rate_ctrl_alg(local, @@ -808,8 +1027,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  	rtnl_unlock(); -	ieee80211_led_init(local); -  	local->network_latency_notifier.notifier_call =  		ieee80211_max_network_latency;  	result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY, @@ -826,12 +1043,22 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  		goto fail_ifa;  #endif -	netif_napi_add(&local->napi_dev, &local->napi, ieee80211_napi_poll, -			local->hw.napi_weight); +#if IS_ENABLED(CONFIG_IPV6) +	local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed; +	result = register_inet6addr_notifier(&local->ifa6_notifier); +	if (result) +		goto fail_ifa6; +#endif  	return 0; +#if IS_ENABLED(CONFIG_IPV6) + fail_ifa6:  #ifdef CONFIG_INET +	unregister_inetaddr_notifier(&local->ifa_notifier); +#endif +#endif +#if defined(CONFIG_INET) || defined(CONFIG_IPV6)   fail_ifa:  	pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,  			       &local->network_latency_notifier); @@ -844,7 +1071,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  	rtnl_unlock();  	ieee80211_wep_free(local);  	sta_info_stop(local); - fail_sta_info:  	destroy_workqueue(local->workqueue);   fail_workqueue:  	wiphy_unregister(local->hw.wiphy); @@ -856,6 +1082,18 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)  }  EXPORT_SYMBOL(ieee80211_register_hw); +void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi, +			struct net_device *napi_dev, +			int (*poll)(struct napi_struct *, int), +			int weight) +{ +	struct ieee80211_local *local = hw_to_local(hw); + +	netif_napi_add(napi_dev, napi, poll, weight); +	local->napi = napi; +} +EXPORT_SYMBOL_GPL(ieee80211_napi_add); +  void ieee80211_unregister_hw(struct ieee80211_hw *hw)  {  	struct ieee80211_local *local = hw_to_local(hw); @@ -868,6 +1106,9 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)  #ifdef CONFIG_INET  	unregister_inetaddr_notifier(&local->ifa_notifier);  #endif +#if IS_ENABLED(CONFIG_IPV6) +	unregister_inet6addr_notifier(&local->ifa6_notifier); +#endif  	rtnl_lock(); @@ -880,17 +1121,11 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)  	rtnl_unlock(); -	/* -	 * Now all work items will be gone, but the -	 * timer might still be armed, so delete it -	 */ -	del_timer_sync(&local->work_timer); -  	cancel_work_sync(&local->restart_work);  	cancel_work_sync(&local->reconfig_filter); +	flush_work(&local->sched_scan_stopped_work);  	ieee80211_clear_tx_pending(local); -	sta_info_stop(local);  	rate_control_deinitialize(local);  	if (skb_queue_len(&local->skb_queue) || @@ -901,12 +1136,20 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)  	destroy_workqueue(local->workqueue);  	wiphy_unregister(local->hw.wiphy); +	sta_info_stop(local);  	ieee80211_wep_free(local);  	ieee80211_led_exit(local);  	kfree(local->int_scan_req);  }  EXPORT_SYMBOL(ieee80211_unregister_hw); +static int ieee80211_free_ack_frame(int id, void *p, void *data) +{ +	WARN_ONCE(1, "Have pending ack frames!\n"); +	kfree_skb(p); +	return 0; +} +  void ieee80211_free_hw(struct ieee80211_hw *hw)  {  	struct ieee80211_local *local = hw_to_local(hw); @@ -917,6 +1160,12 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)  	if (local->wiphy_ciphers_allocated)  		kfree(local->hw.wiphy->cipher_suites); +	idr_for_each(&local->ack_status_frames, +		     ieee80211_free_ack_frame, NULL); +	idr_destroy(&local->ack_status_frames); + +	kfree(rcu_access_pointer(local->tx_latency)); +  	wiphy_free(local->hw.wiphy);  }  EXPORT_SYMBOL(ieee80211_free_hw); @@ -963,10 +1212,11 @@ static void __exit ieee80211_exit(void)  	rc80211_minstrel_ht_exit();  	rc80211_minstrel_exit(); -	if (mesh_allocated) -		ieee80211s_stop(); +	ieee80211s_stop();  	ieee80211_iface_exit(); + +	rcu_barrier();  } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index c8a4f19ed13..6495a3f0428 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -12,21 +12,17 @@  #include <asm/unaligned.h>  #include "ieee80211_i.h"  #include "mesh.h" +#include "driver-ops.h" -#define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ) -#define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ) -#define IEEE80211_MESH_RANN_INTERVAL	     (1 * HZ) - -#define MESHCONF_CAPAB_ACCEPT_PLINKS 0x01 -#define MESHCONF_CAPAB_FORWARDING    0x08 - -#define TMR_RUNNING_HK	0 -#define TMR_RUNNING_MP	1 -#define TMR_RUNNING_MPR	2 - -int mesh_allocated; +static int mesh_allocated;  static struct kmem_cache *rm_cache; +bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt) +{ +	return (mgmt->u.action.u.mesh_action.action_code == +			WLAN_MESH_ACTION_HWMP_PATH_SELECTION); +} +  void ieee80211s_init(void)  {  	mesh_pathtbl_init(); @@ -37,6 +33,8 @@ void ieee80211s_init(void)  void ieee80211s_stop(void)  { +	if (!mesh_allocated) +		return;  	mesh_pathtbl_unregister();  	kmem_cache_destroy(rm_cache);  } @@ -49,26 +47,24 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data)  	set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags); -	if (local->quiescing) { -		set_bit(TMR_RUNNING_HK, &ifmsh->timers_running); -		return; -	} -  	ieee80211_queue_work(&local->hw, &sdata->work);  }  /**   * mesh_matches_local - check if the config of a mesh point matches ours   * - * @ie: information elements of a management frame from the mesh peer   * @sdata: local mesh subif + * @ie: information elements of a management frame from the mesh peer   *   * This function checks if the mesh configuration of a mesh point matches the   * local mesh configuration, i.e. if both nodes belong to the same mesh network.   */ -bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_data *sdata) +bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, +			struct ieee802_11_elems *ie)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	u32 basic_rates = 0; +	struct cfg80211_chan_def sta_chan_def;  	/*  	 * As support for each feature is added, check for matching @@ -80,16 +76,29 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat  	 *   - MDA enabled  	 * - Power management control on fc  	 */ -	if (ifmsh->mesh_id_len == ie->mesh_id_len && -		memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 && -		(ifmsh->mesh_pp_id == ie->mesh_config->meshconf_psel) && -		(ifmsh->mesh_pm_id == ie->mesh_config->meshconf_pmetric) && -		(ifmsh->mesh_cc_id == ie->mesh_config->meshconf_congest) && -		(ifmsh->mesh_sp_id == ie->mesh_config->meshconf_synch) && -		(ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)) -		return true; +	if (!(ifmsh->mesh_id_len == ie->mesh_id_len && +	     memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 && +	     (ifmsh->mesh_pp_id == ie->mesh_config->meshconf_psel) && +	     (ifmsh->mesh_pm_id == ie->mesh_config->meshconf_pmetric) && +	     (ifmsh->mesh_cc_id == ie->mesh_config->meshconf_congest) && +	     (ifmsh->mesh_sp_id == ie->mesh_config->meshconf_synch) && +	     (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth))) +		return false; + +	ieee80211_sta_get_rates(sdata, ie, ieee80211_get_sdata_band(sdata), +				&basic_rates); -	return false; +	if (sdata->vif.bss_conf.basic_rates != basic_rates) +		return false; + +	ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, +				     ie->ht_operation, &sta_chan_def); + +	if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef, +					 &sta_chan_def)) +		return false; + +	return true;  }  /** @@ -100,17 +109,20 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat  bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie)  {  	return (ie->mesh_config->meshconf_cap & -	    MESHCONF_CAPAB_ACCEPT_PLINKS) != 0; +			IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS) != 0;  }  /** - * mesh_accept_plinks_update: update accepting_plink in local mesh beacons + * mesh_accept_plinks_update - update accepting_plink in local mesh beacons   *   * @sdata: mesh interface in which mesh beacons are going to be updated + * + * Returns: beacon changed flag if the beacon content changed.   */ -void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata) +u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata)  {  	bool free_plinks; +	u32 changed = 0;  	/* In case mesh_plink_free_count > 0 and mesh_plinktbl_capacity == 0,  	 * the mesh interface might be able to establish plinks with peers that @@ -120,17 +132,37 @@ void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata)  	 */  	free_plinks = mesh_plink_availables(sdata); -	if (free_plinks != sdata->u.mesh.accepting_plinks) -		ieee80211_mesh_housekeeping_timer((unsigned long) sdata); +	if (free_plinks != sdata->u.mesh.accepting_plinks) { +		sdata->u.mesh.accepting_plinks = free_plinks; +		changed = BSS_CHANGED_BEACON; +	} + +	return changed;  } -void mesh_ids_set_default(struct ieee80211_if_mesh *sta) +/* + * mesh_sta_cleanup - clean up any mesh sta state + * + * @sta: mesh sta to clean up. + */ +void mesh_sta_cleanup(struct sta_info *sta)  { -	sta->mesh_pp_id = 0;	/* HWMP */ -	sta->mesh_pm_id = 0;	/* Airtime */ -	sta->mesh_cc_id = 0;	/* Disabled */ -	sta->mesh_sp_id = 0;	/* Neighbor Offset */ -	sta->mesh_auth_id = 0;	/* Disabled */ +	struct ieee80211_sub_if_data *sdata = sta->sdata; +	u32 changed; + +	/* +	 * maybe userspace handles peer allocation and peering, but in either +	 * case the beacon is still generated by the kernel and we might need +	 * an update. +	 */ +	changed = mesh_accept_plinks_update(sdata); +	if (!sdata->u.mesh.user_mpm) { +		changed |= mesh_plink_deactivate(sta); +		del_timer_sync(&sta->plink_timer); +	} + +	if (changed) +		ieee80211_mbss_info_change_notify(sdata, changed);  }  int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) @@ -142,7 +174,7 @@ int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)  		return -ENOMEM;  	sdata->u.mesh.rmc->idx_mask = RMC_BUCKETS - 1;  	for (i = 0; i < RMC_BUCKETS; i++) -		INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i].list); +		INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i]);  	return 0;  } @@ -155,11 +187,12 @@ void mesh_rmc_free(struct ieee80211_sub_if_data *sdata)  	if (!sdata->u.mesh.rmc)  		return; -	for (i = 0; i < RMC_BUCKETS; i++) -		list_for_each_entry_safe(p, n, &rmc->bucket[i].list, list) { +	for (i = 0; i < RMC_BUCKETS; i++) { +		list_for_each_entry_safe(p, n, &rmc->bucket[i], list) {  			list_del(&p->list);  			kmem_cache_free(rm_cache, p);  		} +	}  	kfree(rmc);  	sdata->u.mesh.rmc = NULL; @@ -168,6 +201,7 @@ void mesh_rmc_free(struct ieee80211_sub_if_data *sdata)  /**   * mesh_rmc_check - Check frame in recent multicast cache and add if absent.   * + * @sdata:	interface   * @sa:		source address   * @mesh_hdr:	mesh_header   * @@ -177,8 +211,8 @@ void mesh_rmc_free(struct ieee80211_sub_if_data *sdata)   * received this frame lately. If the frame is not in the cache, it is added to   * it.   */ -int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, -		   struct ieee80211_sub_if_data *sdata) +int mesh_rmc_check(struct ieee80211_sub_if_data *sdata, +		   const u8 *sa, struct ieee80211s_hdr *mesh_hdr)  {  	struct mesh_rmc *rmc = sdata->u.mesh.rmc;  	u32 seqnum = 0; @@ -189,163 +223,256 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr,  	/* Don't care about endianness since only match matters */  	memcpy(&seqnum, &mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum));  	idx = le32_to_cpu(mesh_hdr->seqnum) & rmc->idx_mask; -	list_for_each_entry_safe(p, n, &rmc->bucket[idx].list, list) { +	list_for_each_entry_safe(p, n, &rmc->bucket[idx], list) {  		++entries;  		if (time_after(jiffies, p->exp_time) || -				(entries == RMC_QUEUE_MAX_LEN)) { +		    entries == RMC_QUEUE_MAX_LEN) {  			list_del(&p->list);  			kmem_cache_free(rm_cache, p);  			--entries; -		} else if ((seqnum == p->seqnum) && -			   (memcmp(sa, p->sa, ETH_ALEN) == 0)) +		} else if ((seqnum == p->seqnum) && ether_addr_equal(sa, p->sa))  			return -1;  	}  	p = kmem_cache_alloc(rm_cache, GFP_ATOMIC); -	if (!p) { -		printk(KERN_DEBUG "o11s: could not allocate RMC entry\n"); +	if (!p)  		return 0; -	} +  	p->seqnum = seqnum;  	p->exp_time = jiffies + RMC_TIMEOUT;  	memcpy(p->sa, sa, ETH_ALEN); -	list_add(&p->list, &rmc->bucket[idx].list); +	list_add(&p->list, &rmc->bucket[idx]);  	return 0;  } -void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) +int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata, +			 struct sk_buff *skb)  { -	struct ieee80211_local *local = sdata->local; -	struct ieee80211_supported_band *sband; -	u8 *pos; -	int len, i, rate; -	u8 neighbors; - -	sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; -	len = sband->n_bitrates; -	if (len > 8) -		len = 8; -	pos = skb_put(skb, len + 2); -	*pos++ = WLAN_EID_SUPP_RATES; -	*pos++ = len; -	for (i = 0; i < len; i++) { -		rate = sband->bitrates[i].bitrate; -		*pos++ = (u8) (rate / 5); -	} - -	if (sband->n_bitrates > len) { -		pos = skb_put(skb, sband->n_bitrates - len + 2); -		*pos++ = WLAN_EID_EXT_SUPP_RATES; -		*pos++ = sband->n_bitrates - len; -		for (i = len; i < sband->n_bitrates; i++) { -			rate = sband->bitrates[i].bitrate; -			*pos++ = (u8) (rate / 5); -		} -	} - -	if (sband->band == IEEE80211_BAND_2GHZ) { -		pos = skb_put(skb, 2 + 1); -		*pos++ = WLAN_EID_DS_PARAMS; -		*pos++ = 1; -		*pos++ = ieee80211_frequency_to_channel(local->hw.conf.channel->center_freq); -	} +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	u8 *pos, neighbors; +	u8 meshconf_len = sizeof(struct ieee80211_meshconf_ie); -	pos = skb_put(skb, 2 + sdata->u.mesh.mesh_id_len); -	*pos++ = WLAN_EID_MESH_ID; -	*pos++ = sdata->u.mesh.mesh_id_len; -	if (sdata->u.mesh.mesh_id_len) -		memcpy(pos, sdata->u.mesh.mesh_id, sdata->u.mesh.mesh_id_len); +	if (skb_tailroom(skb) < 2 + meshconf_len) +		return -ENOMEM; -	pos = skb_put(skb, 2 + sizeof(struct ieee80211_meshconf_ie)); +	pos = skb_put(skb, 2 + meshconf_len);  	*pos++ = WLAN_EID_MESH_CONFIG; -	*pos++ = sizeof(struct ieee80211_meshconf_ie); +	*pos++ = meshconf_len; -	/* Active path selection protocol ID */ -	*pos++ = sdata->u.mesh.mesh_pp_id; +	/* save a pointer for quick updates in pre-tbtt */ +	ifmsh->meshconf_offset = pos - skb->data; +	/* Active path selection protocol ID */ +	*pos++ = ifmsh->mesh_pp_id;  	/* Active path selection metric ID   */ -	*pos++ = sdata->u.mesh.mesh_pm_id; - +	*pos++ = ifmsh->mesh_pm_id;  	/* Congestion control mode identifier */ -	*pos++ = sdata->u.mesh.mesh_cc_id; - +	*pos++ = ifmsh->mesh_cc_id;  	/* Synchronization protocol identifier */ -	*pos++ = sdata->u.mesh.mesh_sp_id; - +	*pos++ = ifmsh->mesh_sp_id;  	/* Authentication Protocol identifier */ -	*pos++ = sdata->u.mesh.mesh_auth_id; - +	*pos++ = ifmsh->mesh_auth_id;  	/* Mesh Formation Info - number of neighbors */ -	neighbors = atomic_read(&sdata->u.mesh.mshstats.estab_plinks); -	/* Number of neighbor mesh STAs or 15 whichever is smaller */ -	neighbors = (neighbors > 15) ? 15 : neighbors; +	neighbors = atomic_read(&ifmsh->estab_plinks); +	neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS);  	*pos++ = neighbors << 1; -  	/* Mesh capability */ -	sdata->u.mesh.accepting_plinks = mesh_plink_availables(sdata); -	*pos = MESHCONF_CAPAB_FORWARDING; -	*pos++ |= sdata->u.mesh.accepting_plinks ? -	    MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; +	*pos = 0x00; +	*pos |= ifmsh->mshcfg.dot11MeshForwarding ? +			IEEE80211_MESHCONF_CAPAB_FORWARDING : 0x00; +	*pos |= ifmsh->accepting_plinks ? +			IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; +	/* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */ +	*pos |= ifmsh->ps_peers_deep_sleep ? +			IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL : 0x00; +	*pos++ |= ifmsh->adjusting_tbtt ? +			IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;  	*pos++ = 0x00; + +	return 0;  } -u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, struct mesh_table *tbl) +int mesh_add_meshid_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)  { -	/* Use last four bytes of hw addr and interface index as hash index */ -	return jhash_2words(*(u32 *)(addr+2), sdata->dev->ifindex, tbl->hash_rnd) -		& tbl->hash_mask; +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	u8 *pos; + +	if (skb_tailroom(skb) < 2 + ifmsh->mesh_id_len) +		return -ENOMEM; + +	pos = skb_put(skb, 2 + ifmsh->mesh_id_len); +	*pos++ = WLAN_EID_MESH_ID; +	*pos++ = ifmsh->mesh_id_len; +	if (ifmsh->mesh_id_len) +		memcpy(pos, ifmsh->mesh_id, ifmsh->mesh_id_len); + +	return 0;  } -struct mesh_table *mesh_table_alloc(int size_order) +static int mesh_add_awake_window_ie(struct ieee80211_sub_if_data *sdata, +				    struct sk_buff *skb)  { -	int i; -	struct mesh_table *newtbl; +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	u8 *pos; -	newtbl = kmalloc(sizeof(struct mesh_table), GFP_KERNEL); -	if (!newtbl) -		return NULL; +	/* see IEEE802.11-2012 13.14.6 */ +	if (ifmsh->ps_peers_light_sleep == 0 && +	    ifmsh->ps_peers_deep_sleep == 0 && +	    ifmsh->nonpeer_pm == NL80211_MESH_POWER_ACTIVE) +		return 0; -	newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) * -			(1 << size_order), GFP_KERNEL); +	if (skb_tailroom(skb) < 4) +		return -ENOMEM; -	if (!newtbl->hash_buckets) { -		kfree(newtbl); -		return NULL; +	pos = skb_put(skb, 2 + 2); +	*pos++ = WLAN_EID_MESH_AWAKE_WINDOW; +	*pos++ = 2; +	put_unaligned_le16(ifmsh->mshcfg.dot11MeshAwakeWindowDuration, pos); + +	return 0; +} + +int mesh_add_vendor_ies(struct ieee80211_sub_if_data *sdata, +			struct sk_buff *skb) +{ +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	u8 offset, len; +	const u8 *data; + +	if (!ifmsh->ie || !ifmsh->ie_len) +		return 0; + +	/* fast-forward to vendor IEs */ +	offset = ieee80211_ie_split_vendor(ifmsh->ie, ifmsh->ie_len, 0); + +	if (offset) { +		len = ifmsh->ie_len - offset; +		data = ifmsh->ie + offset; +		if (skb_tailroom(skb) < len) +			return -ENOMEM; +		memcpy(skb_put(skb, len), data, len);  	} -	newtbl->hashwlock = kmalloc(sizeof(spinlock_t) * -			(1 << size_order), GFP_KERNEL); -	if (!newtbl->hashwlock) { -		kfree(newtbl->hash_buckets); -		kfree(newtbl); -		return NULL; +	return 0; +} + +int mesh_add_rsn_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) +{ +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	u8 len = 0; +	const u8 *data; + +	if (!ifmsh->ie || !ifmsh->ie_len) +		return 0; + +	/* find RSN IE */ +	data = cfg80211_find_ie(WLAN_EID_RSN, ifmsh->ie, ifmsh->ie_len); +	if (!data) +		return 0; + +	len = data[1] + 2; + +	if (skb_tailroom(skb) < len) +		return -ENOMEM; +	memcpy(skb_put(skb, len), data, len); + +	return 0; +} + +static int mesh_add_ds_params_ie(struct ieee80211_sub_if_data *sdata, +				 struct sk_buff *skb) +{ +	struct ieee80211_chanctx_conf *chanctx_conf; +	struct ieee80211_channel *chan; +	u8 *pos; + +	if (skb_tailroom(skb) < 3) +		return -ENOMEM; + +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	if (WARN_ON(!chanctx_conf)) { +		rcu_read_unlock(); +		return -EINVAL;  	} +	chan = chanctx_conf->def.chan; +	rcu_read_unlock(); -	newtbl->size_order = size_order; -	newtbl->hash_mask = (1 << size_order) - 1; -	atomic_set(&newtbl->entries,  0); -	get_random_bytes(&newtbl->hash_rnd, -			sizeof(newtbl->hash_rnd)); -	for (i = 0; i <= newtbl->hash_mask; i++) -		spin_lock_init(&newtbl->hashwlock[i]); +	pos = skb_put(skb, 2 + 1); +	*pos++ = WLAN_EID_DS_PARAMS; +	*pos++ = 1; +	*pos++ = ieee80211_frequency_to_channel(chan->center_freq); -	return newtbl; +	return 0;  } +int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata, +		       struct sk_buff *skb) +{ +	struct ieee80211_local *local = sdata->local; +	enum ieee80211_band band = ieee80211_get_sdata_band(sdata); +	struct ieee80211_supported_band *sband; +	u8 *pos; + +	sband = local->hw.wiphy->bands[band]; +	if (!sband->ht_cap.ht_supported || +	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || +	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || +	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10) +		return 0; -static void ieee80211_mesh_path_timer(unsigned long data) +	if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap)) +		return -ENOMEM; + +	pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_cap)); +	ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap); + +	return 0; +} + +int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata, +			struct sk_buff *skb)  { -	struct ieee80211_sub_if_data *sdata = -		(struct ieee80211_sub_if_data *) data; -	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;  	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx_conf *chanctx_conf; +	struct ieee80211_channel *channel; +	enum nl80211_channel_type channel_type = +		cfg80211_get_chandef_type(&sdata->vif.bss_conf.chandef); +	struct ieee80211_supported_band *sband; +	struct ieee80211_sta_ht_cap *ht_cap; +	u8 *pos; -	if (local->quiescing) { -		set_bit(TMR_RUNNING_MP, &ifmsh->timers_running); -		return; +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	if (WARN_ON(!chanctx_conf)) { +		rcu_read_unlock(); +		return -EINVAL;  	} +	channel = chanctx_conf->def.chan; +	rcu_read_unlock(); -	ieee80211_queue_work(&local->hw, &sdata->work); +	sband = local->hw.wiphy->bands[channel->band]; +	ht_cap = &sband->ht_cap; + +	if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT) +		return 0; + +	if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_operation)) +		return -ENOMEM; + +	pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation)); +	ieee80211_ie_build_ht_oper(pos, ht_cap, &sdata->vif.bss_conf.chandef, +				   sdata->vif.bss_conf.ht_operation_mode); + +	return 0; +} + +static void ieee80211_mesh_path_timer(unsigned long data) +{ +	struct ieee80211_sub_if_data *sdata = +		(struct ieee80211_sub_if_data *) data; + +	ieee80211_queue_work(&sdata->local->hw, &sdata->work);  }  static void ieee80211_mesh_path_root_timer(unsigned long data) @@ -353,21 +480,15 @@ static void ieee80211_mesh_path_root_timer(unsigned long data)  	struct ieee80211_sub_if_data *sdata =  		(struct ieee80211_sub_if_data *) data;  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; -	struct ieee80211_local *local = sdata->local;  	set_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags); -	if (local->quiescing) { -		set_bit(TMR_RUNNING_MPR, &ifmsh->timers_running); -		return; -	} - -	ieee80211_queue_work(&local->hw, &sdata->work); +	ieee80211_queue_work(&sdata->local->hw, &sdata->work);  }  void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh)  { -	if (ifmsh->mshcfg.dot11MeshHWMPRootMode) +	if (ifmsh->mshcfg.dot11MeshHWMPRootMode > IEEE80211_ROOTMODE_ROOT)  		set_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags);  	else {  		clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags); @@ -378,7 +499,7 @@ void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh)  /**   * ieee80211_fill_mesh_addresses - fill addresses of a locally originated mesh frame - * @hdr:    	802.11 frame header + * @hdr:	802.11 frame header   * @fc:		frame control field   * @meshda:	destination address in the mesh   * @meshsa:	source address address in the mesh.  Same as TA, as frame is @@ -397,8 +518,7 @@ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,  		memcpy(hdr->addr3, meshsa, ETH_ALEN);  		return 24;  	} else { -		*fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | -				IEEE80211_FCTL_TODS); +		*fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);  		/* RA TA DA SA */  		memset(hdr->addr1, 0, ETH_ALEN);   /* RA is resolved later */  		memcpy(hdr->addr2, meshsa, ETH_ALEN); @@ -410,130 +530,471 @@ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,  /**   * ieee80211_new_mesh_header - create a new mesh header - * @meshhdr:    uninitialized mesh header   * @sdata:	mesh interface to be used - * @addr4:	addr4 of the mesh frame (1st in ae header) - *              may be NULL - * @addr5:	addr5 of the mesh frame (1st or 2nd in ae header) - *              may be NULL unless addr6 is present - * @addr6:	addr6 of the mesh frame (2nd or 3rd in ae header) - * 		may be NULL unless addr5 is present + * @meshhdr:    uninitialized mesh header + * @addr4or5:   1st address in the ae header, which may correspond to address 4 + *              (if addr6 is NULL) or address 5 (if addr6 is present). It may + *              be NULL. + * @addr6:	2nd address in the ae header, which corresponds to addr6 of the + *              mesh frame   *   * Return the header length.   */ -int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, -		struct ieee80211_sub_if_data *sdata, char *addr4, -		char *addr5, char *addr6) +int ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata, +			      struct ieee80211s_hdr *meshhdr, +			      const char *addr4or5, const char *addr6)  { -	int aelen = 0; +	if (WARN_ON(!addr4or5 && addr6)) +		return 0; +  	memset(meshhdr, 0, sizeof(*meshhdr)); +  	meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL; + +	/* FIXME: racy -- TX on multiple queues can be concurrent */  	put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum);  	sdata->u.mesh.mesh_seqnum++; -	if (addr4) { + +	if (addr4or5 && !addr6) {  		meshhdr->flags |= MESH_FLAGS_AE_A4; -		aelen += ETH_ALEN; -		memcpy(meshhdr->eaddr1, addr4, ETH_ALEN); -	} -	if (addr5 && addr6) { +		memcpy(meshhdr->eaddr1, addr4or5, ETH_ALEN); +		return 2 * ETH_ALEN; +	} else if (addr4or5 && addr6) {  		meshhdr->flags |= MESH_FLAGS_AE_A5_A6; -		aelen += 2 * ETH_ALEN; -		if (!addr4) { -			memcpy(meshhdr->eaddr1, addr5, ETH_ALEN); -			memcpy(meshhdr->eaddr2, addr6, ETH_ALEN); -		} else { -			memcpy(meshhdr->eaddr2, addr5, ETH_ALEN); -			memcpy(meshhdr->eaddr3, addr6, ETH_ALEN); -		} +		memcpy(meshhdr->eaddr1, addr4or5, ETH_ALEN); +		memcpy(meshhdr->eaddr2, addr6, ETH_ALEN); +		return 3 * ETH_ALEN;  	} -	return 6 + aelen; + +	return ETH_ALEN;  } -static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata, -			   struct ieee80211_if_mesh *ifmsh) +static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata)  { -	bool free_plinks; - -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -	printk(KERN_DEBUG "%s: running mesh housekeeping\n", -	       sdata->name); -#endif +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	u32 changed; -	ieee80211_sta_expire(sdata, IEEE80211_MESH_PEER_INACTIVITY_LIMIT); +	ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ);  	mesh_path_expire(sdata); -	free_plinks = mesh_plink_availables(sdata); -	if (free_plinks != sdata->u.mesh.accepting_plinks) -		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); +	changed = mesh_accept_plinks_update(sdata); +	ieee80211_mbss_info_change_notify(sdata, changed);  	mod_timer(&ifmsh->housekeeping_timer, -		  round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL)); +		  round_jiffies(jiffies + +				IEEE80211_MESH_HOUSEKEEPING_INTERVAL));  }  static void ieee80211_mesh_rootpath(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	u32 interval;  	mesh_path_tx_root_frame(sdata); + +	if (ifmsh->mshcfg.dot11MeshHWMPRootMode == IEEE80211_PROACTIVE_RANN) +		interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval; +	else +		interval = ifmsh->mshcfg.dot11MeshHWMProotInterval; +  	mod_timer(&ifmsh->mesh_path_root_timer, -		  round_jiffies(jiffies + IEEE80211_MESH_RANN_INTERVAL)); +		  round_jiffies(TU_TO_EXP_TIME(interval)));  } -#ifdef CONFIG_PM -void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata) +static int +ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)  { -	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	struct beacon_data *bcn; +	int head_len, tail_len; +	struct sk_buff *skb; +	struct ieee80211_mgmt *mgmt; +	struct ieee80211_chanctx_conf *chanctx_conf; +	struct mesh_csa_settings *csa; +	enum ieee80211_band band; +	u8 *pos; +	struct ieee80211_sub_if_data *sdata; +	int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) + +		      sizeof(mgmt->u.beacon); -	/* use atomic bitops in case both timers fire at the same time */ +	sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh); +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	band = chanctx_conf->def.chan->band; +	rcu_read_unlock(); + +	head_len = hdr_len + +		   2 + /* NULL SSID */ +		   /* Channel Switch Announcement */ +		   2 + sizeof(struct ieee80211_channel_sw_ie) + +		   /* Mesh Channel Swith Parameters */ +		   2 + sizeof(struct ieee80211_mesh_chansw_params_ie) + +		   2 + 8 + /* supported rates */ +		   2 + 3; /* DS params */ +	tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) + +		   2 + sizeof(struct ieee80211_ht_cap) + +		   2 + sizeof(struct ieee80211_ht_operation) + +		   2 + ifmsh->mesh_id_len + +		   2 + sizeof(struct ieee80211_meshconf_ie) + +		   2 + sizeof(__le16) + /* awake window */ +		   ifmsh->ie_len; + +	bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL); +	/* need an skb for IE builders to operate on */ +	skb = dev_alloc_skb(max(head_len, tail_len)); + +	if (!bcn || !skb) +		goto out_free; + +	/* +	 * pointers go into the block we allocated, +	 * memory is | beacon_data | head | tail | +	 */ +	bcn->head = ((u8 *) bcn) + sizeof(*bcn); + +	/* fill in the head */ +	mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); +	memset(mgmt, 0, hdr_len); +	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | +					  IEEE80211_STYPE_BEACON); +	eth_broadcast_addr(mgmt->da); +	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); +	memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); +	ieee80211_mps_set_frame_flags(sdata, NULL, (void *) mgmt); +	mgmt->u.beacon.beacon_int = +		cpu_to_le16(sdata->vif.bss_conf.beacon_int); +	mgmt->u.beacon.capab_info |= cpu_to_le16( +		sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0); + +	pos = skb_put(skb, 2); +	*pos++ = WLAN_EID_SSID; +	*pos++ = 0x0; -	if (del_timer_sync(&ifmsh->housekeeping_timer)) -		set_bit(TMR_RUNNING_HK, &ifmsh->timers_running); -	if (del_timer_sync(&ifmsh->mesh_path_timer)) -		set_bit(TMR_RUNNING_MP, &ifmsh->timers_running); -	if (del_timer_sync(&ifmsh->mesh_path_root_timer)) -		set_bit(TMR_RUNNING_MPR, &ifmsh->timers_running); +	rcu_read_lock(); +	csa = rcu_dereference(ifmsh->csa); +	if (csa) { +		pos = skb_put(skb, 13); +		memset(pos, 0, 13); +		*pos++ = WLAN_EID_CHANNEL_SWITCH; +		*pos++ = 3; +		*pos++ = 0x0; +		*pos++ = ieee80211_frequency_to_channel( +				csa->settings.chandef.chan->center_freq); +		sdata->csa_counter_offset_beacon[0] = hdr_len + 6; +		*pos++ = csa->settings.count; +		*pos++ = WLAN_EID_CHAN_SWITCH_PARAM; +		*pos++ = 6; +		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT) { +			*pos++ = ifmsh->mshcfg.dot11MeshTTL; +			*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; +		} else { +			*pos++ = ifmsh->chsw_ttl; +		} +		*pos++ |= csa->settings.block_tx ? +			  WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; +		put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); +		pos += 2; +		put_unaligned_le16(ifmsh->pre_value, pos); +		pos += 2; +	} +	rcu_read_unlock(); + +	if (ieee80211_add_srates_ie(sdata, skb, true, band) || +	    mesh_add_ds_params_ie(sdata, skb)) +		goto out_free; + +	bcn->head_len = skb->len; +	memcpy(bcn->head, skb->data, bcn->head_len); + +	/* now the tail */ +	skb_trim(skb, 0); +	bcn->tail = bcn->head + bcn->head_len; + +	if (ieee80211_add_ext_srates_ie(sdata, skb, true, band) || +	    mesh_add_rsn_ie(sdata, skb) || +	    mesh_add_ht_cap_ie(sdata, skb) || +	    mesh_add_ht_oper_ie(sdata, skb) || +	    mesh_add_meshid_ie(sdata, skb) || +	    mesh_add_meshconf_ie(sdata, skb) || +	    mesh_add_awake_window_ie(sdata, skb) || +	    mesh_add_vendor_ies(sdata, skb)) +		goto out_free; + +	bcn->tail_len = skb->len; +	memcpy(bcn->tail, skb->data, bcn->tail_len); +	bcn->meshconf = (struct ieee80211_meshconf_ie *) +					(bcn->tail + ifmsh->meshconf_offset); + +	dev_kfree_skb(skb); +	rcu_assign_pointer(ifmsh->beacon, bcn); +	return 0; +out_free: +	kfree(bcn); +	dev_kfree_skb(skb); +	return -ENOMEM; +} + +static int +ieee80211_mesh_rebuild_beacon(struct ieee80211_sub_if_data *sdata) +{ +	struct beacon_data *old_bcn; +	int ret; + +	old_bcn = rcu_dereference_protected(sdata->u.mesh.beacon, +					    lockdep_is_held(&sdata->wdev.mtx)); +	ret = ieee80211_mesh_build_beacon(&sdata->u.mesh); +	if (ret) +		/* just reuse old beacon */ +		return ret; + +	if (old_bcn) +		kfree_rcu(old_bcn, rcu_head); +	return 0;  } -void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata) +void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata, +				       u32 changed)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	unsigned long bits = changed; +	u32 bit; -	if (test_and_clear_bit(TMR_RUNNING_HK, &ifmsh->timers_running)) -		add_timer(&ifmsh->housekeeping_timer); -	if (test_and_clear_bit(TMR_RUNNING_MP, &ifmsh->timers_running)) -		add_timer(&ifmsh->mesh_path_timer); -	if (test_and_clear_bit(TMR_RUNNING_MPR, &ifmsh->timers_running)) -		add_timer(&ifmsh->mesh_path_root_timer); -	ieee80211_mesh_root_setup(ifmsh); +	if (!bits) +		return; + +	/* if we race with running work, worst case this work becomes a noop */ +	for_each_set_bit(bit, &bits, sizeof(changed) * BITS_PER_BYTE) +		set_bit(bit, &ifmsh->mbss_changed); +	set_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags); +	ieee80211_queue_work(&sdata->local->hw, &sdata->work);  } -#endif -void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) +int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;  	struct ieee80211_local *local = sdata->local; - +	u32 changed = BSS_CHANGED_BEACON | +		      BSS_CHANGED_BEACON_ENABLED | +		      BSS_CHANGED_HT | +		      BSS_CHANGED_BASIC_RATES | +		      BSS_CHANGED_BEACON_INT; + +	local->fif_other_bss++; +	/* mesh ifaces must set allmulti to forward mcast traffic */ +	atomic_inc(&local->iff_allmultis); +	ieee80211_configure_filter(local); + +	ifmsh->mesh_cc_id = 0;	/* Disabled */ +	/* register sync ops from extensible synchronization framework */ +	ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id); +	ifmsh->adjusting_tbtt = false; +	ifmsh->sync_offset_clockdrift_max = 0;  	set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags);  	ieee80211_mesh_root_setup(ifmsh);  	ieee80211_queue_work(&local->hw, &sdata->work); -	sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; -	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | -						BSS_CHANGED_BEACON_ENABLED | -						BSS_CHANGED_BEACON_INT); +	sdata->vif.bss_conf.ht_operation_mode = +				ifmsh->mshcfg.ht_opmode; +	sdata->vif.bss_conf.enable_beacon = true; + +	changed |= ieee80211_mps_local_status_update(sdata); + +	if (ieee80211_mesh_build_beacon(ifmsh)) { +		ieee80211_stop_mesh(sdata); +		return -ENOMEM; +	} + +	ieee80211_recalc_dtim(local, sdata); +	ieee80211_bss_info_change_notify(sdata, changed); + +	netif_carrier_on(sdata->dev); +	return 0;  }  void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)  { +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	struct beacon_data *bcn; + +	netif_carrier_off(sdata->dev); + +	/* stop the beacon */ +	ifmsh->mesh_id_len = 0; +	sdata->vif.bss_conf.enable_beacon = false; +	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); +	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); +	bcn = rcu_dereference_protected(ifmsh->beacon, +					lockdep_is_held(&sdata->wdev.mtx)); +	RCU_INIT_POINTER(ifmsh->beacon, NULL); +	kfree_rcu(bcn, rcu_head); + +	/* flush STAs and mpaths on this iface */ +	sta_info_flush(sdata); +	mesh_path_flush_by_iface(sdata); + +	/* free all potentially still buffered group-addressed frames */ +	local->total_ps_buffered -= skb_queue_len(&ifmsh->ps.bc_buf); +	skb_queue_purge(&ifmsh->ps.bc_buf); +  	del_timer_sync(&sdata->u.mesh.housekeeping_timer);  	del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); -	/* -	 * If the timer fired while we waited for it, it will have -	 * requeued the work. Now the work will be running again -	 * but will not rearm the timer again because it checks -	 * whether the interface is running, which, at this point, -	 * it no longer is. -	 */ -	cancel_work_sync(&sdata->work); +	del_timer_sync(&sdata->u.mesh.mesh_path_timer); + +	/* clear any mesh work (for next join) we may have accrued */ +	ifmsh->wrkq_flags = 0; +	ifmsh->mbss_changed = 0; + +	local->fif_other_bss--; +	atomic_dec(&local->iff_allmultis); +	ieee80211_configure_filter(local); +} + +static bool +ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, +				 struct ieee802_11_elems *elems, bool beacon) +{ +	struct cfg80211_csa_settings params; +	struct ieee80211_csa_ie csa_ie; +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	enum ieee80211_band band = ieee80211_get_sdata_band(sdata); +	int err; +	u32 sta_flags; + +	sdata_assert_lock(sdata); + +	sta_flags = IEEE80211_STA_DISABLE_VHT; +	switch (sdata->vif.bss_conf.chandef.width) { +	case NL80211_CHAN_WIDTH_20_NOHT: +		sta_flags |= IEEE80211_STA_DISABLE_HT; +	case NL80211_CHAN_WIDTH_20: +		sta_flags |= IEEE80211_STA_DISABLE_40MHZ; +		break; +	default: +		break; +	} + +	memset(¶ms, 0, sizeof(params)); +	memset(&csa_ie, 0, sizeof(csa_ie)); +	err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band, +					   sta_flags, sdata->vif.addr, +					   &csa_ie); +	if (err < 0) +		return false; +	if (err) +		return false; + +	params.chandef = csa_ie.chandef; +	params.count = csa_ie.count; + +	if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef, +				     IEEE80211_CHAN_DISABLED)) { +		sdata_info(sdata, +			   "mesh STA %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), aborting\n", +			   sdata->vif.addr, +			   params.chandef.chan->center_freq, +			   params.chandef.width, +			   params.chandef.center_freq1, +			   params.chandef.center_freq2); +		return false; +	} + +	err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, +					    ¶ms.chandef, +					    NL80211_IFTYPE_MESH_POINT); +	if (err < 0) +		return false; +	if (err > 0) +		/* TODO: DFS not (yet) supported */ +		return false; + +	params.radar_required = err; + +	if (cfg80211_chandef_identical(¶ms.chandef, +				       &sdata->vif.bss_conf.chandef)) { +		mcsa_dbg(sdata, +			 "received csa with an identical chandef, ignoring\n"); +		return true; +	} + +	mcsa_dbg(sdata, +		 "received channel switch announcement to go to channel %d MHz\n", +		 params.chandef.chan->center_freq); + +	params.block_tx = csa_ie.mode & WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT; +	if (beacon) { +		ifmsh->chsw_ttl = csa_ie.ttl - 1; +		if (ifmsh->pre_value >= csa_ie.pre_value) +			return false; +		ifmsh->pre_value = csa_ie.pre_value; +	} + +	if (ifmsh->chsw_ttl >= ifmsh->mshcfg.dot11MeshTTL) +		return false; + +	ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_REPEATER; + +	if (ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev, +				     ¶ms) < 0) +		return false; + +	return true; +} + +static void +ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata, +			    struct ieee80211_mgmt *mgmt, size_t len) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	struct sk_buff *presp; +	struct beacon_data *bcn; +	struct ieee80211_mgmt *hdr; +	struct ieee802_11_elems elems; +	size_t baselen; +	u8 *pos; + +	pos = mgmt->u.probe_req.variable; +	baselen = (u8 *) pos - (u8 *) mgmt; +	if (baselen > len) +		return; + +	ieee802_11_parse_elems(pos, len - baselen, false, &elems); + +	if (!elems.mesh_id) +		return; + +	/* 802.11-2012 10.1.4.3.2 */ +	if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) && +	     !is_broadcast_ether_addr(mgmt->da)) || +	    elems.ssid_len != 0) +		return; + +	if (elems.mesh_id_len != 0 && +	    (elems.mesh_id_len != ifmsh->mesh_id_len || +	     memcmp(elems.mesh_id, ifmsh->mesh_id, ifmsh->mesh_id_len))) +		return; + +	rcu_read_lock(); +	bcn = rcu_dereference(ifmsh->beacon); + +	if (!bcn) +		goto out; + +	presp = dev_alloc_skb(local->tx_headroom + +			      bcn->head_len + bcn->tail_len); +	if (!presp) +		goto out; + +	skb_reserve(presp, local->tx_headroom); +	memcpy(skb_put(presp, bcn->head_len), bcn->head, bcn->head_len); +	memcpy(skb_put(presp, bcn->tail_len), bcn->tail, bcn->tail_len); +	hdr = (struct ieee80211_mgmt *) presp->data; +	hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | +					 IEEE80211_STYPE_PROBE_RESP); +	memcpy(hdr->da, mgmt->sa, ETH_ALEN); +	IEEE80211_SKB_CB(presp)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; +	ieee80211_tx_skb(sdata, presp); +out: +	rcu_read_unlock();  }  static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, @@ -543,16 +1004,16 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,  					struct ieee80211_rx_status *rx_status)  {  	struct ieee80211_local *local = sdata->local; +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;  	struct ieee802_11_elems elems;  	struct ieee80211_channel *channel; -	u32 supp_rates = 0;  	size_t baselen;  	int freq;  	enum ieee80211_band band = rx_status->band;  	/* ignore ProbeResp to foreign address */  	if (stype == IEEE80211_STYPE_PROBE_RESP && -	    compare_ether_addr(mgmt->da, sdata->vif.addr)) +	    !ether_addr_equal(mgmt->da, sdata->vif.addr))  		return;  	baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; @@ -560,10 +1021,16 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,  		return;  	ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, -			       &elems); +			       false, &elems); -	if (elems.ds_params && elems.ds_params_len == 1) -		freq = ieee80211_channel_to_frequency(elems.ds_params[0]); +	/* ignore non-mesh or secure / unsecure mismatch */ +	if ((!elems.mesh_id || !elems.mesh_config) || +	    (elems.rsn && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) || +	    (!elems.rsn && sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE)) +		return; + +	if (elems.ds_params) +		freq = ieee80211_channel_to_frequency(elems.ds_params[0], band);  	else  		freq = rx_status->freq; @@ -572,12 +1039,144 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,  	if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)  		return; -	if (elems.mesh_id && elems.mesh_config && -	    mesh_matches_local(&elems, sdata)) { -		supp_rates = ieee80211_sta_get_rates(local, &elems, band); +	if (mesh_matches_local(sdata, &elems)) +		mesh_neighbour_update(sdata, mgmt->sa, &elems); + +	if (ifmsh->sync_ops) +		ifmsh->sync_ops->rx_bcn_presp(sdata, +			stype, mgmt, &elems, rx_status); -		mesh_neighbour_update(mgmt->sa, supp_rates, sdata, -				      mesh_peer_accepts_plinks(&elems)); +	if (ifmsh->csa_role != IEEE80211_MESH_CSA_ROLE_INIT && +	    !sdata->vif.csa_active) +		ieee80211_mesh_process_chnswitch(sdata, &elems, true); +} + +int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	struct mesh_csa_settings *tmp_csa_settings; +	int ret = 0; +	int changed = 0; + +	/* Reset the TTL value and Initiator flag */ +	ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE; +	ifmsh->chsw_ttl = 0; + +	/* Remove the CSA and MCSP elements from the beacon */ +	tmp_csa_settings = rcu_dereference(ifmsh->csa); +	RCU_INIT_POINTER(ifmsh->csa, NULL); +	if (tmp_csa_settings) +		kfree_rcu(tmp_csa_settings, rcu_head); +	ret = ieee80211_mesh_rebuild_beacon(sdata); +	if (ret) +		return -EINVAL; + +	changed |= BSS_CHANGED_BEACON; + +	mcsa_dbg(sdata, "complete switching to center freq %d MHz", +		 sdata->vif.bss_conf.chandef.chan->center_freq); +	return changed; +} + +int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, +			      struct cfg80211_csa_settings *csa_settings) +{ +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	struct mesh_csa_settings *tmp_csa_settings; +	int ret = 0; + +	tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings), +				   GFP_ATOMIC); +	if (!tmp_csa_settings) +		return -ENOMEM; + +	memcpy(&tmp_csa_settings->settings, csa_settings, +	       sizeof(struct cfg80211_csa_settings)); + +	rcu_assign_pointer(ifmsh->csa, tmp_csa_settings); + +	ret = ieee80211_mesh_rebuild_beacon(sdata); +	if (ret) { +		tmp_csa_settings = rcu_dereference(ifmsh->csa); +		RCU_INIT_POINTER(ifmsh->csa, NULL); +		kfree_rcu(tmp_csa_settings, rcu_head); +		return ret; +	} + +	return BSS_CHANGED_BEACON; +} + +static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata, +			       struct ieee80211_mgmt *mgmt, size_t len) +{ +	struct ieee80211_mgmt *mgmt_fwd; +	struct sk_buff *skb; +	struct ieee80211_local *local = sdata->local; +	u8 *pos = mgmt->u.action.u.chan_switch.variable; +	size_t offset_ttl; + +	skb = dev_alloc_skb(local->tx_headroom + len); +	if (!skb) +		return -ENOMEM; +	skb_reserve(skb, local->tx_headroom); +	mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len); + +	/* offset_ttl is based on whether the secondary channel +	 * offset is available or not. Substract 1 from the mesh TTL +	 * and disable the initiator flag before forwarding. +	 */ +	offset_ttl = (len < 42) ? 7 : 10; +	*(pos + offset_ttl) -= 1; +	*(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; + +	memcpy(mgmt_fwd, mgmt, len); +	eth_broadcast_addr(mgmt_fwd->da); +	memcpy(mgmt_fwd->sa, sdata->vif.addr, ETH_ALEN); +	memcpy(mgmt_fwd->bssid, sdata->vif.addr, ETH_ALEN); + +	ieee80211_tx_skb(sdata, skb); +	return 0; +} + +static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata, +			      struct ieee80211_mgmt *mgmt, size_t len) +{ +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	struct ieee802_11_elems elems; +	u16 pre_value; +	bool fwd_csa = true; +	size_t baselen; +	u8 *pos; + +	if (mgmt->u.action.u.measurement.action_code != +	    WLAN_ACTION_SPCT_CHL_SWITCH) +		return; + +	pos = mgmt->u.action.u.chan_switch.variable; +	baselen = offsetof(struct ieee80211_mgmt, +			   u.action.u.chan_switch.variable); +	ieee802_11_parse_elems(pos, len - baselen, false, &elems); + +	ifmsh->chsw_ttl = elems.mesh_chansw_params_ie->mesh_ttl; +	if (!--ifmsh->chsw_ttl) +		fwd_csa = false; + +	pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value); +	if (ifmsh->pre_value >= pre_value) +		return; + +	ifmsh->pre_value = pre_value; + +	if (!sdata->vif.csa_active && +	    !ieee80211_mesh_process_chnswitch(sdata, &elems, false)) { +		mcsa_dbg(sdata, "Failed to process CSA action frame"); +		return; +	} + +	/* forward or re-broadcast the CSA frame */ +	if (fwd_csa) { +		if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0) +			mcsa_dbg(sdata, "Failed to forward the CSA frame");  	}  } @@ -587,11 +1186,21 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,  					  struct ieee80211_rx_status *rx_status)  {  	switch (mgmt->u.action.category) { -	case WLAN_CATEGORY_MESH_PLINK: -		mesh_rx_plink_frame(sdata, mgmt, len, rx_status); +	case WLAN_CATEGORY_SELF_PROTECTED: +		switch (mgmt->u.action.u.self_prot.action_code) { +		case WLAN_SP_MESH_PEERING_OPEN: +		case WLAN_SP_MESH_PEERING_CLOSE: +		case WLAN_SP_MESH_PEERING_CONFIRM: +			mesh_rx_plink_frame(sdata, mgmt, len, rx_status); +			break; +		} +		break; +	case WLAN_CATEGORY_MESH_ACTION: +		if (mesh_action_is_path_sel(mgmt)) +			mesh_rx_path_sel_frame(sdata, mgmt, len);  		break; -	case WLAN_CATEGORY_MESH_PATH_SEL: -		mesh_rx_path_sel_frame(sdata, mgmt, len); +	case WLAN_CATEGORY_SPECTRUM_MGMT: +		mesh_rx_csa_frame(sdata, mgmt, len);  		break;  	}  } @@ -600,11 +1209,14 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  				   struct sk_buff *skb)  {  	struct ieee80211_rx_status *rx_status; -	struct ieee80211_if_mesh *ifmsh;  	struct ieee80211_mgmt *mgmt;  	u16 stype; -	ifmsh = &sdata->u.mesh; +	sdata_lock(sdata); + +	/* mesh already went down */ +	if (!sdata->u.mesh.mesh_id_len) +		goto out;  	rx_status = IEEE80211_SKB_RXCB(skb);  	mgmt = (struct ieee80211_mgmt *) skb->data; @@ -616,16 +1228,49 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  		ieee80211_mesh_rx_bcn_presp(sdata, stype, mgmt, skb->len,  					    rx_status);  		break; +	case IEEE80211_STYPE_PROBE_REQ: +		ieee80211_mesh_rx_probe_req(sdata, mgmt, skb->len); +		break;  	case IEEE80211_STYPE_ACTION:  		ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status);  		break;  	} +out: +	sdata_unlock(sdata); +} + +static void mesh_bss_info_changed(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	u32 bit, changed = 0; + +	for_each_set_bit(bit, &ifmsh->mbss_changed, +			 sizeof(changed) * BITS_PER_BYTE) { +		clear_bit(bit, &ifmsh->mbss_changed); +		changed |= BIT(bit); +	} + +	if (sdata->vif.bss_conf.enable_beacon && +	    (changed & (BSS_CHANGED_BEACON | +			BSS_CHANGED_HT | +			BSS_CHANGED_BASIC_RATES | +			BSS_CHANGED_BEACON_INT))) +		if (ieee80211_mesh_rebuild_beacon(sdata)) +			return; + +	ieee80211_bss_info_change_notify(sdata, changed);  }  void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	sdata_lock(sdata); + +	/* mesh already went down */ +	if (!sdata->u.mesh.mesh_id_len) +		goto out; +  	if (ifmsh->preq_queue_len &&  	    time_after(jiffies,  		       ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval))) @@ -634,14 +1279,22 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)  	if (test_and_clear_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags))  		mesh_mpath_table_grow(); -	if (test_and_clear_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags)) +	if (test_and_clear_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags))  		mesh_mpp_table_grow();  	if (test_and_clear_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags)) -		ieee80211_mesh_housekeeping(sdata, ifmsh); +		ieee80211_mesh_housekeeping(sdata);  	if (test_and_clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags))  		ieee80211_mesh_rootpath(sdata); + +	if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags)) +		mesh_sync_adjust_tbtt(sdata); + +	if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags)) +		mesh_bss_info_changed(sdata); +out: +	sdata_unlock(sdata);  }  void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) @@ -650,7 +1303,8 @@ void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)  	rcu_read_lock();  	list_for_each_entry_rcu(sdata, &local->interfaces, list) -		if (ieee80211_vif_is_mesh(&sdata->vif)) +		if (ieee80211_vif_is_mesh(&sdata->vif) && +		    ieee80211_sdata_running(sdata))  			ieee80211_queue_work(&local->hw, &sdata->work);  	rcu_read_unlock();  } @@ -658,41 +1312,21 @@ void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)  void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	static u8 zero_addr[ETH_ALEN] = {};  	setup_timer(&ifmsh->housekeeping_timer,  		    ieee80211_mesh_housekeeping_timer,  		    (unsigned long) sdata); -	ifmsh->mshcfg.dot11MeshRetryTimeout = MESH_RET_T; -	ifmsh->mshcfg.dot11MeshConfirmTimeout = MESH_CONF_T; -	ifmsh->mshcfg.dot11MeshHoldingTimeout = MESH_HOLD_T; -	ifmsh->mshcfg.dot11MeshMaxRetries = MESH_MAX_RETR; -	ifmsh->mshcfg.dot11MeshTTL = MESH_TTL; -	ifmsh->mshcfg.auto_open_plinks = true; -	ifmsh->mshcfg.dot11MeshMaxPeerLinks = -		MESH_MAX_ESTAB_PLINKS; -	ifmsh->mshcfg.dot11MeshHWMPactivePathTimeout = -		MESH_PATH_TIMEOUT; -	ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval = -		MESH_PREQ_MIN_INT; -	ifmsh->mshcfg.dot11MeshHWMPnetDiameterTraversalTime = -		MESH_DIAM_TRAVERSAL_TIME; -	ifmsh->mshcfg.dot11MeshHWMPmaxPREQretries = -		MESH_MAX_PREQ_RETRIES; -	ifmsh->mshcfg.path_refresh_time = -		MESH_PATH_REFRESH_TIME; -	ifmsh->mshcfg.min_discovery_timeout = -		MESH_MIN_DISCOVERY_TIMEOUT;  	ifmsh->accepting_plinks = true; -	ifmsh->preq_id = 0; -	ifmsh->sn = 0;  	atomic_set(&ifmsh->mpaths, 0);  	mesh_rmc_init(sdata);  	ifmsh->last_preq = jiffies; +	ifmsh->next_perr = jiffies; +	ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;  	/* Allocate all mesh structures when creating the first mesh interface. */  	if (!mesh_allocated)  		ieee80211s_init(); -	mesh_ids_set_default(ifmsh);  	setup_timer(&ifmsh->mesh_path_timer,  		    ieee80211_mesh_path_timer,  		    (unsigned long) sdata); @@ -700,5 +1334,10 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)  		    ieee80211_mesh_path_root_timer,  		    (unsigned long) sdata);  	INIT_LIST_HEAD(&ifmsh->preq_queue.list); +	skb_queue_head_init(&ifmsh->ps.bc_buf);  	spin_lock_init(&ifmsh->mesh_preq_queue_lock); +	spin_lock_init(&ifmsh->sync_offset_lock); +	RCU_INIT_POINTER(ifmsh->beacon, NULL); + +	sdata->vif.bss_conf.bssid = zero_addr;  } diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 58e74112896..f39a19f9090 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -13,7 +13,6 @@  #include <linux/types.h>  #include <linux/jhash.h> -#include <asm/unaligned.h>  #include "ieee80211_i.h" @@ -27,10 +26,12 @@   * @MESH_PATH_ACTIVE: the mesh path can be used for forwarding   * @MESH_PATH_RESOLVING: the discovery process is running for this mesh path   * @MESH_PATH_SN_VALID: the mesh path contains a valid destination sequence - * 	number + *	number   * @MESH_PATH_FIXED: the mesh path has been manually set and should not be - * 	modified + *	modified   * @MESH_PATH_RESOLVED: the mesh path can has been resolved + * @MESH_PATH_REQ_QUEUED: there is an unsent path request for this destination + *	already queued up, waiting for the discovery process to start.   *   * MESH_PATH_RESOLVED is used by the mesh path timer to   * decide when to stop or cancel the mesh path discovery. @@ -41,6 +42,7 @@ enum mesh_path_flags {  	MESH_PATH_SN_VALID =	BIT(2),  	MESH_PATH_FIXED	=	BIT(3),  	MESH_PATH_RESOLVED =	BIT(4), +	MESH_PATH_REQ_QUEUED =	BIT(5),  };  /** @@ -54,12 +56,17 @@ enum mesh_path_flags {   * @MESH_WORK_GROW_MPP_TABLE: the mesh portals table is full and needs to   * grow   * @MESH_WORK_ROOT: the mesh root station needs to send a frame + * @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other + * mesh nodes + * @MESH_WORK_MBSS_CHANGED: rebuild beacon and notify driver of BSS changes   */  enum mesh_deferred_task_flags {  	MESH_WORK_HOUSEKEEPING,  	MESH_WORK_GROW_MPATH_TABLE,  	MESH_WORK_GROW_MPP_TABLE,  	MESH_WORK_ROOT, +	MESH_WORK_DRIFT_ADJUST, +	MESH_WORK_MBSS_CHANGED,  };  /** @@ -68,19 +75,26 @@ enum mesh_deferred_task_flags {   * @dst: mesh path destination mac address   * @sdata: mesh subif   * @next_hop: mesh neighbor to which frames for this destination will be - * 	forwarded + *	forwarded   * @timer: mesh path discovery timer   * @frame_queue: pending queue for frames sent to this destination while the - * 	path is unresolved + *	path is unresolved   * @sn: target sequence number   * @metric: current metric to this destination   * @hop_count: hops to destination   * @exp_time: in jiffies, when the path will expire or when it expired   * @discovery_timeout: timeout (lapse in jiffies) used for the last discovery - * 	retry + *	retry   * @discovery_retries: number of discovery retries   * @flags: mesh path flags, as specified on &enum mesh_path_flags - * @state_lock: mesh path state lock + * @state_lock: mesh path state lock used to protect changes to the + * mpath itself.  No need to take this lock when adding or removing + * an mpath to a hash bucket on a path table. + * @rann_snd_addr: the RANN sender address + * @rann_metric: the aggregated path metric towards the root node + * @last_preq_to_root: Timestamp of last PREQ sent to root + * @is_root: the destination station of this path is a root node + * @is_gate: the destination station of this path is a mesh gate   *   *   * The combination of dst and sdata is unique in the mesh path table. Since the @@ -92,7 +106,7 @@ struct mesh_path {  	u8 dst[ETH_ALEN];  	u8 mpp[ETH_ALEN];	/* used for MPP or MAP */  	struct ieee80211_sub_if_data *sdata; -	struct sta_info *next_hop; +	struct sta_info __rcu *next_hop;  	struct timer_list timer;  	struct sk_buff_head frame_queue;  	struct rcu_head rcu; @@ -104,6 +118,11 @@ struct mesh_path {  	u8 discovery_retries;  	enum mesh_path_flags flags;  	spinlock_t state_lock; +	u8 rann_snd_addr[ETH_ALEN]; +	u32 rann_metric; +	unsigned long last_preq_to_root; +	bool is_root; +	bool is_gate;  };  /** @@ -120,6 +139,10 @@ struct mesh_path {   *	buckets   * @mean_chain_len: maximum average length for the hash buckets' list, if it is   *	reached, the table will grow + * @known_gates: list of known mesh gates and their mpaths by the station. The + * gate's mpath may or may not be resolved and active. + * + * rcu_head: RCU head to free the table   */  struct mesh_table {  	/* Number of buckets will be 2^N */ @@ -132,6 +155,10 @@ struct mesh_table {  	int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl);  	int size_order;  	int mean_chain_len; +	struct hlist_head *known_gates; +	spinlock_t gates_lock; + +	struct rcu_head rcu_head;  };  /* Recent multicast cache */ @@ -159,152 +186,157 @@ struct rmc_entry {  };  struct mesh_rmc { -	struct rmc_entry bucket[RMC_BUCKETS]; +	struct list_head bucket[RMC_BUCKETS];  	u32 idx_mask;  }; +#define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ) -/* - * MESH_CFG_COMP_LEN Includes: - * 	- Active path selection protocol ID. - * 	- Active path selection metric ID. - * 	- Congestion control mode identifier. - * 	- Channel precedence. - * Does not include mesh capabilities, which may vary across nodes in the same - * mesh - */ -#define MESH_CFG_CMP_LEN 	(IEEE80211_MESH_CONFIG_LEN - 2) - -/* Default values, timeouts in ms */ -#define MESH_TTL 		31 -#define MESH_MAX_RETR	 	3 -#define MESH_RET_T 		100 -#define MESH_CONF_T 		100 -#define MESH_HOLD_T 		100 - -#define MESH_PATH_TIMEOUT	5000 -/* Minimum interval between two consecutive PREQs originated by the same - * interface - */ -#define MESH_PREQ_MIN_INT	10 -#define MESH_DIAM_TRAVERSAL_TIME 50 -/* A path will be refreshed if it is used PATH_REFRESH_TIME milliseconds before - * timing out.  This way it will remain ACTIVE and no data frames will be - * unnecesarily held in the pending queue. - */ -#define MESH_PATH_REFRESH_TIME			1000 -#define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME) -#define MESH_DEFAULT_BEACON_INTERVAL		1000 	/* in 1024 us units */ - -#define MESH_MAX_PREQ_RETRIES 4  #define MESH_PATH_EXPIRE (600 * HZ) -/* Default maximum number of established plinks per interface */ -#define MESH_MAX_ESTAB_PLINKS	32 -  /* Default maximum number of plinks per interface */  #define MESH_MAX_PLINKS		256  /* Maximum number of paths per interface */  #define MESH_MAX_MPATHS		1024 -/* Pending ANA approval */ -#define MESH_PATH_SEL_ACTION	0 - -/* PERR reason codes */ -#define PEER_RCODE_UNSPECIFIED  11 -#define PERR_RCODE_NO_ROUTE     12 -#define PERR_RCODE_DEST_UNREACH 13 +/* Number of frames buffered per destination for unresolved destinations */ +#define MESH_FRAME_QUEUE_LEN	10  /* Public interfaces */  /* Various */  int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,  				  const u8 *da, const u8 *sa); -int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, -		struct ieee80211_sub_if_data *sdata, char *addr4, -		char *addr5, char *addr6); -int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr, -		struct ieee80211_sub_if_data *sdata); -bool mesh_matches_local(struct ieee802_11_elems *ie, -		struct ieee80211_sub_if_data *sdata); +int ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata, +			      struct ieee80211s_hdr *meshhdr, +			      const char *addr4or5, const char *addr6); +int mesh_rmc_check(struct ieee80211_sub_if_data *sdata, +		   const u8 *addr, struct ieee80211s_hdr *mesh_hdr); +bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, +			struct ieee802_11_elems *ie);  void mesh_ids_set_default(struct ieee80211_if_mesh *mesh); -void mesh_mgmt_ies_add(struct sk_buff *skb, -		struct ieee80211_sub_if_data *sdata); +int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata, +			 struct sk_buff *skb); +int mesh_add_meshid_ie(struct ieee80211_sub_if_data *sdata, +		       struct sk_buff *skb); +int mesh_add_rsn_ie(struct ieee80211_sub_if_data *sdata, +		    struct sk_buff *skb); +int mesh_add_vendor_ies(struct ieee80211_sub_if_data *sdata, +			struct sk_buff *skb); +int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata, +		       struct sk_buff *skb); +int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata, +			struct sk_buff *skb);  void mesh_rmc_free(struct ieee80211_sub_if_data *sdata);  int mesh_rmc_init(struct ieee80211_sub_if_data *sdata);  void ieee80211s_init(void);  void ieee80211s_update_metric(struct ieee80211_local *local, -		struct sta_info *stainfo, struct sk_buff *skb); -void ieee80211s_stop(void); +			      struct sta_info *sta, struct sk_buff *skb);  void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata); -void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata); +int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);  void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);  void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh); +const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method); +/* wrapper for ieee80211_bss_info_change_notify() */ +void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata, +				       u32 changed); + +/* mesh power save */ +u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata); +u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta, +				   enum nl80211_mesh_power_mode pm); +void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata, +				   struct sta_info *sta, +				   struct ieee80211_hdr *hdr); +void ieee80211_mps_sta_status_update(struct sta_info *sta); +void ieee80211_mps_rx_h_sta_process(struct sta_info *sta, +				    struct ieee80211_hdr *hdr); +void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta, +				    bool tx, bool acked); +void ieee80211_mps_frame_release(struct sta_info *sta, +				 struct ieee802_11_elems *elems);  /* Mesh paths */ -int mesh_nexthop_lookup(struct sk_buff *skb, -		struct ieee80211_sub_if_data *sdata); +int mesh_nexthop_lookup(struct ieee80211_sub_if_data *sdata, +			struct sk_buff *skb); +int mesh_nexthop_resolve(struct ieee80211_sub_if_data *sdata, +			 struct sk_buff *skb);  void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata); -struct mesh_path *mesh_path_lookup(u8 *dst, -		struct ieee80211_sub_if_data *sdata); -struct mesh_path *mpp_path_lookup(u8 *dst, -				  struct ieee80211_sub_if_data *sdata); -int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata); -struct mesh_path *mesh_path_lookup_by_idx(int idx, -		struct ieee80211_sub_if_data *sdata); +struct mesh_path *mesh_path_lookup(struct ieee80211_sub_if_data *sdata, +				   const u8 *dst); +struct mesh_path *mpp_path_lookup(struct ieee80211_sub_if_data *sdata, +				  const u8 *dst); +int mpp_path_add(struct ieee80211_sub_if_data *sdata, +		 const u8 *dst, const u8 *mpp); +struct mesh_path * +mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx);  void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop);  void mesh_path_expire(struct ieee80211_sub_if_data *sdata); -void mesh_path_flush(struct ieee80211_sub_if_data *sdata);  void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, -		struct ieee80211_mgmt *mgmt, size_t len); -int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata); +			    struct ieee80211_mgmt *mgmt, size_t len); +struct mesh_path * +mesh_path_add(struct ieee80211_sub_if_data *sdata, const u8 *dst); + +int mesh_path_add_gate(struct mesh_path *mpath); +int mesh_path_send_to_gates(struct mesh_path *mpath); +int mesh_gate_num(struct ieee80211_sub_if_data *sdata); +  /* Mesh plinks */ -void mesh_neighbour_update(u8 *hw_addr, u32 rates, -		struct ieee80211_sub_if_data *sdata, bool add); +void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, +			   u8 *hw_addr, struct ieee802_11_elems *ie);  bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie); -void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata); +u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata);  void mesh_plink_broken(struct sta_info *sta); -void mesh_plink_deactivate(struct sta_info *sta); -int mesh_plink_open(struct sta_info *sta); -void mesh_plink_block(struct sta_info *sta); +u32 mesh_plink_deactivate(struct sta_info *sta); +u32 mesh_plink_open(struct sta_info *sta); +u32 mesh_plink_block(struct sta_info *sta);  void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,  			 struct ieee80211_mgmt *mgmt, size_t len,  			 struct ieee80211_rx_status *rx_status); +void mesh_sta_cleanup(struct sta_info *sta);  /* Private interfaces */  /* Mesh tables */ -struct mesh_table *mesh_table_alloc(int size_order); -void mesh_table_free(struct mesh_table *tbl, bool free_leafs);  void mesh_mpath_table_grow(void);  void mesh_mpp_table_grow(void); -u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, -		struct mesh_table *tbl);  /* Mesh paths */ -int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, __le16 target_rcode, -		       const u8 *ra, struct ieee80211_sub_if_data *sdata); +int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata, +		       u8 ttl, const u8 *target, u32 target_sn, +		       u16 target_rcode, const u8 *ra);  void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta);  void mesh_path_flush_pending(struct mesh_path *mpath);  void mesh_path_tx_pending(struct mesh_path *mpath);  int mesh_pathtbl_init(void);  void mesh_pathtbl_unregister(void); -int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata); +int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr);  void mesh_path_timer(unsigned long data);  void mesh_path_flush_by_nexthop(struct sta_info *sta); -void mesh_path_discard_frame(struct sk_buff *skb, -		struct ieee80211_sub_if_data *sdata); -void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata); -void mesh_path_restart(struct ieee80211_sub_if_data *sdata); +void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata, +			     struct sk_buff *skb);  void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata); +bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);  extern int mesh_paths_generation;  #ifdef CONFIG_MAC80211_MESH -extern int mesh_allocated; +static inline +u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) +{ +	atomic_inc(&sdata->u.mesh.estab_plinks); +	return mesh_accept_plinks_update(sdata) | BSS_CHANGED_BEACON; +} + +static inline +u32 mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) +{ +	atomic_dec(&sdata->u.mesh.estab_plinks); +	return mesh_accept_plinks_update(sdata) | BSS_CHANGED_BEACON; +}  static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata)  {  	return sdata->u.mesh.mshcfg.dot11MeshMaxPeerLinks - -	       atomic_read(&sdata->u.mesh.mshstats.estab_plinks); +	       atomic_read(&sdata->u.mesh.estab_plinks);  }  static inline bool mesh_plink_availables(struct ieee80211_sub_if_data *sdata) @@ -318,26 +350,24 @@ static inline void mesh_path_activate(struct mesh_path *mpath)  	mpath->flags |= MESH_PATH_ACTIVE | MESH_PATH_RESOLVED;  } -#define for_each_mesh_entry(x, p, node, i) \ -	for (i = 0; i <= x->hash_mask; i++) \ -		hlist_for_each_entry_rcu(node, p, &x->hash_buckets[i], list) +static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata) +{ +	return sdata->u.mesh.mesh_pp_id == IEEE80211_PATH_PROTOCOL_HWMP; +}  void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local); -void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata); -void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata); -void mesh_plink_quiesce(struct sta_info *sta); -void mesh_plink_restart(struct sta_info *sta); +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 -#define mesh_allocated	0  static inline void  ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {} -static inline void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata) -{} -static inline void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata) +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)  {} -static inline void mesh_plink_quiesce(struct sta_info *sta) {} -static inline void mesh_plink_restart(struct sta_info *sta) {} +static inline void ieee80211s_stop(void) {}  #endif  #endif /* IEEE80211S_H */ diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 829e08a657d..94758b9c9ed 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -8,20 +8,15 @@   */  #include <linux/slab.h> +#include <linux/etherdevice.h> +#include <asm/unaligned.h> +#include "wme.h"  #include "mesh.h" -#ifdef CONFIG_MAC80211_VERBOSE_MHWMP_DEBUG -#define mhwmp_dbg(fmt, args...)   printk(KERN_DEBUG "Mesh HWMP: " fmt, ##args) -#else -#define mhwmp_dbg(fmt, args...)   do { (void)(0); } while (0) -#endif -  #define TEST_FRAME_LEN	8192  #define MAX_METRIC	0xffffffff  #define ARITH_SHIFT	8 -/* Number of frames buffered per destination for unresolved destinations */ -#define MESH_FRAME_QUEUE_LEN	10  #define MAX_PREQ_QUEUE_LEN	64  /* Destination only */ @@ -35,14 +30,14 @@  static void mesh_queue_preq(struct mesh_path *, u8); -static inline u32 u32_field_get(u8 *preq_elem, int offset, bool ae) +static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae)  {  	if (ae)  		offset += 6;  	return get_unaligned_le32(preq_elem + offset);  } -static inline u32 u16_field_get(u8 *preq_elem, int offset, bool ae) +static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)  {  	if (ae)  		offset += 6; @@ -57,33 +52,33 @@ static inline u32 u16_field_get(u8 *preq_elem, int offset, bool ae)  #define PREQ_IE_TTL(x)		(*(x + 2))  #define PREQ_IE_PREQ_ID(x)	u32_field_get(x, 3, 0)  #define PREQ_IE_ORIG_ADDR(x)	(x + 7) -#define PREQ_IE_ORIG_SN(x)	u32_field_get(x, 13, 0); -#define PREQ_IE_LIFETIME(x)	u32_field_get(x, 17, AE_F_SET(x)); -#define PREQ_IE_METRIC(x) 	u32_field_get(x, 21, AE_F_SET(x)); +#define PREQ_IE_ORIG_SN(x)	u32_field_get(x, 13, 0) +#define PREQ_IE_LIFETIME(x)	u32_field_get(x, 17, AE_F_SET(x)) +#define PREQ_IE_METRIC(x) 	u32_field_get(x, 21, AE_F_SET(x))  #define PREQ_IE_TARGET_F(x)	(*(AE_F_SET(x) ? x + 32 : x + 26))  #define PREQ_IE_TARGET_ADDR(x) 	(AE_F_SET(x) ? x + 33 : x + 27) -#define PREQ_IE_TARGET_SN(x) 	u32_field_get(x, 33, AE_F_SET(x)); +#define PREQ_IE_TARGET_SN(x) 	u32_field_get(x, 33, AE_F_SET(x))  #define PREP_IE_FLAGS(x)	PREQ_IE_FLAGS(x)  #define PREP_IE_HOPCOUNT(x)	PREQ_IE_HOPCOUNT(x)  #define PREP_IE_TTL(x)		PREQ_IE_TTL(x) -#define PREP_IE_ORIG_ADDR(x)	(x + 3) -#define PREP_IE_ORIG_SN(x)	u32_field_get(x, 9, 0); -#define PREP_IE_LIFETIME(x)	u32_field_get(x, 13, AE_F_SET(x)); -#define PREP_IE_METRIC(x)	u32_field_get(x, 17, AE_F_SET(x)); -#define PREP_IE_TARGET_ADDR(x)	(AE_F_SET(x) ? x + 27 : x + 21) -#define PREP_IE_TARGET_SN(x)	u32_field_get(x, 27, AE_F_SET(x)); +#define PREP_IE_ORIG_ADDR(x)	(AE_F_SET(x) ? x + 27 : x + 21) +#define PREP_IE_ORIG_SN(x)	u32_field_get(x, 27, AE_F_SET(x)) +#define PREP_IE_LIFETIME(x)	u32_field_get(x, 13, AE_F_SET(x)) +#define PREP_IE_METRIC(x)	u32_field_get(x, 17, AE_F_SET(x)) +#define PREP_IE_TARGET_ADDR(x)	(x + 3) +#define PREP_IE_TARGET_SN(x)	u32_field_get(x, 9, 0)  #define PERR_IE_TTL(x)		(*(x))  #define PERR_IE_TARGET_FLAGS(x)	(*(x + 2))  #define PERR_IE_TARGET_ADDR(x)	(x + 3) -#define PERR_IE_TARGET_SN(x)	u32_field_get(x, 9, 0); -#define PERR_IE_TARGET_RCODE(x)	u16_field_get(x, 13, 0); +#define PERR_IE_TARGET_SN(x)	u32_field_get(x, 9, 0) +#define PERR_IE_TARGET_RCODE(x)	u16_field_get(x, 13, 0)  #define MSEC_TO_TU(x) (x*1000/1024) -#define SN_GT(x, y) ((long) (y) - (long) (x) < 0) -#define SN_LT(x, y) ((long) (x) - (long) (y) < 0) +#define SN_GT(x, y) ((s32)(y - x) < 0) +#define SN_LT(x, y) ((s32)(x - y) < 0)  #define net_traversal_jiffies(s) \  	msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime) @@ -94,6 +89,8 @@ static inline u32 u16_field_get(u8 *preq_elem, int offset, bool ae)  #define max_preq_retries(s) (s->u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries)  #define disc_timeout_jiff(s) \  	msecs_to_jiffies(sdata->u.mesh.mshcfg.min_discovery_timeout) +#define root_path_confirmation_jiffies(s) \ +	msecs_to_jiffies(sdata->u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval)  enum mpath_frame_type {  	MPATH_PREQ = 0, @@ -105,26 +102,28 @@ enum mpath_frame_type {  static const u8 broadcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};  static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, -		u8 *orig_addr, __le32 orig_sn, u8 target_flags, u8 *target, -		__le32 target_sn, const u8 *da, u8 hop_count, u8 ttl, -		__le32 lifetime, __le32 metric, __le32 preq_id, -		struct ieee80211_sub_if_data *sdata) +				  const u8 *orig_addr, u32 orig_sn, +				  u8 target_flags, const u8 *target, +				  u32 target_sn, const u8 *da, +				  u8 hop_count, u8 ttl, +				  u32 lifetime, u32 metric, u32 preq_id, +				  struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_local *local = sdata->local; -	struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); +	struct sk_buff *skb;  	struct ieee80211_mgmt *mgmt; -	u8 *pos; -	int ie_len; +	u8 *pos, ie_len; +	int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.mesh_action) + +		      sizeof(mgmt->u.action.u.mesh_action); +	skb = dev_alloc_skb(local->tx_headroom + +			    hdr_len + +			    2 + 37); /* max HWMP IE */  	if (!skb)  		return -1; -	skb_reserve(skb, local->hw.extra_tx_headroom); -	/* 25 is the size of the common mgmt part (24) plus the size of the -	 * common action part (1) -	 */ -	mgmt = (struct ieee80211_mgmt *) -		skb_put(skb, 25 + sizeof(mgmt->u.action.u.mesh_action)); -	memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.mesh_action)); +	skb_reserve(skb, local->tx_headroom); +	mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); +	memset(mgmt, 0, hdr_len);  	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |  					  IEEE80211_STYPE_ACTION); @@ -132,24 +131,25 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,  	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);  	/* BSSID == SA */  	memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); -	mgmt->u.action.category = WLAN_CATEGORY_MESH_PATH_SEL; -	mgmt->u.action.u.mesh_action.action_code = MESH_PATH_SEL_ACTION; +	mgmt->u.action.category = WLAN_CATEGORY_MESH_ACTION; +	mgmt->u.action.u.mesh_action.action_code = +					WLAN_MESH_ACTION_HWMP_PATH_SELECTION;  	switch (action) {  	case MPATH_PREQ: -		mhwmp_dbg("sending PREQ to %pM\n", target); +		mhwmp_dbg(sdata, "sending PREQ to %pM\n", target);  		ie_len = 37;  		pos = skb_put(skb, 2 + ie_len);  		*pos++ = WLAN_EID_PREQ;  		break;  	case MPATH_PREP: -		mhwmp_dbg("sending PREP to %pM\n", target); +		mhwmp_dbg(sdata, "sending PREP to %pM\n", orig_addr);  		ie_len = 31;  		pos = skb_put(skb, 2 + ie_len);  		*pos++ = WLAN_EID_PREP;  		break;  	case MPATH_RANN: -		mhwmp_dbg("sending RANN from %pM\n", orig_addr); +		mhwmp_dbg(sdata, "sending RANN from %pM\n", orig_addr);  		ie_len = sizeof(struct ieee80211_rann_ie);  		pos = skb_put(skb, 2 + ie_len);  		*pos++ = WLAN_EID_RANN; @@ -163,76 +163,121 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,  	*pos++ = flags;  	*pos++ = hop_count;  	*pos++ = ttl; -	if (action == MPATH_PREQ) { -		memcpy(pos, &preq_id, 4); +	if (action == MPATH_PREP) { +		memcpy(pos, target, ETH_ALEN); +		pos += ETH_ALEN; +		put_unaligned_le32(target_sn, pos);  		pos += 4; -	} -	memcpy(pos, orig_addr, ETH_ALEN); -	pos += ETH_ALEN; -	memcpy(pos, &orig_sn, 4); -	pos += 4; -	if (action != MPATH_RANN) { -		memcpy(pos, &lifetime, 4); +	} else { +		if (action == MPATH_PREQ) { +			put_unaligned_le32(preq_id, pos); +			pos += 4; +		} +		memcpy(pos, orig_addr, ETH_ALEN); +		pos += ETH_ALEN; +		put_unaligned_le32(orig_sn, pos);  		pos += 4;  	} -	memcpy(pos, &metric, 4); +	put_unaligned_le32(lifetime, pos); /* interval for RANN */ +	pos += 4; +	put_unaligned_le32(metric, pos);  	pos += 4;  	if (action == MPATH_PREQ) { -		/* destination count */ -		*pos++ = 1; +		*pos++ = 1; /* destination count */  		*pos++ = target_flags; -	} -	if (action != MPATH_RANN) {  		memcpy(pos, target, ETH_ALEN);  		pos += ETH_ALEN; -		memcpy(pos, &target_sn, 4); +		put_unaligned_le32(target_sn, pos); +		pos += 4; +	} else if (action == MPATH_PREP) { +		memcpy(pos, orig_addr, ETH_ALEN); +		pos += ETH_ALEN; +		put_unaligned_le32(orig_sn, pos); +		pos += 4;  	}  	ieee80211_tx_skb(sdata, skb);  	return 0;  } + +/*  Headroom is not adjusted.  Caller should ensure that skb has sufficient + *  headroom in case the frame is encrypted. */ +static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata, +		struct sk_buff *skb) +{ +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + +	skb_set_mac_header(skb, 0); +	skb_set_network_header(skb, 0); +	skb_set_transport_header(skb, 0); + +	/* Send all internal mgmt frames on VO. Accordingly set TID to 7. */ +	skb_set_queue_mapping(skb, IEEE80211_AC_VO); +	skb->priority = 7; + +	info->control.vif = &sdata->vif; +	info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; +	ieee80211_set_qos_hdr(sdata, skb); +	ieee80211_mps_set_frame_flags(sdata, NULL, hdr); +} +  /** - * mesh_send_path error - Sends a PERR mesh management frame + * mesh_path_error_tx - Sends a PERR mesh management frame   * + * @ttl: allowed remaining hops   * @target: broken destination   * @target_sn: SN of the broken destination   * @target_rcode: reason code for this PERR   * @ra: node this frame is addressed to + * @sdata: local mesh subif + * + * Note: This function may be called with driver locks taken that the driver + * also acquires in the TX path.  To avoid a deadlock we don't transmit the + * frame directly but add it to the pending queue instead.   */ -int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, -		       __le16 target_rcode, const u8 *ra, -		       struct ieee80211_sub_if_data *sdata) +int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata, +		       u8 ttl, const u8 *target, u32 target_sn, +		       u16 target_rcode, const u8 *ra)  {  	struct ieee80211_local *local = sdata->local; -	struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); +	struct sk_buff *skb; +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;  	struct ieee80211_mgmt *mgmt; -	u8 *pos; -	int ie_len; - +	u8 *pos, ie_len; +	int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.mesh_action) + +		      sizeof(mgmt->u.action.u.mesh_action); + +	if (time_before(jiffies, ifmsh->next_perr)) +		return -EAGAIN; + +	skb = dev_alloc_skb(local->tx_headroom + +			    sdata->encrypt_headroom + +			    IEEE80211_ENCRYPT_TAILROOM + +			    hdr_len + +			    2 + 15 /* PERR IE */);  	if (!skb)  		return -1; -	skb_reserve(skb, local->hw.extra_tx_headroom); -	/* 25 is the size of the common mgmt part (24) plus the size of the -	 * common action part (1) -	 */ -	mgmt = (struct ieee80211_mgmt *) -		skb_put(skb, 25 + sizeof(mgmt->u.action.u.mesh_action)); -	memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.mesh_action)); +	skb_reserve(skb, local->tx_headroom + sdata->encrypt_headroom); +	mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); +	memset(mgmt, 0, hdr_len);  	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |  					  IEEE80211_STYPE_ACTION);  	memcpy(mgmt->da, ra, ETH_ALEN);  	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); -	/* BSSID is left zeroed, wildcard value */ -	mgmt->u.action.category = WLAN_CATEGORY_MESH_PATH_SEL; -	mgmt->u.action.u.mesh_action.action_code = MESH_PATH_SEL_ACTION; +	/* BSSID == SA */ +	memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); +	mgmt->u.action.category = WLAN_CATEGORY_MESH_ACTION; +	mgmt->u.action.u.mesh_action.action_code = +					WLAN_MESH_ACTION_HWMP_PATH_SELECTION;  	ie_len = 15;  	pos = skb_put(skb, 2 + ie_len);  	*pos++ = WLAN_EID_PERR;  	*pos++ = ie_len;  	/* ttl */ -	*pos++ = MESH_TTL; +	*pos++ = ttl;  	/* number of destinations */  	*pos++ = 1;  	/* @@ -247,16 +292,20 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn,  	pos++;  	memcpy(pos, target, ETH_ALEN);  	pos += ETH_ALEN; -	memcpy(pos, &target_sn, 4); +	put_unaligned_le32(target_sn, pos);  	pos += 4; -	memcpy(pos, &target_rcode, 2); +	put_unaligned_le16(target_rcode, pos); -	ieee80211_tx_skb(sdata, skb); +	/* see note in function header */ +	prepare_frame_for_deferred_tx(sdata, skb); +	ifmsh->next_perr = TU_TO_EXP_TIME( +				   ifmsh->mshcfg.dot11MeshHWMPperrMinInterval); +	ieee80211_add_pending_skb(local, skb);  	return 0;  }  void ieee80211s_update_metric(struct ieee80211_local *local, -		struct sta_info *stainfo, struct sk_buff *skb) +		struct sta_info *sta, struct sk_buff *skb)  {  	struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; @@ -268,15 +317,15 @@ void ieee80211s_update_metric(struct ieee80211_local *local,  	failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK);  	/* moving average, scaled to 100 */ -	stainfo->fail_avg = ((80 * stainfo->fail_avg + 5) / 100 + 20 * failed); -	if (stainfo->fail_avg > 95) -		mesh_plink_broken(stainfo); +	sta->fail_avg = ((80 * sta->fail_avg + 5) / 100 + 20 * failed); +	if (sta->fail_avg > 95) +		mesh_plink_broken(sta);  }  static u32 airtime_link_metric_get(struct ieee80211_local *local,  				   struct sta_info *sta)  { -	struct ieee80211_supported_band *sband; +	struct rate_info rinfo;  	/* This should be adjusted for each device */  	int device_constant = 1 << ARITH_SHIFT;  	int test_frame_len = TEST_FRAME_LEN << ARITH_SHIFT; @@ -285,12 +334,12 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,  	u32 tx_time, estimated_retx;  	u64 result; -	sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; -  	if (sta->fail_avg >= 100)  		return MAX_METRIC; -	if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS) +	sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo); +	rate = cfg80211_calculate_bitrate(&rinfo); +	if (WARN_ON(!rate))  		return MAX_METRIC;  	err = (sta->fail_avg << ARITH_SHIFT) / 100; @@ -298,7 +347,6 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,  	/* bitrate is in units of 100 Kbps, while we need rate in units of  	 * 1Mbps. This will be corrected on tx_time computation.  	 */ -	rate = sband->bitrates[sta->last_tx_rate.idx].bitrate;  	tx_time = (device_constant + 10 * test_frame_len / rate);  	estimated_retx = ((1 << (2 * ARITH_SHIFT)) / (s_unit - err));  	result = (tx_time * estimated_retx) >> (2 * ARITH_SHIFT) ; @@ -311,6 +359,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,   * @sdata: local mesh subif   * @mgmt: mesh management frame   * @hwmp_ie: hwmp information element (PREP or PREQ) + * @action: type of hwmp ie   *   * This function updates the path routing information to the originator and the   * transmitter of a HWMP PREQ or PREP frame. @@ -322,14 +371,14 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,   * path routing information is updated.   */  static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, -			    struct ieee80211_mgmt *mgmt, -			    u8 *hwmp_ie, enum mpath_frame_type action) +			       struct ieee80211_mgmt *mgmt, +			       const u8 *hwmp_ie, enum mpath_frame_type action)  {  	struct ieee80211_local *local = sdata->local;  	struct mesh_path *mpath;  	struct sta_info *sta;  	bool fresh_info; -	u8 *orig_addr, *ta; +	const u8 *orig_addr, *ta;  	u32 orig_sn, orig_metric;  	unsigned long orig_lifetime, exp_time;  	u32 last_hop_metric, new_metric; @@ -354,15 +403,13 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,  		orig_metric = PREQ_IE_METRIC(hwmp_ie);  		break;  	case MPATH_PREP: -		/* Originator here refers to the MP that was the destination in -		 * the Path Request. The draft refers to that MP as the -		 * destination address, even though usually it is the origin of -		 * the PREP frame. We divert from the nomenclature in the draft +		/* Originator here refers to the MP that was the target in the +		 * Path Request. We divert from the nomenclature in the draft  		 * so that we can easily use a single function to gather path  		 * information from both PREQ and PREP frames.  		 */ -		orig_addr = PREP_IE_ORIG_ADDR(hwmp_ie); -		orig_sn = PREP_IE_ORIG_SN(hwmp_ie); +		orig_addr = PREP_IE_TARGET_ADDR(hwmp_ie); +		orig_sn = PREP_IE_TARGET_SN(hwmp_ie);  		orig_lifetime = PREP_IE_LIFETIME(hwmp_ie);  		orig_metric = PREP_IE_METRIC(hwmp_ie);  		break; @@ -375,14 +422,14 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,  		new_metric = MAX_METRIC;  	exp_time = TU_TO_EXP_TIME(orig_lifetime); -	if (memcmp(orig_addr, sdata->vif.addr, ETH_ALEN) == 0) { +	if (ether_addr_equal(orig_addr, sdata->vif.addr)) {  		/* This MP is the originator, we are not interested in this  		 * frame, except for updating transmitter's path info.  		 */  		process = false;  		fresh_info = false;  	} else { -		mpath = mesh_path_lookup(orig_addr, sdata); +		mpath = mesh_path_lookup(sdata, orig_addr);  		if (mpath) {  			spin_lock_bh(&mpath->state_lock);  			if (mpath->flags & MESH_PATH_FIXED) @@ -391,16 +438,14 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,  			    (mpath->flags & MESH_PATH_SN_VALID)) {  				if (SN_GT(mpath->sn, orig_sn) ||  				    (mpath->sn == orig_sn && -				     action == MPATH_PREQ &&  				     new_metric >= mpath->metric)) {  					process = false;  					fresh_info = false;  				}  			}  		} else { -			mesh_path_add(orig_addr, sdata); -			mpath = mesh_path_lookup(orig_addr, sdata); -			if (!mpath) { +			mpath = mesh_path_add(sdata, orig_addr); +			if (IS_ERR(mpath)) {  				rcu_read_unlock();  				return 0;  			} @@ -426,12 +471,12 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,  	/* Update and check transmitter routing info */  	ta = mgmt->sa; -	if (memcmp(orig_addr, ta, ETH_ALEN) == 0) +	if (ether_addr_equal(orig_addr, ta))  		fresh_info = false;  	else {  		fresh_info = true; -		mpath = mesh_path_lookup(ta, sdata); +		mpath = mesh_path_lookup(sdata, ta);  		if (mpath) {  			spin_lock_bh(&mpath->state_lock);  			if ((mpath->flags & MESH_PATH_FIXED) || @@ -439,9 +484,8 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,  					(last_hop_metric > mpath->metric)))  				fresh_info = false;  		} else { -			mesh_path_add(ta, sdata); -			mpath = mesh_path_lookup(ta, sdata); -			if (!mpath) { +			mpath = mesh_path_add(sdata, ta); +			if (IS_ERR(mpath)) {  				rcu_read_unlock();  				return 0;  			} @@ -450,7 +494,6 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,  		if (fresh_info) {  			mesh_path_assign_nexthop(mpath, sta); -			mpath->flags &= ~MESH_PATH_SN_VALID;  			mpath->metric = last_hop_metric;  			mpath->exp_time = time_after(mpath->exp_time, exp_time)  					  ?  mpath->exp_time : exp_time; @@ -468,15 +511,17 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,  static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,  				    struct ieee80211_mgmt *mgmt, -				    u8 *preq_elem, u32 metric) +				    const u8 *preq_elem, u32 metric)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; -	struct mesh_path *mpath; -	u8 *target_addr, *orig_addr; -	u8 target_flags, ttl; -	u32 orig_sn, target_sn, lifetime; +	struct mesh_path *mpath = NULL; +	const u8 *target_addr, *orig_addr; +	const u8 *da; +	u8 target_flags, ttl, flags; +	u32 orig_sn, target_sn, lifetime, orig_metric;  	bool reply = false;  	bool forward = true; +	bool root_is_gate;  	/* Update target SN, if present */  	target_addr = PREQ_IE_TARGET_ADDR(preq_elem); @@ -484,23 +529,44 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,  	target_sn = PREQ_IE_TARGET_SN(preq_elem);  	orig_sn = PREQ_IE_ORIG_SN(preq_elem);  	target_flags = PREQ_IE_TARGET_F(preq_elem); +	orig_metric = metric; +	/* Proactive PREQ gate announcements */ +	flags = PREQ_IE_FLAGS(preq_elem); +	root_is_gate = !!(flags & RANN_FLAG_IS_GATE); -	mhwmp_dbg("received PREQ from %pM\n", orig_addr); +	mhwmp_dbg(sdata, "received PREQ from %pM\n", orig_addr); -	if (memcmp(target_addr, sdata->vif.addr, ETH_ALEN) == 0) { -		mhwmp_dbg("PREQ is for us\n"); +	if (ether_addr_equal(target_addr, sdata->vif.addr)) { +		mhwmp_dbg(sdata, "PREQ is for us\n");  		forward = false;  		reply = true;  		metric = 0;  		if (time_after(jiffies, ifmsh->last_sn_update +  					net_traversal_jiffies(sdata)) ||  		    time_before(jiffies, ifmsh->last_sn_update)) { -			target_sn = ++ifmsh->sn; +			++ifmsh->sn;  			ifmsh->last_sn_update = jiffies;  		} +		target_sn = ifmsh->sn; +	} else if (is_broadcast_ether_addr(target_addr) && +		   (target_flags & IEEE80211_PREQ_TO_FLAG)) { +		rcu_read_lock(); +		mpath = mesh_path_lookup(sdata, orig_addr); +		if (mpath) { +			if (flags & IEEE80211_PREQ_PROACTIVE_PREP_FLAG) { +				reply = true; +				target_addr = sdata->vif.addr; +				target_sn = ++ifmsh->sn; +				metric = 0; +				ifmsh->last_sn_update = jiffies; +			} +			if (root_is_gate) +				mesh_path_add_gate(mpath); +		} +		rcu_read_unlock();  	} else {  		rcu_read_lock(); -		mpath = mesh_path_lookup(target_addr, sdata); +		mpath = mesh_path_lookup(sdata, target_addr);  		if (mpath) {  			if ((!(mpath->flags & MESH_PATH_SN_VALID)) ||  					SN_LT(mpath->sn, target_sn)) { @@ -522,21 +588,21 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,  	if (reply) {  		lifetime = PREQ_IE_LIFETIME(preq_elem); -		ttl = ifmsh->mshcfg.dot11MeshTTL; +		ttl = ifmsh->mshcfg.element_ttl;  		if (ttl != 0) { -			mhwmp_dbg("replying to the PREQ\n"); -			mesh_path_sel_frame_tx(MPATH_PREP, 0, target_addr, -				cpu_to_le32(target_sn), 0, orig_addr, -				cpu_to_le32(orig_sn), mgmt->sa, 0, ttl, -				cpu_to_le32(lifetime), cpu_to_le32(metric), -				0, sdata); -		} else +			mhwmp_dbg(sdata, "replying to the PREQ\n"); +			mesh_path_sel_frame_tx(MPATH_PREP, 0, orig_addr, +					       orig_sn, 0, target_addr, +					       target_sn, mgmt->sa, 0, ttl, +					       lifetime, metric, 0, sdata); +		} else {  			ifmsh->mshstats.dropped_frames_ttl++; +		}  	} -	if (forward) { +	if (forward && ifmsh->mshcfg.dot11MeshForwarding) {  		u32 preq_id; -		u8 hopcount, flags; +		u8 hopcount;  		ttl = PREQ_IE_TTL(preq_elem);  		lifetime = PREQ_IE_LIFETIME(preq_elem); @@ -544,46 +610,62 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,  			ifmsh->mshstats.dropped_frames_ttl++;  			return;  		} -		mhwmp_dbg("forwarding the PREQ from %pM\n", orig_addr); +		mhwmp_dbg(sdata, "forwarding the PREQ from %pM\n", orig_addr);  		--ttl; -		flags = PREQ_IE_FLAGS(preq_elem);  		preq_id = PREQ_IE_PREQ_ID(preq_elem);  		hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1; +		da = (mpath && mpath->is_root) ? +			mpath->rann_snd_addr : broadcast_addr; + +		if (flags & IEEE80211_PREQ_PROACTIVE_PREP_FLAG) { +			target_addr = PREQ_IE_TARGET_ADDR(preq_elem); +			target_sn = PREQ_IE_TARGET_SN(preq_elem); +			metric = orig_metric; +		} +  		mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr, -				cpu_to_le32(orig_sn), target_flags, target_addr, -				cpu_to_le32(target_sn), broadcast_addr, -				hopcount, ttl, cpu_to_le32(lifetime), -				cpu_to_le32(metric), cpu_to_le32(preq_id), -				sdata); -		ifmsh->mshstats.fwded_mcast++; +				       orig_sn, target_flags, target_addr, +				       target_sn, da, hopcount, ttl, lifetime, +				       metric, preq_id, sdata); +		if (!is_multicast_ether_addr(da)) +			ifmsh->mshstats.fwded_unicast++; +		else +			ifmsh->mshstats.fwded_mcast++;  		ifmsh->mshstats.fwded_frames++;  	}  } +static inline struct sta_info * +next_hop_deref_protected(struct mesh_path *mpath) +{ +	return rcu_dereference_protected(mpath->next_hop, +					 lockdep_is_held(&mpath->state_lock)); +} + +  static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,  				    struct ieee80211_mgmt *mgmt, -				    u8 *prep_elem, u32 metric) +				    const u8 *prep_elem, u32 metric)  { +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;  	struct mesh_path *mpath; -	u8 *target_addr, *orig_addr; +	const u8 *target_addr, *orig_addr;  	u8 ttl, hopcount, flags;  	u8 next_hop[ETH_ALEN];  	u32 target_sn, orig_sn, lifetime; -	mhwmp_dbg("received PREP from %pM\n", PREP_IE_ORIG_ADDR(prep_elem)); +	mhwmp_dbg(sdata, "received PREP from %pM\n", +		  PREP_IE_TARGET_ADDR(prep_elem)); -	/* Note that we divert from the draft nomenclature and denominate -	 * destination to what the draft refers to as origininator. So in this -	 * function destnation refers to the final destination of the PREP, -	 * which corresponds with the originator of the PREQ which this PREP -	 * replies -	 */ -	target_addr = PREP_IE_TARGET_ADDR(prep_elem); -	if (memcmp(target_addr, sdata->vif.addr, ETH_ALEN) == 0) +	orig_addr = PREP_IE_ORIG_ADDR(prep_elem); +	if (ether_addr_equal(orig_addr, sdata->vif.addr))  		/* destination, no forwarding required */  		return; +	if (!ifmsh->mshcfg.dot11MeshForwarding) +		return; +  	ttl = PREP_IE_TTL(prep_elem);  	if (ttl <= 1) {  		sdata->u.mesh.mshstats.dropped_frames_ttl++; @@ -591,7 +673,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,  	}  	rcu_read_lock(); -	mpath = mesh_path_lookup(target_addr, sdata); +	mpath = mesh_path_lookup(sdata, orig_addr);  	if (mpath)  		spin_lock_bh(&mpath->state_lock);  	else @@ -600,21 +682,19 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,  		spin_unlock_bh(&mpath->state_lock);  		goto fail;  	} -	memcpy(next_hop, mpath->next_hop->sta.addr, ETH_ALEN); +	memcpy(next_hop, next_hop_deref_protected(mpath)->sta.addr, ETH_ALEN);  	spin_unlock_bh(&mpath->state_lock);  	--ttl;  	flags = PREP_IE_FLAGS(prep_elem);  	lifetime = PREP_IE_LIFETIME(prep_elem);  	hopcount = PREP_IE_HOPCOUNT(prep_elem) + 1; -	orig_addr = PREP_IE_ORIG_ADDR(prep_elem); +	target_addr = PREP_IE_TARGET_ADDR(prep_elem);  	target_sn = PREP_IE_TARGET_SN(prep_elem);  	orig_sn = PREP_IE_ORIG_SN(prep_elem); -	mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr, -		cpu_to_le32(orig_sn), 0, target_addr, -		cpu_to_le32(target_sn), next_hop, hopcount, -		ttl, cpu_to_le32(lifetime), cpu_to_le32(metric), -		0, sdata); +	mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr, orig_sn, 0, +			       target_addr, target_sn, next_hop, hopcount, +			       ttl, lifetime, metric, 0, sdata);  	rcu_read_unlock();  	sdata->u.mesh.mshstats.fwded_unicast++; @@ -627,13 +707,13 @@ fail:  }  static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, -			     struct ieee80211_mgmt *mgmt, u8 *perr_elem) +				    struct ieee80211_mgmt *mgmt, +				    const u8 *perr_elem)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;  	struct mesh_path *mpath;  	u8 ttl; -	u8 *ta, *target_addr; -	u8 target_flags; +	const u8 *ta, *target_addr;  	u32 target_sn;  	u16 target_rcode; @@ -644,98 +724,155 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,  		return;  	}  	ttl--; -	target_flags = PERR_IE_TARGET_FLAGS(perr_elem);  	target_addr = PERR_IE_TARGET_ADDR(perr_elem);  	target_sn = PERR_IE_TARGET_SN(perr_elem);  	target_rcode = PERR_IE_TARGET_RCODE(perr_elem);  	rcu_read_lock(); -	mpath = mesh_path_lookup(target_addr, sdata); +	mpath = mesh_path_lookup(sdata, target_addr);  	if (mpath) { +		struct sta_info *sta; +  		spin_lock_bh(&mpath->state_lock); +		sta = next_hop_deref_protected(mpath);  		if (mpath->flags & MESH_PATH_ACTIVE && -		    memcmp(ta, mpath->next_hop->sta.addr, ETH_ALEN) == 0 && +		    ether_addr_equal(ta, sta->sta.addr) &&  		    (!(mpath->flags & MESH_PATH_SN_VALID) ||  		    SN_GT(target_sn, mpath->sn))) {  			mpath->flags &= ~MESH_PATH_ACTIVE;  			mpath->sn = target_sn;  			spin_unlock_bh(&mpath->state_lock); -			mesh_path_error_tx(ttl, target_addr, cpu_to_le32(target_sn), -					   cpu_to_le16(target_rcode), -					   broadcast_addr, sdata); +			if (!ifmsh->mshcfg.dot11MeshForwarding) +				goto endperr; +			mesh_path_error_tx(sdata, ttl, target_addr, +					   target_sn, target_rcode, +					   broadcast_addr);  		} else  			spin_unlock_bh(&mpath->state_lock);  	} +endperr:  	rcu_read_unlock();  }  static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, -				struct ieee80211_mgmt *mgmt, -				struct ieee80211_rann_ie *rann) +				    struct ieee80211_mgmt *mgmt, +				    const struct ieee80211_rann_ie *rann)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	struct ieee80211_local *local = sdata->local; +	struct sta_info *sta;  	struct mesh_path *mpath; -	u8 *ta;  	u8 ttl, flags, hopcount; -	u8 *orig_addr; -	u32 orig_sn, metric; +	const u8 *orig_addr; +	u32 orig_sn, metric, metric_txsta, interval; +	bool root_is_gate; -	ta = mgmt->sa;  	ttl = rann->rann_ttl; -	if (ttl <= 1) { -		ifmsh->mshstats.dropped_frames_ttl++; -		return; -	} -	ttl--;  	flags = rann->rann_flags; +	root_is_gate = !!(flags & RANN_FLAG_IS_GATE);  	orig_addr = rann->rann_addr; -	orig_sn = rann->rann_seq; +	orig_sn = le32_to_cpu(rann->rann_seq); +	interval = le32_to_cpu(rann->rann_interval);  	hopcount = rann->rann_hopcount;  	hopcount++; -	metric = rann->rann_metric; -	mhwmp_dbg("received RANN from %pM\n", orig_addr); +	metric = le32_to_cpu(rann->rann_metric); + +	/*  Ignore our own RANNs */ +	if (ether_addr_equal(orig_addr, sdata->vif.addr)) +		return; + +	mhwmp_dbg(sdata, +		  "received RANN from %pM via neighbour %pM (is_gate=%d)\n", +		  orig_addr, mgmt->sa, root_is_gate);  	rcu_read_lock(); -	mpath = mesh_path_lookup(orig_addr, sdata); +	sta = sta_info_get(sdata, mgmt->sa); +	if (!sta) { +		rcu_read_unlock(); +		return; +	} + +	metric_txsta = airtime_link_metric_get(local, sta); + +	mpath = mesh_path_lookup(sdata, orig_addr);  	if (!mpath) { -		mesh_path_add(orig_addr, sdata); -		mpath = mesh_path_lookup(orig_addr, sdata); -		if (!mpath) { +		mpath = mesh_path_add(sdata, orig_addr); +		if (IS_ERR(mpath)) {  			rcu_read_unlock();  			sdata->u.mesh.mshstats.dropped_frames_no_route++;  			return;  		} -		mesh_queue_preq(mpath, -				PREQ_Q_F_START | PREQ_Q_F_REFRESH);  	} -	if (mpath->sn < orig_sn) { + +	if (!(SN_LT(mpath->sn, orig_sn)) && +	    !(mpath->sn == orig_sn && metric < mpath->rann_metric)) { +		rcu_read_unlock(); +		return; +	} + +	if ((!(mpath->flags & (MESH_PATH_ACTIVE | MESH_PATH_RESOLVING)) || +	     (time_after(jiffies, mpath->last_preq_to_root + +				  root_path_confirmation_jiffies(sdata)) || +	     time_before(jiffies, mpath->last_preq_to_root))) && +	     !(mpath->flags & MESH_PATH_FIXED) && (ttl != 0)) { +		mhwmp_dbg(sdata, +			  "time to refresh root mpath %pM\n", +			  orig_addr); +		mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH); +		mpath->last_preq_to_root = jiffies; +	} + +	mpath->sn = orig_sn; +	mpath->rann_metric = metric + metric_txsta; +	mpath->is_root = true; +	/* Recording RANNs sender address to send individually +	 * addressed PREQs destined for root mesh STA */ +	memcpy(mpath->rann_snd_addr, mgmt->sa, ETH_ALEN); + +	if (root_is_gate) +		mesh_path_add_gate(mpath); + +	if (ttl <= 1) { +		ifmsh->mshstats.dropped_frames_ttl++; +		rcu_read_unlock(); +		return; +	} +	ttl--; + +	if (ifmsh->mshcfg.dot11MeshForwarding) {  		mesh_path_sel_frame_tx(MPATH_RANN, flags, orig_addr, -				       cpu_to_le32(orig_sn), -				       0, NULL, 0, broadcast_addr, -				       hopcount, ttl, 0, -				       cpu_to_le32(metric + mpath->metric), -				       0, sdata); -		mpath->sn = orig_sn; +				       orig_sn, 0, NULL, 0, broadcast_addr, +				       hopcount, ttl, interval, +				       metric + metric_txsta, 0, sdata);  	} +  	rcu_read_unlock();  }  void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, -			    struct ieee80211_mgmt *mgmt, -			    size_t len) +			    struct ieee80211_mgmt *mgmt, size_t len)  {  	struct ieee802_11_elems elems;  	size_t baselen;  	u32 last_hop_metric; +	struct sta_info *sta;  	/* need action_code */  	if (len < IEEE80211_MIN_ACTION_SIZE + 1)  		return; +	rcu_read_lock(); +	sta = sta_info_get(sdata, mgmt->sa); +	if (!sta || sta->plink_state != NL80211_PLINK_ESTAB) { +		rcu_read_unlock(); +		return; +	} +	rcu_read_unlock(); +  	baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;  	ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable, -			len - baselen, &elems); +			       len - baselen, false, &elems);  	if (elems.preq) {  		if (elems.preq_len != 37) @@ -784,25 +921,36 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags)  	preq_node = kmalloc(sizeof(struct mesh_preq_queue), GFP_ATOMIC);  	if (!preq_node) { -		mhwmp_dbg("could not allocate PREQ node\n"); +		mhwmp_dbg(sdata, "could not allocate PREQ node\n");  		return;  	} -	spin_lock(&ifmsh->mesh_preq_queue_lock); +	spin_lock_bh(&ifmsh->mesh_preq_queue_lock);  	if (ifmsh->preq_queue_len == MAX_PREQ_QUEUE_LEN) { -		spin_unlock(&ifmsh->mesh_preq_queue_lock); +		spin_unlock_bh(&ifmsh->mesh_preq_queue_lock);  		kfree(preq_node);  		if (printk_ratelimit()) -			mhwmp_dbg("PREQ node queue full\n"); +			mhwmp_dbg(sdata, "PREQ node queue full\n"); +		return; +	} + +	spin_lock(&mpath->state_lock); +	if (mpath->flags & MESH_PATH_REQ_QUEUED) { +		spin_unlock(&mpath->state_lock); +		spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); +		kfree(preq_node);  		return;  	}  	memcpy(preq_node->dst, mpath->dst, ETH_ALEN);  	preq_node->flags = flags; +	mpath->flags |= MESH_PATH_REQ_QUEUED; +	spin_unlock(&mpath->state_lock); +  	list_add_tail(&preq_node->list, &ifmsh->preq_queue.list);  	++ifmsh->preq_queue_len; -	spin_unlock(&ifmsh->mesh_preq_queue_lock); +	spin_unlock_bh(&ifmsh->mesh_preq_queue_lock);  	if (time_after(jiffies, ifmsh->last_preq + min_preq_int_jiff(sdata)))  		ieee80211_queue_work(&sdata->local->hw, &sdata->work); @@ -829,6 +977,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)  	struct mesh_preq_queue *preq_node;  	struct mesh_path *mpath;  	u8 ttl, target_flags; +	const u8 *da;  	u32 lifetime;  	spin_lock_bh(&ifmsh->mesh_preq_queue_lock); @@ -846,11 +995,12 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)  	spin_unlock_bh(&ifmsh->mesh_preq_queue_lock);  	rcu_read_lock(); -	mpath = mesh_path_lookup(preq_node->dst, sdata); +	mpath = mesh_path_lookup(sdata, preq_node->dst);  	if (!mpath)  		goto enddiscovery;  	spin_lock_bh(&mpath->state_lock); +	mpath->flags &= ~MESH_PATH_REQ_QUEUED;  	if (preq_node->flags & PREQ_Q_F_START) {  		if (mpath->flags & MESH_PATH_RESOLVING) {  			spin_unlock_bh(&mpath->state_lock); @@ -877,7 +1027,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)  		sdata->u.mesh.last_sn_update = jiffies;  	}  	lifetime = default_lifetime(sdata); -	ttl = sdata->u.mesh.mshcfg.dot11MeshTTL; +	ttl = sdata->u.mesh.mshcfg.element_ttl;  	if (ttl == 0) {  		sdata->u.mesh.mshstats.dropped_frames_ttl++;  		spin_unlock_bh(&mpath->state_lock); @@ -890,11 +1040,10 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)  		target_flags = MP_F_RF;  	spin_unlock_bh(&mpath->state_lock); -	mesh_path_sel_frame_tx(MPATH_PREQ, 0, sdata->vif.addr, -			cpu_to_le32(ifmsh->sn), target_flags, mpath->dst, -			cpu_to_le32(mpath->sn), broadcast_addr, 0, -			ttl, cpu_to_le32(lifetime), 0, -			cpu_to_le32(ifmsh->preq_id++), sdata); +	da = (mpath->is_root) ? mpath->rann_snd_addr : broadcast_addr; +	mesh_path_sel_frame_tx(MPATH_PREQ, 0, sdata->vif.addr, ifmsh->sn, +			       target_flags, mpath->dst, mpath->sn, da, 0, +			       ttl, lifetime, 0, ifmsh->preq_id++, sdata);  	mod_timer(&mpath->timer, jiffies + mpath->discovery_timeout);  enddiscovery: @@ -903,115 +1052,174 @@ enddiscovery:  }  /** - * mesh_nexthop_lookup - put the appropriate next hop on a mesh frame + * mesh_nexthop_resolve - lookup next hop; conditionally start path discovery   *   * @skb: 802.11 frame to be sent   * @sdata: network subif the frame will be sent through   * - * Returns: 0 if the next hop was found. Nonzero otherwise. If no next hop is - * found, the function will start a path discovery and queue the frame so it is - * sent when the path is resolved. This means the caller must not free the skb - * in this case. + * Lookup next hop for given skb and start path discovery if no + * forwarding information is found. + * + * Returns: 0 if the next hop was found and -ENOENT if the frame was queued. + * skb is freeed here if no mpath could be allocated.   */ -int mesh_nexthop_lookup(struct sk_buff *skb, -			struct ieee80211_sub_if_data *sdata) +int mesh_nexthop_resolve(struct ieee80211_sub_if_data *sdata, +			 struct sk_buff *skb)  { -	struct sk_buff *skb_to_free = NULL; -	struct mesh_path *mpath;  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct mesh_path *mpath; +	struct sk_buff *skb_to_free = NULL;  	u8 *target_addr = hdr->addr3;  	int err = 0; +	/* Nulls are only sent to peers for PS and should be pre-addressed */ +	if (ieee80211_is_qos_nullfunc(hdr->frame_control)) +		return 0; +  	rcu_read_lock(); -	mpath = mesh_path_lookup(target_addr, sdata); +	err = mesh_nexthop_lookup(sdata, skb); +	if (!err) +		goto endlookup; +	/* no nexthop found, start resolving */ +	mpath = mesh_path_lookup(sdata, target_addr);  	if (!mpath) { -		mesh_path_add(target_addr, sdata); -		mpath = mesh_path_lookup(target_addr, sdata); -		if (!mpath) { -			sdata->u.mesh.mshstats.dropped_frames_no_route++; -			err = -ENOSPC; +		mpath = mesh_path_add(sdata, target_addr); +		if (IS_ERR(mpath)) { +			mesh_path_discard_frame(sdata, skb); +			err = PTR_ERR(mpath);  			goto endlookup;  		}  	} -	if (mpath->flags & MESH_PATH_ACTIVE) { -		if (time_after(jiffies, -			       mpath->exp_time - -			       msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time)) && -		    !memcmp(sdata->vif.addr, hdr->addr4, ETH_ALEN) && -		    !(mpath->flags & MESH_PATH_RESOLVING) && -		    !(mpath->flags & MESH_PATH_FIXED)) { -			mesh_queue_preq(mpath, -					PREQ_Q_F_START | PREQ_Q_F_REFRESH); -		} -		memcpy(hdr->addr1, mpath->next_hop->sta.addr, ETH_ALEN); -	} else { -		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); -		if (!(mpath->flags & MESH_PATH_RESOLVING)) { -			/* Start discovery only if it is not running yet */ -			mesh_queue_preq(mpath, PREQ_Q_F_START); -		} +	if (!(mpath->flags & MESH_PATH_RESOLVING)) +		mesh_queue_preq(mpath, PREQ_Q_F_START); -		if (skb_queue_len(&mpath->frame_queue) >= -				MESH_FRAME_QUEUE_LEN) -			skb_to_free = skb_dequeue(&mpath->frame_queue); +	if (skb_queue_len(&mpath->frame_queue) >= MESH_FRAME_QUEUE_LEN) +		skb_to_free = skb_dequeue(&mpath->frame_queue); -		info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; -		skb_queue_tail(&mpath->frame_queue, skb); -		if (skb_to_free) -			mesh_path_discard_frame(skb_to_free, sdata); -		err = -ENOENT; -	} +	info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; +	ieee80211_set_qos_hdr(sdata, skb); +	skb_queue_tail(&mpath->frame_queue, skb); +	err = -ENOENT; +	if (skb_to_free) +		mesh_path_discard_frame(sdata, skb_to_free);  endlookup:  	rcu_read_unlock();  	return err;  } -void mesh_path_timer(unsigned long data) +/** + * mesh_nexthop_lookup - put the appropriate next hop on a mesh frame. Calling + * this function is considered "using" the associated mpath, so preempt a path + * refresh if this mpath expires soon. + * + * @skb: 802.11 frame to be sent + * @sdata: network subif the frame will be sent through + * + * Returns: 0 if the next hop was found. Nonzero otherwise. + */ +int mesh_nexthop_lookup(struct ieee80211_sub_if_data *sdata, +			struct sk_buff *skb)  { -	struct ieee80211_sub_if_data *sdata;  	struct mesh_path *mpath; +	struct sta_info *next_hop; +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; +	u8 *target_addr = hdr->addr3; +	int err = -ENOENT;  	rcu_read_lock(); -	mpath = (struct mesh_path *) data; -	mpath = rcu_dereference(mpath); -	if (!mpath) -		goto endmpathtimer; -	sdata = mpath->sdata; +	mpath = mesh_path_lookup(sdata, target_addr); + +	if (!mpath || !(mpath->flags & MESH_PATH_ACTIVE)) +		goto endlookup; + +	if (time_after(jiffies, +		       mpath->exp_time - +		       msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time)) && +	    ether_addr_equal(sdata->vif.addr, hdr->addr4) && +	    !(mpath->flags & MESH_PATH_RESOLVING) && +	    !(mpath->flags & MESH_PATH_FIXED)) +		mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH); + +	next_hop = rcu_dereference(mpath->next_hop); +	if (next_hop) { +		memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN); +		memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); +		ieee80211_mps_set_frame_flags(sdata, next_hop, hdr); +		err = 0; +	} -	if (sdata->local->quiescing) { -		rcu_read_unlock(); +endlookup: +	rcu_read_unlock(); +	return err; +} + +void mesh_path_timer(unsigned long data) +{ +	struct mesh_path *mpath = (void *) data; +	struct ieee80211_sub_if_data *sdata = mpath->sdata; +	int ret; + +	if (sdata->local->quiescing)  		return; -	}  	spin_lock_bh(&mpath->state_lock);  	if (mpath->flags & MESH_PATH_RESOLVED || -			(!(mpath->flags & MESH_PATH_RESOLVING))) +			(!(mpath->flags & MESH_PATH_RESOLVING))) {  		mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED); -	else if (mpath->discovery_retries < max_preq_retries(sdata)) { +		spin_unlock_bh(&mpath->state_lock); +	} else if (mpath->discovery_retries < max_preq_retries(sdata)) {  		++mpath->discovery_retries;  		mpath->discovery_timeout *= 2; +		mpath->flags &= ~MESH_PATH_REQ_QUEUED; +		spin_unlock_bh(&mpath->state_lock);  		mesh_queue_preq(mpath, 0);  	} else {  		mpath->flags = 0;  		mpath->exp_time = jiffies; -		mesh_path_flush_pending(mpath); +		spin_unlock_bh(&mpath->state_lock); +		if (!mpath->is_gate && mesh_gate_num(sdata) > 0) { +			ret = mesh_path_send_to_gates(mpath); +			if (ret) +				mhwmp_dbg(sdata, "no gate was reachable\n"); +		} else +			mesh_path_flush_pending(mpath);  	} - -	spin_unlock_bh(&mpath->state_lock); -endmpathtimer: -	rcu_read_unlock();  } -void -mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata) +void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - -	mesh_path_sel_frame_tx(MPATH_RANN, 0, sdata->vif.addr, -			       cpu_to_le32(++ifmsh->sn), -			       0, NULL, 0, broadcast_addr, -			       0, MESH_TTL, 0, 0, 0, sdata); +	u32 interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval; +	u8 flags, target_flags = 0; + +	flags = (ifmsh->mshcfg.dot11MeshGateAnnouncementProtocol) +			? RANN_FLAG_IS_GATE : 0; + +	switch (ifmsh->mshcfg.dot11MeshHWMPRootMode) { +	case IEEE80211_PROACTIVE_RANN: +		mesh_path_sel_frame_tx(MPATH_RANN, flags, sdata->vif.addr, +				       ++ifmsh->sn, 0, NULL, 0, broadcast_addr, +				       0, ifmsh->mshcfg.element_ttl, +				       interval, 0, 0, sdata); +		break; +	case IEEE80211_PROACTIVE_PREQ_WITH_PREP: +		flags |= IEEE80211_PREQ_PROACTIVE_PREP_FLAG; +	case IEEE80211_PROACTIVE_PREQ_NO_PREP: +		interval = ifmsh->mshcfg.dot11MeshHWMPactivePathToRootTimeout; +		target_flags |= IEEE80211_PREQ_TO_FLAG | +				IEEE80211_PREQ_USN_FLAG; +		mesh_path_sel_frame_tx(MPATH_PREQ, flags, sdata->vif.addr, +				       ++ifmsh->sn, target_flags, +				       (u8 *) broadcast_addr, 0, broadcast_addr, +				       0, ifmsh->mshcfg.element_ttl, interval, +				       0, ifmsh->preq_id++, sdata); +		break; +	default: +		mhwmp_dbg(sdata, "Proactive mechanism not supported\n"); +		return; +	}  } diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 349e466cf08..cf032a8db9d 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -14,6 +14,7 @@  #include <linux/spinlock.h>  #include <linux/string.h>  #include <net/mac80211.h> +#include "wme.h"  #include "ieee80211_i.h"  #include "mesh.h" @@ -23,9 +24,12 @@  /* Keep the mean chain length below this constant */  #define MEAN_CHAIN_LEN		2 -#define MPATH_EXPIRED(mpath) ((mpath->flags & MESH_PATH_ACTIVE) && \ -				time_after(jiffies, mpath->exp_time) && \ -				!(mpath->flags & MESH_PATH_FIXED)) +static inline bool mpath_expired(struct mesh_path *mpath) +{ +	return (mpath->flags & MESH_PATH_ACTIVE) && +	       time_after(jiffies, mpath->exp_time) && +	       !(mpath->flags & MESH_PATH_FIXED); +}  struct mpath_node {  	struct hlist_node list; @@ -36,10 +40,80 @@ struct mpath_node {  	struct mesh_path *mpath;  }; -static struct mesh_table *mesh_paths; -static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */ +static struct mesh_table __rcu *mesh_paths; +static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */  int mesh_paths_generation; + +/* This lock will have the grow table function as writer and add / delete nodes + * as readers. RCU provides sufficient protection only when reading the table + * (i.e. doing lookups).  Adding or adding or removing nodes requires we take + * the read lock or we risk operating on an old table.  The write lock is only + * needed when modifying the number of buckets a table. + */ +static DEFINE_RWLOCK(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)); +} + +static inline struct mesh_table *resize_dereference_mpp_paths(void) +{ +	return rcu_dereference_protected(mpp_paths, +		lockdep_is_held(&pathtbl_resize_lock)); +} + +/* + * CAREFUL -- "tbl" must not be an expression, + * in particular not an rcu_dereference(), since + * it's used twice. So it is illegal to do + *	for_each_mesh_entry(rcu_dereference(...), ...) + */ +#define for_each_mesh_entry(tbl, node, i) \ +	for (i = 0; i <= tbl->hash_mask; i++) \ +		hlist_for_each_entry_rcu(node, &tbl->hash_buckets[i], list) + + +static struct mesh_table *mesh_table_alloc(int size_order) +{ +	int i; +	struct mesh_table *newtbl; + +	newtbl = kmalloc(sizeof(struct mesh_table), GFP_ATOMIC); +	if (!newtbl) +		return NULL; + +	newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) * +			(1 << size_order), GFP_ATOMIC); + +	if (!newtbl->hash_buckets) { +		kfree(newtbl); +		return NULL; +	} + +	newtbl->hashwlock = kmalloc(sizeof(spinlock_t) * +			(1 << size_order), GFP_ATOMIC); +	if (!newtbl->hashwlock) { +		kfree(newtbl->hash_buckets); +		kfree(newtbl); +		return NULL; +	} + +	newtbl->size_order = size_order; +	newtbl->hash_mask = (1 << size_order) - 1; +	atomic_set(&newtbl->entries,  0); +	get_random_bytes(&newtbl->hash_rnd, +			sizeof(newtbl->hash_rnd)); +	for (i = 0; i <= newtbl->hash_mask; i++) +		spin_lock_init(&newtbl->hashwlock[i]); +	spin_lock_init(&newtbl->gates_lock); + +	return newtbl; +} +  static void __mesh_table_free(struct mesh_table *tbl)  {  	kfree(tbl->hash_buckets); @@ -47,68 +121,77 @@ static void __mesh_table_free(struct mesh_table *tbl)  	kfree(tbl);  } -void mesh_table_free(struct mesh_table *tbl, bool free_leafs) +static void mesh_table_free(struct mesh_table *tbl, bool free_leafs)  {  	struct hlist_head *mesh_hash;  	struct hlist_node *p, *q; +	struct mpath_node *gate;  	int i;  	mesh_hash = tbl->hash_buckets;  	for (i = 0; i <= tbl->hash_mask; i++) { -		spin_lock(&tbl->hashwlock[i]); +		spin_lock_bh(&tbl->hashwlock[i]);  		hlist_for_each_safe(p, q, &mesh_hash[i]) {  			tbl->free_node(p, free_leafs);  			atomic_dec(&tbl->entries);  		} -		spin_unlock(&tbl->hashwlock[i]); +		spin_unlock_bh(&tbl->hashwlock[i]);  	} +	if (free_leafs) { +		spin_lock_bh(&tbl->gates_lock); +		hlist_for_each_entry_safe(gate, q, +					 tbl->known_gates, list) { +			hlist_del(&gate->list); +			kfree(gate); +		} +		kfree(tbl->known_gates); +		spin_unlock_bh(&tbl->gates_lock); +	} +  	__mesh_table_free(tbl);  } -static struct mesh_table *mesh_table_grow(struct mesh_table *tbl) +static int mesh_table_grow(struct mesh_table *oldtbl, +			   struct mesh_table *newtbl)  { -	struct mesh_table *newtbl;  	struct hlist_head *oldhash;  	struct hlist_node *p, *q;  	int i; -	if (atomic_read(&tbl->entries) -			< tbl->mean_chain_len * (tbl->hash_mask + 1)) -		goto endgrow; +	if (atomic_read(&oldtbl->entries) +			< oldtbl->mean_chain_len * (oldtbl->hash_mask + 1)) +		return -EAGAIN; -	newtbl = mesh_table_alloc(tbl->size_order + 1); -	if (!newtbl) -		goto endgrow; +	newtbl->free_node = oldtbl->free_node; +	newtbl->mean_chain_len = oldtbl->mean_chain_len; +	newtbl->copy_node = oldtbl->copy_node; +	newtbl->known_gates = oldtbl->known_gates; +	atomic_set(&newtbl->entries, atomic_read(&oldtbl->entries)); -	newtbl->free_node = tbl->free_node; -	newtbl->mean_chain_len = tbl->mean_chain_len; -	newtbl->copy_node = tbl->copy_node; -	atomic_set(&newtbl->entries, atomic_read(&tbl->entries)); - -	oldhash = tbl->hash_buckets; -	for (i = 0; i <= tbl->hash_mask; i++) +	oldhash = oldtbl->hash_buckets; +	for (i = 0; i <= oldtbl->hash_mask; i++)  		hlist_for_each(p, &oldhash[i]) -			if (tbl->copy_node(p, newtbl) < 0) +			if (oldtbl->copy_node(p, newtbl) < 0)  				goto errcopy; -	return newtbl; +	return 0;  errcopy:  	for (i = 0; i <= newtbl->hash_mask; i++) {  		hlist_for_each_safe(p, q, &newtbl->hash_buckets[i]) -			tbl->free_node(p, 0); +			oldtbl->free_node(p, 0);  	} -	__mesh_table_free(newtbl); -endgrow: -	return NULL; +	return -ENOMEM;  } +static u32 mesh_table_hash(const u8 *addr, struct ieee80211_sub_if_data *sdata, +			   struct mesh_table *tbl) +{ +	/* Use last four bytes of hw addr and interface index as hash index */ +	return jhash_2words(*(u32 *)(addr+2), sdata->dev->ifindex, +			    tbl->hash_rnd) & tbl->hash_mask; +} -/* This lock will have the grow table function as writer and add / delete nodes - * as readers. When reading the table (i.e. doing lookups) we are well protected - * by RCU - */ -static DEFINE_RWLOCK(pathtbl_resize_lock);  /**   * @@ -123,81 +206,146 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta)  {  	struct sk_buff *skb;  	struct ieee80211_hdr *hdr; -	struct sk_buff_head tmpq;  	unsigned long flags;  	rcu_assign_pointer(mpath->next_hop, sta); -	__skb_queue_head_init(&tmpq); -  	spin_lock_irqsave(&mpath->frame_queue.lock, flags); - -	while ((skb = __skb_dequeue(&mpath->frame_queue)) != NULL) { +	skb_queue_walk(&mpath->frame_queue, skb) {  		hdr = (struct ieee80211_hdr *) skb->data;  		memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN); -		__skb_queue_tail(&tmpq, skb); +		memcpy(hdr->addr2, mpath->sdata->vif.addr, ETH_ALEN); +		ieee80211_mps_set_frame_flags(sta->sdata, sta, hdr);  	} -	skb_queue_splice(&tmpq, &mpath->frame_queue);  	spin_unlock_irqrestore(&mpath->frame_queue.lock, flags);  } +static void prepare_for_gate(struct sk_buff *skb, char *dst_addr, +			     struct mesh_path *gate_mpath) +{ +	struct ieee80211_hdr *hdr; +	struct ieee80211s_hdr *mshdr; +	int mesh_hdrlen, hdrlen; +	char *next_hop; + +	hdr = (struct ieee80211_hdr *) skb->data; +	hdrlen = ieee80211_hdrlen(hdr->frame_control); +	mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); + +	if (!(mshdr->flags & MESH_FLAGS_AE)) { +		/* size of the fixed part of the mesh header */ +		mesh_hdrlen = 6; + +		/* make room for the two extended addresses */ +		skb_push(skb, 2 * ETH_ALEN); +		memmove(skb->data, hdr, hdrlen + mesh_hdrlen); + +		hdr = (struct ieee80211_hdr *) skb->data; + +		/* we preserve the previous mesh header and only add +		 * the new addreses */ +		mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); +		mshdr->flags = MESH_FLAGS_AE_A5_A6; +		memcpy(mshdr->eaddr1, hdr->addr3, ETH_ALEN); +		memcpy(mshdr->eaddr2, hdr->addr4, ETH_ALEN); +	} + +	/* update next hop */ +	hdr = (struct ieee80211_hdr *) skb->data; +	rcu_read_lock(); +	next_hop = rcu_dereference(gate_mpath->next_hop)->sta.addr; +	memcpy(hdr->addr1, next_hop, ETH_ALEN); +	rcu_read_unlock(); +	memcpy(hdr->addr2, gate_mpath->sdata->vif.addr, ETH_ALEN); +	memcpy(hdr->addr3, dst_addr, ETH_ALEN); +}  /** - * mesh_path_lookup - look up a path in the mesh path table - * @dst: hardware address (ETH_ALEN length) of destination - * @sdata: local subif   * - * Returns: pointer to the mesh path structure, or NULL if not found + * mesh_path_move_to_queue - Move or copy frames from one mpath queue to another   * - * Locking: must be called within a read rcu section. + * This function is used to transfer or copy frames from an unresolved mpath to + * a gate mpath.  The function also adds the Address Extension field and + * updates the next hop. + * + * If a frame already has an Address Extension field, only the next hop and + * destination addresses are updated. + * + * The gate mpath must be an active mpath with a valid mpath->next_hop. + * + * @mpath: An active mpath the frames will be sent to (i.e. the gate) + * @from_mpath: The failed mpath + * @copy: When true, copy all the frames to the new mpath queue.  When false, + * move them.   */ -struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) +static void mesh_path_move_to_queue(struct mesh_path *gate_mpath, +				    struct mesh_path *from_mpath, +				    bool copy)  { -	struct mesh_path *mpath; -	struct hlist_node *n; -	struct hlist_head *bucket; -	struct mesh_table *tbl; -	struct mpath_node *node; +	struct sk_buff *skb, *fskb, *tmp; +	struct sk_buff_head failq; +	unsigned long flags; -	tbl = rcu_dereference(mesh_paths); +	if (WARN_ON(gate_mpath == from_mpath)) +		return; +	if (WARN_ON(!gate_mpath->next_hop)) +		return; -	bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)]; -	hlist_for_each_entry_rcu(node, n, bucket, list) { -		mpath = node->mpath; -		if (mpath->sdata == sdata && -				memcmp(dst, mpath->dst, ETH_ALEN) == 0) { -			if (MPATH_EXPIRED(mpath)) { -				spin_lock_bh(&mpath->state_lock); -				if (MPATH_EXPIRED(mpath)) -					mpath->flags &= ~MESH_PATH_ACTIVE; -				spin_unlock_bh(&mpath->state_lock); -			} -			return mpath; +	__skb_queue_head_init(&failq); + +	spin_lock_irqsave(&from_mpath->frame_queue.lock, flags); +	skb_queue_splice_init(&from_mpath->frame_queue, &failq); +	spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags); + +	skb_queue_walk_safe(&failq, fskb, tmp) { +		if (skb_queue_len(&gate_mpath->frame_queue) >= +				  MESH_FRAME_QUEUE_LEN) { +			mpath_dbg(gate_mpath->sdata, "mpath queue full!\n"); +			break;  		} + +		skb = skb_copy(fskb, GFP_ATOMIC); +		if (WARN_ON(!skb)) +			break; + +		prepare_for_gate(skb, gate_mpath->dst, gate_mpath); +		skb_queue_tail(&gate_mpath->frame_queue, skb); + +		if (copy) +			continue; + +		__skb_unlink(fskb, &failq); +		kfree_skb(fskb);  	} -	return NULL; + +	mpath_dbg(gate_mpath->sdata, "Mpath queue for gate %pM has %d frames\n", +		  gate_mpath->dst, skb_queue_len(&gate_mpath->frame_queue)); + +	if (!copy) +		return; + +	spin_lock_irqsave(&from_mpath->frame_queue.lock, flags); +	skb_queue_splice(&failq, &from_mpath->frame_queue); +	spin_unlock_irqrestore(&from_mpath->frame_queue.lock, flags);  } -struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) + +static struct mesh_path *mpath_lookup(struct mesh_table *tbl, const u8 *dst, +				      struct ieee80211_sub_if_data *sdata)  {  	struct mesh_path *mpath; -	struct hlist_node *n;  	struct hlist_head *bucket; -	struct mesh_table *tbl;  	struct mpath_node *node; -	tbl = rcu_dereference(mpp_paths); -  	bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)]; -	hlist_for_each_entry_rcu(node, n, bucket, list) { +	hlist_for_each_entry_rcu(node, bucket, list) {  		mpath = node->mpath;  		if (mpath->sdata == sdata && -		    memcmp(dst, mpath->dst, ETH_ALEN) == 0) { -			if (MPATH_EXPIRED(mpath)) { +		    ether_addr_equal(dst, mpath->dst)) { +			if (mpath_expired(mpath)) {  				spin_lock_bh(&mpath->state_lock); -				if (MPATH_EXPIRED(mpath)) -					mpath->flags &= ~MESH_PATH_ACTIVE; +				mpath->flags &= ~MESH_PATH_ACTIVE;  				spin_unlock_bh(&mpath->state_lock);  			}  			return mpath; @@ -206,6 +354,27 @@ struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata)  	return NULL;  } +/** + * mesh_path_lookup - look up a path in the mesh path table + * @sdata: local subif + * @dst: hardware address (ETH_ALEN length) of destination + * + * Returns: pointer to the mesh path structure, or NULL if not found + * + * Locking: must be called within a read rcu section. + */ +struct mesh_path * +mesh_path_lookup(struct ieee80211_sub_if_data *sdata, const u8 *dst) +{ +	return mpath_lookup(rcu_dereference(mesh_paths), dst, sdata); +} + +struct mesh_path * +mpp_path_lookup(struct ieee80211_sub_if_data *sdata, const u8 *dst) +{ +	return mpath_lookup(rcu_dereference(mpp_paths), dst, sdata); +} +  /**   * mesh_path_lookup_by_idx - look up a path in the mesh path table by its index @@ -216,21 +385,21 @@ struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata)   *   * Locking: must be called within a read rcu section.   */ -struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata) +struct mesh_path * +mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx)  { +	struct mesh_table *tbl = rcu_dereference(mesh_paths);  	struct mpath_node *node; -	struct hlist_node *p;  	int i;  	int j = 0; -	for_each_mesh_entry(mesh_paths, p, node, i) { +	for_each_mesh_entry(tbl, node, i) {  		if (sdata && node->mpath->sdata != sdata)  			continue;  		if (j++ == idx) { -			if (MPATH_EXPIRED(node->mpath)) { +			if (mpath_expired(node->mpath)) {  				spin_lock_bh(&node->mpath->state_lock); -				if (MPATH_EXPIRED(node->mpath)) -					node->mpath->flags &= ~MESH_PATH_ACTIVE; +				node->mpath->flags &= ~MESH_PATH_ACTIVE;  				spin_unlock_bh(&node->mpath->state_lock);  			}  			return node->mpath; @@ -241,35 +410,128 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data  }  /** + * mesh_path_add_gate - add the given mpath to a mesh gate to our path table + * @mpath: gate path to add to table + */ +int mesh_path_add_gate(struct mesh_path *mpath) +{ +	struct mesh_table *tbl; +	struct mpath_node *gate, *new_gate; +	int err; + +	rcu_read_lock(); +	tbl = rcu_dereference(mesh_paths); + +	hlist_for_each_entry_rcu(gate, tbl->known_gates, list) +		if (gate->mpath == mpath) { +			err = -EEXIST; +			goto err_rcu; +		} + +	new_gate = kzalloc(sizeof(struct mpath_node), GFP_ATOMIC); +	if (!new_gate) { +		err = -ENOMEM; +		goto err_rcu; +	} + +	mpath->is_gate = true; +	mpath->sdata->u.mesh.num_gates++; +	new_gate->mpath = mpath; +	spin_lock_bh(&tbl->gates_lock); +	hlist_add_head_rcu(&new_gate->list, tbl->known_gates); +	spin_unlock_bh(&tbl->gates_lock); +	mpath_dbg(mpath->sdata, +		  "Mesh path: Recorded new gate: %pM. %d known gates\n", +		  mpath->dst, mpath->sdata->u.mesh.num_gates); +	err = 0; +err_rcu: +	rcu_read_unlock(); +	return err; +} + +/** + * mesh_gate_del - remove a mesh gate from the list of known gates + * @tbl: table which holds our list of known gates + * @mpath: gate mpath + * + * Locking: must be called inside rcu_read_lock() section + */ +static void mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath) +{ +	struct mpath_node *gate; +	struct hlist_node *q; + +	hlist_for_each_entry_safe(gate, q, tbl->known_gates, list) { +		if (gate->mpath != mpath) +			continue; +		spin_lock_bh(&tbl->gates_lock); +		hlist_del_rcu(&gate->list); +		kfree_rcu(gate, rcu); +		spin_unlock_bh(&tbl->gates_lock); +		mpath->sdata->u.mesh.num_gates--; +		mpath->is_gate = false; +		mpath_dbg(mpath->sdata, +			  "Mesh path: Deleted gate: %pM. %d known gates\n", +			  mpath->dst, mpath->sdata->u.mesh.num_gates); +		break; +	} +} + +/** + * mesh_gate_num - number of gates known to this interface + * @sdata: subif data + */ +int mesh_gate_num(struct ieee80211_sub_if_data *sdata) +{ +	return sdata->u.mesh.num_gates; +} + +/**   * mesh_path_add - allocate and add a new path to the mesh path table - * @addr: destination address of the path (ETH_ALEN length) + * @dst: destination address of the path (ETH_ALEN length)   * @sdata: local subif   *   * Returns: 0 on success   *   * State: the initial state of the new path is set to 0   */ -int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) +struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata, +				const u8 *dst)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;  	struct ieee80211_local *local = sdata->local; +	struct mesh_table *tbl;  	struct mesh_path *mpath, *new_mpath;  	struct mpath_node *node, *new_node;  	struct hlist_head *bucket; -	struct hlist_node *n;  	int grow = 0; -	int err = 0; +	int err;  	u32 hash_idx; -	if (memcmp(dst, sdata->vif.addr, ETH_ALEN) == 0) +	if (ether_addr_equal(dst, sdata->vif.addr))  		/* never add ourselves as neighbours */ -		return -ENOTSUPP; +		return ERR_PTR(-ENOTSUPP);  	if (is_multicast_ether_addr(dst)) -		return -ENOTSUPP; +		return ERR_PTR(-ENOTSUPP);  	if (atomic_add_unless(&sdata->u.mesh.mpaths, 1, MESH_MAX_MPATHS) == 0) -		return -ENOSPC; +		return ERR_PTR(-ENOSPC); + +	read_lock_bh(&pathtbl_resize_lock); +	tbl = resize_dereference_mesh_paths(); + +	hash_idx = mesh_table_hash(dst, 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(dst, mpath->dst)) +			goto found; +	}  	err = -ENOMEM;  	new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC); @@ -280,8 +542,9 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)  	if (!new_node)  		goto err_node_alloc; -	read_lock(&pathtbl_resize_lock);  	memcpy(new_mpath->dst, dst, ETH_ALEN); +	eth_broadcast_addr(new_mpath->rann_snd_addr); +	new_mpath->is_root = false;  	new_mpath->sdata = sdata;  	new_mpath->flags = 0;  	skb_queue_head_init(&new_mpath->frame_queue); @@ -292,93 +555,94 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)  	spin_lock_init(&new_mpath->state_lock);  	init_timer(&new_mpath->timer); -	hash_idx = mesh_table_hash(dst, sdata, mesh_paths); -	bucket = &mesh_paths->hash_buckets[hash_idx]; - -	spin_lock(&mesh_paths->hashwlock[hash_idx]); - -	err = -EEXIST; -	hlist_for_each_entry(node, n, bucket, list) { -		mpath = node->mpath; -		if (mpath->sdata == sdata && memcmp(dst, mpath->dst, ETH_ALEN) == 0) -			goto err_exists; -	} -  	hlist_add_head_rcu(&new_node->list, bucket); -	if (atomic_inc_return(&mesh_paths->entries) >= -		mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1)) +	if (atomic_inc_return(&tbl->entries) >= +	    tbl->mean_chain_len * (tbl->hash_mask + 1))  		grow = 1;  	mesh_paths_generation++; -	spin_unlock(&mesh_paths->hashwlock[hash_idx]); -	read_unlock(&pathtbl_resize_lock);  	if (grow) {  		set_bit(MESH_WORK_GROW_MPATH_TABLE,  &ifmsh->wrkq_flags);  		ieee80211_queue_work(&local->hw, &sdata->work);  	} -	return 0; +	mpath = new_mpath; +found: +	spin_unlock(&tbl->hashwlock[hash_idx]); +	read_unlock_bh(&pathtbl_resize_lock); +	return mpath; -err_exists: -	spin_unlock(&mesh_paths->hashwlock[hash_idx]); -	read_unlock(&pathtbl_resize_lock); -	kfree(new_node);  err_node_alloc:  	kfree(new_mpath);  err_path_alloc:  	atomic_dec(&sdata->u.mesh.mpaths); -	return err; +	spin_unlock(&tbl->hashwlock[hash_idx]); +	read_unlock_bh(&pathtbl_resize_lock); +	return ERR_PTR(err); +} + +static void mesh_table_free_rcu(struct rcu_head *rcu) +{ +	struct mesh_table *tbl = container_of(rcu, struct mesh_table, rcu_head); + +	mesh_table_free(tbl, false);  }  void mesh_mpath_table_grow(void)  {  	struct mesh_table *oldtbl, *newtbl; -	write_lock(&pathtbl_resize_lock); -	oldtbl = mesh_paths; -	newtbl = mesh_table_grow(mesh_paths); -	if (!newtbl) { -		write_unlock(&pathtbl_resize_lock); -		return; +	write_lock_bh(&pathtbl_resize_lock); +	oldtbl = resize_dereference_mesh_paths(); +	newtbl = mesh_table_alloc(oldtbl->size_order + 1); +	if (!newtbl) +		goto out; +	if (mesh_table_grow(oldtbl, newtbl) < 0) { +		__mesh_table_free(newtbl); +		goto out;  	}  	rcu_assign_pointer(mesh_paths, newtbl); -	write_unlock(&pathtbl_resize_lock); -	synchronize_rcu(); -	mesh_table_free(oldtbl, false); +	call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu); + + out: +	write_unlock_bh(&pathtbl_resize_lock);  }  void mesh_mpp_table_grow(void)  {  	struct mesh_table *oldtbl, *newtbl; -	write_lock(&pathtbl_resize_lock); -	oldtbl = mpp_paths; -	newtbl = mesh_table_grow(mpp_paths); -	if (!newtbl) { -		write_unlock(&pathtbl_resize_lock); -		return; +	write_lock_bh(&pathtbl_resize_lock); +	oldtbl = resize_dereference_mpp_paths(); +	newtbl = mesh_table_alloc(oldtbl->size_order + 1); +	if (!newtbl) +		goto out; +	if (mesh_table_grow(oldtbl, newtbl) < 0) { +		__mesh_table_free(newtbl); +		goto out;  	}  	rcu_assign_pointer(mpp_paths, newtbl); -	write_unlock(&pathtbl_resize_lock); +	call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu); -	synchronize_rcu(); -	mesh_table_free(oldtbl, false); + out: +	write_unlock_bh(&pathtbl_resize_lock);  } -int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) +int mpp_path_add(struct ieee80211_sub_if_data *sdata, +		 const u8 *dst, const u8 *mpp)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;  	struct ieee80211_local *local = sdata->local; +	struct mesh_table *tbl;  	struct mesh_path *mpath, *new_mpath;  	struct mpath_node *node, *new_node;  	struct hlist_head *bucket; -	struct hlist_node *n;  	int grow = 0;  	int err = 0;  	u32 hash_idx; -	if (memcmp(dst, sdata->vif.addr, ETH_ALEN) == 0) +	if (ether_addr_equal(dst, sdata->vif.addr))  		/* never add ourselves as neighbours */  		return -ENOTSUPP; @@ -394,35 +658,39 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)  	if (!new_node)  		goto err_node_alloc; -	read_lock(&pathtbl_resize_lock); +	read_lock_bh(&pathtbl_resize_lock);  	memcpy(new_mpath->dst, dst, ETH_ALEN);  	memcpy(new_mpath->mpp, mpp, ETH_ALEN);  	new_mpath->sdata = sdata;  	new_mpath->flags = 0;  	skb_queue_head_init(&new_mpath->frame_queue);  	new_node->mpath = new_mpath; +	init_timer(&new_mpath->timer);  	new_mpath->exp_time = jiffies;  	spin_lock_init(&new_mpath->state_lock); -	hash_idx = mesh_table_hash(dst, sdata, mpp_paths); -	bucket = &mpp_paths->hash_buckets[hash_idx]; +	tbl = resize_dereference_mpp_paths(); -	spin_lock(&mpp_paths->hashwlock[hash_idx]); +	hash_idx = mesh_table_hash(dst, sdata, tbl); +	bucket = &tbl->hash_buckets[hash_idx]; + +	spin_lock(&tbl->hashwlock[hash_idx]);  	err = -EEXIST; -	hlist_for_each_entry(node, n, bucket, list) { +	hlist_for_each_entry(node, bucket, list) {  		mpath = node->mpath; -		if (mpath->sdata == sdata && memcmp(dst, mpath->dst, ETH_ALEN) == 0) +		if (mpath->sdata == sdata && +		    ether_addr_equal(dst, mpath->dst))  			goto err_exists;  	}  	hlist_add_head_rcu(&new_node->list, bucket); -	if (atomic_inc_return(&mpp_paths->entries) >= -		mpp_paths->mean_chain_len * (mpp_paths->hash_mask + 1)) +	if (atomic_inc_return(&tbl->entries) >= +	    tbl->mean_chain_len * (tbl->hash_mask + 1))  		grow = 1; -	spin_unlock(&mpp_paths->hashwlock[hash_idx]); -	read_unlock(&pathtbl_resize_lock); +	spin_unlock(&tbl->hashwlock[hash_idx]); +	read_unlock_bh(&pathtbl_resize_lock);  	if (grow) {  		set_bit(MESH_WORK_GROW_MPP_TABLE,  &ifmsh->wrkq_flags);  		ieee80211_queue_work(&local->hw, &sdata->work); @@ -430,8 +698,8 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)  	return 0;  err_exists: -	spin_unlock(&mpp_paths->hashwlock[hash_idx]); -	read_unlock(&pathtbl_resize_lock); +	spin_unlock(&tbl->hashwlock[hash_idx]); +	read_unlock_bh(&pathtbl_resize_lock);  	kfree(new_node);  err_node_alloc:  	kfree(new_mpath); @@ -450,37 +718,63 @@ err_path_alloc:   */  void mesh_plink_broken(struct sta_info *sta)  { +	struct mesh_table *tbl;  	static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};  	struct mesh_path *mpath;  	struct mpath_node *node; -	struct hlist_node *p;  	struct ieee80211_sub_if_data *sdata = sta->sdata;  	int i;  	rcu_read_lock(); -	for_each_mesh_entry(mesh_paths, p, node, i) { +	tbl = rcu_dereference(mesh_paths); +	for_each_mesh_entry(tbl, node, i) {  		mpath = node->mpath; -		spin_lock_bh(&mpath->state_lock); -		if (mpath->next_hop == sta && +		if (rcu_dereference(mpath->next_hop) == sta &&  		    mpath->flags & MESH_PATH_ACTIVE &&  		    !(mpath->flags & MESH_PATH_FIXED)) { +			spin_lock_bh(&mpath->state_lock);  			mpath->flags &= ~MESH_PATH_ACTIVE;  			++mpath->sn;  			spin_unlock_bh(&mpath->state_lock); -			mesh_path_error_tx(MESH_TTL, mpath->dst, -					cpu_to_le32(mpath->sn), -					cpu_to_le16(PERR_RCODE_DEST_UNREACH), -					bcast, sdata); -		} else -		spin_unlock_bh(&mpath->state_lock); +			mesh_path_error_tx(sdata, +				sdata->u.mesh.mshcfg.element_ttl, +				mpath->dst, mpath->sn, +				WLAN_REASON_MESH_PATH_DEST_UNREACHABLE, bcast); +		}  	}  	rcu_read_unlock();  } +static void mesh_path_node_reclaim(struct rcu_head *rp) +{ +	struct mpath_node *node = container_of(rp, struct mpath_node, rcu); +	struct ieee80211_sub_if_data *sdata = node->mpath->sdata; + +	del_timer_sync(&node->mpath->timer); +	atomic_dec(&sdata->u.mesh.mpaths); +	kfree(node->mpath); +	kfree(node); +} + +/* needs to be called with the corresponding hashwlock taken */ +static void __mesh_path_del(struct mesh_table *tbl, struct mpath_node *node) +{ +	struct mesh_path *mpath; +	mpath = node->mpath; +	spin_lock(&mpath->state_lock); +	mpath->flags |= MESH_PATH_RESOLVING; +	if (mpath->is_gate) +		mesh_gate_del(tbl, mpath); +	hlist_del_rcu(&node->list); +	call_rcu(&node->rcu, mesh_path_node_reclaim); +	spin_unlock(&mpath->state_lock); +	atomic_dec(&tbl->entries); +} +  /**   * mesh_path_flush_by_nexthop - Deletes mesh paths if their next hop matches   * - * @sta - mesh peer to match + * @sta: mesh peer to match   *   * RCU notes: this function is called when a mesh plink transitions from   * PLINK_ESTAB to any other state, since PLINK_ESTAB state is the only one that @@ -490,41 +784,64 @@ void mesh_plink_broken(struct sta_info *sta)   */  void mesh_path_flush_by_nexthop(struct sta_info *sta)  { +	struct mesh_table *tbl;  	struct mesh_path *mpath;  	struct mpath_node *node; -	struct hlist_node *p;  	int i; -	for_each_mesh_entry(mesh_paths, p, node, i) { +	rcu_read_lock(); +	read_lock_bh(&pathtbl_resize_lock); +	tbl = resize_dereference_mesh_paths(); +	for_each_mesh_entry(tbl, node, i) {  		mpath = node->mpath; -		if (mpath->next_hop == sta) -			mesh_path_del(mpath->dst, mpath->sdata); +		if (rcu_dereference(mpath->next_hop) == sta) { +			spin_lock(&tbl->hashwlock[i]); +			__mesh_path_del(tbl, node); +			spin_unlock(&tbl->hashwlock[i]); +		}  	} +	read_unlock_bh(&pathtbl_resize_lock); +	rcu_read_unlock();  } -void mesh_path_flush(struct ieee80211_sub_if_data *sdata) +static void table_flush_by_iface(struct mesh_table *tbl, +				 struct ieee80211_sub_if_data *sdata)  {  	struct mesh_path *mpath;  	struct mpath_node *node; -	struct hlist_node *p;  	int i; -	for_each_mesh_entry(mesh_paths, p, node, i) { +	WARN_ON(!rcu_read_lock_held()); +	for_each_mesh_entry(tbl, node, i) {  		mpath = node->mpath; -		if (mpath->sdata == sdata) -			mesh_path_del(mpath->dst, mpath->sdata); +		if (mpath->sdata != sdata) +			continue; +		spin_lock_bh(&tbl->hashwlock[i]); +		__mesh_path_del(tbl, node); +		spin_unlock_bh(&tbl->hashwlock[i]);  	}  } -static void mesh_path_node_reclaim(struct rcu_head *rp) +/** + * mesh_path_flush_by_iface - Deletes all mesh paths associated with a given iface + * + * This function deletes both mesh paths as well as mesh portal paths. + * + * @sdata: interface data to match + * + */ +void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata)  { -	struct mpath_node *node = container_of(rp, struct mpath_node, rcu); -	struct ieee80211_sub_if_data *sdata = node->mpath->sdata; +	struct mesh_table *tbl; -	del_timer_sync(&node->mpath->timer); -	atomic_dec(&sdata->u.mesh.mpaths); -	kfree(node->mpath); -	kfree(node); +	rcu_read_lock(); +	read_lock_bh(&pathtbl_resize_lock); +	tbl = resize_dereference_mesh_paths(); +	table_flush_by_iface(tbl, sdata); +	tbl = resize_dereference_mpp_paths(); +	table_flush_by_iface(tbl, sdata); +	read_unlock_bh(&pathtbl_resize_lock); +	rcu_read_unlock();  }  /** @@ -535,30 +852,26 @@ static void mesh_path_node_reclaim(struct rcu_head *rp)   *   * Returns: 0 if successful   */ -int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) +int mesh_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; -	struct hlist_node *n;  	int hash_idx;  	int err = 0; -	read_lock(&pathtbl_resize_lock); -	hash_idx = mesh_table_hash(addr, sdata, mesh_paths); -	bucket = &mesh_paths->hash_buckets[hash_idx]; +	read_lock_bh(&pathtbl_resize_lock); +	tbl = resize_dereference_mesh_paths(); +	hash_idx = mesh_table_hash(addr, sdata, tbl); +	bucket = &tbl->hash_buckets[hash_idx]; -	spin_lock(&mesh_paths->hashwlock[hash_idx]); -	hlist_for_each_entry(node, n, bucket, list) { +	spin_lock(&tbl->hashwlock[hash_idx]); +	hlist_for_each_entry(node, bucket, list) {  		mpath = node->mpath;  		if (mpath->sdata == sdata && -				memcmp(addr, mpath->dst, ETH_ALEN) == 0) { -			spin_lock_bh(&mpath->state_lock); -			mpath->flags |= MESH_PATH_RESOLVING; -			hlist_del_rcu(&node->list); -			call_rcu(&node->rcu, mesh_path_node_reclaim); -			atomic_dec(&mesh_paths->entries); -			spin_unlock_bh(&mpath->state_lock); +		    ether_addr_equal(addr, mpath->dst)) { +			__mesh_path_del(tbl, node);  			goto enddel;  		}  	} @@ -566,8 +879,8 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)  	err = -ENXIO;  enddel:  	mesh_paths_generation++; -	spin_unlock(&mesh_paths->hashwlock[hash_idx]); -	read_unlock(&pathtbl_resize_lock); +	spin_unlock(&tbl->hashwlock[hash_idx]); +	read_unlock_bh(&pathtbl_resize_lock);  	return err;  } @@ -587,37 +900,68 @@ void mesh_path_tx_pending(struct mesh_path *mpath)  }  /** + * mesh_path_send_to_gates - sends pending frames to all known mesh gates + * + * @mpath: mesh path whose queue will be emptied + * + * If there is only one gate, the frames are transferred from the failed mpath + * queue to that gate's queue.  If there are more than one gates, the frames + * are copied from each gate to the next.  After frames are copied, the + * mpath queues are emptied onto the transmission queue. + */ +int mesh_path_send_to_gates(struct mesh_path *mpath) +{ +	struct ieee80211_sub_if_data *sdata = mpath->sdata; +	struct mesh_table *tbl; +	struct mesh_path *from_mpath = mpath; +	struct mpath_node *gate = NULL; +	bool copy = false; +	struct hlist_head *known_gates; + +	rcu_read_lock(); +	tbl = rcu_dereference(mesh_paths); +	known_gates = tbl->known_gates; +	rcu_read_unlock(); + +	if (!known_gates) +		return -EHOSTUNREACH; + +	hlist_for_each_entry_rcu(gate, known_gates, list) { +		if (gate->mpath->sdata != sdata) +			continue; + +		if (gate->mpath->flags & MESH_PATH_ACTIVE) { +			mpath_dbg(sdata, "Forwarding to %pM\n", gate->mpath->dst); +			mesh_path_move_to_queue(gate->mpath, from_mpath, copy); +			from_mpath = gate->mpath; +			copy = true; +		} else { +			mpath_dbg(sdata, +				  "Not forwarding %p (flags %#x)\n", +				  gate->mpath, gate->mpath->flags); +		} +	} + +	hlist_for_each_entry_rcu(gate, known_gates, list) +		if (gate->mpath->sdata == sdata) { +			mpath_dbg(sdata, "Sending to %pM\n", gate->mpath->dst); +			mesh_path_tx_pending(gate->mpath); +		} + +	return (from_mpath == mpath) ? -EHOSTUNREACH : 0; +} + +/**   * mesh_path_discard_frame - discard a frame whose path could not be resolved   *   * @skb: frame to discard   * @sdata: network subif the frame was to be sent through   * - * If the frame was being forwarded from another MP, a PERR frame will be sent - * to the precursor.  The precursor's address (i.e. the previous hop) was saved - * in addr1 of the frame-to-be-forwarded, and would only be overwritten once - * the destination is successfully resolved. - *   * Locking: the function must me called within a rcu_read_lock region   */ -void mesh_path_discard_frame(struct sk_buff *skb, -			     struct ieee80211_sub_if_data *sdata) +void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata, +			     struct sk_buff *skb)  { -	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; -	struct mesh_path *mpath; -	u32 sn = 0; - -	if (memcmp(hdr->addr4, sdata->vif.addr, ETH_ALEN) != 0) { -		u8 *ra, *da; - -		da = hdr->addr3; -		ra = hdr->addr1; -		mpath = mesh_path_lookup(da, sdata); -		if (mpath) -			sn = ++mpath->sn; -		mesh_path_error_tx(MESH_TTL, skb->data, cpu_to_le32(sn), -				   cpu_to_le16(PERR_RCODE_NO_ROUTE), ra, sdata); -	} -  	kfree_skb(skb);  	sdata->u.mesh.mshstats.dropped_frames_no_route++;  } @@ -627,15 +971,14 @@ void mesh_path_discard_frame(struct sk_buff *skb,   *   * @mpath: mesh path whose queue has to be freed   * - * Locking: the function must me called withing a rcu_read_lock region + * Locking: the function must me called within a rcu_read_lock region   */  void mesh_path_flush_pending(struct mesh_path *mpath)  {  	struct sk_buff *skb; -	while ((skb = skb_dequeue(&mpath->frame_queue)) && -			(mpath->flags & MESH_PATH_ACTIVE)) -		mesh_path_discard_frame(skb, mpath->sdata); +	while ((skb = skb_dequeue(&mpath->frame_queue)) != NULL) +		mesh_path_discard_frame(mpath->sdata, skb);  }  /** @@ -666,8 +1009,10 @@ static void mesh_path_node_free(struct hlist_node *p, bool free_leafs)  	struct mpath_node *node = hlist_entry(p, struct mpath_node, list);  	mpath = node->mpath;  	hlist_del_rcu(p); -	if (free_leafs) +	if (free_leafs) { +		del_timer_sync(&mpath->timer);  		kfree(mpath); +	}  	kfree(node);  } @@ -692,52 +1037,75 @@ static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl)  int mesh_pathtbl_init(void)  { -	mesh_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); -	if (!mesh_paths) -		return -ENOMEM; -	mesh_paths->free_node = &mesh_path_node_free; -	mesh_paths->copy_node = &mesh_path_node_copy; -	mesh_paths->mean_chain_len = MEAN_CHAIN_LEN; +	struct mesh_table *tbl_path, *tbl_mpp; +	int ret; -	mpp_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); -	if (!mpp_paths) { -		mesh_table_free(mesh_paths, true); +	tbl_path = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); +	if (!tbl_path)  		return -ENOMEM; +	tbl_path->free_node = &mesh_path_node_free; +	tbl_path->copy_node = &mesh_path_node_copy; +	tbl_path->mean_chain_len = MEAN_CHAIN_LEN; +	tbl_path->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC); +	if (!tbl_path->known_gates) { +		ret = -ENOMEM; +		goto free_path; +	} +	INIT_HLIST_HEAD(tbl_path->known_gates); + + +	tbl_mpp = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); +	if (!tbl_mpp) { +		ret = -ENOMEM; +		goto free_path; +	} +	tbl_mpp->free_node = &mesh_path_node_free; +	tbl_mpp->copy_node = &mesh_path_node_copy; +	tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN; +	tbl_mpp->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC); +	if (!tbl_mpp->known_gates) { +		ret = -ENOMEM; +		goto free_mpp;  	} -	mpp_paths->free_node = &mesh_path_node_free; -	mpp_paths->copy_node = &mesh_path_node_copy; -	mpp_paths->mean_chain_len = MEAN_CHAIN_LEN; +	INIT_HLIST_HEAD(tbl_mpp->known_gates); + +	/* Need no locking since this is during init */ +	RCU_INIT_POINTER(mesh_paths, tbl_path); +	RCU_INIT_POINTER(mpp_paths, tbl_mpp);  	return 0; + +free_mpp: +	mesh_table_free(tbl_mpp, true); +free_path: +	mesh_table_free(tbl_path, true); +	return ret;  }  void mesh_path_expire(struct ieee80211_sub_if_data *sdata)  { +	struct mesh_table *tbl;  	struct mesh_path *mpath;  	struct mpath_node *node; -	struct hlist_node *p;  	int i; -	read_lock(&pathtbl_resize_lock); -	for_each_mesh_entry(mesh_paths, p, node, i) { +	rcu_read_lock(); +	tbl = rcu_dereference(mesh_paths); +	for_each_mesh_entry(tbl, node, i) {  		if (node->mpath->sdata != sdata)  			continue;  		mpath = node->mpath; -		spin_lock_bh(&mpath->state_lock);  		if ((!(mpath->flags & MESH_PATH_RESOLVING)) &&  		    (!(mpath->flags & MESH_PATH_FIXED)) && -			time_after(jiffies, -			 mpath->exp_time + MESH_PATH_EXPIRE)) { -			spin_unlock_bh(&mpath->state_lock); -			mesh_path_del(mpath->dst, mpath->sdata); -		} else -			spin_unlock_bh(&mpath->state_lock); +		     time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) +			mesh_path_del(mpath->sdata, mpath->dst);  	} -	read_unlock(&pathtbl_resize_lock); +	rcu_read_unlock();  }  void mesh_pathtbl_unregister(void)  { -	mesh_table_free(mesh_paths, true); -	mesh_table_free(mpp_paths, true); +	/* no need for locking during exit path */ +	mesh_table_free(rcu_dereference_protected(mesh_paths, 1), true); +	mesh_table_free(rcu_dereference_protected(mpp_paths, 1), true);  } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 1c91f0f3c30..e8f60aa2e84 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -13,41 +13,12 @@  #include "rate.h"  #include "mesh.h" -#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG -#define mpl_dbg(fmt, args...)	printk(KERN_DEBUG fmt, ##args) -#else -#define mpl_dbg(fmt, args...)	do { (void)(0); } while (0) -#endif - -#define PLINK_GET_LLID(p) (p + 4) -#define PLINK_GET_PLID(p) (p + 6) +#define PLINK_GET_LLID(p) (p + 2) +#define PLINK_GET_PLID(p) (p + 4)  #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \  				jiffies + HZ * t / 1000)) -/* Peer link cancel reasons, all subject to ANA approval */ -#define MESH_LINK_CANCELLED			2 -#define MESH_MAX_NEIGHBORS			3 -#define MESH_CAPABILITY_POLICY_VIOLATION	4 -#define MESH_CLOSE_RCVD				5 -#define MESH_MAX_RETRIES			6 -#define MESH_CONFIRM_TIMEOUT			7 -#define MESH_SECURITY_ROLE_NEGOTIATION_DIFFERS	8 -#define MESH_SECURITY_AUTHENTICATION_IMPOSSIBLE	9 -#define MESH_SECURITY_FAILED_VERIFICATION	10 - -#define dot11MeshMaxRetries(s) (s->u.mesh.mshcfg.dot11MeshMaxRetries) -#define dot11MeshRetryTimeout(s) (s->u.mesh.mshcfg.dot11MeshRetryTimeout) -#define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout) -#define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout) -#define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks) - -enum plink_frame_type { -	PLINK_OPEN = 0, -	PLINK_CONFIRM, -	PLINK_CLOSE -}; -  enum plink_event {  	PLINK_UNDEFINED,  	OPN_ACPT, @@ -60,18 +31,40 @@ enum plink_event {  	CLS_IGNR  }; -static inline -void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) -{ -	atomic_inc(&sdata->u.mesh.mshstats.estab_plinks); -	mesh_accept_plinks_update(sdata); -} +static const char * const mplstates[] = { +	[NL80211_PLINK_LISTEN] = "LISTEN", +	[NL80211_PLINK_OPN_SNT] = "OPN-SNT", +	[NL80211_PLINK_OPN_RCVD] = "OPN-RCVD", +	[NL80211_PLINK_CNF_RCVD] = "CNF_RCVD", +	[NL80211_PLINK_ESTAB] = "ESTAB", +	[NL80211_PLINK_HOLDING] = "HOLDING", +	[NL80211_PLINK_BLOCKED] = "BLOCKED" +}; + +static const char * const mplevents[] = { +	[PLINK_UNDEFINED] = "NONE", +	[OPN_ACPT] = "OPN_ACPT", +	[OPN_RJCT] = "OPN_RJCT", +	[OPN_IGNR] = "OPN_IGNR", +	[CNF_ACPT] = "CNF_ACPT", +	[CNF_RJCT] = "CNF_RJCT", +	[CNF_IGNR] = "CNF_IGNR", +	[CLS_ACPT] = "CLS_ACPT", +	[CLS_IGNR] = "CLS_IGNR" +}; + +static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, +			       enum ieee80211_self_protected_actioncode action, +			       u8 *da, u16 llid, u16 plid, u16 reason); -static inline -void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) + +/* We only need a valid sta if user configured a minimum rssi_threshold. */ +static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata, +				 struct sta_info *sta)  { -	atomic_dec(&sdata->u.mesh.mshstats.estab_plinks); -	mesh_accept_plinks_update(sdata); +	s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold; +	return rssi_threshold == 0 || +	       (sta && (s8) -ewma_read(&sta->avg_signal) > rssi_threshold);  }  /** @@ -83,33 +76,133 @@ void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)   */  static inline void mesh_plink_fsm_restart(struct sta_info *sta)  { -	sta->plink_state = PLINK_LISTEN; +	sta->plink_state = NL80211_PLINK_LISTEN;  	sta->llid = sta->plid = sta->reason = 0;  	sta->plink_retries = 0;  }  /* - * NOTE: This is just an alias for sta_info_alloc(), see notes - *       on it in the lifecycle management section! + * mesh_set_short_slot_time - enable / disable ERP short slot time. + * + * The standard indirectly mandates mesh STAs to turn off short slot time by + * disallowing advertising this (802.11-2012 8.4.1.4), but that doesn't mean we + * can't be sneaky about it. Enable short slot time if all mesh STAs in the + * MBSS support ERP rates. + * + * Returns BSS_CHANGED_ERP_SLOT or 0 for no change.   */ -static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, -					 u8 *hw_addr, u32 rates) +static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_local *local = sdata->local; +	enum ieee80211_band band = ieee80211_get_sdata_band(sdata); +	struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];  	struct sta_info *sta; +	u32 erp_rates = 0, changed = 0; +	int i; +	bool short_slot = false; + +	if (band == IEEE80211_BAND_5GHZ) { +		/* (IEEE 802.11-2012 19.4.5) */ +		short_slot = true; +		goto out; +	} else if (band != IEEE80211_BAND_2GHZ || +		   (band == IEEE80211_BAND_2GHZ && +		    local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) +		goto out; + +	for (i = 0; i < sband->n_bitrates; i++) +		if (sband->bitrates[i].flags & IEEE80211_RATE_ERP_G) +			erp_rates |= BIT(i); + +	if (!erp_rates) +		goto out; -	if (local->num_sta >= MESH_MAX_PLINKS) -		return NULL; +	rcu_read_lock(); +	list_for_each_entry_rcu(sta, &local->sta_list, list) { +		if (sdata != sta->sdata || +		    sta->plink_state != NL80211_PLINK_ESTAB) +			continue; + +		short_slot = false; +		if (erp_rates & sta->sta.supp_rates[band]) +			short_slot = true; +		 else +			break; +	} +	rcu_read_unlock(); -	sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); -	if (!sta) -		return NULL; +out: +	if (sdata->vif.bss_conf.use_short_slot != short_slot) { +		sdata->vif.bss_conf.use_short_slot = short_slot; +		changed = BSS_CHANGED_ERP_SLOT; +		mpl_dbg(sdata, "mesh_plink %pM: ERP short slot time %d\n", +			sdata->vif.addr, short_slot); +	} +	return changed; +} + +/** + * mesh_set_ht_prot_mode - set correct HT protection mode + * + * Section 9.23.3.5 of IEEE 80211-2012 describes the protection rules for HT + * mesh STA in a MBSS. Three HT protection modes are supported for now, non-HT + * mixed mode, 20MHz-protection and no-protection mode. non-HT mixed mode is + * selected if any non-HT peers are present in our MBSS.  20MHz-protection mode + * is selected if all peers in our 20/40MHz MBSS support HT and atleast one + * HT20 peer is present. Otherwise no-protection mode is selected. + */ +static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local; +	struct sta_info *sta; +	u16 ht_opmode; +	bool non_ht_sta = false, ht20_sta = false; + +	switch (sdata->vif.bss_conf.chandef.width) { +	case NL80211_CHAN_WIDTH_20_NOHT: +	case NL80211_CHAN_WIDTH_5: +	case NL80211_CHAN_WIDTH_10: +		return 0; +	default: +		break; +	} + +	rcu_read_lock(); +	list_for_each_entry_rcu(sta, &local->sta_list, list) { +		if (sdata != sta->sdata || +		    sta->plink_state != NL80211_PLINK_ESTAB) +			continue; + +		if (sta->sta.bandwidth > IEEE80211_STA_RX_BW_20) +			continue; + +		if (!sta->sta.ht_cap.ht_supported) { +			mpl_dbg(sdata, "nonHT sta (%pM) is present\n", +				       sta->sta.addr); +			non_ht_sta = true; +			break; +		} -	sta->flags = WLAN_STA_AUTHORIZED; -	sta->sta.supp_rates[local->hw.conf.channel->band] = rates; -	rate_control_rate_init(sta); +		mpl_dbg(sdata, "HT20 sta (%pM) is present\n", sta->sta.addr); +		ht20_sta = true; +	} +	rcu_read_unlock(); -	return sta; +	if (non_ht_sta) +		ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED; +	else if (ht20_sta && +		 sdata->vif.bss_conf.chandef.width > NL80211_CHAN_WIDTH_20) +		ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ; +	else +		ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE; + +	if (sdata->vif.bss_conf.ht_operation_mode == ht_opmode) +		return 0; + +	sdata->vif.bss_conf.ht_operation_mode = ht_opmode; +	sdata->u.mesh.mshcfg.ht_opmode = ht_opmode; +	mpl_dbg(sdata, "selected new HT protection mode %d\n", ht_opmode); +	return BSS_CHANGED_HT;  }  /** @@ -118,22 +211,25 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,   * @sta: mesh peer link to deactivate   *   * All mesh paths with this peer as next hop will be flushed + * Returns beacon changed flag if the beacon content changed.   *   * Locking: the caller must hold sta->lock   */ -static bool __mesh_plink_deactivate(struct sta_info *sta) +static u32 __mesh_plink_deactivate(struct sta_info *sta)  {  	struct ieee80211_sub_if_data *sdata = sta->sdata; -	bool deactivated = false; +	u32 changed = 0; -	if (sta->plink_state == PLINK_ESTAB) { -		mesh_plink_dec_estab_count(sdata); -		deactivated = true; -	} -	sta->plink_state = PLINK_BLOCKED; +	if (sta->plink_state == NL80211_PLINK_ESTAB) +		changed = mesh_plink_dec_estab_count(sdata); +	sta->plink_state = NL80211_PLINK_BLOCKED;  	mesh_path_flush_by_nexthop(sta); -	return deactivated; +	ieee80211_mps_sta_status_update(sta); +	changed |= ieee80211_mps_set_sta_local_pm(sta, +			NL80211_MESH_POWER_UNKNOWN); + +	return changed;  }  /** @@ -143,135 +239,306 @@ static bool __mesh_plink_deactivate(struct sta_info *sta)   *   * All mesh paths with this peer as next hop will be flushed   */ -void mesh_plink_deactivate(struct sta_info *sta) +u32 mesh_plink_deactivate(struct sta_info *sta)  {  	struct ieee80211_sub_if_data *sdata = sta->sdata; -	bool deactivated; +	u32 changed;  	spin_lock_bh(&sta->lock); -	deactivated = __mesh_plink_deactivate(sta); +	changed = __mesh_plink_deactivate(sta); +	sta->reason = WLAN_REASON_MESH_PEER_CANCELED; +	mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, +			    sta->sta.addr, sta->llid, sta->plid, +			    sta->reason);  	spin_unlock_bh(&sta->lock); -	if (deactivated) -		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); +	return changed;  }  static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, -		enum plink_frame_type action, u8 *da, __le16 llid, __le16 plid, -		__le16 reason) { +			       enum ieee80211_self_protected_actioncode action, +			       u8 *da, u16 llid, u16 plid, u16 reason) +{  	struct ieee80211_local *local = sdata->local; -	struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); +	struct sk_buff *skb; +	struct ieee80211_tx_info *info;  	struct ieee80211_mgmt *mgmt;  	bool include_plid = false; -	static const u8 meshpeeringproto[] = { 0x00, 0x0F, 0xAC, 0x2A }; -	u8 *pos; -	int ie_len; - +	u16 peering_proto = 0; +	u8 *pos, ie_len = 4; +	int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.self_prot) + +		      sizeof(mgmt->u.action.u.self_prot); +	int err = -ENOMEM; + +	skb = dev_alloc_skb(local->tx_headroom + +			    hdr_len + +			    2 + /* capability info */ +			    2 + /* AID */ +			    2 + 8 + /* supported rates */ +			    2 + (IEEE80211_MAX_SUPP_RATES - 8) + +			    2 + sdata->u.mesh.mesh_id_len + +			    2 + sizeof(struct ieee80211_meshconf_ie) + +			    2 + sizeof(struct ieee80211_ht_cap) + +			    2 + sizeof(struct ieee80211_ht_operation) + +			    2 + 8 + /* peering IE */ +			    sdata->u.mesh.ie_len);  	if (!skb) -		return -1; -	skb_reserve(skb, local->hw.extra_tx_headroom); -	/* 25 is the size of the common mgmt part (24) plus the size of the -	 * common action part (1) -	 */ -	mgmt = (struct ieee80211_mgmt *) -		skb_put(skb, 25 + sizeof(mgmt->u.action.u.plink_action)); -	memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.plink_action)); +		return err; +	info = IEEE80211_SKB_CB(skb); +	skb_reserve(skb, local->tx_headroom); +	mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); +	memset(mgmt, 0, hdr_len);  	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |  					  IEEE80211_STYPE_ACTION);  	memcpy(mgmt->da, da, ETH_ALEN);  	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); -	/* BSSID is left zeroed, wildcard value */ -	mgmt->u.action.category = WLAN_CATEGORY_MESH_PLINK; -	mgmt->u.action.u.plink_action.action_code = action; - -	if (action == PLINK_CLOSE) -		mgmt->u.action.u.plink_action.aux = reason; -	else { -		mgmt->u.action.u.plink_action.aux = cpu_to_le16(0x0); -		if (action == PLINK_CONFIRM) { -			pos = skb_put(skb, 4); -			/* two-byte status code followed by two-byte AID */ -			memset(pos, 0, 2); -			memcpy(pos + 2, &plid, 2); +	memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); +	mgmt->u.action.category = WLAN_CATEGORY_SELF_PROTECTED; +	mgmt->u.action.u.self_prot.action_code = action; + +	if (action != WLAN_SP_MESH_PEERING_CLOSE) { +		enum ieee80211_band band = ieee80211_get_sdata_band(sdata); + +		/* capability info */ +		pos = skb_put(skb, 2); +		memset(pos, 0, 2); +		if (action == WLAN_SP_MESH_PEERING_CONFIRM) { +			/* AID */ +			pos = skb_put(skb, 2); +			put_unaligned_le16(plid, pos + 2);  		} -		mesh_mgmt_ies_add(skb, sdata); +		if (ieee80211_add_srates_ie(sdata, skb, true, band) || +		    ieee80211_add_ext_srates_ie(sdata, skb, true, band) || +		    mesh_add_rsn_ie(sdata, skb) || +		    mesh_add_meshid_ie(sdata, skb) || +		    mesh_add_meshconf_ie(sdata, skb)) +			goto free; +	} else {	/* WLAN_SP_MESH_PEERING_CLOSE */ +		info->flags |= IEEE80211_TX_CTL_NO_ACK; +		if (mesh_add_meshid_ie(sdata, skb)) +			goto free;  	} -	/* Add Peer Link Management element */ +	/* Add Mesh Peering Management element */  	switch (action) { -	case PLINK_OPEN: -		ie_len = 6; +	case WLAN_SP_MESH_PEERING_OPEN:  		break; -	case PLINK_CONFIRM: -		ie_len = 8; +	case WLAN_SP_MESH_PEERING_CONFIRM: +		ie_len += 2;  		include_plid = true;  		break; -	case PLINK_CLOSE: -	default: -		if (!plid) -			ie_len = 8; -		else { -			ie_len = 10; +	case WLAN_SP_MESH_PEERING_CLOSE: +		if (plid) { +			ie_len += 2;  			include_plid = true;  		} +		ie_len += 2;	/* reason code */  		break; +	default: +		err = -EINVAL; +		goto free;  	} +	if (WARN_ON(skb_tailroom(skb) < 2 + ie_len)) +		goto free; +  	pos = skb_put(skb, 2 + ie_len); -	*pos++ = WLAN_EID_PEER_LINK; +	*pos++ = WLAN_EID_PEER_MGMT;  	*pos++ = ie_len; -	memcpy(pos, meshpeeringproto, sizeof(meshpeeringproto)); -	pos += 4; -	memcpy(pos, &llid, 2); +	memcpy(pos, &peering_proto, 2); +	pos += 2; +	put_unaligned_le16(llid, pos); +	pos += 2;  	if (include_plid) { +		put_unaligned_le16(plid, pos);  		pos += 2; -		memcpy(pos, &plid, 2);  	} -	if (action == PLINK_CLOSE) { +	if (action == WLAN_SP_MESH_PEERING_CLOSE) { +		put_unaligned_le16(reason, pos);  		pos += 2; -		memcpy(pos, &reason, 2);  	} +	if (action != WLAN_SP_MESH_PEERING_CLOSE) { +		if (mesh_add_ht_cap_ie(sdata, skb) || +		    mesh_add_ht_oper_ie(sdata, skb)) +			goto free; +	} + +	if (mesh_add_vendor_ies(sdata, skb)) +		goto free; +  	ieee80211_tx_skb(sdata, skb);  	return 0; +free: +	kfree_skb(skb); +	return err;  } -void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data *sdata, -			   bool peer_accepting_plinks) +static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, +			       struct sta_info *sta, +			       struct ieee802_11_elems *elems, bool insert)  {  	struct ieee80211_local *local = sdata->local; +	enum ieee80211_band band = ieee80211_get_sdata_band(sdata); +	struct ieee80211_supported_band *sband; +	u32 rates, basic_rates = 0, changed = 0; + +	sband = local->hw.wiphy->bands[band]; +	rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates); + +	spin_lock_bh(&sta->lock); +	sta->last_rx = jiffies; + +	/* rates and capabilities don't change during peering */ +	if (sta->plink_state == NL80211_PLINK_ESTAB) +		goto out; + +	if (sta->sta.supp_rates[band] != rates) +		changed |= IEEE80211_RC_SUPP_RATES_CHANGED; +	sta->sta.supp_rates[band] = rates; + +	if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, +					      elems->ht_cap_elem, sta)) +		changed |= IEEE80211_RC_BW_CHANGED; + +	/* HT peer is operating 20MHz-only */ +	if (elems->ht_operation && +	    !(elems->ht_operation->ht_param & +	      IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) { +		if (sta->sta.bandwidth != IEEE80211_STA_RX_BW_20) +			changed |= IEEE80211_RC_BW_CHANGED; +		sta->sta.bandwidth = IEEE80211_STA_RX_BW_20; +	} + +	if (insert) +		rate_control_rate_init(sta); +	else +		rate_control_rate_update(local, sband, sta, changed); +out: +	spin_unlock_bh(&sta->lock); +} + +static struct sta_info * +__mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr) +{  	struct sta_info *sta; -	rcu_read_lock(); +	if (sdata->local->num_sta >= MESH_MAX_PLINKS) +		return NULL; -	sta = sta_info_get(sdata, hw_addr); -	if (!sta) { -		rcu_read_unlock(); +	sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); +	if (!sta) +		return NULL; + +	sta->plink_state = NL80211_PLINK_LISTEN; + +	sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); +	sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); +	sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); + +	set_sta_flag(sta, WLAN_STA_WME); +	sta->sta.wme = true; + +	return sta; +} + +static struct sta_info * +mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr, +		    struct ieee802_11_elems *elems) +{ +	struct sta_info *sta = NULL; + +	/* Userspace handles station allocation */ +	if (sdata->u.mesh.user_mpm || +	    sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) +		cfg80211_notify_new_peer_candidate(sdata->dev, addr, +						   elems->ie_start, +						   elems->total_len, +						   GFP_KERNEL); +	else +		sta = __mesh_sta_info_alloc(sdata, addr); + +	return sta; +} + +/* + * mesh_sta_info_get - return mesh sta info entry for @addr. + * + * @sdata: local meshif + * @addr: peer's address + * @elems: IEs from beacon or mesh peering frame. + * + * Return existing or newly allocated sta_info under RCU read lock. + * (re)initialize with given IEs. + */ +static struct sta_info * +mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, +		  u8 *addr, struct ieee802_11_elems *elems) __acquires(RCU) +{ +	struct sta_info *sta = NULL; -		sta = mesh_plink_alloc(sdata, hw_addr, rates); -		if (!sta) -			return; -		if (sta_info_insert_rcu(sta)) { -			rcu_read_unlock(); -			return; +	rcu_read_lock(); +	sta = sta_info_get(sdata, addr); +	if (sta) { +		mesh_sta_info_init(sdata, sta, elems, false); +	} else { +		rcu_read_unlock(); +		/* can't run atomic */ +		sta = mesh_sta_info_alloc(sdata, addr, elems); +		if (!sta) { +			rcu_read_lock(); +			return NULL;  		} + +		mesh_sta_info_init(sdata, sta, elems, true); + +		if (sta_info_insert_rcu(sta)) +			return NULL;  	} -	sta->last_rx = jiffies; -	sta->sta.supp_rates[local->hw.conf.channel->band] = rates; -	if (peer_accepting_plinks && sta->plink_state == PLINK_LISTEN && -			sdata->u.mesh.accepting_plinks && -			sdata->u.mesh.mshcfg.auto_open_plinks) -		mesh_plink_open(sta); +	return sta; +} + +/* + * mesh_neighbour_update - update or initialize new mesh neighbor. + * + * @sdata: local meshif + * @addr: peer's address + * @elems: IEs from beacon or mesh peering frame + * + * Initiates peering if appropriate. + */ +void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, +			   u8 *hw_addr, +			   struct ieee802_11_elems *elems) +{ +	struct sta_info *sta; +	u32 changed = 0; + +	sta = mesh_sta_info_get(sdata, hw_addr, elems); +	if (!sta) +		goto out; +	if (mesh_peer_accepts_plinks(elems) && +	    sta->plink_state == NL80211_PLINK_LISTEN && +	    sdata->u.mesh.accepting_plinks && +	    sdata->u.mesh.mshcfg.auto_open_plinks && +	    rssi_threshold_check(sdata, sta)) +		changed = mesh_plink_open(sta); + +	ieee80211_mps_frame_release(sta, elems); +out:  	rcu_read_unlock(); +	ieee80211_mbss_info_change_notify(sdata, changed);  }  static void mesh_plink_timer(unsigned long data)  {  	struct sta_info *sta; -	__le16 llid, plid, reason; +	u16 reason = 0;  	struct ieee80211_sub_if_data *sdata; +	struct mesh_config *mshcfg; +	enum ieee80211_self_protected_actioncode action = 0;  	/*  	 * This STA is valid because sta_info_destroy() will @@ -280,10 +547,8 @@ static void mesh_plink_timer(unsigned long data)  	 */  	sta = (struct sta_info *) data; -	if (sta->sdata->local->quiescing) { -		sta->plink_timer_was_running = true; +	if (sta->sdata->local->quiescing)  		return; -	}  	spin_lock_bh(&sta->lock);  	if (sta->ignore_plink_timer) { @@ -291,20 +556,20 @@ static void mesh_plink_timer(unsigned long data)  		spin_unlock_bh(&sta->lock);  		return;  	} -	mpl_dbg("Mesh plink timer for %pM fired on state %d\n", -		sta->sta.addr, sta->plink_state); -	reason = 0; -	llid = sta->llid; -	plid = sta->plid; +	mpl_dbg(sta->sdata, +		"Mesh plink timer for %pM fired on state %s\n", +		sta->sta.addr, mplstates[sta->plink_state]);  	sdata = sta->sdata; +	mshcfg = &sdata->u.mesh.mshcfg;  	switch (sta->plink_state) { -	case PLINK_OPN_RCVD: -	case PLINK_OPN_SNT: +	case NL80211_PLINK_OPN_RCVD: +	case NL80211_PLINK_OPN_SNT:  		/* retry timer */ -		if (sta->plink_retries < dot11MeshMaxRetries(sdata)) { +		if (sta->plink_retries < mshcfg->dot11MeshMaxRetries) {  			u32 rand; -			mpl_dbg("Mesh plink for %pM (retry, timeout): %d %d\n", +			mpl_dbg(sta->sdata, +				"Mesh plink for %pM (retry, timeout): %d %d\n",  				sta->sta.addr, sta->plink_retries,  				sta->plink_timeout);  			get_random_bytes(&rand, sizeof(u32)); @@ -312,51 +577,33 @@ static void mesh_plink_timer(unsigned long data)  					     rand % sta->plink_timeout;  			++sta->plink_retries;  			mod_plink_timer(sta, sta->plink_timeout); -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, PLINK_OPEN, sta->sta.addr, llid, -					    0, 0); +			action = WLAN_SP_MESH_PEERING_OPEN;  			break;  		} -		reason = cpu_to_le16(MESH_MAX_RETRIES); +		reason = WLAN_REASON_MESH_MAX_RETRIES;  		/* fall through on else */ -	case PLINK_CNF_RCVD: +	case NL80211_PLINK_CNF_RCVD:  		/* confirm timer */  		if (!reason) -			reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT); -		sta->plink_state = PLINK_HOLDING; -		mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); -		spin_unlock_bh(&sta->lock); -		mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, plid, -				    reason); +			reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT; +		sta->plink_state = NL80211_PLINK_HOLDING; +		mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); +		action = WLAN_SP_MESH_PEERING_CLOSE;  		break; -	case PLINK_HOLDING: +	case NL80211_PLINK_HOLDING:  		/* holding timer */  		del_timer(&sta->plink_timer);  		mesh_plink_fsm_restart(sta); -		spin_unlock_bh(&sta->lock);  		break;  	default: -		spin_unlock_bh(&sta->lock);  		break;  	} +	spin_unlock_bh(&sta->lock); +	if (action) +		mesh_plink_frame_tx(sdata, action, sta->sta.addr, +				    sta->llid, sta->plid, reason);  } -#ifdef CONFIG_PM -void mesh_plink_quiesce(struct sta_info *sta) -{ -	if (del_timer_sync(&sta->plink_timer)) -		sta->plink_timer_was_running = true; -} - -void mesh_plink_restart(struct sta_info *sta) -{ -	if (sta->plink_timer_was_running) { -		add_timer(&sta->plink_timer); -		sta->plink_timer_was_running = false; -	} -} -#endif -  static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)  {  	sta->plink_timer.expires = jiffies + (HZ * timeout / 1000); @@ -366,421 +613,495 @@ static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)  	add_timer(&sta->plink_timer);  } -int mesh_plink_open(struct sta_info *sta) +static bool llid_in_use(struct ieee80211_sub_if_data *sdata, +			u16 llid)  { -	__le16 llid; -	struct ieee80211_sub_if_data *sdata = sta->sdata; +	struct ieee80211_local *local = sdata->local; +	bool in_use = false; +	struct sta_info *sta; -	spin_lock_bh(&sta->lock); -	get_random_bytes(&llid, 2); -	sta->llid = llid; -	if (sta->plink_state != PLINK_LISTEN) { -		spin_unlock_bh(&sta->lock); -		return -EBUSY; +	rcu_read_lock(); +	list_for_each_entry_rcu(sta, &local->sta_list, list) { +		if (!memcmp(&sta->llid, &llid, sizeof(llid))) { +			in_use = true; +			break; +		}  	} -	sta->plink_state = PLINK_OPN_SNT; -	mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); -	spin_unlock_bh(&sta->lock); -	mpl_dbg("Mesh plink: starting establishment with %pM\n", -		sta->sta.addr); +	rcu_read_unlock(); -	return mesh_plink_frame_tx(sdata, PLINK_OPEN, -				   sta->sta.addr, llid, 0, 0); +	return in_use;  } -void mesh_plink_block(struct sta_info *sta) +static u16 mesh_get_new_llid(struct ieee80211_sub_if_data *sdata)  { -	struct ieee80211_sub_if_data *sdata = sta->sdata; -	bool deactivated; +	u16 llid; -	spin_lock_bh(&sta->lock); -	deactivated = __mesh_plink_deactivate(sta); -	sta->plink_state = PLINK_BLOCKED; -	spin_unlock_bh(&sta->lock); +	do { +		get_random_bytes(&llid, sizeof(llid)); +		/* for mesh PS we still only have the AID range for TIM bits */ +		llid = (llid % IEEE80211_MAX_AID) + 1; +	} while (llid_in_use(sdata, llid)); -	if (deactivated) -		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); +	return llid;  } - -void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, -			 size_t len, struct ieee80211_rx_status *rx_status) +u32 mesh_plink_open(struct sta_info *sta)  { -	struct ieee80211_local *local = sdata->local; -	struct ieee802_11_elems elems; -	struct sta_info *sta; -	enum plink_event event; -	enum plink_frame_type ftype; -	size_t baselen; -	bool deactivated, matches_local = true; -	u8 ie_len; -	u8 *baseaddr; -	__le16 plid, llid, reason; -#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG -	static const char *mplstates[] = { -		[PLINK_LISTEN] = "LISTEN", -		[PLINK_OPN_SNT] = "OPN-SNT", -		[PLINK_OPN_RCVD] = "OPN-RCVD", -		[PLINK_CNF_RCVD] = "CNF_RCVD", -		[PLINK_ESTAB] = "ESTAB", -		[PLINK_HOLDING] = "HOLDING", -		[PLINK_BLOCKED] = "BLOCKED" -	}; -#endif +	struct ieee80211_sub_if_data *sdata = sta->sdata; +	u32 changed; -	/* need action_code, aux */ -	if (len < IEEE80211_MIN_ACTION_SIZE + 3) -		return; +	if (!test_sta_flag(sta, WLAN_STA_AUTH)) +		return 0; -	if (is_multicast_ether_addr(mgmt->da)) { -		mpl_dbg("Mesh plink: ignore frame from multicast address"); -		return; +	spin_lock_bh(&sta->lock); +	sta->llid = mesh_get_new_llid(sdata); +	if (sta->plink_state != NL80211_PLINK_LISTEN && +	    sta->plink_state != NL80211_PLINK_BLOCKED) { +		spin_unlock_bh(&sta->lock); +		return 0;  	} +	sta->plink_state = NL80211_PLINK_OPN_SNT; +	mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout); +	spin_unlock_bh(&sta->lock); +	mpl_dbg(sdata, +		"Mesh plink: starting establishment with %pM\n", +		sta->sta.addr); -	baseaddr = mgmt->u.action.u.plink_action.variable; -	baselen = (u8 *) mgmt->u.action.u.plink_action.variable - (u8 *) mgmt; -	if (mgmt->u.action.u.plink_action.action_code == PLINK_CONFIRM) { -		baseaddr += 4; -		baselen += 4; -	} -	ieee802_11_parse_elems(baseaddr, len - baselen, &elems); -	if (!elems.peer_link) { -		mpl_dbg("Mesh plink: missing necessary peer link ie\n"); -		return; -	} +	/* set the non-peer mode to active during peering */ +	changed = ieee80211_mps_local_status_update(sdata); -	ftype = mgmt->u.action.u.plink_action.action_code; -	ie_len = elems.peer_link_len; -	if ((ftype == PLINK_OPEN && ie_len != 6) || -	    (ftype == PLINK_CONFIRM && ie_len != 8) || -	    (ftype == PLINK_CLOSE && ie_len != 8 && ie_len != 10)) { -		mpl_dbg("Mesh plink: incorrect plink ie length %d %d\n", -		    ftype, ie_len); -		return; -	} +	mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, +			    sta->sta.addr, sta->llid, 0, 0); +	return changed; +} -	if (ftype != PLINK_CLOSE && (!elems.mesh_id || !elems.mesh_config)) { -		mpl_dbg("Mesh plink: missing necessary ie\n"); -		return; -	} -	/* Note the lines below are correct, the llid in the frame is the plid -	 * from the point of view of this host. -	 */ -	memcpy(&plid, PLINK_GET_LLID(elems.peer_link), 2); -	if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 10)) -		memcpy(&llid, PLINK_GET_PLID(elems.peer_link), 2); +u32 mesh_plink_block(struct sta_info *sta) +{ +	u32 changed; -	rcu_read_lock(); +	spin_lock_bh(&sta->lock); +	changed = __mesh_plink_deactivate(sta); +	sta->plink_state = NL80211_PLINK_BLOCKED; +	spin_unlock_bh(&sta->lock); -	sta = sta_info_get(sdata, mgmt->sa); -	if (!sta && ftype != PLINK_OPEN) { -		mpl_dbg("Mesh plink: cls or cnf from unknown peer\n"); -		rcu_read_unlock(); -		return; -	} +	return changed; +} -	if (sta && sta->plink_state == PLINK_BLOCKED) { -		rcu_read_unlock(); -		return; -	} +static void mesh_plink_close(struct ieee80211_sub_if_data *sdata, +			     struct sta_info *sta, +			     enum plink_event event) +{ +	struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; -	/* Now we will figure out the appropriate event... */ -	event = PLINK_UNDEFINED; -	if (ftype != PLINK_CLOSE && (!mesh_matches_local(&elems, sdata))) { -		matches_local = false; -		switch (ftype) { -		case PLINK_OPEN: -			event = OPN_RJCT; -			break; -		case PLINK_CONFIRM: -			event = CNF_RJCT; -			break; -		case PLINK_CLOSE: -			/* avoid warning */ -			break; -		} -	} +	u16 reason = (event == CLS_ACPT) ? +		     WLAN_REASON_MESH_CLOSE : WLAN_REASON_MESH_CONFIG; -	if (!sta && !matches_local) { -		rcu_read_unlock(); -		reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); -		llid = 0; -		mesh_plink_frame_tx(sdata, PLINK_CLOSE, mgmt->sa, llid, -				    plid, reason); -		return; -	} else if (!sta) { -		/* ftype == PLINK_OPEN */ -		u32 rates; +	sta->reason = reason; +	sta->plink_state = NL80211_PLINK_HOLDING; +	mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); +} -		rcu_read_unlock(); +static u32 mesh_plink_establish(struct ieee80211_sub_if_data *sdata, +				struct sta_info *sta) +{ +	struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; +	u32 changed = 0; + +	del_timer(&sta->plink_timer); +	sta->plink_state = NL80211_PLINK_ESTAB; +	changed |= mesh_plink_inc_estab_count(sdata); +	changed |= mesh_set_ht_prot_mode(sdata); +	changed |= mesh_set_short_slot_time(sdata); +	mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); +	ieee80211_mps_sta_status_update(sta); +	changed |= ieee80211_mps_set_sta_local_pm(sta, mshcfg->power_mode); +	return changed; +} -		if (!mesh_plink_free_count(sdata)) { -			mpl_dbg("Mesh plink error: no more free plinks\n"); -			return; -		} +/** + * mesh_plink_fsm - step @sta MPM based on @event + * + * @sdata: interface + * @sta: mesh neighbor + * @event: peering event + * + * Return: changed MBSS flags + */ +static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata, +			  struct sta_info *sta, enum plink_event event) +{ +	struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; +	enum ieee80211_self_protected_actioncode action = 0; +	u32 changed = 0; -		rates = ieee80211_sta_get_rates(local, &elems, rx_status->band); -		sta = mesh_plink_alloc(sdata, mgmt->sa, rates); -		if (!sta) { -			mpl_dbg("Mesh plink error: plink table full\n"); -			return; -		} -		if (sta_info_insert_rcu(sta)) { -			rcu_read_unlock(); -			return; -		} -		event = OPN_ACPT; -		spin_lock_bh(&sta->lock); -	} else if (matches_local) { -		spin_lock_bh(&sta->lock); -		switch (ftype) { -		case PLINK_OPEN: -			if (!mesh_plink_free_count(sdata) || -			    (sta->plid && sta->plid != plid)) -				event = OPN_IGNR; -			else -				event = OPN_ACPT; -			break; -		case PLINK_CONFIRM: -			if (!mesh_plink_free_count(sdata) || -			    (sta->llid != llid || sta->plid != plid)) -				event = CNF_IGNR; -			else -				event = CNF_ACPT; -			break; -		case PLINK_CLOSE: -			if (sta->plink_state == PLINK_ESTAB) -				/* Do not check for llid or plid. This does not -				 * follow the standard but since multiple plinks -				 * per sta are not supported, it is necessary in -				 * order to avoid a livelock when MP A sees an -				 * establish peer link to MP B but MP B does not -				 * see it. This can be caused by a timeout in -				 * B's peer link establishment or B beign -				 * restarted. -				 */ -				event = CLS_ACPT; -			else if (sta->plid != plid) -				event = CLS_IGNR; -			else if (ie_len == 7 && sta->llid != llid) -				event = CLS_IGNR; -			else -				event = CLS_ACPT; -			break; -		default: -			mpl_dbg("Mesh plink: unknown frame subtype\n"); -			spin_unlock_bh(&sta->lock); -			rcu_read_unlock(); -			return; -		} -	} else { -		spin_lock_bh(&sta->lock); -	} +	mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr, +		mplstates[sta->plink_state], mplevents[event]); -	mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %s %d %d %d\n", -		mgmt->sa, mplstates[sta->plink_state], -		le16_to_cpu(sta->llid), le16_to_cpu(sta->plid), -		event); -	reason = 0; +	spin_lock_bh(&sta->lock);  	switch (sta->plink_state) { -		/* spin_unlock as soon as state is updated at each case */ -	case PLINK_LISTEN: +	case NL80211_PLINK_LISTEN:  		switch (event) {  		case CLS_ACPT:  			mesh_plink_fsm_restart(sta); -			spin_unlock_bh(&sta->lock);  			break;  		case OPN_ACPT: -			sta->plink_state = PLINK_OPN_RCVD; -			sta->plid = plid; -			get_random_bytes(&llid, 2); -			sta->llid = llid; -			mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, PLINK_OPEN, sta->sta.addr, llid, -					    0, 0); -			mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, -					    llid, plid, 0); +			sta->plink_state = NL80211_PLINK_OPN_RCVD; +			sta->llid = mesh_get_new_llid(sdata); +			mesh_plink_timer_set(sta, +					     mshcfg->dot11MeshRetryTimeout); + +			/* set the non-peer mode to active during peering */ +			changed |= ieee80211_mps_local_status_update(sdata); +			action = WLAN_SP_MESH_PEERING_OPEN;  			break;  		default: -			spin_unlock_bh(&sta->lock);  			break;  		}  		break; - -	case PLINK_OPN_SNT: +	case NL80211_PLINK_OPN_SNT:  		switch (event) {  		case OPN_RJCT:  		case CNF_RJCT: -			reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION);  		case CLS_ACPT: -			if (!reason) -				reason = cpu_to_le16(MESH_CLOSE_RCVD); -			sta->reason = reason; -			sta->plink_state = PLINK_HOLDING; -			if (!mod_plink_timer(sta, -					     dot11MeshHoldingTimeout(sdata))) -				sta->ignore_plink_timer = true; - -			llid = sta->llid; -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, -					    plid, reason); +			mesh_plink_close(sdata, sta, event); +			action = WLAN_SP_MESH_PEERING_CLOSE;  			break;  		case OPN_ACPT:  			/* retry timer is left untouched */ -			sta->plink_state = PLINK_OPN_RCVD; -			sta->plid = plid; -			llid = sta->llid; -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, -					    plid, 0); +			sta->plink_state = NL80211_PLINK_OPN_RCVD; +			action = WLAN_SP_MESH_PEERING_CONFIRM;  			break;  		case CNF_ACPT: -			sta->plink_state = PLINK_CNF_RCVD; +			sta->plink_state = NL80211_PLINK_CNF_RCVD;  			if (!mod_plink_timer(sta, -					     dot11MeshConfirmTimeout(sdata))) +					     mshcfg->dot11MeshConfirmTimeout))  				sta->ignore_plink_timer = true; - -			spin_unlock_bh(&sta->lock);  			break;  		default: -			spin_unlock_bh(&sta->lock);  			break;  		}  		break; - -	case PLINK_OPN_RCVD: +	case NL80211_PLINK_OPN_RCVD:  		switch (event) {  		case OPN_RJCT:  		case CNF_RJCT: -			reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION);  		case CLS_ACPT: -			if (!reason) -				reason = cpu_to_le16(MESH_CLOSE_RCVD); -			sta->reason = reason; -			sta->plink_state = PLINK_HOLDING; -			if (!mod_plink_timer(sta, -					     dot11MeshHoldingTimeout(sdata))) -				sta->ignore_plink_timer = true; - -			llid = sta->llid; -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, -					    plid, reason); +			mesh_plink_close(sdata, sta, event); +			action = WLAN_SP_MESH_PEERING_CLOSE;  			break;  		case OPN_ACPT: -			llid = sta->llid; -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, -					    plid, 0); +			action = WLAN_SP_MESH_PEERING_CONFIRM;  			break;  		case CNF_ACPT: -			del_timer(&sta->plink_timer); -			sta->plink_state = PLINK_ESTAB; -			spin_unlock_bh(&sta->lock); -			mesh_plink_inc_estab_count(sdata); -			ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); -			mpl_dbg("Mesh plink with %pM ESTABLISHED\n", -				sta->sta.addr); +			changed |= mesh_plink_establish(sdata, sta);  			break;  		default: -			spin_unlock_bh(&sta->lock);  			break;  		}  		break; - -	case PLINK_CNF_RCVD: +	case NL80211_PLINK_CNF_RCVD:  		switch (event) {  		case OPN_RJCT:  		case CNF_RJCT: -			reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION);  		case CLS_ACPT: -			if (!reason) -				reason = cpu_to_le16(MESH_CLOSE_RCVD); -			sta->reason = reason; -			sta->plink_state = PLINK_HOLDING; -			if (!mod_plink_timer(sta, -					     dot11MeshHoldingTimeout(sdata))) -				sta->ignore_plink_timer = true; - -			llid = sta->llid; -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, -					    plid, reason); +			mesh_plink_close(sdata, sta, event); +			action = WLAN_SP_MESH_PEERING_CLOSE;  			break;  		case OPN_ACPT: -			del_timer(&sta->plink_timer); -			sta->plink_state = PLINK_ESTAB; -			spin_unlock_bh(&sta->lock); -			mesh_plink_inc_estab_count(sdata); -			ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); -			mpl_dbg("Mesh plink with %pM ESTABLISHED\n", -				sta->sta.addr); -			mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, -					    plid, 0); +			changed |= mesh_plink_establish(sdata, sta); +			action = WLAN_SP_MESH_PEERING_CONFIRM;  			break;  		default: -			spin_unlock_bh(&sta->lock);  			break;  		}  		break; - -	case PLINK_ESTAB: +	case NL80211_PLINK_ESTAB:  		switch (event) {  		case CLS_ACPT: -			reason = cpu_to_le16(MESH_CLOSE_RCVD); -			sta->reason = reason; -			deactivated = __mesh_plink_deactivate(sta); -			sta->plink_state = PLINK_HOLDING; -			llid = sta->llid; -			mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); -			spin_unlock_bh(&sta->lock); -			if (deactivated) -				ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); -			mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, -					    plid, reason); +			changed |= __mesh_plink_deactivate(sta); +			changed |= mesh_set_ht_prot_mode(sdata); +			changed |= mesh_set_short_slot_time(sdata); +			mesh_plink_close(sdata, sta, event); +			action = WLAN_SP_MESH_PEERING_CLOSE;  			break;  		case OPN_ACPT: -			llid = sta->llid; -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, -					    plid, 0); +			action = WLAN_SP_MESH_PEERING_CONFIRM;  			break;  		default: -			spin_unlock_bh(&sta->lock);  			break;  		}  		break; -	case PLINK_HOLDING: +	case NL80211_PLINK_HOLDING:  		switch (event) {  		case CLS_ACPT:  			if (del_timer(&sta->plink_timer))  				sta->ignore_plink_timer = 1;  			mesh_plink_fsm_restart(sta); -			spin_unlock_bh(&sta->lock);  			break;  		case OPN_ACPT:  		case CNF_ACPT:  		case OPN_RJCT:  		case CNF_RJCT: -			llid = sta->llid; -			reason = sta->reason; -			spin_unlock_bh(&sta->lock); -			mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, -					    llid, plid, reason); +			action = WLAN_SP_MESH_PEERING_CLOSE;  			break;  		default: -			spin_unlock_bh(&sta->lock); +			break;  		}  		break;  	default:  		/* should not get here, PLINK_BLOCKED is dealt with at the  		 * beginning of the function  		 */ -		spin_unlock_bh(&sta->lock);  		break;  	} +	spin_unlock_bh(&sta->lock); +	if (action) { +		mesh_plink_frame_tx(sdata, action, sta->sta.addr, +				    sta->llid, sta->plid, sta->reason); + +		/* also send confirm in open case */ +		if (action == WLAN_SP_MESH_PEERING_OPEN) { +			mesh_plink_frame_tx(sdata, +					    WLAN_SP_MESH_PEERING_CONFIRM, +					    sta->sta.addr, sta->llid, +					    sta->plid, 0); +		} +	} + +	return changed; +} + +/* + * mesh_plink_get_event - get correct MPM event + * + * @sdata: interface + * @sta: peer, leave NULL if processing a frame from a new suitable peer + * @elems: peering management IEs + * @ftype: frame type + * @llid: peer's peer link ID + * @plid: peer's local link ID + * + * Return: new peering event for @sta, but PLINK_UNDEFINED should be treated as + * an error. + */ +static enum plink_event +mesh_plink_get_event(struct ieee80211_sub_if_data *sdata, +		     struct sta_info *sta, +		     struct ieee802_11_elems *elems, +		     enum ieee80211_self_protected_actioncode ftype, +		     u16 llid, u16 plid) +{ +	enum plink_event event = PLINK_UNDEFINED; +	u8 ie_len = elems->peering_len; +	bool matches_local; + +	matches_local = (ftype == WLAN_SP_MESH_PEERING_CLOSE || +			 mesh_matches_local(sdata, elems)); + +	/* deny open request from non-matching peer */ +	if (!matches_local && !sta) { +		event = OPN_RJCT; +		goto out; +	} +	if (!sta) { +		if (ftype != WLAN_SP_MESH_PEERING_OPEN) { +			mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n"); +			goto out; +		} +		/* ftype == WLAN_SP_MESH_PEERING_OPEN */ +		if (!mesh_plink_free_count(sdata)) { +			mpl_dbg(sdata, "Mesh plink error: no more free plinks\n"); +			goto out; +		} +	} else { +		if (!test_sta_flag(sta, WLAN_STA_AUTH)) { +			mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n"); +			goto out; +		} +		if (sta->plink_state == NL80211_PLINK_BLOCKED) +			goto out; +	} + +	/* new matching peer */ +	if (!sta) { +		event = OPN_ACPT; +		goto out; +	} + +	switch (ftype) { +	case WLAN_SP_MESH_PEERING_OPEN: +		if (!matches_local) +			event = OPN_RJCT; +		if (!mesh_plink_free_count(sdata) || +		    (sta->plid && sta->plid != plid)) +			event = OPN_IGNR; +		else +			event = OPN_ACPT; +		break; +	case WLAN_SP_MESH_PEERING_CONFIRM: +		if (!matches_local) +			event = CNF_RJCT; +		if (!mesh_plink_free_count(sdata) || +		    (sta->llid != llid || sta->plid != plid)) +			event = CNF_IGNR; +		else +			event = CNF_ACPT; +		break; +	case WLAN_SP_MESH_PEERING_CLOSE: +		if (sta->plink_state == NL80211_PLINK_ESTAB) +			/* Do not check for llid or plid. This does not +			 * follow the standard but since multiple plinks +			 * per sta are not supported, it is necessary in +			 * order to avoid a livelock when MP A sees an +			 * establish peer link to MP B but MP B does not +			 * see it. This can be caused by a timeout in +			 * B's peer link establishment or B beign +			 * restarted. +			 */ +			event = CLS_ACPT; +		else if (sta->plid != plid) +			event = CLS_IGNR; +		else if (ie_len == 8 && sta->llid != llid) +			event = CLS_IGNR; +		else +			event = CLS_ACPT; +		break; +	default: +		mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n"); +		break; +	} + +out: +	return event; +} + +static void +mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata, +			 struct ieee80211_mgmt *mgmt, +			 struct ieee802_11_elems *elems) +{ + +	struct sta_info *sta; +	enum plink_event event; +	enum ieee80211_self_protected_actioncode ftype; +	u32 changed = 0; +	u8 ie_len = elems->peering_len; +	__le16 _plid, _llid; +	u16 plid, llid = 0; + +	if (!elems->peering) { +		mpl_dbg(sdata, +			"Mesh plink: missing necessary peer link ie\n"); +		return; +	} + +	if (elems->rsn_len && +	    sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { +		mpl_dbg(sdata, +			"Mesh plink: can't establish link with secure peer\n"); +		return; +	} + +	ftype = mgmt->u.action.u.self_prot.action_code; +	if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) || +	    (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) || +	    (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6 +							&& ie_len != 8)) { +		mpl_dbg(sdata, +			"Mesh plink: incorrect plink ie length %d %d\n", +			ftype, ie_len); +		return; +	} + +	if (ftype != WLAN_SP_MESH_PEERING_CLOSE && +	    (!elems->mesh_id || !elems->mesh_config)) { +		mpl_dbg(sdata, "Mesh plink: missing necessary ie\n"); +		return; +	} +	/* Note the lines below are correct, the llid in the frame is the plid +	 * from the point of view of this host. +	 */ +	memcpy(&_plid, PLINK_GET_LLID(elems->peering), sizeof(__le16)); +	plid = le16_to_cpu(_plid); +	if (ftype == WLAN_SP_MESH_PEERING_CONFIRM || +	    (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) { +		memcpy(&_llid, PLINK_GET_PLID(elems->peering), sizeof(__le16)); +		llid = le16_to_cpu(_llid); +	} + +	/* WARNING: Only for sta pointer, is dropped & re-acquired */ +	rcu_read_lock(); + +	sta = sta_info_get(sdata, mgmt->sa); + +	if (ftype == WLAN_SP_MESH_PEERING_OPEN && +	    !rssi_threshold_check(sdata, sta)) { +		mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n", +			mgmt->sa); +		goto unlock_rcu; +	} + +	/* Now we will figure out the appropriate event... */ +	event = mesh_plink_get_event(sdata, sta, elems, ftype, llid, plid); + +	if (event == OPN_ACPT) { +		rcu_read_unlock(); +		/* allocate sta entry if necessary and update info */ +		sta = mesh_sta_info_get(sdata, mgmt->sa, elems); +		if (!sta) { +			mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); +			goto unlock_rcu; +		} +		sta->plid = plid; +	} else if (!sta && event == OPN_RJCT) { +		mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, +				    mgmt->sa, 0, plid, +				    WLAN_REASON_MESH_CONFIG); +		goto unlock_rcu; +	} else if (!sta || event == PLINK_UNDEFINED) { +		/* something went wrong */ +		goto unlock_rcu; +	} + +	changed |= mesh_plink_fsm(sdata, sta, event); + +unlock_rcu:  	rcu_read_unlock(); + +	if (changed) +		ieee80211_mbss_info_change_notify(sdata, changed); +} + +void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, +			 struct ieee80211_mgmt *mgmt, size_t len, +			 struct ieee80211_rx_status *rx_status) +{ +	struct ieee802_11_elems elems; +	size_t baselen; +	u8 *baseaddr; + +	/* need action_code, aux */ +	if (len < IEEE80211_MIN_ACTION_SIZE + 3) +		return; + +	if (sdata->u.mesh.user_mpm) +		/* userspace must register for these */ +		return; + +	if (is_multicast_ether_addr(mgmt->da)) { +		mpl_dbg(sdata, +			"Mesh plink: ignore frame from multicast address\n"); +		return; +	} + +	baseaddr = mgmt->u.action.u.self_prot.variable; +	baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt; +	if (mgmt->u.action.u.self_prot.action_code == +						WLAN_SP_MESH_PEERING_CONFIRM) { +		baseaddr += 4; +		baselen += 4; +	} +	ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems); +	mesh_process_plink_frame(sdata, mgmt, &elems);  } diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c new file mode 100644 index 00000000000..ad8b377b4b9 --- /dev/null +++ b/net/mac80211/mesh_ps.c @@ -0,0 +1,605 @@ +/* + * Copyright 2012-2013, Marco Porsch <marco.porsch@s2005.tu-chemnitz.de> + * Copyright 2012-2013, cozybit Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "mesh.h" +#include "wme.h" + + +/* mesh PS management */ + +/** + * mps_qos_null_get - create pre-addressed QoS Null frame for mesh powersave + */ +static struct sk_buff *mps_qos_null_get(struct sta_info *sta) +{ +	struct ieee80211_sub_if_data *sdata = sta->sdata; +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_hdr *nullfunc; /* use 4addr header */ +	struct sk_buff *skb; +	int size = sizeof(*nullfunc); +	__le16 fc; + +	skb = dev_alloc_skb(local->hw.extra_tx_headroom + size + 2); +	if (!skb) +		return NULL; +	skb_reserve(skb, local->hw.extra_tx_headroom); + +	nullfunc = (struct ieee80211_hdr *) skb_put(skb, size); +	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC); +	ieee80211_fill_mesh_addresses(nullfunc, &fc, sta->sta.addr, +				      sdata->vif.addr); +	nullfunc->frame_control = fc; +	nullfunc->duration_id = 0; +	nullfunc->seq_ctrl = 0; +	/* no address resolution for this frame -> set addr 1 immediately */ +	memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN); +	memset(skb_put(skb, 2), 0, 2); /* append QoS control field */ +	ieee80211_mps_set_frame_flags(sdata, sta, nullfunc); + +	return skb; +} + +/** + * mps_qos_null_tx - send a QoS Null to indicate link-specific power mode + */ +static void mps_qos_null_tx(struct sta_info *sta) +{ +	struct sk_buff *skb; + +	skb = mps_qos_null_get(sta); +	if (!skb) +		return; + +	mps_dbg(sta->sdata, "announcing peer-specific power mode to %pM\n", +		sta->sta.addr); + +	/* don't unintentionally start a MPSP */ +	if (!test_sta_flag(sta, WLAN_STA_PS_STA)) { +		u8 *qc = ieee80211_get_qos_ctl((void *) skb->data); + +		qc[0] |= IEEE80211_QOS_CTL_EOSP; +	} + +	ieee80211_tx_skb(sta->sdata, skb); +} + +/** + * ieee80211_mps_local_status_update - track status of local link-specific PMs + * + * @sdata: local mesh subif + * + * sets the non-peer power mode and triggers the driver PS (re-)configuration + * Return BSS_CHANGED_BEACON if a beacon update is necessary. + */ +u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	struct sta_info *sta; +	bool peering = false; +	int light_sleep_cnt = 0; +	int deep_sleep_cnt = 0; +	u32 changed = 0; +	enum nl80211_mesh_power_mode nonpeer_pm; + +	rcu_read_lock(); +	list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { +		if (sdata != sta->sdata) +			continue; + +		switch (sta->plink_state) { +		case NL80211_PLINK_OPN_SNT: +		case NL80211_PLINK_OPN_RCVD: +		case NL80211_PLINK_CNF_RCVD: +			peering = true; +			break; +		case NL80211_PLINK_ESTAB: +			if (sta->local_pm == NL80211_MESH_POWER_LIGHT_SLEEP) +				light_sleep_cnt++; +			else if (sta->local_pm == NL80211_MESH_POWER_DEEP_SLEEP) +				deep_sleep_cnt++; +			break; +		default: +			break; +		} +	} +	rcu_read_unlock(); + +	/* +	 * Set non-peer mode to active during peering/scanning/authentication +	 * (see IEEE802.11-2012 13.14.8.3). The non-peer mesh power mode is +	 * deep sleep if the local STA is in light or deep sleep towards at +	 * least one mesh peer (see 13.14.3.1). Otherwise, set it to the +	 * user-configured default value. +	 */ +	if (peering) { +		mps_dbg(sdata, "setting non-peer PM to active for peering\n"); +		nonpeer_pm = NL80211_MESH_POWER_ACTIVE; +	} else if (light_sleep_cnt || deep_sleep_cnt) { +		mps_dbg(sdata, "setting non-peer PM to deep sleep\n"); +		nonpeer_pm = NL80211_MESH_POWER_DEEP_SLEEP; +	} else { +		mps_dbg(sdata, "setting non-peer PM to user value\n"); +		nonpeer_pm = ifmsh->mshcfg.power_mode; +	} + +	/* need update if sleep counts move between 0 and non-zero */ +	if (ifmsh->nonpeer_pm != nonpeer_pm || +	    !ifmsh->ps_peers_light_sleep != !light_sleep_cnt || +	    !ifmsh->ps_peers_deep_sleep != !deep_sleep_cnt) +		changed = BSS_CHANGED_BEACON; + +	ifmsh->nonpeer_pm = nonpeer_pm; +	ifmsh->ps_peers_light_sleep = light_sleep_cnt; +	ifmsh->ps_peers_deep_sleep = deep_sleep_cnt; + +	return changed; +} + +/** + * ieee80211_mps_set_sta_local_pm - set local PM towards a mesh STA + * + * @sta: mesh STA + * @pm: the power mode to set + * Return BSS_CHANGED_BEACON if a beacon update is in order. + */ +u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta, +				   enum nl80211_mesh_power_mode pm) +{ +	struct ieee80211_sub_if_data *sdata = sta->sdata; + +	if (sta->local_pm == pm) +		return 0; + +	mps_dbg(sdata, "local STA operates in mode %d with %pM\n", +		pm, sta->sta.addr); + +	sta->local_pm = pm; + +	/* +	 * announce peer-specific power mode transition +	 * (see IEEE802.11-2012 13.14.3.2 and 13.14.3.3) +	 */ +	if (sta->plink_state == NL80211_PLINK_ESTAB) +		mps_qos_null_tx(sta); + +	return ieee80211_mps_local_status_update(sdata); +} + +/** + * ieee80211_mps_set_frame_flags - set mesh PS flags in FC (and QoS Control) + * + * @sdata: local mesh subif + * @sta: mesh STA + * @hdr: 802.11 frame header + * + * see IEEE802.11-2012 8.2.4.1.7 and 8.2.4.5.11 + * + * NOTE: sta must be given when an individually-addressed QoS frame header + * is handled, for group-addressed and management frames it is not used + */ +void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata, +				   struct sta_info *sta, +				   struct ieee80211_hdr *hdr) +{ +	enum nl80211_mesh_power_mode pm; +	u8 *qc; + +	if (WARN_ON(is_unicast_ether_addr(hdr->addr1) && +		    ieee80211_is_data_qos(hdr->frame_control) && +		    !sta)) +		return; + +	if (is_unicast_ether_addr(hdr->addr1) && +	    ieee80211_is_data_qos(hdr->frame_control) && +	    sta->plink_state == NL80211_PLINK_ESTAB) +		pm = sta->local_pm; +	else +		pm = sdata->u.mesh.nonpeer_pm; + +	if (pm == NL80211_MESH_POWER_ACTIVE) +		hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_PM); +	else +		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); + +	if (!ieee80211_is_data_qos(hdr->frame_control)) +		return; + +	qc = ieee80211_get_qos_ctl(hdr); + +	if ((is_unicast_ether_addr(hdr->addr1) && +	     pm == NL80211_MESH_POWER_DEEP_SLEEP) || +	    (is_multicast_ether_addr(hdr->addr1) && +	     sdata->u.mesh.ps_peers_deep_sleep > 0)) +		qc[1] |= (IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8); +	else +		qc[1] &= ~(IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8); +} + +/** + * ieee80211_mps_sta_status_update - update buffering status of neighbor STA + * + * @sta: mesh STA + * + * called after change of peering status or non-peer/peer-specific power mode + */ +void ieee80211_mps_sta_status_update(struct sta_info *sta) +{ +	enum nl80211_mesh_power_mode pm; +	bool do_buffer; + +	/* For non-assoc STA, prevent buffering or frame transmission */ +	if (sta->sta_state < IEEE80211_STA_ASSOC) +		return; + +	/* +	 * use peer-specific power mode if peering is established and the +	 * peer's power mode is known +	 */ +	if (sta->plink_state == NL80211_PLINK_ESTAB && +	    sta->peer_pm != NL80211_MESH_POWER_UNKNOWN) +		pm = sta->peer_pm; +	else +		pm = sta->nonpeer_pm; + +	do_buffer = (pm != NL80211_MESH_POWER_ACTIVE); + +	/* clear the MPSP flags for non-peers or active STA */ +	if (sta->plink_state != NL80211_PLINK_ESTAB) { +		clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); +		clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); +	} else if (!do_buffer) { +		clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); +	} + +	/* Don't let the same PS state be set twice */ +	if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer) +		return; + +	if (do_buffer) { +		set_sta_flag(sta, WLAN_STA_PS_STA); +		atomic_inc(&sta->sdata->u.mesh.ps.num_sta_ps); +		mps_dbg(sta->sdata, "start PS buffering frames towards %pM\n", +			sta->sta.addr); +	} else { +		ieee80211_sta_ps_deliver_wakeup(sta); +	} +} + +static void mps_set_sta_peer_pm(struct sta_info *sta, +				struct ieee80211_hdr *hdr) +{ +	enum nl80211_mesh_power_mode pm; +	u8 *qc = ieee80211_get_qos_ctl(hdr); + +	/* +	 * Test Power Management field of frame control (PW) and +	 * mesh power save level subfield of QoS control field (PSL) +	 * +	 * | PM | PSL| Mesh PM | +	 * +----+----+---------+ +	 * | 0  |Rsrv|  Active | +	 * | 1  | 0  |  Light  | +	 * | 1  | 1  |  Deep   | +	 */ +	if (ieee80211_has_pm(hdr->frame_control)) { +		if (qc[1] & (IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8)) +			pm = NL80211_MESH_POWER_DEEP_SLEEP; +		else +			pm = NL80211_MESH_POWER_LIGHT_SLEEP; +	} else { +		pm = NL80211_MESH_POWER_ACTIVE; +	} + +	if (sta->peer_pm == pm) +		return; + +	mps_dbg(sta->sdata, "STA %pM enters mode %d\n", +		sta->sta.addr, pm); + +	sta->peer_pm = pm; + +	ieee80211_mps_sta_status_update(sta); +} + +static void mps_set_sta_nonpeer_pm(struct sta_info *sta, +				   struct ieee80211_hdr *hdr) +{ +	enum nl80211_mesh_power_mode pm; + +	if (ieee80211_has_pm(hdr->frame_control)) +		pm = NL80211_MESH_POWER_DEEP_SLEEP; +	else +		pm = NL80211_MESH_POWER_ACTIVE; + +	if (sta->nonpeer_pm == pm) +		return; + +	mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %d\n", +		sta->sta.addr, pm); + +	sta->nonpeer_pm = pm; + +	ieee80211_mps_sta_status_update(sta); +} + +/** + * ieee80211_mps_rx_h_sta_process - frame receive handler for mesh powersave + * + * @sta: STA info that transmitted the frame + * @hdr: IEEE 802.11 (QoS) Header + */ +void ieee80211_mps_rx_h_sta_process(struct sta_info *sta, +				    struct ieee80211_hdr *hdr) +{ +	if (is_unicast_ether_addr(hdr->addr1) && +	    ieee80211_is_data_qos(hdr->frame_control)) { +		/* +		 * individually addressed QoS Data/Null frames contain +		 * peer link-specific PS mode towards the local STA +		 */ +		mps_set_sta_peer_pm(sta, hdr); + +		/* check for mesh Peer Service Period trigger frames */ +		ieee80211_mpsp_trigger_process(ieee80211_get_qos_ctl(hdr), +					       sta, false, false); +	} else { +		/* +		 * can only determine non-peer PS mode +		 * (see IEEE802.11-2012 8.2.4.1.7) +		 */ +		mps_set_sta_nonpeer_pm(sta, hdr); +	} +} + + +/* mesh PS frame release */ + +static void mpsp_trigger_send(struct sta_info *sta, bool rspi, bool eosp) +{ +	struct ieee80211_sub_if_data *sdata = sta->sdata; +	struct sk_buff *skb; +	struct ieee80211_hdr *nullfunc; +	struct ieee80211_tx_info *info; +	u8 *qc; + +	skb = mps_qos_null_get(sta); +	if (!skb) +		return; + +	nullfunc = (struct ieee80211_hdr *) skb->data; +	if (!eosp) +		nullfunc->frame_control |= +				cpu_to_le16(IEEE80211_FCTL_MOREDATA); +	/* +	 * | RSPI | EOSP |  MPSP triggering   | +	 * +------+------+--------------------+ +	 * |  0   |  0   | local STA is owner | +	 * |  0   |  1   | no MPSP (MPSP end) | +	 * |  1   |  0   | both STA are owner | +	 * |  1   |  1   | peer STA is owner  | see IEEE802.11-2012 13.14.9.2 +	 */ +	qc = ieee80211_get_qos_ctl(nullfunc); +	if (rspi) +		qc[1] |= (IEEE80211_QOS_CTL_RSPI >> 8); +	if (eosp) +		qc[0] |= IEEE80211_QOS_CTL_EOSP; + +	info = IEEE80211_SKB_CB(skb); + +	info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER | +		       IEEE80211_TX_CTL_REQ_TX_STATUS; + +	mps_dbg(sdata, "sending MPSP trigger%s%s to %pM\n", +		rspi ? " RSPI" : "", eosp ? " EOSP" : "", sta->sta.addr); + +	ieee80211_tx_skb(sdata, skb); +} + +/** + * mpsp_qos_null_append - append QoS Null frame to MPSP skb queue if needed + * + * To properly end a mesh MPSP the last transmitted frame has to set the EOSP + * flag in the QoS Control field. In case the current tailing frame is not a + * QoS Data frame, append a QoS Null to carry the flag. + */ +static void mpsp_qos_null_append(struct sta_info *sta, +				 struct sk_buff_head *frames) +{ +	struct ieee80211_sub_if_data *sdata = sta->sdata; +	struct sk_buff *new_skb, *skb = skb_peek_tail(frames); +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; +	struct ieee80211_tx_info *info; + +	if (ieee80211_is_data_qos(hdr->frame_control)) +		return; + +	new_skb = mps_qos_null_get(sta); +	if (!new_skb) +		return; + +	mps_dbg(sdata, "appending QoS Null in MPSP towards %pM\n", +		sta->sta.addr); +	/* +	 * This frame has to be transmitted last. Assign lowest priority to +	 * make sure it cannot pass other frames when releasing multiple ACs. +	 */ +	new_skb->priority = 1; +	skb_set_queue_mapping(new_skb, IEEE80211_AC_BK); +	ieee80211_set_qos_hdr(sdata, new_skb); + +	info = IEEE80211_SKB_CB(new_skb); +	info->control.vif = &sdata->vif; +	info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; + +	__skb_queue_tail(frames, new_skb); +} + +/** + * mps_frame_deliver - transmit frames during mesh powersave + * + * @sta: STA info to transmit to + * @n_frames: number of frames to transmit. -1 for all + */ +static void mps_frame_deliver(struct sta_info *sta, int n_frames) +{ +	struct ieee80211_local *local = sta->sdata->local; +	int ac; +	struct sk_buff_head frames; +	struct sk_buff *skb; +	bool more_data = false; + +	skb_queue_head_init(&frames); + +	/* collect frame(s) from buffers */ +	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { +		while (n_frames != 0) { +			skb = skb_dequeue(&sta->tx_filtered[ac]); +			if (!skb) { +				skb = skb_dequeue( +					&sta->ps_tx_buf[ac]); +				if (skb) +					local->total_ps_buffered--; +			} +			if (!skb) +				break; +			n_frames--; +			__skb_queue_tail(&frames, skb); +		} + +		if (!skb_queue_empty(&sta->tx_filtered[ac]) || +		    !skb_queue_empty(&sta->ps_tx_buf[ac])) +			more_data = true; +	} + +	/* nothing to send? -> EOSP */ +	if (skb_queue_empty(&frames)) { +		mpsp_trigger_send(sta, false, true); +		return; +	} + +	/* in a MPSP make sure the last skb is a QoS Data frame */ +	if (test_sta_flag(sta, WLAN_STA_MPSP_OWNER)) +		mpsp_qos_null_append(sta, &frames); + +	mps_dbg(sta->sdata, "sending %d frames to PS STA %pM\n", +		skb_queue_len(&frames), sta->sta.addr); + +	/* prepare collected frames for transmission */ +	skb_queue_walk(&frames, skb) { +		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +		struct ieee80211_hdr *hdr = (void *) skb->data; + +		/* +		 * Tell TX path to send this frame even though the +		 * STA may still remain is PS mode after this frame +		 * exchange. +		 */ +		info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER; + +		if (more_data || !skb_queue_is_last(&frames, skb)) +			hdr->frame_control |= +				cpu_to_le16(IEEE80211_FCTL_MOREDATA); +		else +			hdr->frame_control &= +				cpu_to_le16(~IEEE80211_FCTL_MOREDATA); + +		if (skb_queue_is_last(&frames, skb) && +		    ieee80211_is_data_qos(hdr->frame_control)) { +			u8 *qoshdr = ieee80211_get_qos_ctl(hdr); + +			/* MPSP trigger frame ends service period */ +			*qoshdr |= IEEE80211_QOS_CTL_EOSP; +			info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; +		} +	} + +	ieee80211_add_pending_skbs(local, &frames); +	sta_info_recalc_tim(sta); +} + +/** + * ieee80211_mpsp_trigger_process - track status of mesh Peer Service Periods + * + * @qc: QoS Control field + * @sta: peer to start a MPSP with + * @tx: frame was transmitted by the local STA + * @acked: frame has been transmitted successfully + * + * NOTE: active mode STA may only serve as MPSP owner + */ +void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta, +				    bool tx, bool acked) +{ +	u8 rspi = qc[1] & (IEEE80211_QOS_CTL_RSPI >> 8); +	u8 eosp = qc[0] & IEEE80211_QOS_CTL_EOSP; + +	if (tx) { +		if (rspi && acked) +			set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); + +		if (eosp) +			clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); +		else if (acked && +			 test_sta_flag(sta, WLAN_STA_PS_STA) && +			 !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER)) +			mps_frame_deliver(sta, -1); +	} else { +		if (eosp) +			clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); +		else if (sta->local_pm != NL80211_MESH_POWER_ACTIVE) +			set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); + +		if (rspi && !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER)) +			mps_frame_deliver(sta, -1); +	} +} + +/** + * ieee80211_mps_frame_release - release frames buffered due to mesh power save + * + * @sta: mesh STA + * @elems: IEs of beacon or probe response + * + * For peers if we have individually-addressed frames buffered or the peer + * indicates buffered frames, send a corresponding MPSP trigger frame. Since + * we do not evaluate the awake window duration, QoS Nulls are used as MPSP + * trigger frames. If the neighbour STA is not a peer, only send single frames. + */ +void ieee80211_mps_frame_release(struct sta_info *sta, +				 struct ieee802_11_elems *elems) +{ +	int ac, buffer_local = 0; +	bool has_buffered = false; + +	if (sta->plink_state == NL80211_PLINK_ESTAB) +		has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len, +						   sta->llid); + +	if (has_buffered) +		mps_dbg(sta->sdata, "%pM indicates buffered frames\n", +			sta->sta.addr); + +	/* only transmit to PS STA with announced, non-zero awake window */ +	if (test_sta_flag(sta, WLAN_STA_PS_STA) && +	    (!elems->awake_window || !le16_to_cpu(*elems->awake_window))) +		return; + +	if (!test_sta_flag(sta, WLAN_STA_MPSP_OWNER)) +		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) +			buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) + +					skb_queue_len(&sta->tx_filtered[ac]); + +	if (!has_buffered && !buffer_local) +		return; + +	if (sta->plink_state == NL80211_PLINK_ESTAB) +		mpsp_trigger_send(sta, has_buffered, !buffer_local); +	else +		mps_frame_deliver(sta, 1); +} diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c new file mode 100644 index 00000000000..09625d6205c --- /dev/null +++ b/net/mac80211/mesh_sync.c @@ -0,0 +1,225 @@ +/* + * Copyright 2011-2012, Pavel Zubarev <pavel.zubarev@gmail.com> + * Copyright 2011-2012, Marco Porsch <marco.porsch@s2005.tu-chemnitz.de> + * Copyright 2011-2012, cozybit Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "ieee80211_i.h" +#include "mesh.h" +#include "driver-ops.h" + +/* This is not in the standard.  It represents a tolerable tbtt drift below + * which we do no TSF adjustment. + */ +#define TOFFSET_MINIMUM_ADJUSTMENT 10 + +/* This is not in the standard. It is a margin added to the + * Toffset setpoint to mitigate TSF overcorrection + * introduced by TSF adjustment latency. + */ +#define TOFFSET_SET_MARGIN 20 + +/* This is not in the standard.  It represents the maximum Toffset jump above + * which we'll invalidate the Toffset setpoint and choose a new setpoint.  This + * could be, for instance, in case a neighbor is restarted and its TSF counter + * reset. + */ +#define TOFFSET_MAXIMUM_ADJUSTMENT 30000		/* 30 ms */ + +struct sync_method { +	u8 method; +	struct ieee80211_mesh_sync_ops ops; +}; + +/** + * mesh_peer_tbtt_adjusting - check if an mp is currently adjusting its TBTT + * + * @ie: information elements of a management frame from the mesh peer + */ +static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie) +{ +	return (ie->mesh_config->meshconf_cap & +			IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING) != 0; +} + +void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	/* sdata->vif.bss_conf.beacon_int in 1024us units, 0.04% */ +	u64 beacon_int_fraction = sdata->vif.bss_conf.beacon_int * 1024 / 2500; +	u64 tsf; +	u64 tsfdelta; + +	spin_lock_bh(&ifmsh->sync_offset_lock); +	if (ifmsh->sync_offset_clockdrift_max < beacon_int_fraction) { +		msync_dbg(sdata, "TBTT : max clockdrift=%lld; adjusting\n", +			  (long long) ifmsh->sync_offset_clockdrift_max); +		tsfdelta = -ifmsh->sync_offset_clockdrift_max; +		ifmsh->sync_offset_clockdrift_max = 0; +	} else { +		msync_dbg(sdata, "TBTT : max clockdrift=%lld; adjusting by %llu\n", +			  (long long) ifmsh->sync_offset_clockdrift_max, +			  (unsigned long long) beacon_int_fraction); +		tsfdelta = -beacon_int_fraction; +		ifmsh->sync_offset_clockdrift_max -= beacon_int_fraction; +	} +	spin_unlock_bh(&ifmsh->sync_offset_lock); + +	tsf = drv_get_tsf(local, sdata); +	if (tsf != -1ULL) +		drv_set_tsf(local, sdata, tsf + tsfdelta); +} + +static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, +				   u16 stype, +				   struct ieee80211_mgmt *mgmt, +				   struct ieee802_11_elems *elems, +				   struct ieee80211_rx_status *rx_status) +{ +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	struct ieee80211_local *local = sdata->local; +	struct sta_info *sta; +	u64 t_t, t_r; + +	WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); + +	/* standard mentions only beacons */ +	if (stype != IEEE80211_STYPE_BEACON) +		return; + +	/* +	 * Get time when timestamp field was received.  If we don't +	 * have rx timestamps, then use current tsf as an approximation. +	 * drv_get_tsf() must be called before entering the rcu-read +	 * section. +	 */ +	if (ieee80211_have_rx_timestamp(rx_status)) +		t_r = ieee80211_calculate_rx_timestamp(local, rx_status, +						       24 + 12 + +						       elems->total_len + +						       FCS_LEN, +						       24); +	else +		t_r = drv_get_tsf(local, sdata); + +	rcu_read_lock(); +	sta = sta_info_get(sdata, mgmt->sa); +	if (!sta) +		goto no_sync; + +	/* check offset sync conditions (13.13.2.2.1) +	 * +	 * TODO also sync to +	 * dot11MeshNbrOffsetMaxNeighbor non-peer non-MBSS neighbors +	 */ + +	if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) { +		clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); +		msync_dbg(sdata, "STA %pM : is adjusting TBTT\n", +			  sta->sta.addr); +		goto no_sync; +	} + +	/* Timing offset calculation (see 13.13.2.2.2) */ +	t_t = le64_to_cpu(mgmt->u.beacon.timestamp); +	sta->t_offset = t_t - t_r; + +	if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { +		s64 t_clockdrift = sta->t_offset_setpoint - sta->t_offset; +		msync_dbg(sdata, +			  "STA %pM : sta->t_offset=%lld, sta->t_offset_setpoint=%lld, t_clockdrift=%lld\n", +			  sta->sta.addr, (long long) sta->t_offset, +			  (long long) sta->t_offset_setpoint, +			  (long long) t_clockdrift); + +		if (t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT || +		    t_clockdrift < -TOFFSET_MAXIMUM_ADJUSTMENT) { +			msync_dbg(sdata, +				  "STA %pM : t_clockdrift=%lld too large, setpoint reset\n", +				  sta->sta.addr, +				  (long long) t_clockdrift); +			clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); +			goto no_sync; +		} + +		spin_lock_bh(&ifmsh->sync_offset_lock); +		if (t_clockdrift > ifmsh->sync_offset_clockdrift_max) +			ifmsh->sync_offset_clockdrift_max = t_clockdrift; +		spin_unlock_bh(&ifmsh->sync_offset_lock); +	} else { +		sta->t_offset_setpoint = sta->t_offset - TOFFSET_SET_MARGIN; +		set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); +		msync_dbg(sdata, +			  "STA %pM : offset was invalid, sta->t_offset=%lld\n", +			  sta->sta.addr, +			  (long long) sta->t_offset); +	} + +no_sync: +	rcu_read_unlock(); +} + +static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata, +					 struct beacon_data *beacon) +{ +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	u8 cap; + +	WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); +	WARN_ON(!rcu_read_lock_held()); +	cap = beacon->meshconf->meshconf_cap; + +	spin_lock_bh(&ifmsh->sync_offset_lock); + +	if (ifmsh->sync_offset_clockdrift_max > TOFFSET_MINIMUM_ADJUSTMENT) { +		/* Since ajusting the tsf here would +		 * require a possibly blocking call +		 * to the driver tsf setter, we punt +		 * the tsf adjustment to the mesh tasklet +		 */ +		msync_dbg(sdata, +			  "TBTT : kicking off TBTT adjustment with clockdrift_max=%lld\n", +			  ifmsh->sync_offset_clockdrift_max); +		set_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags); + +		ifmsh->adjusting_tbtt = true; +	} else { +		msync_dbg(sdata, +			  "TBTT : max clockdrift=%lld; too small to adjust\n", +			  (long long)ifmsh->sync_offset_clockdrift_max); +		ifmsh->sync_offset_clockdrift_max = 0; + +		ifmsh->adjusting_tbtt = false; +	} +	spin_unlock_bh(&ifmsh->sync_offset_lock); + +	beacon->meshconf->meshconf_cap = ifmsh->adjusting_tbtt ? +			IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING | cap : +			~IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING & cap; +} + +static const struct sync_method sync_methods[] = { +	{ +		.method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, +		.ops = { +			.rx_bcn_presp = &mesh_sync_offset_rx_bcn_presp, +			.adjust_tbtt = &mesh_sync_offset_adjust_tbtt, +		} +	}, +}; + +const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method) +{ +	int i; + +	for (i = 0 ; i < ARRAY_SIZE(sync_methods); ++i) { +		if (sync_methods[i].method == method) +			return &sync_methods[i].ops; +	} +	return NULL; +} diff --git a/net/mac80211/michael.h b/net/mac80211/michael.h index 3b848dad958..0e4886f881f 100644 --- a/net/mac80211/michael.h +++ b/net/mac80211/michael.h @@ -11,6 +11,7 @@  #define MICHAEL_H  #include <linux/types.h> +#include <linux/ieee80211.h>  #define MICHAEL_MIC_LEN 8 diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 79480791494..3345401be1b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -16,10 +16,12 @@  #include <linux/skbuff.h>  #include <linux/if_arp.h>  #include <linux/etherdevice.h> +#include <linux/moduleparam.h>  #include <linux/rtnetlink.h> -#include <linux/pm_qos_params.h> +#include <linux/pm_qos.h>  #include <linux/crc32.h>  #include <linux/slab.h> +#include <linux/export.h>  #include <net/mac80211.h>  #include <asm/unaligned.h> @@ -28,8 +30,25 @@  #include "rate.h"  #include "led.h" -#define IEEE80211_MAX_NULLFUNC_TRIES 2 -#define IEEE80211_MAX_PROBE_TRIES 5 +#define IEEE80211_AUTH_TIMEOUT		(HZ / 5) +#define IEEE80211_AUTH_TIMEOUT_LONG	(HZ / 2) +#define IEEE80211_AUTH_TIMEOUT_SHORT	(HZ / 10) +#define IEEE80211_AUTH_MAX_TRIES	3 +#define IEEE80211_AUTH_WAIT_ASSOC	(HZ * 5) +#define IEEE80211_ASSOC_TIMEOUT		(HZ / 5) +#define IEEE80211_ASSOC_TIMEOUT_LONG	(HZ / 2) +#define IEEE80211_ASSOC_TIMEOUT_SHORT	(HZ / 10) +#define IEEE80211_ASSOC_MAX_TRIES	3 + +static int max_nullfunc_tries = 2; +module_param(max_nullfunc_tries, int, 0644); +MODULE_PARM_DESC(max_nullfunc_tries, +		 "Maximum nullfunc tx tries before disconnecting (reason 4)."); + +static int max_probe_tries = 5; +module_param(max_probe_tries, int, 0644); +MODULE_PARM_DESC(max_probe_tries, +		 "Maximum probe tries before disconnecting (reason 4).");  /*   * Beacon loss timeout is calculated as N frames times the @@ -39,7 +58,10 @@   * probe on beacon miss before declaring the connection lost   * default to what we want.   */ -#define IEEE80211_BEACON_LOSS_COUNT	7 +static int beacon_loss_count = 7; +module_param(beacon_loss_count, int, 0644); +MODULE_PARM_DESC(beacon_loss_count, +		 "Number of beacon intervals before we decide beacon was lost.");  /*   * Time the connection can be idle before we probe @@ -51,7 +73,11 @@   * a probe request because of beacon loss or for   * checking the connection still works.   */ -#define IEEE80211_PROBE_WAIT		(HZ / 2) +static int probe_wait_ms = 500; +module_param(probe_wait_ms, int, 0644); +MODULE_PARM_DESC(probe_wait_ms, +		 "Maximum time(ms) to wait for probe response" +		 " before disconnecting (reason 4).");  /*   * Weight given to the latest Beacon frame when calculating average signal @@ -66,41 +92,6 @@   */  #define IEEE80211_SIGNAL_AVE_MIN_COUNT	4 -#define TMR_RUNNING_TIMER	0 -#define TMR_RUNNING_CHANSW	1 - -/* - * All cfg80211 functions have to be called outside a locked - * section so that they can acquire a lock themselves... This - * is much simpler than queuing up things in cfg80211, but we - * do need some indirection for that here. - */ -enum rx_mgmt_action { -	/* no action required */ -	RX_MGMT_NONE, - -	/* caller must call cfg80211_send_rx_auth() */ -	RX_MGMT_CFG80211_AUTH, - -	/* caller must call cfg80211_send_rx_assoc() */ -	RX_MGMT_CFG80211_ASSOC, - -	/* caller must call cfg80211_send_deauth() */ -	RX_MGMT_CFG80211_DEAUTH, - -	/* caller must call cfg80211_send_disassoc() */ -	RX_MGMT_CFG80211_DISASSOC, - -	/* caller must tell cfg80211 about internal error */ -	RX_MGMT_CFG80211_ASSOC_ERROR, -}; - -/* utils */ -static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd) -{ -	lockdep_assert_held(&ifmgd->mtx); -} -  /*   * We can have multiple work items (and connection probing)   * scheduling this timer, but we need to take care to only @@ -111,19 +102,22 @@ static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd)   * has happened -- the work that runs from this timer will   * do that.   */ -static void run_again(struct ieee80211_if_managed *ifmgd, -			     unsigned long timeout) +static void run_again(struct ieee80211_sub_if_data *sdata, +		      unsigned long timeout)  { -	ASSERT_MGD_MTX(ifmgd); +	sdata_assert_lock(sdata); -	if (!timer_pending(&ifmgd->timer) || -	    time_before(timeout, ifmgd->timer.expires)) -		mod_timer(&ifmgd->timer, timeout); +	if (!timer_pending(&sdata->u.mgd.timer) || +	    time_before(timeout, sdata->u.mgd.timer.expires)) +		mod_timer(&sdata->u.mgd.timer, timeout);  }  void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata)  { -	if (sdata->local->hw.flags & IEEE80211_HW_BEACON_FILTER) +	if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER) +		return; + +	if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)  		return;  	mod_timer(&sdata->u.mgd.bcn_mon_timer, @@ -134,13 +128,16 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	if (unlikely(!sdata->u.mgd.associated)) +		return; + +	ifmgd->probe_send_count = 0; +  	if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)  		return;  	mod_timer(&sdata->u.mgd.conn_mon_timer,  		  round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); - -	ifmgd->probe_send_count = 0;  }  static int ecw2cw(int ecw) @@ -148,140 +145,717 @@ static int ecw2cw(int ecw)  	return (1 << ecw) - 1;  } -/* - * ieee80211_enable_ht should be called only after the operating band - * has been determined as ht configuration depends on the hw's - * HT abilities for a specific band. - */ -static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, -			       struct ieee80211_ht_info *hti, -			       const u8 *bssid, u16 ap_ht_cap_flags) +static u32 +ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, +			     struct ieee80211_supported_band *sband, +			     struct ieee80211_channel *channel, +			     const struct ieee80211_ht_operation *ht_oper, +			     const struct ieee80211_vht_operation *vht_oper, +			     struct cfg80211_chan_def *chandef, bool tracking)  { -	struct ieee80211_local *local = sdata->local; -	struct ieee80211_supported_band *sband; -	struct sta_info *sta; -	u32 changed = 0; -	u16 ht_opmode; -	bool enable_ht = true; -	enum nl80211_channel_type prev_chantype; -	enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	struct cfg80211_chan_def vht_chandef; +	u32 ht_cfreq, ret; -	sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; +	chandef->chan = channel; +	chandef->width = NL80211_CHAN_WIDTH_20_NOHT; +	chandef->center_freq1 = channel->center_freq; +	chandef->center_freq2 = 0; -	prev_chantype = sdata->vif.bss_conf.channel_type; +	if (!ht_oper || !sband->ht_cap.ht_supported) { +		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; +		goto out; +	} -	/* HT is not supported */ -	if (!sband->ht_cap.ht_supported) -		enable_ht = false; +	chandef->width = NL80211_CHAN_WIDTH_20; +	ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, +						  channel->band);  	/* check that channel matches the right operating channel */ -	if (local->hw.conf.channel->center_freq != -	    ieee80211_channel_to_frequency(hti->control_chan)) -		enable_ht = false; - -	if (enable_ht) { -		channel_type = NL80211_CHAN_HT20; - -		if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) && -		    (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) && -		    (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) { -			switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { -			case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: -				if (!(local->hw.conf.channel->flags & -				    IEEE80211_CHAN_NO_HT40PLUS)) -					channel_type = NL80211_CHAN_HT40PLUS; -				break; -			case IEEE80211_HT_PARAM_CHA_SEC_BELOW: -				if (!(local->hw.conf.channel->flags & -				    IEEE80211_CHAN_NO_HT40MINUS)) -					channel_type = NL80211_CHAN_HT40MINUS; -				break; -			} +	if (!tracking && channel->center_freq != ht_cfreq) { +		/* +		 * It's possible that some APs are confused here; +		 * Netgear WNDR3700 sometimes reports 4 higher than +		 * the actual channel in association responses, but +		 * since we look at probe response/beacon data here +		 * it should be OK. +		 */ +		sdata_info(sdata, +			   "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", +			   channel->center_freq, ht_cfreq, +			   ht_oper->primary_chan, channel->band); +		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; +		goto out; +	} + +	/* check 40 MHz support, if we have it */ +	if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { +		switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { +		case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: +			chandef->width = NL80211_CHAN_WIDTH_40; +			chandef->center_freq1 += 10; +			break; +		case IEEE80211_HT_PARAM_CHA_SEC_BELOW: +			chandef->width = NL80211_CHAN_WIDTH_40; +			chandef->center_freq1 -= 10; +			break;  		} +	} else { +		/* 40 MHz (and 80 MHz) must be supported for VHT */ +		ret = IEEE80211_STA_DISABLE_VHT; +		/* also mark 40 MHz disabled */ +		ret |= IEEE80211_STA_DISABLE_40MHZ; +		goto out;  	} -	if (local->tmp_channel) -		local->tmp_channel_type = channel_type; +	if (!vht_oper || !sband->vht_cap.vht_supported) { +		ret = IEEE80211_STA_DISABLE_VHT; +		goto out; +	} + +	vht_chandef.chan = channel; +	vht_chandef.center_freq1 = +		ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx, +					       channel->band); +	vht_chandef.center_freq2 = 0; -	if (!ieee80211_set_channel_type(local, sdata, channel_type)) { -		/* can only fail due to HT40+/- mismatch */ -		channel_type = NL80211_CHAN_HT20; -		WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type)); +	switch (vht_oper->chan_width) { +	case IEEE80211_VHT_CHANWIDTH_USE_HT: +		vht_chandef.width = chandef->width; +		vht_chandef.center_freq1 = chandef->center_freq1; +		break; +	case IEEE80211_VHT_CHANWIDTH_80MHZ: +		vht_chandef.width = NL80211_CHAN_WIDTH_80; +		break; +	case IEEE80211_VHT_CHANWIDTH_160MHZ: +		vht_chandef.width = NL80211_CHAN_WIDTH_160; +		break; +	case IEEE80211_VHT_CHANWIDTH_80P80MHZ: +		vht_chandef.width = NL80211_CHAN_WIDTH_80P80; +		vht_chandef.center_freq2 = +			ieee80211_channel_to_frequency( +				vht_oper->center_freq_seg2_idx, +				channel->band); +		break; +	default: +		if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) +			sdata_info(sdata, +				   "AP VHT operation IE has invalid channel width (%d), disable VHT\n", +				   vht_oper->chan_width); +		ret = IEEE80211_STA_DISABLE_VHT; +		goto out;  	} -	/* channel_type change automatically detected */ -	ieee80211_hw_config(local, 0); +	if (!cfg80211_chandef_valid(&vht_chandef)) { +		if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) +			sdata_info(sdata, +				   "AP VHT information is invalid, disable VHT\n"); +		ret = IEEE80211_STA_DISABLE_VHT; +		goto out; +	} -	if (prev_chantype != channel_type) { -		rcu_read_lock(); -		sta = sta_info_get(sdata, bssid); -		if (sta) -			rate_control_rate_update(local, sband, sta, -						 IEEE80211_RC_HT_CHANGED, -						 channel_type); -		rcu_read_unlock(); +	if (cfg80211_chandef_identical(chandef, &vht_chandef)) { +		ret = 0; +		goto out; +	} + +	if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) { +		if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) +			sdata_info(sdata, +				   "AP VHT information doesn't match HT, disable VHT\n"); +		ret = IEEE80211_STA_DISABLE_VHT; +		goto out; +	} + +	*chandef = vht_chandef; + +	ret = 0; + +out: +	/* +	 * When tracking the current AP, don't do any further checks if the +	 * new chandef is identical to the one we're currently using for the +	 * connection. This keeps us from playing ping-pong with regulatory, +	 * without it the following can happen (for example): +	 *  - connect to an AP with 80 MHz, world regdom allows 80 MHz +	 *  - AP advertises regdom US +	 *  - CRDA loads regdom US with 80 MHz prohibited (old database) +	 *  - the code below detects an unsupported channel, downgrades, and +	 *    we disconnect from the AP in the caller +	 *  - disconnect causes CRDA to reload world regdomain and the game +	 *    starts anew. +	 * (see https://bugzilla.kernel.org/show_bug.cgi?id=70881) +	 * +	 * It seems possible that there are still scenarios with CSA or real +	 * bandwidth changes where a this could happen, but those cases are +	 * less common and wouldn't completely prevent using the AP. +	 */ +	if (tracking && +	    cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) +		return ret; + +	/* don't print the message below for VHT mismatch if VHT is disabled */ +	if (ret & IEEE80211_STA_DISABLE_VHT) +		vht_chandef = *chandef; + +	/* +	 * Ignore the DISABLED flag when we're already connected and only +	 * tracking the APs beacon for bandwidth changes - otherwise we +	 * might get disconnected here if we connect to an AP, update our +	 * regulatory information based on the AP's country IE and the +	 * information we have is wrong/outdated and disables the channel +	 * that we're actually using for the connection to the AP. +	 */ +	while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, +					tracking ? 0 : +						   IEEE80211_CHAN_DISABLED)) { +		if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) { +			ret = IEEE80211_STA_DISABLE_HT | +			      IEEE80211_STA_DISABLE_VHT; +			break; +		} + +		ret |= ieee80211_chandef_downgrade(chandef);  	} -	ht_opmode = le16_to_cpu(hti->operation_mode); +	if (chandef->width != vht_chandef.width && !tracking) +		sdata_info(sdata, +			   "capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n"); -	/* if bss configuration changed store the new one */ -	if (sdata->ht_opmode_valid != enable_ht || -	    sdata->vif.bss_conf.ht_operation_mode != ht_opmode || -	    prev_chantype != channel_type) { -		changed |= BSS_CHANGED_HT; +	WARN_ON_ONCE(!cfg80211_chandef_valid(chandef)); +	return ret; +} + +static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, +			       struct sta_info *sta, +			       const struct ieee80211_ht_operation *ht_oper, +			       const struct ieee80211_vht_operation *vht_oper, +			       const u8 *bssid, u32 *changed) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	struct ieee80211_supported_band *sband; +	struct ieee80211_channel *chan; +	struct cfg80211_chan_def chandef; +	u16 ht_opmode; +	u32 flags; +	enum ieee80211_sta_rx_bandwidth new_sta_bw; +	int ret; + +	/* if HT was/is disabled, don't track any bandwidth changes */ +	if (ifmgd->flags & IEEE80211_STA_DISABLE_HT || !ht_oper) +		return 0; + +	/* don't check VHT if we associated as non-VHT station */ +	if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) +		vht_oper = NULL; + +	if (WARN_ON_ONCE(!sta)) +		return -EINVAL; + +	/* +	 * if bss configuration changed store the new one - +	 * this may be applicable even if channel is identical +	 */ +	ht_opmode = le16_to_cpu(ht_oper->operation_mode); +	if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) { +		*changed |= BSS_CHANGED_HT;  		sdata->vif.bss_conf.ht_operation_mode = ht_opmode; -		sdata->ht_opmode_valid = enable_ht;  	} -	return changed; +	chan = sdata->vif.bss_conf.chandef.chan; +	sband = local->hw.wiphy->bands[chan->band]; + +	/* calculate new channel (type) based on HT/VHT operation IEs */ +	flags = ieee80211_determine_chantype(sdata, sband, chan, ht_oper, +					     vht_oper, &chandef, true); + +	/* +	 * Downgrade the new channel if we associated with restricted +	 * capabilities. For example, if we associated as a 20 MHz STA +	 * to a 40 MHz AP (due to regulatory, capabilities or config +	 * reasons) then switching to a 40 MHz channel now won't do us +	 * any good -- we couldn't use it with the AP. +	 */ +	if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ && +	    chandef.width == NL80211_CHAN_WIDTH_80P80) +		flags |= ieee80211_chandef_downgrade(&chandef); +	if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ && +	    chandef.width == NL80211_CHAN_WIDTH_160) +		flags |= ieee80211_chandef_downgrade(&chandef); +	if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ && +	    chandef.width > NL80211_CHAN_WIDTH_20) +		flags |= ieee80211_chandef_downgrade(&chandef); + +	if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef)) +		return 0; + +	sdata_info(sdata, +		   "AP %pM changed bandwidth, new config is %d MHz, width %d (%d/%d MHz)\n", +		   ifmgd->bssid, chandef.chan->center_freq, chandef.width, +		   chandef.center_freq1, chandef.center_freq2); + +	if (flags != (ifmgd->flags & (IEEE80211_STA_DISABLE_HT | +				      IEEE80211_STA_DISABLE_VHT | +				      IEEE80211_STA_DISABLE_40MHZ | +				      IEEE80211_STA_DISABLE_80P80MHZ | +				      IEEE80211_STA_DISABLE_160MHZ)) || +	    !cfg80211_chandef_valid(&chandef)) { +		sdata_info(sdata, +			   "AP %pM changed bandwidth in a way we can't support - disconnect\n", +			   ifmgd->bssid); +		return -EINVAL; +	} + +	switch (chandef.width) { +	case NL80211_CHAN_WIDTH_20_NOHT: +	case NL80211_CHAN_WIDTH_20: +		new_sta_bw = IEEE80211_STA_RX_BW_20; +		break; +	case NL80211_CHAN_WIDTH_40: +		new_sta_bw = IEEE80211_STA_RX_BW_40; +		break; +	case NL80211_CHAN_WIDTH_80: +		new_sta_bw = IEEE80211_STA_RX_BW_80; +		break; +	case NL80211_CHAN_WIDTH_80P80: +	case NL80211_CHAN_WIDTH_160: +		new_sta_bw = IEEE80211_STA_RX_BW_160; +		break; +	default: +		return -EINVAL; +	} + +	if (new_sta_bw > sta->cur_max_bandwidth) +		new_sta_bw = sta->cur_max_bandwidth; + +	if (new_sta_bw < sta->sta.bandwidth) { +		sta->sta.bandwidth = new_sta_bw; +		rate_control_rate_update(local, sband, sta, +					 IEEE80211_RC_BW_CHANGED); +	} + +	ret = ieee80211_vif_change_bandwidth(sdata, &chandef, changed); +	if (ret) { +		sdata_info(sdata, +			   "AP %pM changed bandwidth to incompatible one - disconnect\n", +			   ifmgd->bssid); +		return ret; +	} + +	if (new_sta_bw > sta->sta.bandwidth) { +		sta->sta.bandwidth = new_sta_bw; +		rate_control_rate_update(local, sband, sta, +					 IEEE80211_RC_BW_CHANGED); +	} + +	return 0;  }  /* frame sending functions */ -static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, -					   const u8 *bssid, u16 stype, u16 reason, -					   void *cookie, bool send_frame) +static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, +				struct sk_buff *skb, u8 ap_ht_param, +				struct ieee80211_supported_band *sband, +				struct ieee80211_channel *channel, +				enum ieee80211_smps_mode smps) +{ +	u8 *pos; +	u32 flags = channel->flags; +	u16 cap; +	struct ieee80211_sta_ht_cap ht_cap; + +	BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap)); + +	memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); +	ieee80211_apply_htcap_overrides(sdata, &ht_cap); + +	/* determine capability flags */ +	cap = ht_cap.cap; + +	switch (ap_ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { +	case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: +		if (flags & IEEE80211_CHAN_NO_HT40PLUS) { +			cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; +			cap &= ~IEEE80211_HT_CAP_SGI_40; +		} +		break; +	case IEEE80211_HT_PARAM_CHA_SEC_BELOW: +		if (flags & IEEE80211_CHAN_NO_HT40MINUS) { +			cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; +			cap &= ~IEEE80211_HT_CAP_SGI_40; +		} +		break; +	} + +	/* +	 * If 40 MHz was disabled associate as though we weren't +	 * capable of 40 MHz -- some broken APs will never fall +	 * back to trying to transmit in 20 MHz. +	 */ +	if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_40MHZ) { +		cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; +		cap &= ~IEEE80211_HT_CAP_SGI_40; +	} + +	/* set SM PS mode properly */ +	cap &= ~IEEE80211_HT_CAP_SM_PS; +	switch (smps) { +	case IEEE80211_SMPS_AUTOMATIC: +	case IEEE80211_SMPS_NUM_MODES: +		WARN_ON(1); +	case IEEE80211_SMPS_OFF: +		cap |= WLAN_HT_CAP_SM_PS_DISABLED << +			IEEE80211_HT_CAP_SM_PS_SHIFT; +		break; +	case IEEE80211_SMPS_STATIC: +		cap |= WLAN_HT_CAP_SM_PS_STATIC << +			IEEE80211_HT_CAP_SM_PS_SHIFT; +		break; +	case IEEE80211_SMPS_DYNAMIC: +		cap |= WLAN_HT_CAP_SM_PS_DYNAMIC << +			IEEE80211_HT_CAP_SM_PS_SHIFT; +		break; +	} + +	/* reserve and fill IE */ +	pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); +	ieee80211_ie_build_ht_cap(pos, &ht_cap, cap); +} + +static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, +				 struct sk_buff *skb, +				 struct ieee80211_supported_band *sband, +				 struct ieee80211_vht_cap *ap_vht_cap) +{ +	u8 *pos; +	u32 cap; +	struct ieee80211_sta_vht_cap vht_cap; +	u32 mask, ap_bf_sts, our_bf_sts; + +	BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap)); + +	memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap)); +	ieee80211_apply_vhtcap_overrides(sdata, &vht_cap); + +	/* determine capability flags */ +	cap = vht_cap.cap; + +	if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) { +		cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; +		cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; +	} + +	if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) { +		cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160; +		cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; +	} + +	/* +	 * Some APs apparently get confused if our capabilities are better +	 * than theirs, so restrict what we advertise in the assoc request. +	 */ +	if (!(ap_vht_cap->vht_cap_info & +			cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE))) +		cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; + +	mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; + +	ap_bf_sts = le32_to_cpu(ap_vht_cap->vht_cap_info) & mask; +	our_bf_sts = cap & mask; + +	if (ap_bf_sts < our_bf_sts) { +		cap &= ~mask; +		cap |= ap_bf_sts; +	} + +	/* reserve and fill IE */ +	pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); +	ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); +} + +static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;  	struct sk_buff *skb;  	struct ieee80211_mgmt *mgmt; +	u8 *pos, qos_info; +	size_t offset = 0, noffset; +	int i, count, rates_len, supp_rates_len, shift; +	u16 capab; +	struct ieee80211_supported_band *sband; +	struct ieee80211_chanctx_conf *chanctx_conf; +	struct ieee80211_channel *chan; +	u32 rate_flags, rates = 0; -	skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt)); -	if (!skb) { -		printk(KERN_DEBUG "%s: failed to allocate buffer for " -		       "deauth/disassoc frame\n", sdata->name); +	sdata_assert_lock(sdata); + +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	if (WARN_ON(!chanctx_conf)) { +		rcu_read_unlock();  		return;  	} +	chan = chanctx_conf->def.chan; +	rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def); +	rcu_read_unlock(); +	sband = local->hw.wiphy->bands[chan->band]; +	shift = ieee80211_vif_get_shift(&sdata->vif); + +	if (assoc_data->supp_rates_len) { +		/* +		 * Get all rates supported by the device and the AP as +		 * some APs don't like getting a superset of their rates +		 * in the association request (e.g. D-Link DAP 1353 in +		 * b-only mode)... +		 */ +		rates_len = ieee80211_parse_bitrates(&chanctx_conf->def, sband, +						     assoc_data->supp_rates, +						     assoc_data->supp_rates_len, +						     &rates); +	} else { +		/* +		 * In case AP not provide any supported rates information +		 * before association, we send information element(s) with +		 * all rates that we support. +		 */ +		rates_len = 0; +		for (i = 0; i < sband->n_bitrates; i++) { +			if ((rate_flags & sband->bitrates[i].flags) +			    != rate_flags) +				continue; +			rates |= BIT(i); +			rates_len++; +		} +	} + +	skb = alloc_skb(local->hw.extra_tx_headroom + +			sizeof(*mgmt) + /* bit too much but doesn't matter */ +			2 + assoc_data->ssid_len + /* SSID */ +			4 + rates_len + /* (extended) rates */ +			4 + /* power capability */ +			2 + 2 * sband->n_channels + /* supported channels */ +			2 + sizeof(struct ieee80211_ht_cap) + /* HT */ +			2 + sizeof(struct ieee80211_vht_cap) + /* VHT */ +			assoc_data->ie_len + /* extra IEs */ +			9, /* WMM */ +			GFP_KERNEL); +	if (!skb) +		return; +  	skb_reserve(skb, local->hw.extra_tx_headroom); +	capab = WLAN_CAPABILITY_ESS; + +	if (sband->band == IEEE80211_BAND_2GHZ) { +		if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) +			capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; +		if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) +			capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; +	} + +	if (assoc_data->capability & WLAN_CAPABILITY_PRIVACY) +		capab |= WLAN_CAPABILITY_PRIVACY; + +	if ((assoc_data->capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && +	    (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) +		capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; +  	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);  	memset(mgmt, 0, 24); -	memcpy(mgmt->da, bssid, ETH_ALEN); +	memcpy(mgmt->da, assoc_data->bss->bssid, ETH_ALEN);  	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); -	memcpy(mgmt->bssid, bssid, ETH_ALEN); -	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); -	skb_put(skb, 2); -	/* u.deauth.reason_code == u.disassoc.reason_code */ -	mgmt->u.deauth.reason_code = cpu_to_le16(reason); - -	if (stype == IEEE80211_STYPE_DEAUTH) -		if (cookie) -			__cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); -		else -			cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); -	else -		if (cookie) -			__cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); -		else -			cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); -	if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED)) -		IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; +	memcpy(mgmt->bssid, assoc_data->bss->bssid, ETH_ALEN); + +	if (!is_zero_ether_addr(assoc_data->prev_bssid)) { +		skb_put(skb, 10); +		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | +						  IEEE80211_STYPE_REASSOC_REQ); +		mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab); +		mgmt->u.reassoc_req.listen_interval = +				cpu_to_le16(local->hw.conf.listen_interval); +		memcpy(mgmt->u.reassoc_req.current_ap, assoc_data->prev_bssid, +		       ETH_ALEN); +	} else { +		skb_put(skb, 4); +		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | +						  IEEE80211_STYPE_ASSOC_REQ); +		mgmt->u.assoc_req.capab_info = cpu_to_le16(capab); +		mgmt->u.assoc_req.listen_interval = +				cpu_to_le16(local->hw.conf.listen_interval); +	} -	if (send_frame) -		ieee80211_tx_skb(sdata, skb); -	else -		kfree_skb(skb); +	/* SSID */ +	pos = skb_put(skb, 2 + assoc_data->ssid_len); +	*pos++ = WLAN_EID_SSID; +	*pos++ = assoc_data->ssid_len; +	memcpy(pos, assoc_data->ssid, assoc_data->ssid_len); + +	/* add all rates which were marked to be used above */ +	supp_rates_len = rates_len; +	if (supp_rates_len > 8) +		supp_rates_len = 8; + +	pos = skb_put(skb, supp_rates_len + 2); +	*pos++ = WLAN_EID_SUPP_RATES; +	*pos++ = supp_rates_len; + +	count = 0; +	for (i = 0; i < sband->n_bitrates; i++) { +		if (BIT(i) & rates) { +			int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, +						5 * (1 << shift)); +			*pos++ = (u8) rate; +			if (++count == 8) +				break; +		} +	} + +	if (rates_len > count) { +		pos = skb_put(skb, rates_len - count + 2); +		*pos++ = WLAN_EID_EXT_SUPP_RATES; +		*pos++ = rates_len - count; + +		for (i++; i < sband->n_bitrates; i++) { +			if (BIT(i) & rates) { +				int rate; +				rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, +						    5 * (1 << shift)); +				*pos++ = (u8) rate; +			} +		} +	} + +	if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) { +		/* 1. power capabilities */ +		pos = skb_put(skb, 4); +		*pos++ = WLAN_EID_PWR_CAPABILITY; +		*pos++ = 2; +		*pos++ = 0; /* min tx power */ +		 /* max tx power */ +		*pos++ = ieee80211_chandef_max_power(&chanctx_conf->def); + +		/* 2. supported channels */ +		/* TODO: get this in reg domain format */ +		pos = skb_put(skb, 2 * sband->n_channels + 2); +		*pos++ = WLAN_EID_SUPPORTED_CHANNELS; +		*pos++ = 2 * sband->n_channels; +		for (i = 0; i < sband->n_channels; i++) { +			*pos++ = ieee80211_frequency_to_channel( +					sband->channels[i].center_freq); +			*pos++ = 1; /* one channel in the subband*/ +		} +	} + +	/* if present, add any custom IEs that go before HT */ +	if (assoc_data->ie_len) { +		static const u8 before_ht[] = { +			WLAN_EID_SSID, +			WLAN_EID_SUPP_RATES, +			WLAN_EID_EXT_SUPP_RATES, +			WLAN_EID_PWR_CAPABILITY, +			WLAN_EID_SUPPORTED_CHANNELS, +			WLAN_EID_RSN, +			WLAN_EID_QOS_CAPA, +			WLAN_EID_RRM_ENABLED_CAPABILITIES, +			WLAN_EID_MOBILITY_DOMAIN, +			WLAN_EID_SUPPORTED_REGULATORY_CLASSES, +		}; +		noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len, +					     before_ht, ARRAY_SIZE(before_ht), +					     offset); +		pos = skb_put(skb, noffset - offset); +		memcpy(pos, assoc_data->ie + offset, noffset - offset); +		offset = noffset; +	} + +	if (WARN_ON_ONCE((ifmgd->flags & IEEE80211_STA_DISABLE_HT) && +			 !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))) +		ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; + +	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) +		ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, +				    sband, chan, sdata->smps_mode); + +	/* if present, add any custom IEs that go before VHT */ +	if (assoc_data->ie_len) { +		static const u8 before_vht[] = { +			WLAN_EID_SSID, +			WLAN_EID_SUPP_RATES, +			WLAN_EID_EXT_SUPP_RATES, +			WLAN_EID_PWR_CAPABILITY, +			WLAN_EID_SUPPORTED_CHANNELS, +			WLAN_EID_RSN, +			WLAN_EID_QOS_CAPA, +			WLAN_EID_RRM_ENABLED_CAPABILITIES, +			WLAN_EID_MOBILITY_DOMAIN, +			WLAN_EID_SUPPORTED_REGULATORY_CLASSES, +			WLAN_EID_HT_CAPABILITY, +			WLAN_EID_BSS_COEX_2040, +			WLAN_EID_EXT_CAPABILITY, +			WLAN_EID_QOS_TRAFFIC_CAPA, +			WLAN_EID_TIM_BCAST_REQ, +			WLAN_EID_INTERWORKING, +		}; +		noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len, +					     before_vht, ARRAY_SIZE(before_vht), +					     offset); +		pos = skb_put(skb, noffset - offset); +		memcpy(pos, assoc_data->ie + offset, noffset - offset); +		offset = noffset; +	} + +	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) +		ieee80211_add_vht_ie(sdata, skb, sband, +				     &assoc_data->ap_vht_cap); + +	/* if present, add any custom non-vendor IEs that go after HT */ +	if (assoc_data->ie_len) { +		noffset = ieee80211_ie_split_vendor(assoc_data->ie, +						    assoc_data->ie_len, +						    offset); +		pos = skb_put(skb, noffset - offset); +		memcpy(pos, assoc_data->ie + offset, noffset - offset); +		offset = noffset; +	} + +	if (assoc_data->wmm) { +		if (assoc_data->uapsd) { +			qos_info = ifmgd->uapsd_queues; +			qos_info |= (ifmgd->uapsd_max_sp_len << +				     IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT); +		} else { +			qos_info = 0; +		} + +		pos = skb_put(skb, 9); +		*pos++ = WLAN_EID_VENDOR_SPECIFIC; +		*pos++ = 7; /* len */ +		*pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ +		*pos++ = 0x50; +		*pos++ = 0xf2; +		*pos++ = 2; /* WME */ +		*pos++ = 0; /* WME info */ +		*pos++ = 1; /* WME ver */ +		*pos++ = qos_info; +	} + +	/* add any remaining custom (i.e. vendor specific here) IEs */ +	if (assoc_data->ie_len) { +		noffset = assoc_data->ie_len; +		pos = skb_put(skb, noffset - offset); +		memcpy(pos, assoc_data->ie + offset, noffset - offset); +	} + +	drv_mgd_prepare_tx(local, sdata); + +	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; +	if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) +		IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS | +						IEEE80211_TX_INTFL_MLME_CONN_TX; +	ieee80211_tx_skb(sdata, skb);  }  void ieee80211_send_pspoll(struct ieee80211_local *local, @@ -307,6 +881,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,  {  	struct sk_buff *skb;  	struct ieee80211_hdr_3addr *nullfunc; +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif);  	if (!skb) @@ -316,7 +891,15 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,  	if (powersave)  		nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); -	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; +	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | +					IEEE80211_TX_INTFL_OFFCHAN_TX_OK; + +	if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) +		IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + +	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) +		IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE; +  	ieee80211_tx_skb(sdata, skb);  } @@ -331,11 +914,9 @@ static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,  		return;  	skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30); -	if (!skb) { -		printk(KERN_DEBUG "%s: failed to allocate buffer for 4addr " -		       "nullfunc frame\n", sdata->name); +	if (!skb)  		return; -	} +  	skb_reserve(skb, local->hw.extra_tx_headroom);  	nullfunc = (struct ieee80211_hdr *) skb_put(skb, 30); @@ -357,52 +938,77 @@ static void ieee80211_chswitch_work(struct work_struct *work)  {  	struct ieee80211_sub_if_data *sdata =  		container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work); +	struct ieee80211_local *local = sdata->local;  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	u32 changed = 0; +	int ret;  	if (!ieee80211_sdata_running(sdata))  		return; -	mutex_lock(&ifmgd->mtx); +	sdata_lock(sdata);  	if (!ifmgd->associated)  		goto out; -	sdata->local->oper_channel = sdata->local->csa_channel; -	if (!sdata->local->ops->channel_switch) { -		/* call "hw_config" only if doing sw channel switch */ -		ieee80211_hw_config(sdata->local, -			IEEE80211_CONF_CHANGE_CHANNEL); +	mutex_lock(&local->mtx); +	ret = ieee80211_vif_change_channel(sdata, &changed); +	mutex_unlock(&local->mtx); +	if (ret) { +		sdata_info(sdata, +			   "vif channel switch failed, disconnecting\n"); +		ieee80211_queue_work(&sdata->local->hw, +				     &ifmgd->csa_connection_drop_work); +		goto out; +	} + +	if (!local->use_chanctx) { +		local->_oper_chandef = sdata->csa_chandef; +		/* Call "hw_config" only if doing sw channel switch. +		 * Otherwise update the channel directly +		 */ +		if (!local->ops->channel_switch) +			ieee80211_hw_config(local, 0); +		else +			local->hw.conf.chandef = local->_oper_chandef;  	}  	/* XXX: shouldn't really modify cfg80211-owned data! */ -	ifmgd->associated->channel = sdata->local->oper_channel; +	ifmgd->associated->channel = sdata->csa_chandef.chan; + +	ieee80211_bss_info_change_notify(sdata, changed); -	ieee80211_wake_queues_by_reason(&sdata->local->hw, +	mutex_lock(&local->mtx); +	sdata->vif.csa_active = false; +	/* XXX: wait for a beacon first? */ +	if (!ieee80211_csa_needs_block_tx(local)) +		ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_CSA); - out: +	mutex_unlock(&local->mtx); +  	ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; -	mutex_unlock(&ifmgd->mtx); + +	ieee80211_sta_reset_beacon_monitor(sdata); +	ieee80211_sta_reset_conn_monitor(sdata); + +out: +	sdata_unlock(sdata);  }  void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)  { -	struct ieee80211_sub_if_data *sdata; -	struct ieee80211_if_managed *ifmgd; - -	sdata = vif_to_sdata(vif); -	ifmgd = &sdata->u.mgd; +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	trace_api_chswitch_done(sdata, success);  	if (!success) { -		/* -		 * If the channel switch was not successful, stay -		 * around on the old channel. We currently lack -		 * good handling of this situation, possibly we -		 * should just drop the association. -		 */ -		sdata->local->csa_channel = sdata->local->oper_channel; +		sdata_info(sdata, +			   "driver channel switch failed, disconnecting\n"); +		ieee80211_queue_work(&sdata->local->hw, +				     &ifmgd->csa_connection_drop_work); +	} else { +		ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);  	} - -	ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);  }  EXPORT_SYMBOL(ieee80211_chswitch_done); @@ -410,131 +1016,198 @@ static void ieee80211_chswitch_timer(unsigned long data)  {  	struct ieee80211_sub_if_data *sdata =  		(struct ieee80211_sub_if_data *) data; -	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; -	if (sdata->local->quiescing) { -		set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running); -		return; -	} - -	ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); +	ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work);  } -void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, -				      struct ieee80211_channel_sw_ie *sw_elem, -				      struct ieee80211_bss *bss, -				      u64 timestamp) +static void +ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, +				 u64 timestamp, struct ieee802_11_elems *elems, +				 bool beacon)  { -	struct cfg80211_bss *cbss = -		container_of((void *)bss, struct cfg80211_bss, priv); -	struct ieee80211_channel *new_ch; +	struct ieee80211_local *local = sdata->local;  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; -	int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num); +	struct cfg80211_bss *cbss = ifmgd->associated; +	struct ieee80211_chanctx *chanctx; +	enum ieee80211_band current_band; +	struct ieee80211_csa_ie csa_ie; +	int res; -	ASSERT_MGD_MTX(ifmgd); +	sdata_assert_lock(sdata); -	if (!ifmgd->associated) +	if (!cbss)  		return; -	if (sdata->local->scanning) +	if (local->scanning)  		return; -	/* Disregard subsequent beacons if we are already running a timer -	   processing a CSA */ - +	/* disregard subsequent announcements if we are already processing */  	if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)  		return; -	new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); -	if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) +	current_band = cbss->channel->band; +	memset(&csa_ie, 0, sizeof(csa_ie)); +	res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band, +					   ifmgd->flags, +					   ifmgd->associated->bssid, &csa_ie); +	if (res	< 0) +		ieee80211_queue_work(&local->hw, +				     &ifmgd->csa_connection_drop_work); +	if (res)  		return; -	sdata->local->csa_channel = new_ch; +	if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef, +				     IEEE80211_CHAN_DISABLED)) { +		sdata_info(sdata, +			   "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", +			   ifmgd->associated->bssid, +			   csa_ie.chandef.chan->center_freq, +			   csa_ie.chandef.width, csa_ie.chandef.center_freq1, +			   csa_ie.chandef.center_freq2); +		ieee80211_queue_work(&local->hw, +				     &ifmgd->csa_connection_drop_work); +		return; +	} -	if (sdata->local->ops->channel_switch) { -		/* use driver's channel switch callback */ -		struct ieee80211_channel_switch ch_switch; -		memset(&ch_switch, 0, sizeof(ch_switch)); -		ch_switch.timestamp = timestamp; -		if (sw_elem->mode) { -			ch_switch.block_tx = true; -			ieee80211_stop_queues_by_reason(&sdata->local->hw, -					IEEE80211_QUEUE_STOP_REASON_CSA); +	ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; + +	mutex_lock(&local->chanctx_mtx); +	if (local->use_chanctx) { +		u32 num_chanctx = 0; +		list_for_each_entry(chanctx, &local->chanctx_list, list) +		       num_chanctx++; + +		if (num_chanctx > 1 || +		    !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) { +			sdata_info(sdata, +				   "not handling chan-switch with channel contexts\n"); +			ieee80211_queue_work(&local->hw, +					     &ifmgd->csa_connection_drop_work); +			mutex_unlock(&local->chanctx_mtx); +			return;  		} -		ch_switch.channel = new_ch; -		ch_switch.count = sw_elem->count; -		ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; -		drv_channel_switch(sdata->local, &ch_switch); +	} + +	if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) { +		ieee80211_queue_work(&local->hw, +				     &ifmgd->csa_connection_drop_work); +		mutex_unlock(&local->chanctx_mtx); +		return; +	} +	chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf), +			       struct ieee80211_chanctx, conf); +	if (ieee80211_chanctx_refcount(local, chanctx) > 1) { +		sdata_info(sdata, +			   "channel switch with multiple interfaces on the same channel, disconnecting\n"); +		ieee80211_queue_work(&local->hw, +				     &ifmgd->csa_connection_drop_work); +		mutex_unlock(&local->chanctx_mtx);  		return;  	} +	mutex_unlock(&local->chanctx_mtx); -	/* channel switch handled in software */ -	if (sw_elem->count <= 1) { -		ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); -	} else { -		if (sw_elem->mode) -			ieee80211_stop_queues_by_reason(&sdata->local->hw, +	sdata->csa_chandef = csa_ie.chandef; + +	mutex_lock(&local->mtx); +	sdata->vif.csa_active = true; +	sdata->csa_block_tx = csa_ie.mode; + +	if (sdata->csa_block_tx) +		ieee80211_stop_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_CSA); -		ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; -		mod_timer(&ifmgd->chswitch_timer, -			  jiffies + -			  msecs_to_jiffies(sw_elem->count * -					   cbss->beacon_interval)); +	mutex_unlock(&local->mtx); + +	if (local->ops->channel_switch) { +		/* use driver's channel switch callback */ +		struct ieee80211_channel_switch ch_switch = { +			.timestamp = timestamp, +			.block_tx = csa_ie.mode, +			.chandef = csa_ie.chandef, +			.count = csa_ie.count, +		}; + +		drv_channel_switch(local, &ch_switch); +		return;  	} + +	/* channel switch handled in software */ +	if (csa_ie.count <= 1) +		ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work); +	else +		mod_timer(&ifmgd->chswitch_timer, +			  TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));  } -static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, -					u16 capab_info, u8 *pwr_constr_elem, -					u8 pwr_constr_elem_len) +static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, +				       struct ieee80211_channel *channel, +				       const u8 *country_ie, u8 country_ie_len, +				       const u8 *pwr_constr_elem)  { -	struct ieee80211_conf *conf = &sdata->local->hw.conf; +	struct ieee80211_country_ie_triplet *triplet; +	int chan = ieee80211_frequency_to_channel(channel->center_freq); +	int i, chan_pwr, chan_increment, new_ap_level; +	bool have_chan_pwr = false; -	if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT)) -		return; +	/* Invalid IE */ +	if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) +		return 0; -	/* Power constraint IE length should be 1 octet */ -	if (pwr_constr_elem_len != 1) -		return; +	triplet = (void *)(country_ie + 3); +	country_ie_len -= 3; -	if ((*pwr_constr_elem <= conf->channel->max_power) && -	    (*pwr_constr_elem != sdata->local->power_constr_level)) { -		sdata->local->power_constr_level = *pwr_constr_elem; -		ieee80211_hw_config(sdata->local, 0); +	switch (channel->band) { +	default: +		WARN_ON_ONCE(1); +		/* fall through */ +	case IEEE80211_BAND_2GHZ: +	case IEEE80211_BAND_60GHZ: +		chan_increment = 1; +		break; +	case IEEE80211_BAND_5GHZ: +		chan_increment = 4; +		break;  	} -} -void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif) -{ -	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); -	struct ieee80211_local *local = sdata->local; -	struct ieee80211_conf *conf = &local->hw.conf; +	/* find channel */ +	while (country_ie_len >= 3) { +		u8 first_channel = triplet->chans.first_channel; -	WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION || -		!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS) || -		(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)); +		if (first_channel >= IEEE80211_COUNTRY_EXTENSION_ID) +			goto next; -	local->disable_dynamic_ps = false; -	conf->dynamic_ps_timeout = local->dynamic_ps_user_timeout; -} -EXPORT_SYMBOL(ieee80211_enable_dyn_ps); +		for (i = 0; i < triplet->chans.num_channels; i++) { +			if (first_channel + i * chan_increment == chan) { +				have_chan_pwr = true; +				chan_pwr = triplet->chans.max_power; +				break; +			} +		} +		if (have_chan_pwr) +			break; -void ieee80211_disable_dyn_ps(struct ieee80211_vif *vif) -{ -	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); -	struct ieee80211_local *local = sdata->local; -	struct ieee80211_conf *conf = &local->hw.conf; + next: +		triplet++; +		country_ie_len -= 3; +	} -	WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION || -		!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS) || -		(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)); +	if (!have_chan_pwr) +		return 0; -	local->disable_dynamic_ps = true; -	conf->dynamic_ps_timeout = 0; -	del_timer_sync(&local->dynamic_ps_timer); -	ieee80211_queue_work(&local->hw, -			     &local->dynamic_ps_enable_work); +	new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem); + +	if (sdata->ap_power_level == new_ap_level) +		return 0; + +	sdata_info(sdata, +		   "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", +		   new_ap_level, chan_pwr, *pwr_constr_elem, +		   sdata->u.mgd.bssid); +	sdata->ap_power_level = new_ap_level; +	if (__ieee80211_recalc_txpower(sdata)) +		return BSS_CHANGED_TXPOWER; +	return 0;  } -EXPORT_SYMBOL(ieee80211_disable_dyn_ps);  /* powersave */  static void ieee80211_enable_ps(struct ieee80211_local *local, @@ -580,6 +1253,36 @@ static void ieee80211_change_ps(struct ieee80211_local *local)  	}  } +static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_managed *mgd = &sdata->u.mgd; +	struct sta_info *sta = NULL; +	bool authorized = false; + +	if (!mgd->powersave) +		return false; + +	if (mgd->broken_ap) +		return false; + +	if (!mgd->associated) +		return false; + +	if (mgd->flags & IEEE80211_STA_CONNECTION_POLL) +		return false; + +	if (!mgd->have_beacon) +		return false; + +	rcu_read_lock(); +	sta = sta_info_get(sdata, mgd->bssid); +	if (sta) +		authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); +	rcu_read_unlock(); + +	return authorized; +} +  /* need to hold RTNL or interface lock */  void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)  { @@ -592,26 +1295,24 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)  		return;  	} -	if (!list_empty(&local->work_list)) { -		local->ps_sdata = NULL; -		goto change; -	} -  	list_for_each_entry(sdata, &local->interfaces, list) {  		if (!ieee80211_sdata_running(sdata))  			continue; +		if (sdata->vif.type == NL80211_IFTYPE_AP) { +			/* If an AP vif is found, then disable PS +			 * by setting the count to zero thereby setting +			 * ps_sdata to NULL. +			 */ +			count = 0; +			break; +		}  		if (sdata->vif.type != NL80211_IFTYPE_STATION)  			continue;  		found = sdata;  		count++;  	} -	if (count == 1 && found->u.mgd.powersave && -	    found->u.mgd.associated && -	    found->u.mgd.associated->beacon_ies && -	    !(found->u.mgd.flags & (IEEE80211_STA_BEACON_POLL | -				    IEEE80211_STA_CONNECTION_POLL))) { -		struct ieee80211_conf *conf = &local->hw.conf; +	if (count == 1 && ieee80211_powersave_allowed(found)) {  		s32 beaconint_us;  		if (latency < 0) @@ -625,29 +1326,23 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)  			/*  			 * Go to full PSM if the user configures a very low  			 * latency requirement. -			 * The 2 second value is there for compatibility until -			 * the PM_QOS_NETWORK_LATENCY is configured with real -			 * values. +			 * The 2000 second value is there for compatibility +			 * until the PM_QOS_NETWORK_LATENCY is configured +			 * with real values.  			 */ -			if (latency > 1900000000 && latency != 2000000000) +			if (latency > (1900 * USEC_PER_MSEC) && +			    latency != (2000 * USEC_PER_SEC))  				timeout = 0;  			else  				timeout = 100;  		} -		local->dynamic_ps_user_timeout = timeout; -		if (!local->disable_dynamic_ps) -			conf->dynamic_ps_timeout = -				local->dynamic_ps_user_timeout; +		local->hw.conf.dynamic_ps_timeout = timeout;  		if (beaconint_us > latency) {  			local->ps_sdata = NULL;  		} else { -			struct ieee80211_bss *bss;  			int maxslp = 1; -			u8 dtimper; - -			bss = (void *)found->u.mgd.associated->priv; -			dtimper = bss->dtim_period; +			u8 dtimper = found->u.mgd.dtim_period;  			/* If the TIM IE is invalid, pretend the value is 1 */  			if (!dtimper) @@ -664,10 +1359,19 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)  		local->ps_sdata = NULL;  	} - change:  	ieee80211_change_ps(local);  } +void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata) +{ +	bool ps_allowed = ieee80211_powersave_allowed(sdata); + +	if (sdata->vif.bss_conf.ps != ps_allowed) { +		sdata->vif.bss_conf.ps = ps_allowed; +		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_PS); +	} +} +  void ieee80211_dynamic_ps_disable_work(struct work_struct *work)  {  	struct ieee80211_local *local = @@ -680,6 +1384,7 @@ void ieee80211_dynamic_ps_disable_work(struct work_struct *work)  	}  	ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_PS);  } @@ -689,18 +1394,59 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)  		container_of(work, struct ieee80211_local,  			     dynamic_ps_enable_work);  	struct ieee80211_sub_if_data *sdata = local->ps_sdata; -	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	struct ieee80211_if_managed *ifmgd; +	unsigned long flags; +	int q;  	/* can only happen when PS was just disabled anyway */  	if (!sdata)  		return; +	ifmgd = &sdata->u.mgd; +  	if (local->hw.conf.flags & IEEE80211_CONF_PS)  		return; +	if (local->hw.conf.dynamic_ps_timeout > 0) { +		/* don't enter PS if TX frames are pending */ +		if (drv_tx_frames_pending(local)) { +			mod_timer(&local->dynamic_ps_timer, jiffies + +				  msecs_to_jiffies( +				  local->hw.conf.dynamic_ps_timeout)); +			return; +		} + +		/* +		 * transmission can be stopped by others which leads to +		 * dynamic_ps_timer expiry. Postpone the ps timer if it +		 * is not the actual idle state. +		 */ +		spin_lock_irqsave(&local->queue_stop_reason_lock, flags); +		for (q = 0; q < local->hw.queues; q++) { +			if (local->queue_stop_reasons[q]) { +				spin_unlock_irqrestore(&local->queue_stop_reason_lock, +						       flags); +				mod_timer(&local->dynamic_ps_timer, jiffies + +					  msecs_to_jiffies( +					  local->hw.conf.dynamic_ps_timeout)); +				return; +			} +		} +		spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); +	} +  	if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && -	    (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED))) -		ieee80211_send_nullfunc(local, sdata, 1); +	    !(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) { +		if (drv_tx_frames_pending(local)) { +			mod_timer(&local->dynamic_ps_timer, jiffies + +				  msecs_to_jiffies( +				  local->hw.conf.dynamic_ps_timeout)); +		} else { +			ieee80211_send_nullfunc(local, sdata, 1); +			/* Flush to get the tx status of nullfunc frame */ +			ieee80211_flush_queues(local, sdata); +		} +	}  	if (!((local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&  	      (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) || @@ -721,35 +1467,55 @@ void ieee80211_dynamic_ps_timer(unsigned long data)  	ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);  } +void ieee80211_dfs_cac_timer_work(struct work_struct *work) +{ +	struct delayed_work *delayed_work = +		container_of(work, struct delayed_work, work); +	struct ieee80211_sub_if_data *sdata = +		container_of(delayed_work, struct ieee80211_sub_if_data, +			     dfs_cac_timer_work); +	struct cfg80211_chan_def chandef = sdata->vif.bss_conf.chandef; + +	mutex_lock(&sdata->local->mtx); +	if (sdata->wdev.cac_started) { +		ieee80211_vif_release_channel(sdata); +		cfg80211_cac_event(sdata->dev, &chandef, +				   NL80211_RADAR_CAC_FINISHED, +				   GFP_KERNEL); +	} +	mutex_unlock(&sdata->local->mtx); +} +  /* MLME */ -static void ieee80211_sta_wmm_params(struct ieee80211_local *local, +static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,  				     struct ieee80211_sub_if_data *sdata, -				     u8 *wmm_param, size_t wmm_param_len) +				     const u8 *wmm_param, size_t wmm_param_len)  {  	struct ieee80211_tx_queue_params params;  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	size_t left;  	int count; -	u8 *pos, uapsd_queues = 0; +	const u8 *pos; +	u8 uapsd_queues = 0;  	if (!local->ops->conf_tx) -		return; +		return false; -	if (local->hw.queues < 4) -		return; +	if (local->hw.queues < IEEE80211_NUM_ACS) +		return false;  	if (!wmm_param) -		return; +		return false;  	if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1) -		return; +		return false;  	if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) -		uapsd_queues = local->uapsd_queues; +		uapsd_queues = ifmgd->uapsd_queues;  	count = wmm_param[6] & 0x0f;  	if (count == ifmgd->wmm_last_param_set) -		return; +		return false;  	ifmgd->wmm_last_param_set = count;  	pos = wmm_param + 8; @@ -757,7 +1523,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,  	memset(¶ms, 0, sizeof(params)); -	local->wmm_acm = 0; +	sdata->wmm_acm = 0;  	for (; left >= 4; left -= 4, pos += 4) {  		int aci = (pos[0] >> 5) & 0x03;  		int acm = (pos[0] >> 4) & 0x01; @@ -768,21 +1534,21 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,  		case 1: /* AC_BK */  			queue = 3;  			if (acm) -				local->wmm_acm |= BIT(1) | BIT(2); /* BK/- */ +				sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */  			if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)  				uapsd = true;  			break;  		case 2: /* AC_VI */  			queue = 1;  			if (acm) -				local->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */ +				sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */  			if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)  				uapsd = true;  			break;  		case 3: /* AC_VO */  			queue = 0;  			if (acm) -				local->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */ +				sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */  			if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)  				uapsd = true;  			break; @@ -790,7 +1556,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,  		default:  			queue = 2;  			if (acm) -				local->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */ +				sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */  			if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)  				uapsd = true;  			break; @@ -800,25 +1566,39 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,  		params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4);  		params.cw_min = ecw2cw(pos[1] & 0x0f);  		params.txop = get_unaligned_le16(pos + 2); +		params.acm = acm;  		params.uapsd = uapsd; -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -		wiphy_debug(local->hw.wiphy, -			    "WMM queue=%d aci=%d acm=%d aifs=%d " -			    "cWmin=%d cWmax=%d txop=%d uapsd=%d\n", -			    queue, aci, acm, -			    params.aifs, params.cw_min, params.cw_max, -			    params.txop, params.uapsd); -#endif -		if (drv_conf_tx(local, queue, ¶ms)) -			wiphy_debug(local->hw.wiphy, -				    "failed to set TX queue parameters for queue %d\n", -				    queue); +		mlme_dbg(sdata, +			 "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d\n", +			 queue, aci, acm, +			 params.aifs, params.cw_min, params.cw_max, +			 params.txop, params.uapsd); +		sdata->tx_conf[queue] = params; +		if (drv_conf_tx(local, sdata, queue, ¶ms)) +			sdata_err(sdata, +				  "failed to set TX queue parameters for queue %d\n", +				  queue);  	}  	/* enable WMM or activate new settings */  	sdata->vif.bss_conf.qos = true; -	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS); +	return true; +} + +static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) +{ +	lockdep_assert_held(&sdata->local->mtx); + +	sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL; +	ieee80211_run_deferred_scan(sdata->local); +} + +static void ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) +{ +	mutex_lock(&sdata->local->mtx); +	__ieee80211_stop_poll(sdata); +	mutex_unlock(&sdata->local->mtx);  }  static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, @@ -839,7 +1619,7 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,  	}  	use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME); -	if (sdata->local->hw.conf.channel->band == IEEE80211_BAND_5GHZ) +	if (ieee80211_get_sdata_band(sdata) == IEEE80211_BAND_5GHZ)  		use_short_slot = true;  	if (use_protection != bss_conf->use_cts_prot) { @@ -869,153 +1649,194 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;  	bss_info_changed |= BSS_CHANGED_ASSOC; -	/* set timing information */ -	bss_conf->beacon_int = cbss->beacon_interval; -	bss_conf->timestamp = cbss->tsf; - -	bss_info_changed |= BSS_CHANGED_BEACON_INT;  	bss_info_changed |= ieee80211_handle_bss_capability(sdata, -		cbss->capability, bss->has_erp_value, bss->erp_value); +		bss_conf->assoc_capability, bss->has_erp_value, bss->erp_value);  	sdata->u.mgd.beacon_timeout = usecs_to_jiffies(ieee80211_tu_to_usec( -		IEEE80211_BEACON_LOSS_COUNT * bss_conf->beacon_int)); +		beacon_loss_count * bss_conf->beacon_int));  	sdata->u.mgd.associated = cbss;  	memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN);  	sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; +	if (sdata->vif.p2p) { +		const struct cfg80211_bss_ies *ies; + +		rcu_read_lock(); +		ies = rcu_dereference(cbss->ies); +		if (ies) { +			int ret; + +			ret = cfg80211_get_p2p_attr( +					ies->data, ies->len, +					IEEE80211_P2P_ATTR_ABSENCE_NOTICE, +					(u8 *) &bss_conf->p2p_noa_attr, +					sizeof(bss_conf->p2p_noa_attr)); +			if (ret >= 2) { +				sdata->u.mgd.p2p_noa_index = +					bss_conf->p2p_noa_attr.index; +				bss_info_changed |= BSS_CHANGED_P2P_PS; +			} +		} +		rcu_read_unlock(); +	} +  	/* just to be sure */ -	sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL | -				IEEE80211_STA_BEACON_POLL); +	ieee80211_stop_poll(sdata);  	ieee80211_led_assoc(local, 1); -	if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) -		bss_conf->dtim_period = bss->dtim_period; -	else +	if (sdata->u.mgd.have_beacon) { +		/* +		 * If the AP is buggy we may get here with no DTIM period +		 * known, so assume it's 1 which is the only safe assumption +		 * in that case, although if the TIM IE is broken powersave +		 * probably just won't work at all. +		 */ +		bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1; +		bss_conf->beacon_rate = bss->beacon_rate; +		bss_info_changed |= BSS_CHANGED_BEACON_INFO; +	} else { +		bss_conf->beacon_rate = NULL;  		bss_conf->dtim_period = 0; +	}  	bss_conf->assoc = 1; -	/* -	 * For now just always ask the driver to update the basic rateset -	 * when we have associated, we aren't checking whether it actually -	 * changed or not. -	 */ -	bss_info_changed |= BSS_CHANGED_BASIC_RATES; - -	/* And the BSSID changed - we're associated now */ -	bss_info_changed |= BSS_CHANGED_BSSID;  	/* Tell the driver to monitor connection quality (if supported) */ -	if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) && +	if (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI &&  	    bss_conf->cqm_rssi_thold)  		bss_info_changed |= BSS_CHANGED_CQM;  	/* Enable ARP filtering */ -	if (bss_conf->arp_filter_enabled != sdata->arp_filter_state) { -		bss_conf->arp_filter_enabled = sdata->arp_filter_state; +	if (bss_conf->arp_addr_cnt)  		bss_info_changed |= BSS_CHANGED_ARP_FILTER; -	}  	ieee80211_bss_info_change_notify(sdata, bss_info_changed);  	mutex_lock(&local->iflist_mtx);  	ieee80211_recalc_ps(local, -1); -	ieee80211_recalc_smps(local);  	mutex_unlock(&local->iflist_mtx); -	netif_tx_start_all_queues(sdata->dev); +	ieee80211_recalc_smps(sdata); +	ieee80211_recalc_ps_vif(sdata); +  	netif_carrier_on(sdata->dev);  }  static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, -				   bool remove_sta, bool tx) +				   u16 stype, u16 reason, bool tx, +				   u8 *frame_buf)  {  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	struct ieee80211_local *local = sdata->local; -	struct sta_info *sta; -	u32 changed = 0, config_changed = 0; -	u8 bssid[ETH_ALEN]; +	u32 changed = 0; + +	sdata_assert_lock(sdata); -	ASSERT_MGD_MTX(ifmgd); +	if (WARN_ON_ONCE(tx && !frame_buf)) +		return;  	if (WARN_ON(!ifmgd->associated))  		return; -	memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN); +	ieee80211_stop_poll(sdata);  	ifmgd->associated = NULL; -	memset(ifmgd->bssid, 0, ETH_ALEN); +	netif_carrier_off(sdata->dev);  	/* -	 * we need to commit the associated = NULL change because the -	 * scan code uses that to determine whether this iface should -	 * go to/wake up from powersave or not -- and could otherwise -	 * wake the queues erroneously. +	 * if we want to get out of ps before disassoc (why?) we have +	 * to do it before sending disassoc, as otherwise the null-packet +	 * won't be valid.  	 */ -	smp_mb(); +	if (local->hw.conf.flags & IEEE80211_CONF_PS) { +		local->hw.conf.flags &= ~IEEE80211_CONF_PS; +		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); +	} +	local->ps_sdata = NULL; -	/* -	 * Thus, we can only afterwards stop the queues -- to account -	 * for the case where another CPU is finishing a scan at this -	 * time -- we don't want the scan code to enable queues. -	 */ +	/* disable per-vif ps */ +	ieee80211_recalc_ps_vif(sdata); -	netif_tx_stop_all_queues(sdata->dev); -	netif_carrier_off(sdata->dev); +	/* flush out any pending frame (e.g. DELBA) before deauth/disassoc */ +	if (tx) +		ieee80211_flush_queues(local, sdata); -	mutex_lock(&local->sta_mtx); -	sta = sta_info_get(sdata, bssid); -	if (sta) { -		set_sta_flags(sta, WLAN_STA_BLOCK_BA); -		ieee80211_sta_tear_down_BA_sessions(sta, tx); -	} -	mutex_unlock(&local->sta_mtx); +	/* deauthenticate/disassociate now */ +	if (tx || frame_buf) +		ieee80211_send_deauth_disassoc(sdata, ifmgd->bssid, stype, +					       reason, tx, frame_buf); +	/* flush out frame */ +	if (tx) +		ieee80211_flush_queues(local, sdata); + +	/* clear bssid only after building the needed mgmt frames */ +	memset(ifmgd->bssid, 0, ETH_ALEN); + +	/* remove AP and TDLS peers */ +	sta_info_flush(sdata); + +	/* finally reset all BSS / config parameters */  	changed |= ieee80211_reset_erp_info(sdata);  	ieee80211_led_assoc(local, 0);  	changed |= BSS_CHANGED_ASSOC;  	sdata->vif.bss_conf.assoc = false; -	ieee80211_set_wmm_default(sdata); - -	/* channel(_type) changes are handled by ieee80211_hw_config */ -	WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); +	ifmgd->p2p_noa_index = -1; +	memset(&sdata->vif.bss_conf.p2p_noa_attr, 0, +	       sizeof(sdata->vif.bss_conf.p2p_noa_attr)); -	/* on the next assoc, re-program HT parameters */ -	sdata->ht_opmode_valid = false; +	/* on the next assoc, re-program HT/VHT parameters */ +	memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa)); +	memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); +	memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa)); +	memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask)); -	local->power_constr_level = 0; +	sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;  	del_timer_sync(&local->dynamic_ps_timer);  	cancel_work_sync(&local->dynamic_ps_enable_work); -	if (local->hw.conf.flags & IEEE80211_CONF_PS) { -		local->hw.conf.flags &= ~IEEE80211_CONF_PS; -		config_changed |= IEEE80211_CONF_CHANGE_PS; -	} - -	ieee80211_hw_config(local, config_changed); -  	/* Disable ARP filtering */ -	if (sdata->vif.bss_conf.arp_filter_enabled) { -		sdata->vif.bss_conf.arp_filter_enabled = false; +	if (sdata->vif.bss_conf.arp_addr_cnt)  		changed |= BSS_CHANGED_ARP_FILTER; -	} + +	sdata->vif.bss_conf.qos = false; +	changed |= BSS_CHANGED_QOS;  	/* The BSSID (not really interesting) and HT changed */  	changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;  	ieee80211_bss_info_change_notify(sdata, changed); -	if (remove_sta) -		sta_info_destroy_addr(sdata, bssid); +	/* disassociated - set to defaults now */ +	ieee80211_set_wmm_default(sdata, false);  	del_timer_sync(&sdata->u.mgd.conn_mon_timer);  	del_timer_sync(&sdata->u.mgd.bcn_mon_timer);  	del_timer_sync(&sdata->u.mgd.timer);  	del_timer_sync(&sdata->u.mgd.chswitch_timer); + +	sdata->vif.bss_conf.dtim_period = 0; +	sdata->vif.bss_conf.beacon_rate = NULL; + +	ifmgd->have_beacon = false; + +	ifmgd->flags = 0; +	mutex_lock(&local->mtx); +	ieee80211_vif_release_channel(sdata); + +	sdata->vif.csa_active = false; +	if (!ieee80211_csa_needs_block_tx(local)) +		ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA); +	mutex_unlock(&local->mtx); + +	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;  }  void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, @@ -1038,19 +1859,20 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,  static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	struct ieee80211_local *local = sdata->local; -	if (!(ifmgd->flags & (IEEE80211_STA_BEACON_POLL | -			      IEEE80211_STA_CONNECTION_POLL))) -	    return; +	mutex_lock(&local->mtx); +	if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)) +		goto out; -	ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL | -			  IEEE80211_STA_BEACON_POLL); -	mutex_lock(&sdata->local->iflist_mtx); -	ieee80211_recalc_ps(sdata->local, -1); -	mutex_unlock(&sdata->local->iflist_mtx); +	__ieee80211_stop_poll(sdata); + +	mutex_lock(&local->iflist_mtx); +	ieee80211_recalc_ps(local, -1); +	mutex_unlock(&local->iflist_mtx);  	if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) -		return; +		goto out;  	/*  	 * We've received a probe response, but are not sure whether @@ -1062,22 +1884,28 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)  	mod_timer(&ifmgd->conn_mon_timer,  		  round_jiffies_up(jiffies +  				   IEEE80211_CONNECTION_IDLE_TIME)); +out: +	mutex_unlock(&local->mtx);  }  void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, -			     struct ieee80211_hdr *hdr) +			     struct ieee80211_hdr *hdr, bool ack)  { -	if (!ieee80211_is_data(hdr->frame_control) && -	    !ieee80211_is_nullfunc(hdr->frame_control)) +	if (!ieee80211_is_data(hdr->frame_control))  	    return; -	ieee80211_sta_reset_conn_monitor(sdata); -  	if (ieee80211_is_nullfunc(hdr->frame_control) &&  	    sdata->u.mgd.probe_send_count > 0) { -		sdata->u.mgd.probe_send_count = 0; +		if (ack) +			ieee80211_sta_reset_conn_monitor(sdata); +		else +			sdata->u.mgd.nullfunc_failed = true;  		ieee80211_queue_work(&sdata->local->hw, &sdata->work); +		return;  	} + +	if (ack) +		ieee80211_sta_reset_conn_monitor(sdata);  }  static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) @@ -1085,7 +1913,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	const u8 *ssid;  	u8 *dst = ifmgd->associated->bssid; -	u8 unicast_limit = max(1, IEEE80211_MAX_PROBE_TRIES - 3); +	u8 unicast_limit = max(1, max_probe_tries - 3);  	/*  	 * Try sending broadcast probe requests for the last three @@ -1102,16 +1930,31 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)  	 * anymore. The timeout will be reset if the frame is ACKed by  	 * the AP.  	 */ -	if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) +	ifmgd->probe_send_count++; + +	if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) { +		ifmgd->nullfunc_failed = false;  		ieee80211_send_nullfunc(sdata->local, sdata, 0); -	else { +	} else { +		int ssid_len; + +		rcu_read_lock();  		ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); -		ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0); +		if (WARN_ON_ONCE(ssid == NULL)) +			ssid_len = 0; +		else +			ssid_len = ssid[1]; + +		ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL, +					 0, (u32) -1, true, 0, +					 ifmgd->associated->channel, false); +		rcu_read_unlock();  	} -	ifmgd->probe_send_count++; -	ifmgd->probe_timeout = jiffies + IEEE80211_PROBE_WAIT; -	run_again(ifmgd, ifmgd->probe_timeout); +	ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); +	run_again(sdata, ifmgd->probe_timeout); +	if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) +		ieee80211_flush_queues(sdata->local, sdata);  }  static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, @@ -1123,22 +1966,27 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,  	if (!ieee80211_sdata_running(sdata))  		return; -	if (sdata->local->scanning) -		return; +	sdata_lock(sdata); -	if (sdata->local->tmp_channel) -		return; +	if (!ifmgd->associated) +		goto out; -	mutex_lock(&ifmgd->mtx); +	mutex_lock(&sdata->local->mtx); -	if (!ifmgd->associated) +	if (sdata->local->tmp_channel || sdata->local->scanning) { +		mutex_unlock(&sdata->local->mtx);  		goto out; +	} -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -	if (beacon && net_ratelimit()) -		printk(KERN_DEBUG "%s: detected beacon loss from AP " -		       "- sending probe request\n", sdata->name); -#endif +	if (beacon) { +		mlme_dbg_ratelimited(sdata, +				     "detected beacon loss from AP (missed %d beacons) - probing\n", +				     beacon_loss_count); + +		ieee80211_cqm_rssi_notify(&sdata->vif, +					  NL80211_CQM_RSSI_BEACON_LOSS_EVENT, +					  GFP_KERNEL); +	}  	/*  	 * The driver/our work has already reported this event or the @@ -1151,14 +1999,12 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,  	 * because otherwise we would reset the timer every time and  	 * never check whether we received a probe response!  	 */ -	if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | -			    IEEE80211_STA_CONNECTION_POLL)) +	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)  		already = true; -	if (beacon) -		ifmgd->flags |= IEEE80211_STA_BEACON_POLL; -	else -		ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL; +	ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL; + +	mutex_unlock(&sdata->local->mtx);  	if (already)  		goto out; @@ -1170,7 +2016,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,  	ifmgd->probe_send_count = 0;  	ieee80211_mgd_probe_ap_send(sdata);   out: -	mutex_unlock(&ifmgd->mtx); +	sdata_unlock(sdata);  }  struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, @@ -1178,67 +2024,104 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,  {  	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	struct cfg80211_bss *cbss;  	struct sk_buff *skb;  	const u8 *ssid; +	int ssid_len;  	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))  		return NULL; -	ASSERT_MGD_MTX(ifmgd); +	sdata_assert_lock(sdata); -	if (!ifmgd->associated) +	if (ifmgd->associated) +		cbss = ifmgd->associated; +	else if (ifmgd->auth_data) +		cbss = ifmgd->auth_data->bss; +	else if (ifmgd->assoc_data) +		cbss = ifmgd->assoc_data->bss; +	else  		return NULL; -	ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); -	skb = ieee80211_build_probe_req(sdata, ifmgd->associated->bssid, -					ssid + 2, ssid[1], NULL, 0); +	rcu_read_lock(); +	ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID); +	if (WARN_ON_ONCE(ssid == NULL)) +		ssid_len = 0; +	else +		ssid_len = ssid[1]; + +	skb = ieee80211_build_probe_req(sdata, cbss->bssid, +					(u32) -1, cbss->channel, +					ssid + 2, ssid_len, +					NULL, 0, true); +	rcu_read_unlock();  	return skb;  }  EXPORT_SYMBOL(ieee80211_ap_probereq_get); -static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) +static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)  { -	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	struct ieee80211_local *local = sdata->local; -	u8 bssid[ETH_ALEN]; +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; -	mutex_lock(&ifmgd->mtx); +	sdata_lock(sdata);  	if (!ifmgd->associated) { -		mutex_unlock(&ifmgd->mtx); +		sdata_unlock(sdata);  		return;  	} -	memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN); - -	printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid); - -	ieee80211_set_disassoc(sdata, true, true); -	mutex_unlock(&ifmgd->mtx); +	ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, +			       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, +			       true, frame_buf); +	ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;  	mutex_lock(&local->mtx); -	ieee80211_recalc_idle(local); +	sdata->vif.csa_active = false; +	if (!ieee80211_csa_needs_block_tx(local)) +		ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA);  	mutex_unlock(&local->mtx); -	/* -	 * must be outside lock due to cfg80211, -	 * but that's not a problem. -	 */ -	ieee80211_send_deauth_disassoc(sdata, bssid, -				       IEEE80211_STYPE_DEAUTH, -				       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, -				       NULL, true); + +	cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, +			      IEEE80211_DEAUTH_FRAME_LEN); +	sdata_unlock(sdata);  } -void ieee80211_beacon_connection_loss_work(struct work_struct *work) +static void ieee80211_beacon_connection_loss_work(struct work_struct *work)  {  	struct ieee80211_sub_if_data *sdata =  		container_of(work, struct ieee80211_sub_if_data,  			     u.mgd.beacon_connection_loss_work); +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	struct sta_info *sta; -	if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) -		__ieee80211_connection_loss(sdata); -	else +	if (ifmgd->associated) { +		rcu_read_lock(); +		sta = sta_info_get(sdata, ifmgd->bssid); +		if (sta) +			sta->beacon_loss_count++; +		rcu_read_unlock(); +	} + +	if (ifmgd->connection_loss) { +		sdata_info(sdata, "Connection to AP %pM lost\n", +			   ifmgd->bssid); +		__ieee80211_disconnect(sdata); +	} else {  		ieee80211_mgd_probe_ap(sdata, true); +	} +} + +static void ieee80211_csa_connection_drop_work(struct work_struct *work) +{ +	struct ieee80211_sub_if_data *sdata = +		container_of(work, struct ieee80211_sub_if_data, +			     u.mgd.csa_connection_drop_work); + +	__ieee80211_disconnect(sdata);  }  void ieee80211_beacon_loss(struct ieee80211_vif *vif) @@ -1248,7 +2131,7 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif)  	trace_api_beacon_loss(sdata); -	WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR); +	sdata->u.mgd.connection_loss = false;  	ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);  }  EXPORT_SYMBOL(ieee80211_beacon_loss); @@ -1260,90 +2143,358 @@ void ieee80211_connection_loss(struct ieee80211_vif *vif)  	trace_api_connection_loss(sdata); -	WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR)); +	sdata->u.mgd.connection_loss = true;  	ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);  }  EXPORT_SYMBOL(ieee80211_connection_loss); -static enum rx_mgmt_action __must_check -ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, -			 struct ieee80211_mgmt *mgmt, size_t len) +static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, +					bool assoc) +{ +	struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data; + +	sdata_assert_lock(sdata); + +	if (!assoc) { +		sta_info_destroy_addr(sdata, auth_data->bss->bssid); + +		memset(sdata->u.mgd.bssid, 0, ETH_ALEN); +		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); +		sdata->u.mgd.flags = 0; +		mutex_lock(&sdata->local->mtx); +		ieee80211_vif_release_channel(sdata); +		mutex_unlock(&sdata->local->mtx); +	} + +	cfg80211_put_bss(sdata->local->hw.wiphy, auth_data->bss); +	kfree(auth_data); +	sdata->u.mgd.auth_data = NULL; +} + +static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, +				     struct ieee80211_mgmt *mgmt, size_t len) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data; +	u8 *pos; +	struct ieee802_11_elems elems; +	u32 tx_flags = 0; + +	pos = mgmt->u.auth.variable; +	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); +	if (!elems.challenge) +		return; +	auth_data->expected_transaction = 4; +	drv_mgd_prepare_tx(sdata->local, sdata); +	if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) +		tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS | +			   IEEE80211_TX_INTFL_MLME_CONN_TX; +	ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0, +			    elems.challenge - 2, elems.challenge_len + 2, +			    auth_data->bss->bssid, auth_data->bss->bssid, +			    auth_data->key, auth_data->key_len, +			    auth_data->key_idx, tx_flags); +} + +static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, +				   struct ieee80211_mgmt *mgmt, size_t len) +{ +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	u8 bssid[ETH_ALEN]; +	u16 auth_alg, auth_transaction, status_code; +	struct sta_info *sta; + +	sdata_assert_lock(sdata); + +	if (len < 24 + 6) +		return; + +	if (!ifmgd->auth_data || ifmgd->auth_data->done) +		return; + +	memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN); + +	if (!ether_addr_equal(bssid, mgmt->bssid)) +		return; + +	auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); +	auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); +	status_code = le16_to_cpu(mgmt->u.auth.status_code); + +	if (auth_alg != ifmgd->auth_data->algorithm || +	    auth_transaction != ifmgd->auth_data->expected_transaction) { +		sdata_info(sdata, "%pM unexpected authentication state: alg %d (expected %d) transact %d (expected %d)\n", +			   mgmt->sa, auth_alg, ifmgd->auth_data->algorithm, +			   auth_transaction, +			   ifmgd->auth_data->expected_transaction); +		return; +	} + +	if (status_code != WLAN_STATUS_SUCCESS) { +		sdata_info(sdata, "%pM denied authentication (status %d)\n", +			   mgmt->sa, status_code); +		ieee80211_destroy_auth_data(sdata, false); +		cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); +		return; +	} + +	switch (ifmgd->auth_data->algorithm) { +	case WLAN_AUTH_OPEN: +	case WLAN_AUTH_LEAP: +	case WLAN_AUTH_FT: +	case WLAN_AUTH_SAE: +		break; +	case WLAN_AUTH_SHARED_KEY: +		if (ifmgd->auth_data->expected_transaction != 4) { +			ieee80211_auth_challenge(sdata, mgmt, len); +			/* need another frame */ +			return; +		} +		break; +	default: +		WARN_ONCE(1, "invalid auth alg %d", +			  ifmgd->auth_data->algorithm); +		return; +	} + +	sdata_info(sdata, "authenticated\n"); +	ifmgd->auth_data->done = true; +	ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; +	ifmgd->auth_data->timeout_started = true; +	run_again(sdata, ifmgd->auth_data->timeout); + +	if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE && +	    ifmgd->auth_data->expected_transaction != 2) { +		/* +		 * Report auth frame to user space for processing since another +		 * round of Authentication frames is still needed. +		 */ +		cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); +		return; +	} + +	/* move station state to auth */ +	mutex_lock(&sdata->local->sta_mtx); +	sta = sta_info_get(sdata, bssid); +	if (!sta) { +		WARN_ONCE(1, "%s: STA %pM not found", sdata->name, bssid); +		goto out_err; +	} +	if (sta_info_move_state(sta, IEEE80211_STA_AUTH)) { +		sdata_info(sdata, "failed moving %pM to auth\n", bssid); +		goto out_err; +	} +	mutex_unlock(&sdata->local->sta_mtx); + +	cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); +	return; + out_err: +	mutex_unlock(&sdata->local->sta_mtx); +	/* ignore frame -- wait for timeout */ +} + +#define case_WLAN(type) \ +	case WLAN_REASON_##type: return #type + +static const char *ieee80211_get_reason_code_string(u16 reason_code) +{ +	switch (reason_code) { +	case_WLAN(UNSPECIFIED); +	case_WLAN(PREV_AUTH_NOT_VALID); +	case_WLAN(DEAUTH_LEAVING); +	case_WLAN(DISASSOC_DUE_TO_INACTIVITY); +	case_WLAN(DISASSOC_AP_BUSY); +	case_WLAN(CLASS2_FRAME_FROM_NONAUTH_STA); +	case_WLAN(CLASS3_FRAME_FROM_NONASSOC_STA); +	case_WLAN(DISASSOC_STA_HAS_LEFT); +	case_WLAN(STA_REQ_ASSOC_WITHOUT_AUTH); +	case_WLAN(DISASSOC_BAD_POWER); +	case_WLAN(DISASSOC_BAD_SUPP_CHAN); +	case_WLAN(INVALID_IE); +	case_WLAN(MIC_FAILURE); +	case_WLAN(4WAY_HANDSHAKE_TIMEOUT); +	case_WLAN(GROUP_KEY_HANDSHAKE_TIMEOUT); +	case_WLAN(IE_DIFFERENT); +	case_WLAN(INVALID_GROUP_CIPHER); +	case_WLAN(INVALID_PAIRWISE_CIPHER); +	case_WLAN(INVALID_AKMP); +	case_WLAN(UNSUPP_RSN_VERSION); +	case_WLAN(INVALID_RSN_IE_CAP); +	case_WLAN(IEEE8021X_FAILED); +	case_WLAN(CIPHER_SUITE_REJECTED); +	case_WLAN(DISASSOC_UNSPECIFIED_QOS); +	case_WLAN(DISASSOC_QAP_NO_BANDWIDTH); +	case_WLAN(DISASSOC_LOW_ACK); +	case_WLAN(DISASSOC_QAP_EXCEED_TXOP); +	case_WLAN(QSTA_LEAVE_QBSS); +	case_WLAN(QSTA_NOT_USE); +	case_WLAN(QSTA_REQUIRE_SETUP); +	case_WLAN(QSTA_TIMEOUT); +	case_WLAN(QSTA_CIPHER_NOT_SUPP); +	case_WLAN(MESH_PEER_CANCELED); +	case_WLAN(MESH_MAX_PEERS); +	case_WLAN(MESH_CONFIG); +	case_WLAN(MESH_CLOSE); +	case_WLAN(MESH_MAX_RETRIES); +	case_WLAN(MESH_CONFIRM_TIMEOUT); +	case_WLAN(MESH_INVALID_GTK); +	case_WLAN(MESH_INCONSISTENT_PARAM); +	case_WLAN(MESH_INVALID_SECURITY); +	case_WLAN(MESH_PATH_ERROR); +	case_WLAN(MESH_PATH_NOFORWARD); +	case_WLAN(MESH_PATH_DEST_UNREACHABLE); +	case_WLAN(MAC_EXISTS_IN_MBSS); +	case_WLAN(MESH_CHAN_REGULATORY); +	case_WLAN(MESH_CHAN); +	default: return "<unknown>"; +	} +} + +static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, +				     struct ieee80211_mgmt *mgmt, size_t len)  {  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	const u8 *bssid = NULL;  	u16 reason_code; +	sdata_assert_lock(sdata); +  	if (len < 24 + 2) -		return RX_MGMT_NONE; +		return; -	ASSERT_MGD_MTX(ifmgd); +	if (!ifmgd->associated || +	    !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) +		return;  	bssid = ifmgd->associated->bssid;  	reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); -	printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n", -			sdata->name, bssid, reason_code); +	sdata_info(sdata, "deauthenticated from %pM (Reason: %u=%s)\n", +		   bssid, reason_code, ieee80211_get_reason_code_string(reason_code)); -	ieee80211_set_disassoc(sdata, true, false); -	mutex_lock(&sdata->local->mtx); -	ieee80211_recalc_idle(sdata->local); -	mutex_unlock(&sdata->local->mtx); +	ieee80211_set_disassoc(sdata, 0, 0, false, NULL); -	return RX_MGMT_CFG80211_DEAUTH; +	cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);  } -static enum rx_mgmt_action __must_check -ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, -			   struct ieee80211_mgmt *mgmt, size_t len) +static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, +				       struct ieee80211_mgmt *mgmt, size_t len)  {  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	u16 reason_code; +	sdata_assert_lock(sdata); +  	if (len < 24 + 2) -		return RX_MGMT_NONE; +		return; -	ASSERT_MGD_MTX(ifmgd); +	if (!ifmgd->associated || +	    !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) +		return; -	if (WARN_ON(!ifmgd->associated)) -		return RX_MGMT_NONE; +	reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); -	if (WARN_ON(memcmp(ifmgd->associated->bssid, mgmt->sa, ETH_ALEN))) -		return RX_MGMT_NONE; +	sdata_info(sdata, "disassociated from %pM (Reason: %u)\n", +		   mgmt->sa, reason_code); -	reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); +	ieee80211_set_disassoc(sdata, 0, 0, false, NULL); -	printk(KERN_DEBUG "%s: disassociated from %pM (Reason: %u)\n", -			sdata->name, mgmt->sa, reason_code); +	cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); +} -	ieee80211_set_disassoc(sdata, true, false); -	mutex_lock(&sdata->local->mtx); -	ieee80211_recalc_idle(sdata->local); -	mutex_unlock(&sdata->local->mtx); -	return RX_MGMT_CFG80211_DISASSOC; +static void ieee80211_get_rates(struct ieee80211_supported_band *sband, +				u8 *supp_rates, unsigned int supp_rates_len, +				u32 *rates, u32 *basic_rates, +				bool *have_higher_than_11mbit, +				int *min_rate, int *min_rate_index, +				int shift, u32 rate_flags) +{ +	int i, j; + +	for (i = 0; i < supp_rates_len; i++) { +		int rate = supp_rates[i] & 0x7f; +		bool is_basic = !!(supp_rates[i] & 0x80); + +		if ((rate * 5 * (1 << shift)) > 110) +			*have_higher_than_11mbit = true; + +		/* +		 * BSS_MEMBERSHIP_SELECTOR_HT_PHY is defined in 802.11n-2009 +		 * 7.3.2.2 as a magic value instead of a rate. Hence, skip it. +		 * +		 * Note: Even through the membership selector and the basic +		 *	 rate flag share the same bit, they are not exactly +		 *	 the same. +		 */ +		if (!!(supp_rates[i] & 0x80) && +		    (supp_rates[i] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY) +			continue; + +		for (j = 0; j < sband->n_bitrates; j++) { +			struct ieee80211_rate *br; +			int brate; + +			br = &sband->bitrates[j]; +			if ((rate_flags & br->flags) != rate_flags) +				continue; + +			brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5); +			if (brate == rate) { +				*rates |= BIT(j); +				if (is_basic) +					*basic_rates |= BIT(j); +				if ((rate * 5) < *min_rate) { +					*min_rate = rate * 5; +					*min_rate_index = j; +				} +				break; +			} +		} +	}  } +static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, +					 bool assoc) +{ +	struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; + +	sdata_assert_lock(sdata); -static bool ieee80211_assoc_success(struct ieee80211_work *wk, +	if (!assoc) { +		sta_info_destroy_addr(sdata, assoc_data->bss->bssid); + +		memset(sdata->u.mgd.bssid, 0, ETH_ALEN); +		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); +		sdata->u.mgd.flags = 0; +		mutex_lock(&sdata->local->mtx); +		ieee80211_vif_release_channel(sdata); +		mutex_unlock(&sdata->local->mtx); +	} + +	kfree(assoc_data); +	sdata->u.mgd.assoc_data = NULL; +} + +static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, +				    struct cfg80211_bss *cbss,  				    struct ieee80211_mgmt *mgmt, size_t len)  { -	struct ieee80211_sub_if_data *sdata = wk->sdata;  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_supported_band *sband;  	struct sta_info *sta; -	struct cfg80211_bss *cbss = wk->assoc.bss;  	u8 *pos; -	u32 rates, basic_rates;  	u16 capab_info, aid;  	struct ieee802_11_elems elems;  	struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; +	const struct cfg80211_bss_ies *bss_ies = NULL; +	struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;  	u32 changed = 0; -	int i, j, err; -	bool have_higher_than_11mbit = false; -	u16 ap_ht_cap_flags; +	int err; +	bool ret;  	/* AssocResp and ReassocResp have identical structure */ @@ -1351,103 +2502,180 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,  	capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);  	if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) -		printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not " -		       "set\n", sdata->name, aid); +		sdata_info(sdata, "invalid AID value 0x%x; bits 15:14 not set\n", +			   aid);  	aid &= ~(BIT(15) | BIT(14)); -	pos = mgmt->u.assoc_resp.variable; -	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); +	ifmgd->broken_ap = false; -	if (!elems.supp_rates) { -		printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n", -		       sdata->name); -		return false; +	if (aid == 0 || aid > IEEE80211_MAX_AID) { +		sdata_info(sdata, "invalid AID value %d (out of range), turn off PS\n", +			   aid); +		aid = 0; +		ifmgd->broken_ap = true;  	} -	ifmgd->aid = aid; +	pos = mgmt->u.assoc_resp.variable; +	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); -	sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL); -	if (!sta) { -		printk(KERN_DEBUG "%s: failed to alloc STA entry for" -		       " the AP\n", sdata->name); +	if (!elems.supp_rates) { +		sdata_info(sdata, "no SuppRates element in AssocResp\n");  		return false;  	} -	set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC | -			   WLAN_STA_ASSOC_AP); -	if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) -		set_sta_flags(sta, WLAN_STA_AUTHORIZED); - -	rates = 0; -	basic_rates = 0; -	sband = local->hw.wiphy->bands[wk->chan->band]; +	ifmgd->aid = aid; -	for (i = 0; i < elems.supp_rates_len; i++) { -		int rate = (elems.supp_rates[i] & 0x7f) * 5; -		bool is_basic = !!(elems.supp_rates[i] & 0x80); +	/* +	 * Some APs are erroneously not including some information in their +	 * (re)association response frames. Try to recover by using the data +	 * from the beacon or probe response. This seems to afflict mobile +	 * 2G/3G/4G wifi routers, reported models include the "Onda PN51T", +	 * "Vodafone PocketWiFi 2", "ZTE MF60" and a similar T-Mobile device. +	 */ +	if ((assoc_data->wmm && !elems.wmm_param) || +	    (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && +	     (!elems.ht_cap_elem || !elems.ht_operation)) || +	    (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) && +	     (!elems.vht_cap_elem || !elems.vht_operation))) { +		const struct cfg80211_bss_ies *ies; +		struct ieee802_11_elems bss_elems; -		if (rate > 110) -			have_higher_than_11mbit = true; +		rcu_read_lock(); +		ies = rcu_dereference(cbss->ies); +		if (ies) +			bss_ies = kmemdup(ies, sizeof(*ies) + ies->len, +					  GFP_ATOMIC); +		rcu_read_unlock(); +		if (!bss_ies) +			return false; + +		ieee802_11_parse_elems(bss_ies->data, bss_ies->len, +				       false, &bss_elems); +		if (assoc_data->wmm && +		    !elems.wmm_param && bss_elems.wmm_param) { +			elems.wmm_param = bss_elems.wmm_param; +			sdata_info(sdata, +				   "AP bug: WMM param missing from AssocResp\n"); +		} -		for (j = 0; j < sband->n_bitrates; j++) { -			if (sband->bitrates[j].bitrate == rate) { -				rates |= BIT(j); -				if (is_basic) -					basic_rates |= BIT(j); -				break; -			} +		/* +		 * Also check if we requested HT/VHT, otherwise the AP doesn't +		 * have to include the IEs in the (re)association response. +		 */ +		if (!elems.ht_cap_elem && bss_elems.ht_cap_elem && +		    !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) { +			elems.ht_cap_elem = bss_elems.ht_cap_elem; +			sdata_info(sdata, +				   "AP bug: HT capability missing from AssocResp\n"); +		} +		if (!elems.ht_operation && bss_elems.ht_operation && +		    !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) { +			elems.ht_operation = bss_elems.ht_operation; +			sdata_info(sdata, +				   "AP bug: HT operation missing from AssocResp\n"); +		} +		if (!elems.vht_cap_elem && bss_elems.vht_cap_elem && +		    !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) { +			elems.vht_cap_elem = bss_elems.vht_cap_elem; +			sdata_info(sdata, +				   "AP bug: VHT capa missing from AssocResp\n"); +		} +		if (!elems.vht_operation && bss_elems.vht_operation && +		    !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) { +			elems.vht_operation = bss_elems.vht_operation; +			sdata_info(sdata, +				   "AP bug: VHT operation missing from AssocResp\n");  		}  	} -	for (i = 0; i < elems.ext_supp_rates_len; i++) { -		int rate = (elems.ext_supp_rates[i] & 0x7f) * 5; -		bool is_basic = !!(elems.ext_supp_rates[i] & 0x80); +	/* +	 * We previously checked these in the beacon/probe response, so +	 * they should be present here. This is just a safety net. +	 */ +	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && +	    (!elems.wmm_param || !elems.ht_cap_elem || !elems.ht_operation)) { +		sdata_info(sdata, +			   "HT AP is missing WMM params or HT capability/operation\n"); +		ret = false; +		goto out; +	} -		if (rate > 110) -			have_higher_than_11mbit = true; +	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) && +	    (!elems.vht_cap_elem || !elems.vht_operation)) { +		sdata_info(sdata, +			   "VHT AP is missing VHT capability/operation\n"); +		ret = false; +		goto out; +	} -		for (j = 0; j < sband->n_bitrates; j++) { -			if (sband->bitrates[j].bitrate == rate) { -				rates |= BIT(j); -				if (is_basic) -					basic_rates |= BIT(j); -				break; -			} -		} +	mutex_lock(&sdata->local->sta_mtx); +	/* +	 * station info was already allocated and inserted before +	 * the association and should be available to us +	 */ +	sta = sta_info_get(sdata, cbss->bssid); +	if (WARN_ON(!sta)) { +		mutex_unlock(&sdata->local->sta_mtx); +		ret = false; +		goto out;  	} -	sta->sta.supp_rates[wk->chan->band] = rates; -	sdata->vif.bss_conf.basic_rates = basic_rates; +	sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; -	/* cf. IEEE 802.11 9.2.12 */ -	if (wk->chan->band == IEEE80211_BAND_2GHZ && -	    have_higher_than_11mbit) -		sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; -	else -		sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; +	/* Set up internal HT/VHT capabilities */ +	if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) +		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, +						  elems.ht_cap_elem, sta); -	if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) -		ieee80211_ht_cap_ie_to_sta_ht_cap(sband, -				elems.ht_cap_elem, &sta->sta.ht_cap); +	if (elems.vht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) +		ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, +						    elems.vht_cap_elem, sta); -	ap_ht_cap_flags = sta->sta.ht_cap.cap; +	/* +	 * Some APs, e.g. Netgear WNDR3700, report invalid HT operation data +	 * in their association response, so ignore that data for our own +	 * configuration. If it changed since the last beacon, we'll get the +	 * next beacon and update then. +	 */ + +	/* +	 * If an operating mode notification IE is present, override the +	 * NSS calculation (that would be done in rate_control_rate_init()) +	 * and use the # of streams from that element. +	 */ +	if (elems.opmode_notif && +	    !(*elems.opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)) { +		u8 nss; + +		nss = *elems.opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK; +		nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; +		nss += 1; +		sta->sta.rx_nss = nss; +	}  	rate_control_rate_init(sta);  	if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) -		set_sta_flags(sta, WLAN_STA_MFP); +		set_sta_flag(sta, WLAN_STA_MFP);  	if (elems.wmm_param) -		set_sta_flags(sta, WLAN_STA_WME); +		set_sta_flag(sta, WLAN_STA_WME); -	err = sta_info_insert(sta); -	sta = NULL; +	err = sta_info_move_state(sta, IEEE80211_STA_ASSOC); +	if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) +		err = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);  	if (err) { -		printk(KERN_DEBUG "%s: failed to insert STA entry for" -		       " the AP (error %d)\n", sdata->name, err); -		return false; +		sdata_info(sdata, +			   "failed to move station %pM to desired state\n", +			   sta->sta.addr); +		WARN_ON(__sta_info_destroy(sta)); +		mutex_unlock(&sdata->local->sta_mtx); +		ret = false; +		goto out;  	} +	mutex_unlock(&sdata->local->sta_mtx); +  	/*  	 * Always handle WMM once after association regardless  	 * of the first value the AP uses. Setting -1 here has @@ -1456,19 +2684,12 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,  	 */  	ifmgd->wmm_last_param_set = -1; -	if (elems.wmm_param) +	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && elems.wmm_param)  		ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,  					 elems.wmm_param_len);  	else -		ieee80211_set_wmm_default(sdata); - -	local->oper_channel = wk->chan; - -	if (elems.ht_info_elem && elems.wmm_param && -	    (sdata->local->hw.queues >= 4) && -	    !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) -		changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, -					       cbss->bssid, ap_ht_cap_flags); +		ieee80211_set_wmm_default(sdata, false); +	changed |= BSS_CHANGED_QOS;  	/* set AID and assoc capability,  	 * ieee80211_set_associated() will tell the driver */ @@ -1490,60 +2711,114 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,  	ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);  	ieee80211_sta_reset_beacon_monitor(sdata); -	return true; +	ret = true; + out: +	kfree(bss_ies); +	return ret;  } +static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, +					 struct ieee80211_mgmt *mgmt, +					 size_t len) +{ +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; +	u16 capab_info, status_code, aid; +	struct ieee802_11_elems elems; +	u8 *pos; +	bool reassoc; +	struct cfg80211_bss *bss; + +	sdata_assert_lock(sdata); + +	if (!assoc_data) +		return; +	if (!ether_addr_equal(assoc_data->bss->bssid, mgmt->bssid)) +		return; + +	/* +	 * AssocResp and ReassocResp have identical structure, so process both +	 * of them in this function. +	 */ + +	if (len < 24 + 6) +		return; + +	reassoc = ieee80211_is_reassoc_req(mgmt->frame_control); +	capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); +	status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); +	aid = le16_to_cpu(mgmt->u.assoc_resp.aid); + +	sdata_info(sdata, +		   "RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n", +		   reassoc ? "Rea" : "A", mgmt->sa, +		   capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); + +	pos = mgmt->u.assoc_resp.variable; +	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); + +	if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && +	    elems.timeout_int && +	    elems.timeout_int->type == WLAN_TIMEOUT_ASSOC_COMEBACK) { +		u32 tu, ms; +		tu = le32_to_cpu(elems.timeout_int->value); +		ms = tu * 1024 / 1000; +		sdata_info(sdata, +			   "%pM rejected association temporarily; comeback duration %u TU (%u ms)\n", +			   mgmt->sa, tu, ms); +		assoc_data->timeout = jiffies + msecs_to_jiffies(ms); +		assoc_data->timeout_started = true; +		if (ms > IEEE80211_ASSOC_TIMEOUT) +			run_again(sdata, assoc_data->timeout); +		return; +	} + +	bss = assoc_data->bss; + +	if (status_code != WLAN_STATUS_SUCCESS) { +		sdata_info(sdata, "%pM denied association (code=%d)\n", +			   mgmt->sa, status_code); +		ieee80211_destroy_assoc_data(sdata, false); +	} else { +		if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) { +			/* oops -- internal error -- send timeout for now */ +			ieee80211_destroy_assoc_data(sdata, false); +			cfg80211_assoc_timeout(sdata->dev, bss); +			return; +		} +		sdata_info(sdata, "associated\n"); + +		/* +		 * destroy assoc_data afterwards, as otherwise an idle +		 * recalc after assoc_data is NULL but before associated +		 * is set can cause the interface to go idle +		 */ +		ieee80211_destroy_assoc_data(sdata, true); +	} + +	cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len); +}  static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, -				  struct ieee80211_mgmt *mgmt, -				  size_t len, +				  struct ieee80211_mgmt *mgmt, size_t len,  				  struct ieee80211_rx_status *rx_status, -				  struct ieee802_11_elems *elems, -				  bool beacon) +				  struct ieee802_11_elems *elems)  {  	struct ieee80211_local *local = sdata->local; -	int freq;  	struct ieee80211_bss *bss;  	struct ieee80211_channel *channel; -	bool need_ps = false; -	if (sdata->u.mgd.associated) { -		bss = (void *)sdata->u.mgd.associated->priv; -		/* not previously set so we may need to recalc */ -		need_ps = !bss->dtim_period; -	} - -	if (elems->ds_params && elems->ds_params_len == 1) -		freq = ieee80211_channel_to_frequency(elems->ds_params[0]); -	else -		freq = rx_status->freq; +	sdata_assert_lock(sdata); -	channel = ieee80211_get_channel(local->hw.wiphy, freq); - -	if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) +	channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq); +	if (!channel)  		return;  	bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, -					channel, beacon); -	if (bss) +					channel); +	if (bss) { +		sdata->vif.bss_conf.beacon_rate = bss->beacon_rate;  		ieee80211_rx_bss_put(local, bss); - -	if (!sdata->u.mgd.associated) -		return; - -	if (need_ps) { -		mutex_lock(&local->iflist_mtx); -		ieee80211_recalc_ps(local, -1); -		mutex_unlock(&local->iflist_mtx); -	} - -	if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) && -	    (memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid, -							ETH_ALEN) == 0)) { -		struct ieee80211_channel_sw_ie *sw_elem = -			(struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; -		ieee80211_sta_process_chanswitch(sdata, sw_elem, -						 bss, rx_status->mactime);  	}  } @@ -1559,9 +2834,9 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,  	ifmgd = &sdata->u.mgd; -	ASSERT_MGD_MTX(ifmgd); +	sdata_assert_lock(sdata); -	if (memcmp(mgmt->da, sdata->vif.addr, ETH_ALEN)) +	if (!ether_addr_equal(mgmt->da, sdata->vif.addr))  		return; /* ignore ProbeResp to foreign address */  	baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; @@ -1569,13 +2844,23 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,  		return;  	ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, -				&elems); +			       false, &elems); -	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); +	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);  	if (ifmgd->associated && -	    memcmp(mgmt->bssid, ifmgd->associated->bssid, ETH_ALEN) == 0) +	    ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))  		ieee80211_reset_ap_probe(sdata); + +	if (ifmgd->auth_data && !ifmgd->auth_data->bss->proberesp_ies && +	    ether_addr_equal(mgmt->bssid, ifmgd->auth_data->bss->bssid)) { +		/* got probe response, continue with auth */ +		sdata_info(sdata, "direct probe responded\n"); +		ifmgd->auth_data->tries = 0; +		ifmgd->auth_data->timeout = jiffies; +		ifmgd->auth_data->timeout_started = true; +		run_again(sdata, ifmgd->auth_data->timeout); +	}  }  /* @@ -1597,11 +2882,10 @@ static const u64 care_about_ies =  	(1ULL << WLAN_EID_CHANNEL_SWITCH) |  	(1ULL << WLAN_EID_PWR_CONSTRAINT) |  	(1ULL << WLAN_EID_HT_CAPABILITY) | -	(1ULL << WLAN_EID_HT_INFORMATION); +	(1ULL << WLAN_EID_HT_OPERATION);  static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, -				     struct ieee80211_mgmt *mgmt, -				     size_t len, +				     struct ieee80211_mgmt *mgmt, size_t len,  				     struct ieee80211_rx_status *rx_status)  {  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -1609,37 +2893,71 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  	size_t baselen;  	struct ieee802_11_elems elems;  	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx_conf *chanctx_conf; +	struct ieee80211_channel *chan; +	struct sta_info *sta;  	u32 changed = 0; -	bool erp_valid, directed_tim = false; +	bool erp_valid;  	u8 erp_value = 0;  	u32 ncrc;  	u8 *bssid; +	u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN]; -	ASSERT_MGD_MTX(ifmgd); +	sdata_assert_lock(sdata);  	/* Process beacon from the current BSS */  	baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;  	if (baselen > len)  		return; -	if (rx_status->freq != local->hw.conf.channel->center_freq) +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	if (!chanctx_conf) { +		rcu_read_unlock();  		return; +	} -	/* -	 * We might have received a number of frames, among them a -	 * disassoc frame and a beacon... -	 */ -	if (!ifmgd->associated) +	if (rx_status->freq != chanctx_conf->def.chan->center_freq) { +		rcu_read_unlock();  		return; +	} +	chan = chanctx_conf->def.chan; +	rcu_read_unlock(); -	bssid = ifmgd->associated->bssid; +	if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon && +	    ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) { +		ieee802_11_parse_elems(mgmt->u.beacon.variable, +				       len - baselen, false, &elems); -	/* -	 * And in theory even frames from a different AP we were just -	 * associated to a split-second ago! -	 */ -	if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0) +		ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); +		if (elems.tim && !elems.parse_error) { +			const struct ieee80211_tim_ie *tim_ie = elems.tim; +			ifmgd->dtim_period = tim_ie->dtim_period; +		} +		ifmgd->have_beacon = true; +		ifmgd->assoc_data->need_beacon = false; +		if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) { +			sdata->vif.bss_conf.sync_tsf = +				le64_to_cpu(mgmt->u.beacon.timestamp); +			sdata->vif.bss_conf.sync_device_ts = +				rx_status->device_timestamp; +			if (elems.tim) +				sdata->vif.bss_conf.sync_dtim_count = +					elems.tim->dtim_count; +			else +				sdata->vif.bss_conf.sync_dtim_count = 0; +		} +		/* continue assoc process */ +		ifmgd->assoc_data->timeout = jiffies; +		ifmgd->assoc_data->timeout_started = true; +		run_again(sdata, ifmgd->assoc_data->timeout);  		return; +	} + +	if (!ifmgd->associated || +	    !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) +		return; +	bssid = ifmgd->associated->bssid;  	/* Track average RSSI from the Beacon frames of the current AP */  	ifmgd->last_beacon_signal = rx_status->signal; @@ -1648,6 +2966,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  		ifmgd->ave_beacon_signal = rx_status->signal * 16;  		ifmgd->last_cqm_event_signal = 0;  		ifmgd->count_beacon_signal = 1; +		ifmgd->last_ave_beacon_signal = 0;  	} else {  		ifmgd->ave_beacon_signal =  			(IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 + @@ -1655,9 +2974,31 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  			 ifmgd->ave_beacon_signal) / 16;  		ifmgd->count_beacon_signal++;  	} + +	if (ifmgd->rssi_min_thold != ifmgd->rssi_max_thold && +	    ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) { +		int sig = ifmgd->ave_beacon_signal; +		int last_sig = ifmgd->last_ave_beacon_signal; + +		/* +		 * if signal crosses either of the boundaries, invoke callback +		 * with appropriate parameters +		 */ +		if (sig > ifmgd->rssi_max_thold && +		    (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) { +			ifmgd->last_ave_beacon_signal = sig; +			drv_rssi_callback(local, sdata, RSSI_EVENT_HIGH); +		} else if (sig < ifmgd->rssi_min_thold && +			   (last_sig >= ifmgd->rssi_max_thold || +			   last_sig == 0)) { +			ifmgd->last_ave_beacon_signal = sig; +			drv_rssi_callback(local, sdata, RSSI_EVENT_LOW); +		} +	} +  	if (bss_conf->cqm_rssi_thold &&  	    ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT && -	    !(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) { +	    !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) {  		int sig = ifmgd->ave_beacon_signal / 16;  		int last_event = ifmgd->last_cqm_event_signal;  		int thold = bss_conf->cqm_rssi_thold; @@ -1679,17 +3020,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  		}  	} -	if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -		if (net_ratelimit()) { -			printk(KERN_DEBUG "%s: cancelling probereq poll due " -			       "to a received beacon\n", sdata->name); -		} -#endif -		ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL; -		mutex_lock(&local->iflist_mtx); -		ieee80211_recalc_ps(local, -1); -		mutex_unlock(&local->iflist_mtx); +	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) { +		mlme_dbg_ratelimited(sdata, +				     "cancelling AP probe due to a received beacon\n"); +		ieee80211_reset_ap_probe(sdata);  	}  	/* @@ -1700,29 +3034,22 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  	ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);  	ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable, -					  len - baselen, &elems, +					  len - baselen, false, &elems,  					  care_about_ies, ncrc); -	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) -		directed_tim = ieee80211_check_tim(elems.tim, elems.tim_len, -						   ifmgd->aid); - -	if (ncrc != ifmgd->beacon_crc || !ifmgd->beacon_crc_valid) { -		ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, -				      true); - -		ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, -					 elems.wmm_param_len); -	} -  	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) { +		bool directed_tim = ieee80211_check_tim(elems.tim, +							elems.tim_len, +							ifmgd->aid);  		if (directed_tim) {  			if (local->hw.conf.dynamic_ps_timeout > 0) { -				local->hw.conf.flags &= ~IEEE80211_CONF_PS; -				ieee80211_hw_config(local, -						    IEEE80211_CONF_CHANGE_PS); +				if (local->hw.conf.flags & IEEE80211_CONF_PS) { +					local->hw.conf.flags &= ~IEEE80211_CONF_PS; +					ieee80211_hw_config(local, +							    IEEE80211_CONF_CHANGE_PS); +				}  				ieee80211_send_nullfunc(local, sdata, 0); -			} else { +			} else if (!local->pspolling && sdata->u.mgd.powersave) {  				local->pspolling = true;  				/* @@ -1738,12 +3065,84 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  		}  	} +	if (sdata->vif.p2p) { +		struct ieee80211_p2p_noa_attr noa = {}; +		int ret; + +		ret = cfg80211_get_p2p_attr(mgmt->u.beacon.variable, +					    len - baselen, +					    IEEE80211_P2P_ATTR_ABSENCE_NOTICE, +					    (u8 *) &noa, sizeof(noa)); +		if (ret >= 2) { +			if (sdata->u.mgd.p2p_noa_index != noa.index) { +				/* valid noa_attr and index changed */ +				sdata->u.mgd.p2p_noa_index = noa.index; +				memcpy(&bss_conf->p2p_noa_attr, &noa, sizeof(noa)); +				changed |= BSS_CHANGED_P2P_PS; +				/* +				 * make sure we update all information, the CRC +				 * mechanism doesn't look at P2P attributes. +				 */ +				ifmgd->beacon_crc_valid = false; +			} +		} else if (sdata->u.mgd.p2p_noa_index != -1) { +			/* noa_attr not found and we had valid noa_attr before */ +			sdata->u.mgd.p2p_noa_index = -1; +			memset(&bss_conf->p2p_noa_attr, 0, sizeof(bss_conf->p2p_noa_attr)); +			changed |= BSS_CHANGED_P2P_PS; +			ifmgd->beacon_crc_valid = false; +		} +	} +  	if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)  		return;  	ifmgd->beacon_crc = ncrc;  	ifmgd->beacon_crc_valid = true; -	if (elems.erp_info && elems.erp_info_len >= 1) { +	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); + +	ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, +					 &elems, true); + +	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && +	    ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, +				     elems.wmm_param_len)) +		changed |= BSS_CHANGED_QOS; + +	/* +	 * If we haven't had a beacon before, tell the driver about the +	 * DTIM period (and beacon timing if desired) now. +	 */ +	if (!ifmgd->have_beacon) { +		/* a few bogus AP send dtim_period = 0 or no TIM IE */ +		if (elems.tim) +			bss_conf->dtim_period = elems.tim->dtim_period ?: 1; +		else +			bss_conf->dtim_period = 1; + +		if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) { +			sdata->vif.bss_conf.sync_tsf = +				le64_to_cpu(mgmt->u.beacon.timestamp); +			sdata->vif.bss_conf.sync_device_ts = +				rx_status->device_timestamp; +			if (elems.tim) +				sdata->vif.bss_conf.sync_dtim_count = +					elems.tim->dtim_count; +			else +				sdata->vif.bss_conf.sync_dtim_count = 0; +		} + +		changed |= BSS_CHANGED_BEACON_INFO; +		ifmgd->have_beacon = true; + +		mutex_lock(&local->iflist_mtx); +		ieee80211_recalc_ps(local, -1); +		mutex_unlock(&local->iflist_mtx); + +		ieee80211_recalc_ps_vif(sdata); +	} + +	if (elems.erp_info) {  		erp_valid = true;  		erp_value = elems.erp_info[0];  	} else { @@ -1753,43 +3152,32 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  			le16_to_cpu(mgmt->u.beacon.capab_info),  			erp_valid, erp_value); +	mutex_lock(&local->sta_mtx); +	sta = sta_info_get(sdata, bssid); -	if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param && -	    !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) { -		struct sta_info *sta; -		struct ieee80211_supported_band *sband; -		u16 ap_ht_cap_flags; - -		rcu_read_lock(); - -		sta = sta_info_get(sdata, bssid); -		if (WARN_ON(!sta)) { -			rcu_read_unlock(); -			return; -		} - -		sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; - -		ieee80211_ht_cap_ie_to_sta_ht_cap(sband, -				elems.ht_cap_elem, &sta->sta.ht_cap); - -		ap_ht_cap_flags = sta->sta.ht_cap.cap; - -		rcu_read_unlock(); - -		changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, -					       bssid, ap_ht_cap_flags); +	if (ieee80211_config_bw(sdata, sta, elems.ht_operation, +				elems.vht_operation, bssid, &changed)) { +		mutex_unlock(&local->sta_mtx); +		ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, +				       WLAN_REASON_DEAUTH_LEAVING, +				       true, deauth_buf); +		cfg80211_tx_mlme_mgmt(sdata->dev, deauth_buf, +				      sizeof(deauth_buf)); +		return;  	} -	/* Note: country IE parsing is done for us by cfg80211 */ -	if (elems.country_elem) { -		/* TODO: IBSS also needs this */ -		if (elems.pwr_constr_elem) -			ieee80211_handle_pwr_constr(sdata, -				le16_to_cpu(mgmt->u.probe_resp.capab_info), -				elems.pwr_constr_elem, -				elems.pwr_constr_elem_len); -	} +	if (sta && elems.opmode_notif) +		ieee80211_vht_handle_opmode(sdata, sta, *elems.opmode_notif, +					    rx_status->band, true); +	mutex_unlock(&local->sta_mtx); + +	if (elems.country_elem && elems.pwr_constr_elem && +	    mgmt->u.probe_resp.capab_info & +				cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) +		changed |= ieee80211_handle_pwr_constr(sdata, chan, +						       elems.country_elem, +						       elems.country_elem_len, +						       elems.pwr_constr_elem);  	ieee80211_bss_info_change_notify(sdata, changed);  } @@ -1797,119 +3185,246 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  				  struct sk_buff *skb)  { -	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	struct ieee80211_rx_status *rx_status;  	struct ieee80211_mgmt *mgmt; -	enum rx_mgmt_action rma = RX_MGMT_NONE;  	u16 fc; +	struct ieee802_11_elems elems; +	int ies_len;  	rx_status = (struct ieee80211_rx_status *) skb->cb;  	mgmt = (struct ieee80211_mgmt *) skb->data;  	fc = le16_to_cpu(mgmt->frame_control); -	mutex_lock(&ifmgd->mtx); +	sdata_lock(sdata); -	if (ifmgd->associated && -	    memcmp(ifmgd->associated->bssid, mgmt->bssid, ETH_ALEN) == 0) { -		switch (fc & IEEE80211_FCTL_STYPE) { -		case IEEE80211_STYPE_BEACON: -			ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, -						 rx_status); -			break; -		case IEEE80211_STYPE_PROBE_RESP: -			ieee80211_rx_mgmt_probe_resp(sdata, skb); -			break; -		case IEEE80211_STYPE_DEAUTH: -			rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len); -			break; -		case IEEE80211_STYPE_DISASSOC: -			rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len); -			break; -		case IEEE80211_STYPE_ACTION: -			switch (mgmt->u.action.category) { -			case WLAN_CATEGORY_SPECTRUM_MGMT: -				ieee80211_sta_process_chanswitch(sdata, -						&mgmt->u.action.u.chan_switch.sw_elem, -						(void *)ifmgd->associated->priv, -						rx_status->mactime); +	switch (fc & IEEE80211_FCTL_STYPE) { +	case IEEE80211_STYPE_BEACON: +		ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status); +		break; +	case IEEE80211_STYPE_PROBE_RESP: +		ieee80211_rx_mgmt_probe_resp(sdata, skb); +		break; +	case IEEE80211_STYPE_AUTH: +		ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len); +		break; +	case IEEE80211_STYPE_DEAUTH: +		ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len); +		break; +	case IEEE80211_STYPE_DISASSOC: +		ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len); +		break; +	case IEEE80211_STYPE_ASSOC_RESP: +	case IEEE80211_STYPE_REASSOC_RESP: +		ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len); +		break; +	case IEEE80211_STYPE_ACTION: +		if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) { +			ies_len = skb->len - +				  offsetof(struct ieee80211_mgmt, +					   u.action.u.chan_switch.variable); + +			if (ies_len < 0)  				break; -			} -		} -		mutex_unlock(&ifmgd->mtx); -		switch (rma) { -		case RX_MGMT_NONE: -			/* no action */ -			break; -		case RX_MGMT_CFG80211_DEAUTH: -			cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); -			break; -		case RX_MGMT_CFG80211_DISASSOC: -			cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); -			break; -		default: -			WARN(1, "unexpected: %d", rma); -		} -		return; -	} +			ieee802_11_parse_elems( +				mgmt->u.action.u.chan_switch.variable, +				ies_len, true, &elems); -	mutex_unlock(&ifmgd->mtx); +			if (elems.parse_error) +				break; -	if (skb->len >= 24 + 2 /* mgmt + deauth reason */ && -	    (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DEAUTH) { -		struct ieee80211_local *local = sdata->local; -		struct ieee80211_work *wk; +			ieee80211_sta_process_chanswitch(sdata, +							 rx_status->mactime, +							 &elems, false); +		} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { +			ies_len = skb->len - +				  offsetof(struct ieee80211_mgmt, +					   u.action.u.ext_chan_switch.variable); -		mutex_lock(&local->mtx); -		list_for_each_entry(wk, &local->work_list, list) { -			if (wk->sdata != sdata) -				continue; +			if (ies_len < 0) +				break; -			if (wk->type != IEEE80211_WORK_ASSOC && -			    wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT) -				continue; +			ieee802_11_parse_elems( +				mgmt->u.action.u.ext_chan_switch.variable, +				ies_len, true, &elems); -			if (memcmp(mgmt->bssid, wk->filter_ta, ETH_ALEN)) -				continue; -			if (memcmp(mgmt->sa, wk->filter_ta, ETH_ALEN)) -				continue; +			if (elems.parse_error) +				break; -			/* -			 * Printing the message only here means we can't -			 * spuriously print it, but it also means that it -			 * won't be printed when the frame comes in before -			 * we even tried to associate or in similar cases. -			 * -			 * Ultimately, I suspect cfg80211 should print the -			 * messages instead. -			 */ -			printk(KERN_DEBUG -			       "%s: deauthenticated from %pM (Reason: %u)\n", -			       sdata->name, mgmt->bssid, -			       le16_to_cpu(mgmt->u.deauth.reason_code)); +			/* for the handling code pretend this was also an IE */ +			elems.ext_chansw_ie = +				&mgmt->u.action.u.ext_chan_switch.data; -			list_del_rcu(&wk->list); -			free_work(wk); -			break; +			ieee80211_sta_process_chanswitch(sdata, +							 rx_status->mactime, +							 &elems, false);  		} -		mutex_unlock(&local->mtx); - -		cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); +		break;  	} +	sdata_unlock(sdata);  }  static void ieee80211_sta_timer(unsigned long data)  {  	struct ieee80211_sub_if_data *sdata =  		(struct ieee80211_sub_if_data *) data; + +	ieee80211_queue_work(&sdata->local->hw, &sdata->work); +} + +static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, +					  u8 *bssid, u8 reason, bool tx) +{ +	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; + +	ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason, +			       tx, frame_buf); + +	cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, +			      IEEE80211_DEAUTH_FRAME_LEN); +} + +static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local;  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data; +	u32 tx_flags = 0; + +	sdata_assert_lock(sdata); + +	if (WARN_ON_ONCE(!auth_data)) +		return -EINVAL; + +	auth_data->tries++; + +	if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) { +		sdata_info(sdata, "authentication with %pM timed out\n", +			   auth_data->bss->bssid); + +		/* +		 * Most likely AP is not in the range so remove the +		 * bss struct for that AP. +		 */ +		cfg80211_unlink_bss(local->hw.wiphy, auth_data->bss); + +		return -ETIMEDOUT; +	} + +	drv_mgd_prepare_tx(local, sdata); + +	if (auth_data->bss->proberesp_ies) { +		u16 trans = 1; +		u16 status = 0; + +		sdata_info(sdata, "send auth to %pM (try %d/%d)\n", +			   auth_data->bss->bssid, auth_data->tries, +			   IEEE80211_AUTH_MAX_TRIES); + +		auth_data->expected_transaction = 2; + +		if (auth_data->algorithm == WLAN_AUTH_SAE) { +			trans = auth_data->sae_trans; +			status = auth_data->sae_status; +			auth_data->expected_transaction = trans; +		} + +		if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) +			tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS | +				   IEEE80211_TX_INTFL_MLME_CONN_TX; + +		ieee80211_send_auth(sdata, trans, auth_data->algorithm, status, +				    auth_data->data, auth_data->data_len, +				    auth_data->bss->bssid, +				    auth_data->bss->bssid, NULL, 0, 0, +				    tx_flags); +	} else { +		const u8 *ssidie; + +		sdata_info(sdata, "direct probe to %pM (try %d/%i)\n", +			   auth_data->bss->bssid, auth_data->tries, +			   IEEE80211_AUTH_MAX_TRIES); + +		rcu_read_lock(); +		ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID); +		if (!ssidie) { +			rcu_read_unlock(); +			return -EINVAL; +		} +		/* +		 * Direct probe is sent to broadcast address as some APs +		 * will not answer to direct packet in unassociated state. +		 */ +		ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], +					 NULL, 0, (u32) -1, true, 0, +					 auth_data->bss->channel, false); +		rcu_read_unlock(); +	} + +	if (tx_flags == 0) { +		auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; +		auth_data->timeout_started = true; +		run_again(sdata, auth_data->timeout); +	} else { +		auth_data->timeout = +			round_jiffies_up(jiffies + IEEE80211_AUTH_TIMEOUT_LONG); +		auth_data->timeout_started = true; +		run_again(sdata, auth_data->timeout); +	} + +	return 0; +} + +static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;  	struct ieee80211_local *local = sdata->local; -	if (local->quiescing) { -		set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); -		return; +	sdata_assert_lock(sdata); + +	assoc_data->tries++; +	if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) { +		sdata_info(sdata, "association with %pM timed out\n", +			   assoc_data->bss->bssid); + +		/* +		 * Most likely AP is not in the range so remove the +		 * bss struct for that AP. +		 */ +		cfg80211_unlink_bss(local->hw.wiphy, assoc_data->bss); + +		return -ETIMEDOUT;  	} +	sdata_info(sdata, "associate with %pM (try %d/%d)\n", +		   assoc_data->bss->bssid, assoc_data->tries, +		   IEEE80211_ASSOC_MAX_TRIES); +	ieee80211_send_assoc(sdata); + +	if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) { +		assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; +		assoc_data->timeout_started = true; +		run_again(sdata, assoc_data->timeout); +	} else { +		assoc_data->timeout = +			round_jiffies_up(jiffies + +					 IEEE80211_ASSOC_TIMEOUT_LONG); +		assoc_data->timeout_started = true; +		run_again(sdata, assoc_data->timeout); +	} + +	return 0; +} + +void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata, +				  __le16 fc, bool acked) +{ +	struct ieee80211_local *local = sdata->local; + +	sdata->u.mgd.status_fc = fc; +	sdata->u.mgd.status_acked = acked; +	sdata->u.mgd.status_received = true; +  	ieee80211_queue_work(&local->hw, &sdata->work);  } @@ -1918,11 +3433,70 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; -	/* then process the rest of the work */ -	mutex_lock(&ifmgd->mtx); +	sdata_lock(sdata); + +	if (ifmgd->status_received) { +		__le16 fc = ifmgd->status_fc; +		bool status_acked = ifmgd->status_acked; + +		ifmgd->status_received = false; +		if (ifmgd->auth_data && +		    (ieee80211_is_probe_req(fc) || ieee80211_is_auth(fc))) { +			if (status_acked) { +				ifmgd->auth_data->timeout = +					jiffies + IEEE80211_AUTH_TIMEOUT_SHORT; +				run_again(sdata, ifmgd->auth_data->timeout); +			} else { +				ifmgd->auth_data->timeout = jiffies - 1; +			} +			ifmgd->auth_data->timeout_started = true; +		} else if (ifmgd->assoc_data && +			   (ieee80211_is_assoc_req(fc) || +			    ieee80211_is_reassoc_req(fc))) { +			if (status_acked) { +				ifmgd->assoc_data->timeout = +					jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT; +				run_again(sdata, ifmgd->assoc_data->timeout); +			} else { +				ifmgd->assoc_data->timeout = jiffies - 1; +			} +			ifmgd->assoc_data->timeout_started = true; +		} +	} + +	if (ifmgd->auth_data && ifmgd->auth_data->timeout_started && +	    time_after(jiffies, ifmgd->auth_data->timeout)) { +		if (ifmgd->auth_data->done) { +			/* +			 * ok ... we waited for assoc but userspace didn't, +			 * so let's just kill the auth data +			 */ +			ieee80211_destroy_auth_data(sdata, false); +		} else if (ieee80211_probe_auth(sdata)) { +			u8 bssid[ETH_ALEN]; + +			memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN); + +			ieee80211_destroy_auth_data(sdata, false); -	if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | -			    IEEE80211_STA_CONNECTION_POLL) && +			cfg80211_auth_timeout(sdata->dev, bssid); +		} +	} else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started) +		run_again(sdata, ifmgd->auth_data->timeout); + +	if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started && +	    time_after(jiffies, ifmgd->assoc_data->timeout)) { +		if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) || +		    ieee80211_do_assoc(sdata)) { +			struct cfg80211_bss *bss = ifmgd->assoc_data->bss; + +			ieee80211_destroy_assoc_data(sdata, false); +			cfg80211_assoc_timeout(sdata->dev, bss); +		} +	} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started) +		run_again(sdata, ifmgd->assoc_data->timeout); + +	if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL &&  	    ifmgd->associated) {  		u8 bssid[ETH_ALEN];  		int max_tries; @@ -1930,57 +3504,59 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)  		memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);  		if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) -			max_tries = IEEE80211_MAX_NULLFUNC_TRIES; +			max_tries = max_nullfunc_tries;  		else -			max_tries = IEEE80211_MAX_PROBE_TRIES; +			max_tries = max_probe_tries;  		/* ACK received for nullfunc probing frame */  		if (!ifmgd->probe_send_count)  			ieee80211_reset_ap_probe(sdata); - -		else if (time_is_after_jiffies(ifmgd->probe_timeout)) -			run_again(ifmgd, ifmgd->probe_timeout); - -		else if (ifmgd->probe_send_count < max_tries) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -			wiphy_debug(local->hw.wiphy, -				    "%s: No probe response from AP %pM" -				    " after %dms, try %d\n", -				    sdata->name, -				    bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ, -				    ifmgd->probe_send_count); -#endif +		else if (ifmgd->nullfunc_failed) { +			if (ifmgd->probe_send_count < max_tries) { +				mlme_dbg(sdata, +					 "No ack for nullfunc frame to AP %pM, try %d/%i\n", +					 bssid, ifmgd->probe_send_count, +					 max_tries); +				ieee80211_mgd_probe_ap_send(sdata); +			} else { +				mlme_dbg(sdata, +					 "No ack for nullfunc frame to AP %pM, disconnecting.\n", +					 bssid); +				ieee80211_sta_connection_lost(sdata, bssid, +					WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, +					false); +			} +		} else if (time_is_after_jiffies(ifmgd->probe_timeout)) +			run_again(sdata, ifmgd->probe_timeout); +		else if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) { +			mlme_dbg(sdata, +				 "Failed to send nullfunc to AP %pM after %dms, disconnecting\n", +				 bssid, probe_wait_ms); +			ieee80211_sta_connection_lost(sdata, bssid, +				WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false); +		} else if (ifmgd->probe_send_count < max_tries) { +			mlme_dbg(sdata, +				 "No probe response from AP %pM after %dms, try %d/%i\n", +				 bssid, probe_wait_ms, +				 ifmgd->probe_send_count, max_tries);  			ieee80211_mgd_probe_ap_send(sdata);  		} else {  			/*  			 * We actually lost the connection ... or did we?  			 * Let's make sure!  			 */ -			ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL | -					  IEEE80211_STA_BEACON_POLL);  			wiphy_debug(local->hw.wiphy,  				    "%s: No probe response from AP %pM"  				    " after %dms, disconnecting.\n",  				    sdata->name, -				    bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ); -			ieee80211_set_disassoc(sdata, true, true); -			mutex_unlock(&ifmgd->mtx); -			mutex_lock(&local->mtx); -			ieee80211_recalc_idle(local); -			mutex_unlock(&local->mtx); -			/* -			 * must be outside lock due to cfg80211, -			 * but that's not a problem. -			 */ -			ieee80211_send_deauth_disassoc(sdata, bssid, -					IEEE80211_STYPE_DEAUTH, -					WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, -					NULL, true); -			mutex_lock(&ifmgd->mtx); +				    bssid, probe_wait_ms); + +			ieee80211_sta_connection_lost(sdata, bssid, +				WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);  		}  	} -	mutex_unlock(&ifmgd->mtx); +	sdata_unlock(sdata);  }  static void ieee80211_sta_bcn_mon_timer(unsigned long data) @@ -1992,6 +3568,10 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)  	if (local->quiescing)  		return; +	if (sdata->vif.csa_active) +		return; + +	sdata->u.mgd.connection_loss = false;  	ieee80211_queue_work(&sdata->local->hw,  			     &sdata->u.mgd.beacon_connection_loss_work);  } @@ -2006,6 +3586,9 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data)  	if (local->quiescing)  		return; +	if (sdata->vif.csa_active) +		return; +  	ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);  } @@ -2020,55 +3603,75 @@ static void ieee80211_sta_monitor_work(struct work_struct *work)  static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)  { +	u32 flags; +  	if (sdata->vif.type == NL80211_IFTYPE_STATION) { -		sdata->u.mgd.flags &= ~(IEEE80211_STA_BEACON_POLL | -					IEEE80211_STA_CONNECTION_POLL); +		__ieee80211_stop_poll(sdata);  		/* let's probe the connection once */ -		ieee80211_queue_work(&sdata->local->hw, -			   &sdata->u.mgd.monitor_work); +		flags = sdata->local->hw.flags; +		if (!(flags & IEEE80211_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);  	}  }  #ifdef CONFIG_PM -void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) +void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; -	/* -	 * we need to use atomic bitops for the running bits -	 * only because both timers might fire at the same -	 * time -- the code here is properly synchronised. -	 */ +	sdata_lock(sdata); -	cancel_work_sync(&ifmgd->request_smps_work); +	if (ifmgd->auth_data || ifmgd->assoc_data) { +		const u8 *bssid = ifmgd->auth_data ? +				ifmgd->auth_data->bss->bssid : +				ifmgd->assoc_data->bss->bssid; -	cancel_work_sync(&ifmgd->beacon_connection_loss_work); -	if (del_timer_sync(&ifmgd->timer)) -		set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); - -	cancel_work_sync(&ifmgd->chswitch_work); -	if (del_timer_sync(&ifmgd->chswitch_timer)) -		set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running); +		/* +		 * If we are trying to authenticate / associate while suspending, +		 * cfg80211 won't know and won't actually abort those attempts, +		 * thus we need to do that ourselves. +		 */ +		ieee80211_send_deauth_disassoc(sdata, bssid, +					       IEEE80211_STYPE_DEAUTH, +					       WLAN_REASON_DEAUTH_LEAVING, +					       false, frame_buf); +		if (ifmgd->assoc_data) +			ieee80211_destroy_assoc_data(sdata, false); +		if (ifmgd->auth_data) +			ieee80211_destroy_auth_data(sdata, false); +		cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, +				      IEEE80211_DEAUTH_FRAME_LEN); +	} -	cancel_work_sync(&ifmgd->monitor_work); -	/* these will just be re-established on connection */ -	del_timer_sync(&ifmgd->conn_mon_timer); -	del_timer_sync(&ifmgd->bcn_mon_timer); +	sdata_unlock(sdata);  }  void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; -	if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running)) -		add_timer(&ifmgd->timer); -	if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running)) -		add_timer(&ifmgd->chswitch_timer); -	ieee80211_sta_reset_beacon_monitor(sdata); -	ieee80211_restart_sta_timer(sdata); +	sdata_lock(sdata); +	if (!ifmgd->associated) { +		sdata_unlock(sdata); +		return; +	} + +	if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) { +		sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME; +		mlme_dbg(sdata, "driver requested disconnect after resume\n"); +		ieee80211_sta_connection_lost(sdata, +					      ifmgd->associated->bssid, +					      WLAN_REASON_UNSPECIFIED, +					      true); +		sdata_unlock(sdata); +		return; +	} +	sdata_unlock(sdata);  }  #endif @@ -2082,7 +3685,9 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)  	INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);  	INIT_WORK(&ifmgd->beacon_connection_loss_work,  		  ieee80211_beacon_connection_loss_work); -	INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work); +	INIT_WORK(&ifmgd->csa_connection_drop_work, +		  ieee80211_csa_connection_drop_work); +	INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);  	setup_timer(&ifmgd->timer, ieee80211_sta_timer,  		    (unsigned long) sdata);  	setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, @@ -2093,8 +3698,10 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)  		    (unsigned long) sdata);  	ifmgd->flags = 0; - -	mutex_init(&ifmgd->mtx); +	ifmgd->powersave = sdata->wdev.ps; +	ifmgd->uapsd_queues = sdata->local->hw.uapsd_queues; +	ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len; +	ifmgd->p2p_noa_index = -1;  	if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)  		ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC; @@ -2105,12 +3712,14 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)  /* scan finished notification */  void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)  { -	struct ieee80211_sub_if_data *sdata = local->scan_sdata; +	struct ieee80211_sub_if_data *sdata;  	/* Restart STA timers */  	rcu_read_lock(); -	list_for_each_entry_rcu(sdata, &local->interfaces, list) -		ieee80211_restart_sta_timer(sdata); +	list_for_each_entry_rcu(sdata, &local->interfaces, list) { +		if (ieee80211_sdata_running(sdata)) +			ieee80211_restart_sta_timer(sdata); +	}  	rcu_read_unlock();  } @@ -2126,49 +3735,304 @@ int ieee80211_max_network_latency(struct notifier_block *nb,  	ieee80211_recalc_ps(local, latency_usec);  	mutex_unlock(&local->iflist_mtx); -	return 0; +	return NOTIFY_OK;  } -/* config hooks */ -static enum work_done_result -ieee80211_probe_auth_done(struct ieee80211_work *wk, -			  struct sk_buff *skb) +static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata, +				     struct cfg80211_bss *cbss)  { -	if (!skb) { -		cfg80211_send_auth_timeout(wk->sdata->dev, wk->filter_ta); -		return WORK_DONE_DESTROY; +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	const u8 *ht_cap_ie, *vht_cap_ie; +	const struct ieee80211_ht_cap *ht_cap; +	const struct ieee80211_vht_cap *vht_cap; +	u8 chains = 1; + +	if (ifmgd->flags & IEEE80211_STA_DISABLE_HT) +		return chains; + +	ht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY); +	if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) { +		ht_cap = (void *)(ht_cap_ie + 2); +		chains = ieee80211_mcs_to_chains(&ht_cap->mcs); +		/* +		 * TODO: use "Tx Maximum Number Spatial Streams Supported" and +		 *	 "Tx Unequal Modulation Supported" fields. +		 */  	} -	if (wk->type == IEEE80211_WORK_AUTH) { -		cfg80211_send_rx_auth(wk->sdata->dev, skb->data, skb->len); -		return WORK_DONE_DESTROY; +	if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) +		return chains; + +	vht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY); +	if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) { +		u8 nss; +		u16 tx_mcs_map; + +		vht_cap = (void *)(vht_cap_ie + 2); +		tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map); +		for (nss = 8; nss > 0; nss--) { +			if (((tx_mcs_map >> (2 * (nss - 1))) & 3) != +					IEEE80211_VHT_MCS_NOT_SUPPORTED) +				break; +		} +		/* TODO: use "Tx Highest Supported Long GI Data Rate" field? */ +		chains = max(chains, nss);  	} -	mutex_lock(&wk->sdata->u.mgd.mtx); -	ieee80211_rx_mgmt_probe_resp(wk->sdata, skb); -	mutex_unlock(&wk->sdata->u.mgd.mtx); +	return chains; +} + +static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, +				  struct cfg80211_bss *cbss) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	const struct ieee80211_ht_operation *ht_oper = NULL; +	const struct ieee80211_vht_operation *vht_oper = NULL; +	struct ieee80211_supported_band *sband; +	struct cfg80211_chan_def chandef; +	int ret; + +	sband = local->hw.wiphy->bands[cbss->channel->band]; + +	ifmgd->flags &= ~(IEEE80211_STA_DISABLE_40MHZ | +			  IEEE80211_STA_DISABLE_80P80MHZ | +			  IEEE80211_STA_DISABLE_160MHZ); + +	rcu_read_lock(); + +	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && +	    sband->ht_cap.ht_supported) { +		const u8 *ht_oper_ie, *ht_cap; + +		ht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_OPERATION); +		if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper)) +			ht_oper = (void *)(ht_oper_ie + 2); -	wk->type = IEEE80211_WORK_AUTH; -	wk->probe_auth.tries = 0; -	return WORK_DONE_REQUEUE; +		ht_cap = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY); +		if (!ht_cap || ht_cap[1] < sizeof(struct ieee80211_ht_cap)) { +			ifmgd->flags |= IEEE80211_STA_DISABLE_HT; +			ht_oper = NULL; +		} +	} + +	if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) && +	    sband->vht_cap.vht_supported) { +		const u8 *vht_oper_ie, *vht_cap; + +		vht_oper_ie = ieee80211_bss_get_ie(cbss, +						   WLAN_EID_VHT_OPERATION); +		if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper)) +			vht_oper = (void *)(vht_oper_ie + 2); +		if (vht_oper && !ht_oper) { +			vht_oper = NULL; +			sdata_info(sdata, +				   "AP advertised VHT without HT, disabling both\n"); +			ifmgd->flags |= IEEE80211_STA_DISABLE_HT; +			ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; +		} + +		vht_cap = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY); +		if (!vht_cap || vht_cap[1] < sizeof(struct ieee80211_vht_cap)) { +			ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; +			vht_oper = NULL; +		} +	} + +	ifmgd->flags |= ieee80211_determine_chantype(sdata, sband, +						     cbss->channel, +						     ht_oper, vht_oper, +						     &chandef, false); + +	sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss), +				      local->rx_chains); + +	rcu_read_unlock(); + +	/* will change later if needed */ +	sdata->smps_mode = IEEE80211_SMPS_OFF; + +	mutex_lock(&local->mtx); +	/* +	 * If this fails (possibly due to channel context sharing +	 * on incompatible channels, e.g. 80+80 and 160 sharing the +	 * same control channel) try to use a smaller bandwidth. +	 */ +	ret = ieee80211_vif_use_channel(sdata, &chandef, +					IEEE80211_CHANCTX_SHARED); + +	/* don't downgrade for 5 and 10 MHz channels, though. */ +	if (chandef.width == NL80211_CHAN_WIDTH_5 || +	    chandef.width == NL80211_CHAN_WIDTH_10) +		goto out; + +	while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) { +		ifmgd->flags |= ieee80211_chandef_downgrade(&chandef); +		ret = ieee80211_vif_use_channel(sdata, &chandef, +						IEEE80211_CHANCTX_SHARED); +	} + out: +	mutex_unlock(&local->mtx); +	return ret; +} + +static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, +				     struct cfg80211_bss *cbss, bool assoc) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	struct ieee80211_bss *bss = (void *)cbss->priv; +	struct sta_info *new_sta = NULL; +	bool have_sta = false; +	int err; + +	if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data)) +		return -EINVAL; + +	if (assoc) { +		rcu_read_lock(); +		have_sta = sta_info_get(sdata, cbss->bssid); +		rcu_read_unlock(); +	} + +	if (!have_sta) { +		new_sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL); +		if (!new_sta) +			return -ENOMEM; +	} +	if (new_sta) { +		u32 rates = 0, basic_rates = 0; +		bool have_higher_than_11mbit; +		int min_rate = INT_MAX, min_rate_index = -1; +		struct ieee80211_chanctx_conf *chanctx_conf; +		struct ieee80211_supported_band *sband; +		const struct cfg80211_bss_ies *ies; +		int shift; +		u32 rate_flags; + +		sband = local->hw.wiphy->bands[cbss->channel->band]; + +		err = ieee80211_prep_channel(sdata, cbss); +		if (err) { +			sta_info_free(local, new_sta); +			return -EINVAL; +		} +		shift = ieee80211_vif_get_shift(&sdata->vif); + +		rcu_read_lock(); +		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +		if (WARN_ON(!chanctx_conf)) { +			rcu_read_unlock(); +			sta_info_free(local, new_sta); +			return -EINVAL; +		} +		rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def); +		rcu_read_unlock(); + +		ieee80211_get_rates(sband, bss->supp_rates, +				    bss->supp_rates_len, +				    &rates, &basic_rates, +				    &have_higher_than_11mbit, +				    &min_rate, &min_rate_index, +				    shift, rate_flags); + +		/* +		 * This used to be a workaround for basic rates missing +		 * in the association response frame. Now that we no +		 * longer use the basic rates from there, it probably +		 * doesn't happen any more, but keep the workaround so +		 * in case some *other* APs are buggy in different ways +		 * we can connect -- with a warning. +		 */ +		if (!basic_rates && min_rate_index >= 0) { +			sdata_info(sdata, +				   "No basic rates, using min rate instead\n"); +			basic_rates = BIT(min_rate_index); +		} + +		new_sta->sta.supp_rates[cbss->channel->band] = rates; +		sdata->vif.bss_conf.basic_rates = basic_rates; + +		/* cf. IEEE 802.11 9.2.12 */ +		if (cbss->channel->band == IEEE80211_BAND_2GHZ && +		    have_higher_than_11mbit) +			sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; +		else +			sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; + +		memcpy(ifmgd->bssid, cbss->bssid, ETH_ALEN); + +		/* set timing information */ +		sdata->vif.bss_conf.beacon_int = cbss->beacon_interval; +		rcu_read_lock(); +		ies = rcu_dereference(cbss->beacon_ies); +		if (ies) { +			const u8 *tim_ie; + +			sdata->vif.bss_conf.sync_tsf = ies->tsf; +			sdata->vif.bss_conf.sync_device_ts = +				bss->device_ts_beacon; +			tim_ie = cfg80211_find_ie(WLAN_EID_TIM, +						  ies->data, ies->len); +			if (tim_ie && tim_ie[1] >= 2) +				sdata->vif.bss_conf.sync_dtim_count = tim_ie[2]; +			else +				sdata->vif.bss_conf.sync_dtim_count = 0; +		} else if (!(local->hw.flags & +					IEEE80211_HW_TIMING_BEACON_ONLY)) { +			ies = rcu_dereference(cbss->proberesp_ies); +			/* must be non-NULL since beacon IEs were NULL */ +			sdata->vif.bss_conf.sync_tsf = ies->tsf; +			sdata->vif.bss_conf.sync_device_ts = +				bss->device_ts_presp; +			sdata->vif.bss_conf.sync_dtim_count = 0; +		} else { +			sdata->vif.bss_conf.sync_tsf = 0; +			sdata->vif.bss_conf.sync_device_ts = 0; +			sdata->vif.bss_conf.sync_dtim_count = 0; +		} +		rcu_read_unlock(); + +		/* tell driver about BSSID, basic rates and timing */ +		ieee80211_bss_info_change_notify(sdata, +			BSS_CHANGED_BSSID | BSS_CHANGED_BASIC_RATES | +			BSS_CHANGED_BEACON_INT); + +		if (assoc) +			sta_info_pre_move_state(new_sta, IEEE80211_STA_AUTH); + +		err = sta_info_insert(new_sta); +		new_sta = NULL; +		if (err) { +			sdata_info(sdata, +				   "failed to insert STA entry for the AP (error %d)\n", +				   err); +			return err; +		} +	} else +		WARN_ON_ONCE(!ether_addr_equal(ifmgd->bssid, cbss->bssid)); + +	return 0;  } +/* config hooks */  int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,  		       struct cfg80211_auth_request *req)  { -	const u8 *ssid; -	struct ieee80211_work *wk; +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	struct ieee80211_mgd_auth_data *auth_data;  	u16 auth_alg; +	int err; -	if (req->local_state_change) -		return 0; /* no need to update mac80211 state */ +	/* prepare auth data structure */  	switch (req->auth_type) {  	case NL80211_AUTHTYPE_OPEN_SYSTEM:  		auth_alg = WLAN_AUTH_OPEN;  		break;  	case NL80211_AUTHTYPE_SHARED_KEY: -		if (IS_ERR(sdata->local->wep_tx_tfm)) +		if (IS_ERR(local->wep_tx_tfm))  			return -EOPNOTSUPP;  		auth_alg = WLAN_AUTH_SHARED_KEY;  		break; @@ -2178,157 +4042,227 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,  	case NL80211_AUTHTYPE_NETWORK_EAP:  		auth_alg = WLAN_AUTH_LEAP;  		break; +	case NL80211_AUTHTYPE_SAE: +		auth_alg = WLAN_AUTH_SAE; +		break;  	default:  		return -EOPNOTSUPP;  	} -	wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL); -	if (!wk) +	auth_data = kzalloc(sizeof(*auth_data) + req->sae_data_len + +			    req->ie_len, GFP_KERNEL); +	if (!auth_data)  		return -ENOMEM; -	memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN); +	auth_data->bss = req->bss; + +	if (req->sae_data_len >= 4) { +		__le16 *pos = (__le16 *) req->sae_data; +		auth_data->sae_trans = le16_to_cpu(pos[0]); +		auth_data->sae_status = le16_to_cpu(pos[1]); +		memcpy(auth_data->data, req->sae_data + 4, +		       req->sae_data_len - 4); +		auth_data->data_len += req->sae_data_len - 4; +	}  	if (req->ie && req->ie_len) { -		memcpy(wk->ie, req->ie, req->ie_len); -		wk->ie_len = req->ie_len; +		memcpy(&auth_data->data[auth_data->data_len], +		       req->ie, req->ie_len); +		auth_data->data_len += req->ie_len;  	}  	if (req->key && req->key_len) { -		wk->probe_auth.key_len = req->key_len; -		wk->probe_auth.key_idx = req->key_idx; -		memcpy(wk->probe_auth.key, req->key, req->key_len); +		auth_data->key_len = req->key_len; +		auth_data->key_idx = req->key_idx; +		memcpy(auth_data->key, req->key, req->key_len);  	} -	ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); -	memcpy(wk->probe_auth.ssid, ssid + 2, ssid[1]); -	wk->probe_auth.ssid_len = ssid[1]; +	auth_data->algorithm = auth_alg; -	wk->probe_auth.algorithm = auth_alg; -	wk->probe_auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY; +	/* try to authenticate/probe */ -	/* if we already have a probe, don't probe again */ -	if (req->bss->proberesp_ies) -		wk->type = IEEE80211_WORK_AUTH; -	else -		wk->type = IEEE80211_WORK_DIRECT_PROBE; -	wk->chan = req->bss->channel; -	wk->sdata = sdata; -	wk->done = ieee80211_probe_auth_done; +	if ((ifmgd->auth_data && !ifmgd->auth_data->done) || +	    ifmgd->assoc_data) { +		err = -EBUSY; +		goto err_free; +	} + +	if (ifmgd->auth_data) +		ieee80211_destroy_auth_data(sdata, false); -	ieee80211_add_work(wk); +	/* prep auth_data so we don't go into idle on disassoc */ +	ifmgd->auth_data = auth_data; + +	if (ifmgd->associated) { +		u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; + +		ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, +				       WLAN_REASON_UNSPECIFIED, +				       false, frame_buf); + +		cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, +				      sizeof(frame_buf)); +	} + +	sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid); + +	err = ieee80211_prep_connection(sdata, req->bss, false); +	if (err) +		goto err_clear; + +	err = ieee80211_probe_auth(sdata); +	if (err) { +		sta_info_destroy_addr(sdata, req->bss->bssid); +		goto err_clear; +	} + +	/* hold our own reference */ +	cfg80211_ref_bss(local->hw.wiphy, auth_data->bss);  	return 0; + + err_clear: +	memset(ifmgd->bssid, 0, ETH_ALEN); +	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); +	ifmgd->auth_data = NULL; + err_free: +	kfree(auth_data); +	return err;  } -static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk, -						  struct sk_buff *skb) +static bool ieee80211_usable_wmm_params(struct ieee80211_sub_if_data *sdata, +					const u8 *wmm_param, int len)  { -	struct ieee80211_mgmt *mgmt; -	struct ieee80211_rx_status *rx_status; -	struct ieee802_11_elems elems; -	u16 status; +	const u8 *pos; +	size_t left; -	if (!skb) { -		cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta); -		return WORK_DONE_DESTROY; -	} +	if (len < 8) +		return false; -	if (wk->type == IEEE80211_WORK_ASSOC_BEACON_WAIT) { -		mutex_lock(&wk->sdata->u.mgd.mtx); -		rx_status = (void *) skb->cb; -		ieee802_11_parse_elems(skb->data + 24 + 12, skb->len - 24 - 12, &elems); -		ieee80211_rx_bss_info(wk->sdata, (void *)skb->data, skb->len, rx_status, -				      &elems, true); -		mutex_unlock(&wk->sdata->u.mgd.mtx); +	if (wmm_param[5] != 1 /* version */) +		return false; -		wk->type = IEEE80211_WORK_ASSOC; -		/* not really done yet */ -		return WORK_DONE_REQUEUE; -	} +	pos = wmm_param + 8; +	left = len - 8; -	mgmt = (void *)skb->data; -	status = le16_to_cpu(mgmt->u.assoc_resp.status_code); +	for (; left >= 4; left -= 4, pos += 4) { +		u8 aifsn = pos[0] & 0x0f; +		u8 ecwmin = pos[1] & 0x0f; +		u8 ecwmax = (pos[1] & 0xf0) >> 4; +		int aci = (pos[0] >> 5) & 0x03; -	if (status == WLAN_STATUS_SUCCESS) { -		mutex_lock(&wk->sdata->u.mgd.mtx); -		if (!ieee80211_assoc_success(wk, mgmt, skb->len)) { -			mutex_unlock(&wk->sdata->u.mgd.mtx); -			/* oops -- internal error -- send timeout for now */ -			cfg80211_send_assoc_timeout(wk->sdata->dev, -						    wk->filter_ta); -			return WORK_DONE_DESTROY; +		if (aifsn < 2) { +			sdata_info(sdata, +				   "AP has invalid WMM params (AIFSN=%d for ACI %d), disabling WMM\n", +				   aifsn, aci); +			return false; +		} +		if (ecwmin > ecwmax) { +			sdata_info(sdata, +				   "AP has invalid WMM params (ECWmin/max=%d/%d for ACI %d), disabling WMM\n", +				   ecwmin, ecwmax, aci); +			return false;  		} - -		mutex_unlock(&wk->sdata->u.mgd.mtx);  	} -	cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len); -	return WORK_DONE_DESTROY; +	return true;  }  int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  			struct cfg80211_assoc_request *req)  { +	struct ieee80211_local *local = sdata->local;  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	struct ieee80211_bss *bss = (void *)req->bss->priv; -	struct ieee80211_work *wk; -	const u8 *ssid; -	int i; +	struct ieee80211_mgd_assoc_data *assoc_data; +	const struct cfg80211_bss_ies *beacon_ies; +	struct ieee80211_supported_band *sband; +	const u8 *ssidie, *ht_ie, *vht_ie; +	int i, err; + +	assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); +	if (!assoc_data) +		return -ENOMEM; + +	rcu_read_lock(); +	ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); +	if (!ssidie) { +		rcu_read_unlock(); +		kfree(assoc_data); +		return -EINVAL; +	} +	memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]); +	assoc_data->ssid_len = ssidie[1]; +	rcu_read_unlock(); -	mutex_lock(&ifmgd->mtx);  	if (ifmgd->associated) { -		if (!req->prev_bssid || -		    memcmp(req->prev_bssid, ifmgd->associated->bssid, -			   ETH_ALEN)) { -			/* -			 * We are already associated and the request was not a -			 * reassociation request from the current BSS, so -			 * reject it. -			 */ -			mutex_unlock(&ifmgd->mtx); -			return -EALREADY; -		} +		u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; -		/* Trying to reassociate - clear previous association state */ -		ieee80211_set_disassoc(sdata, true, false); +		ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, +				       WLAN_REASON_UNSPECIFIED, +				       false, frame_buf); + +		cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, +				      sizeof(frame_buf));  	} -	mutex_unlock(&ifmgd->mtx); -	wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL); -	if (!wk) -		return -ENOMEM; +	if (ifmgd->auth_data && !ifmgd->auth_data->done) { +		err = -EBUSY; +		goto err_free; +	} -	ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N; -	ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED; +	if (ifmgd->assoc_data) { +		err = -EBUSY; +		goto err_free; +	} -	ifmgd->beacon_crc_valid = false; +	if (ifmgd->auth_data) { +		bool match; -	for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) -		if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || -		    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || -		    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) -			ifmgd->flags |= IEEE80211_STA_DISABLE_11N; +		/* keep sta info, bssid if matching */ +		match = ether_addr_equal(ifmgd->bssid, req->bss->bssid); +		ieee80211_destroy_auth_data(sdata, match); +	} +	/* prepare assoc data */ -	if (req->ie && req->ie_len) { -		memcpy(wk->ie, req->ie, req->ie_len); -		wk->ie_len = req->ie_len; -	} else -		wk->ie_len = 0; +	ifmgd->beacon_crc_valid = false; -	wk->assoc.bss = req->bss; +	assoc_data->wmm = bss->wmm_used && +			  (local->hw.queues >= IEEE80211_NUM_ACS); +	if (assoc_data->wmm) { +		/* try to check validity of WMM params IE */ +		const struct cfg80211_bss_ies *ies; +		const u8 *wp, *start, *end; -	memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN); +		rcu_read_lock(); +		ies = rcu_dereference(req->bss->ies); +		start = ies->data; +		end = start + ies->len; + +		while (true) { +			wp = cfg80211_find_vendor_ie( +				WLAN_OUI_MICROSOFT, +				WLAN_OUI_TYPE_MICROSOFT_WMM, +				start, end - start); +			if (!wp) +				break; +			start = wp + wp[1] + 2; +			/* if this IE is too short, try the next */ +			if (wp[1] <= 4) +				continue; +			/* if this IE is WMM params, we found what we wanted */ +			if (wp[6] == 1) +				break; +		} -	/* new association always uses requested smps mode */ -	if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { -		if (ifmgd->powersave) -			ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC; -		else -			ifmgd->ap_smps = IEEE80211_SMPS_OFF; -	} else -		ifmgd->ap_smps = ifmgd->req_smps; +		if (!wp || !ieee80211_usable_wmm_params(sdata, wp + 2, +							wp[1] - 2)) { +			assoc_data->wmm = false; +			ifmgd->flags |= IEEE80211_STA_DISABLE_WMM; +		} +		rcu_read_unlock(); +	} -	wk->assoc.smps = ifmgd->ap_smps;  	/*  	 * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.  	 * We still associate in non-HT mode (11a/b/g) if any one of these @@ -2336,38 +4270,102 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  	 * We can set this to true for non-11n hardware, that'll be checked  	 * separately along with the peer capabilities.  	 */ -	wk->assoc.use_11n = !(ifmgd->flags & IEEE80211_STA_DISABLE_11N); -	wk->assoc.capability = req->bss->capability; -	wk->assoc.wmm_used = bss->wmm_used; -	wk->assoc.supp_rates = bss->supp_rates; -	wk->assoc.supp_rates_len = bss->supp_rates_len; -	wk->assoc.ht_information_ie = -		ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION); +	for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) { +		if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || +		    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || +		    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) { +			ifmgd->flags |= IEEE80211_STA_DISABLE_HT; +			ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; +			netdev_info(sdata->dev, +				    "disabling HT/VHT due to WEP/TKIP use\n"); +		} +	} + +	if (req->flags & ASSOC_REQ_DISABLE_HT) { +		ifmgd->flags |= IEEE80211_STA_DISABLE_HT; +		ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; +	} + +	if (req->flags & ASSOC_REQ_DISABLE_VHT) +		ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; + +	/* Also disable HT if we don't support it or the AP doesn't use WMM */ +	sband = local->hw.wiphy->bands[req->bss->channel->band]; +	if (!sband->ht_cap.ht_supported || +	    local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used || +	    ifmgd->flags & IEEE80211_STA_DISABLE_WMM) { +		ifmgd->flags |= IEEE80211_STA_DISABLE_HT; +		if (!bss->wmm_used && +		    !(ifmgd->flags & IEEE80211_STA_DISABLE_WMM)) +			netdev_info(sdata->dev, +				    "disabling HT as WMM/QoS is not supported by the AP\n"); +	} + +	/* disable VHT if we don't support it or the AP doesn't use WMM */ +	if (!sband->vht_cap.vht_supported || +	    local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used || +	    ifmgd->flags & IEEE80211_STA_DISABLE_WMM) { +		ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; +		if (!bss->wmm_used && +		    !(ifmgd->flags & IEEE80211_STA_DISABLE_WMM)) +			netdev_info(sdata->dev, +				    "disabling VHT as WMM/QoS is not supported by the AP\n"); +	} + +	memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa)); +	memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask, +	       sizeof(ifmgd->ht_capa_mask)); + +	memcpy(&ifmgd->vht_capa, &req->vht_capa, sizeof(ifmgd->vht_capa)); +	memcpy(&ifmgd->vht_capa_mask, &req->vht_capa_mask, +	       sizeof(ifmgd->vht_capa_mask)); + +	if (req->ie && req->ie_len) { +		memcpy(assoc_data->ie, req->ie, req->ie_len); +		assoc_data->ie_len = req->ie_len; +	} + +	assoc_data->bss = req->bss; + +	if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { +		if (ifmgd->powersave) +			sdata->smps_mode = IEEE80211_SMPS_DYNAMIC; +		else +			sdata->smps_mode = IEEE80211_SMPS_OFF; +	} else +		sdata->smps_mode = ifmgd->req_smps; + +	assoc_data->capability = req->bss->capability; +	assoc_data->supp_rates = bss->supp_rates; +	assoc_data->supp_rates_len = bss->supp_rates_len; + +	rcu_read_lock(); +	ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION); +	if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation)) +		assoc_data->ap_ht_param = +			((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param; +	else +		ifmgd->flags |= IEEE80211_STA_DISABLE_HT; +	vht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_VHT_CAPABILITY); +	if (vht_ie && vht_ie[1] >= sizeof(struct ieee80211_vht_cap)) +		memcpy(&assoc_data->ap_vht_cap, vht_ie + 2, +		       sizeof(struct ieee80211_vht_cap)); +	else +		ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; +	rcu_read_unlock();  	if (bss->wmm_used && bss->uapsd_supported && -	    (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { -		wk->assoc.uapsd_used = true; +	    (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) && +	    sdata->wmm_acm != 0xff) { +		assoc_data->uapsd = true;  		ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;  	} else { -		wk->assoc.uapsd_used = false; +		assoc_data->uapsd = false;  		ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED;  	} -	ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); -	memcpy(wk->assoc.ssid, ssid + 2, ssid[1]); -	wk->assoc.ssid_len = ssid[1]; -  	if (req->prev_bssid) -		memcpy(wk->assoc.prev_bssid, req->prev_bssid, ETH_ALEN); - -	wk->chan = req->bss->channel; -	wk->sdata = sdata; -	wk->done = ieee80211_assoc_done; -	if (!bss->dtim_period && -	    sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) -		wk->type = IEEE80211_WORK_ASSOC_BEACON_WAIT; -	else -		wk->type = IEEE80211_WORK_ASSOC; +		memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN);  	if (req->use_mfp) {  		ifmgd->mfp = IEEE80211_MFP_REQUIRED; @@ -2384,92 +4382,136 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  	sdata->control_port_protocol = req->crypto.control_port_ethertype;  	sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt; +	sdata->encrypt_headroom = ieee80211_cs_headroom(local, &req->crypto, +							sdata->vif.type); -	ieee80211_add_work(wk); -	return 0; -} - -int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, -			 struct cfg80211_deauth_request *req, -			 void *cookie) -{ -	struct ieee80211_local *local = sdata->local; -	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; -	struct ieee80211_work *wk; -	u8 bssid[ETH_ALEN]; -	bool assoc_bss = false; - -	mutex_lock(&ifmgd->mtx); - -	memcpy(bssid, req->bss->bssid, ETH_ALEN); -	if (ifmgd->associated == req->bss) { -		ieee80211_set_disassoc(sdata, false, true); -		mutex_unlock(&ifmgd->mtx); -		assoc_bss = true; -	} else { -		bool not_auth_yet = false; +	/* kick off associate process */ -		mutex_unlock(&ifmgd->mtx); +	ifmgd->assoc_data = assoc_data; +	ifmgd->dtim_period = 0; +	ifmgd->have_beacon = false; -		mutex_lock(&local->mtx); -		list_for_each_entry(wk, &local->work_list, list) { -			if (wk->sdata != sdata) -				continue; - -			if (wk->type != IEEE80211_WORK_DIRECT_PROBE && -			    wk->type != IEEE80211_WORK_AUTH && -			    wk->type != IEEE80211_WORK_ASSOC && -			    wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT) -				continue; +	err = ieee80211_prep_connection(sdata, req->bss, true); +	if (err) +		goto err_clear; -			if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN)) -				continue; - -			not_auth_yet = wk->type == IEEE80211_WORK_DIRECT_PROBE; -			list_del_rcu(&wk->list); -			free_work(wk); -			break; -		} -		mutex_unlock(&local->mtx); +	rcu_read_lock(); +	beacon_ies = rcu_dereference(req->bss->beacon_ies); +	if (sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC && +	    !beacon_ies) {  		/* -		 * If somebody requests authentication and we haven't -		 * sent out an auth frame yet there's no need to send -		 * out a deauth frame either. If the state was PROBE, -		 * then this is the case. If it's AUTH we have sent a -		 * frame, and if it's IDLE we have completed the auth -		 * process already. +		 * Wait up to one beacon interval ... +		 * should this be more if we miss one?  		 */ -		if (not_auth_yet) { -			__cfg80211_auth_canceled(sdata->dev, bssid); -			return 0; +		sdata_info(sdata, "waiting for beacon from %pM\n", +			   ifmgd->bssid); +		assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval); +		assoc_data->timeout_started = true; +		assoc_data->need_beacon = true; +	} else if (beacon_ies) { +		const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, +						    beacon_ies->data, +						    beacon_ies->len); +		u8 dtim_count = 0; + +		if (tim_ie && tim_ie[1] >= sizeof(struct ieee80211_tim_ie)) { +			const struct ieee80211_tim_ie *tim; +			tim = (void *)(tim_ie + 2); +			ifmgd->dtim_period = tim->dtim_period; +			dtim_count = tim->dtim_count; +		} +		ifmgd->have_beacon = true; +		assoc_data->timeout = jiffies; +		assoc_data->timeout_started = true; + +		if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) { +			sdata->vif.bss_conf.sync_tsf = beacon_ies->tsf; +			sdata->vif.bss_conf.sync_device_ts = +				bss->device_ts_beacon; +			sdata->vif.bss_conf.sync_dtim_count = dtim_count;  		} +	} else { +		assoc_data->timeout = jiffies; +		assoc_data->timeout_started = true;  	} +	rcu_read_unlock(); -	printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n", -	       sdata->name, bssid, req->reason_code); - -	ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH, -				       req->reason_code, cookie, -				       !req->local_state_change); -	if (assoc_bss) -		sta_info_destroy_addr(sdata, bssid); +	run_again(sdata, assoc_data->timeout); -	mutex_lock(&sdata->local->mtx); -	ieee80211_recalc_idle(sdata->local); -	mutex_unlock(&sdata->local->mtx); +	if (bss->corrupt_data) { +		char *corrupt_type = "data"; +		if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_BEACON) { +			if (bss->corrupt_data & +					IEEE80211_BSS_CORRUPT_PROBE_RESP) +				corrupt_type = "beacon and probe response"; +			else +				corrupt_type = "beacon"; +		} else if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_PROBE_RESP) +			corrupt_type = "probe response"; +		sdata_info(sdata, "associating with AP with corrupt %s\n", +			   corrupt_type); +	}  	return 0; + err_clear: +	memset(ifmgd->bssid, 0, ETH_ALEN); +	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); +	ifmgd->assoc_data = NULL; + err_free: +	kfree(assoc_data); +	return err; +} + +int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, +			 struct cfg80211_deauth_request *req) +{ +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; +	bool tx = !req->local_state_change; + +	if (ifmgd->auth_data && +	    ether_addr_equal(ifmgd->auth_data->bss->bssid, req->bssid)) { +		sdata_info(sdata, +			   "aborting authentication with %pM by local choice (Reason: %u=%s)\n", +			   req->bssid, req->reason_code, +			   ieee80211_get_reason_code_string(req->reason_code)); + +		drv_mgd_prepare_tx(sdata->local, sdata); +		ieee80211_send_deauth_disassoc(sdata, req->bssid, +					       IEEE80211_STYPE_DEAUTH, +					       req->reason_code, tx, +					       frame_buf); +		ieee80211_destroy_auth_data(sdata, false); +		cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, +				      IEEE80211_DEAUTH_FRAME_LEN); + +		return 0; +	} + +	if (ifmgd->associated && +	    ether_addr_equal(ifmgd->associated->bssid, req->bssid)) { +		sdata_info(sdata, +			   "deauthenticating from %pM by local choice (Reason: %u=%s)\n", +			   req->bssid, req->reason_code, +			   ieee80211_get_reason_code_string(req->reason_code)); + +		ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, +				       req->reason_code, tx, frame_buf); +		cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, +				      IEEE80211_DEAUTH_FRAME_LEN); +		return 0; +	} + +	return -ENOTCONN;  }  int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, -			   struct cfg80211_disassoc_request *req, -			   void *cookie) +			   struct cfg80211_disassoc_request *req)  {  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	u8 bssid[ETH_ALEN]; - -	mutex_lock(&ifmgd->mtx); +	u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];  	/*  	 * cfg80211 should catch this ... but it's racy since @@ -2477,29 +4519,49 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,  	 * to cfg80211 while that's in a locked section already  	 * trying to tell us that the user wants to disconnect.  	 */ -	if (ifmgd->associated != req->bss) { -		mutex_unlock(&ifmgd->mtx); +	if (ifmgd->associated != req->bss)  		return -ENOLINK; -	} -	printk(KERN_DEBUG "%s: disassociating from %pM by local choice (reason=%d)\n", -	       sdata->name, req->bss->bssid, req->reason_code); +	sdata_info(sdata, +		   "disassociating from %pM by local choice (Reason: %u=%s)\n", +		   req->bss->bssid, req->reason_code, ieee80211_get_reason_code_string(req->reason_code));  	memcpy(bssid, req->bss->bssid, ETH_ALEN); -	ieee80211_set_disassoc(sdata, false, true); +	ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC, +			       req->reason_code, !req->local_state_change, +			       frame_buf); -	mutex_unlock(&ifmgd->mtx); +	cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, +			      IEEE80211_DEAUTH_FRAME_LEN); -	ieee80211_send_deauth_disassoc(sdata, req->bss->bssid, -			IEEE80211_STYPE_DISASSOC, req->reason_code, -			cookie, !req->local_state_change); -	sta_info_destroy_addr(sdata, bssid); +	return 0; +} -	mutex_lock(&sdata->local->mtx); -	ieee80211_recalc_idle(sdata->local); -	mutex_unlock(&sdata->local->mtx); +void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; -	return 0; +	/* +	 * Make sure some work items will not run after this, +	 * they will not do anything but might not have been +	 * cancelled when disconnecting. +	 */ +	cancel_work_sync(&ifmgd->monitor_work); +	cancel_work_sync(&ifmgd->beacon_connection_loss_work); +	cancel_work_sync(&ifmgd->request_smps_work); +	cancel_work_sync(&ifmgd->csa_connection_drop_work); +	cancel_work_sync(&ifmgd->chswitch_work); + +	sdata_lock(sdata); +	if (ifmgd->assoc_data) { +		struct cfg80211_bss *bss = ifmgd->assoc_data->bss; +		ieee80211_destroy_assoc_data(sdata, false); +		cfg80211_assoc_timeout(sdata->dev, bss); +	} +	if (ifmgd->auth_data) +		ieee80211_destroy_auth_data(sdata, false); +	del_timer_sync(&ifmgd->timer); +	sdata_unlock(sdata);  }  void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 4b564091e51..7a17decd27f 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -12,12 +12,17 @@   * it under the terms of the GNU General Public License version 2 as   * published by the Free Software Foundation.   */ +#include <linux/export.h>  #include <net/mac80211.h>  #include "ieee80211_i.h" +#include "driver-ops.h"  /* - * inform AP that we will go to sleep so that it will buffer the frames - * while we scan + * Tell our hardware to disable PS. + * Optionally inform AP that we will go to sleep so that it will buffer + * the frames while we are doing off-channel work.  This is optional + * because we *may* be doing work on-operating channel, and want our + * hardware unconditionally awake, but still let the AP send us normal frames.   */  static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)  { @@ -40,7 +45,7 @@ static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)  		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);  	} -	if (!(local->offchannel_ps_enabled) || +	if (!local->offchannel_ps_enabled ||  	    !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))  		/*  		 * If power save was enabled, no need to send a nullfunc @@ -76,6 +81,9 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)  		 * we are sleeping, let's just enable power save mode in  		 * hardware.  		 */ +		/* TODO:  Only set hardware if CONF_PS changed? +		 * TODO:  Should we set offchannel_ps_enabled to false? +		 */  		local->hw.conf.flags |= IEEE80211_CONF_PS;  		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);  	} else if (local->hw.conf.dynamic_ps_timeout > 0) { @@ -94,99 +102,399 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)  	ieee80211_sta_reset_conn_monitor(sdata);  } -void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local) +void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)  {  	struct ieee80211_sub_if_data *sdata; +	if (WARN_ON(local->use_chanctx)) +		return; + +	/* +	 * notify the AP about us leaving the channel and stop all +	 * STA interfaces. +	 */ + +	/* +	 * Stop queues and transmit all frames queued by the driver +	 * before sending nullfunc to enable powersave at the AP. +	 */ +	ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL); +	ieee80211_flush_queues(local, NULL); +  	mutex_lock(&local->iflist_mtx);  	list_for_each_entry(sdata, &local->interfaces, list) {  		if (!ieee80211_sdata_running(sdata))  			continue; -		/* disable beaconing */ -		if (sdata->vif.type == NL80211_IFTYPE_AP || -		    sdata->vif.type == NL80211_IFTYPE_ADHOC || -		    sdata->vif.type == NL80211_IFTYPE_MESH_POINT) -			ieee80211_bss_info_change_notify( -				sdata, BSS_CHANGED_BEACON_ENABLED); +		if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) +			continue; -		/* -		 * only handle non-STA interfaces here, STA interfaces -		 * are handled in ieee80211_offchannel_stop_station(), -		 * e.g., from the background scan state machine. -		 * -		 * In addition, do not stop monitor interface to allow it to be -		 * used from user space controlled off-channel operations. -		 */ -		if (sdata->vif.type != NL80211_IFTYPE_STATION && -		    sdata->vif.type != NL80211_IFTYPE_MONITOR) { +		if (sdata->vif.type != NL80211_IFTYPE_MONITOR)  			set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); -			netif_tx_stop_all_queues(sdata->dev); + +		/* Check to see if we should disable beaconing. */ +		if (sdata->vif.bss_conf.enable_beacon) { +			set_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, +				&sdata->state); +			sdata->vif.bss_conf.enable_beacon = false; +			ieee80211_bss_info_change_notify( +				sdata, BSS_CHANGED_BEACON_ENABLED);  		} + +		if (sdata->vif.type == NL80211_IFTYPE_STATION && +		    sdata->u.mgd.associated) +			ieee80211_offchannel_ps_enable(sdata);  	}  	mutex_unlock(&local->iflist_mtx);  } -void ieee80211_offchannel_stop_station(struct ieee80211_local *local) +void ieee80211_offchannel_return(struct ieee80211_local *local)  {  	struct ieee80211_sub_if_data *sdata; -	/* -	 * notify the AP about us leaving the channel and stop all STA interfaces -	 */ +	if (WARN_ON(local->use_chanctx)) +		return; +  	mutex_lock(&local->iflist_mtx);  	list_for_each_entry(sdata, &local->interfaces, list) { +		if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) +			continue; + +		if (sdata->vif.type != NL80211_IFTYPE_MONITOR) +			clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); +  		if (!ieee80211_sdata_running(sdata))  			continue; -		if (sdata->vif.type == NL80211_IFTYPE_STATION) { -			set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); -			netif_tx_stop_all_queues(sdata->dev); -			if (sdata->u.mgd.associated) -				ieee80211_offchannel_ps_enable(sdata); +		/* Tell AP we're back */ +		if (sdata->vif.type == NL80211_IFTYPE_STATION && +		    sdata->u.mgd.associated) +			ieee80211_offchannel_ps_disable(sdata); + +		if (test_and_clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, +				       &sdata->state)) { +			sdata->vif.bss_conf.enable_beacon = true; +			ieee80211_bss_info_change_notify( +				sdata, BSS_CHANGED_BEACON_ENABLED);  		}  	}  	mutex_unlock(&local->iflist_mtx); + +	ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);  } -void ieee80211_offchannel_return(struct ieee80211_local *local, -				 bool enable_beaconing) +void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)  { -	struct ieee80211_sub_if_data *sdata; +	if (roc->notified) +		return; -	mutex_lock(&local->iflist_mtx); -	list_for_each_entry(sdata, &local->interfaces, list) { -		if (!ieee80211_sdata_running(sdata)) -			continue; +	if (roc->mgmt_tx_cookie) { +		if (!WARN_ON(!roc->frame)) { +			ieee80211_tx_skb_tid_band(roc->sdata, roc->frame, 7, +						  roc->chan->band); +			roc->frame = NULL; +		} +	} else { +		cfg80211_ready_on_channel(&roc->sdata->wdev, roc->cookie, +					  roc->chan, roc->req_duration, +					  GFP_KERNEL); +	} -		/* Tell AP we're back */ -		if (sdata->vif.type == NL80211_IFTYPE_STATION) { -			if (sdata->u.mgd.associated) -				ieee80211_offchannel_ps_disable(sdata); +	roc->notified = true; +} + +static void ieee80211_hw_roc_start(struct work_struct *work) +{ +	struct ieee80211_local *local = +		container_of(work, struct ieee80211_local, hw_roc_start); +	struct ieee80211_roc_work *roc, *dep, *tmp; + +	mutex_lock(&local->mtx); + +	if (list_empty(&local->roc_list)) +		goto out_unlock; + +	roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work, +			       list); + +	if (!roc->started) +		goto out_unlock; + +	roc->hw_begun = true; +	roc->hw_start_time = local->hw_roc_start_time; + +	ieee80211_handle_roc_started(roc); +	list_for_each_entry_safe(dep, tmp, &roc->dependents, list) { +		ieee80211_handle_roc_started(dep); + +		if (dep->duration > roc->duration) { +			u32 dur = dep->duration; +			dep->duration = dur - roc->duration; +			roc->duration = dur; +			list_move(&dep->list, &roc->list);  		} +	} + out_unlock: +	mutex_unlock(&local->mtx); +} -		if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { -			clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); +void ieee80211_ready_on_channel(struct ieee80211_hw *hw) +{ +	struct ieee80211_local *local = hw_to_local(hw); + +	local->hw_roc_start_time = jiffies; + +	trace_api_ready_on_channel(local); + +	ieee80211_queue_work(hw, &local->hw_roc_start); +} +EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel); + +void ieee80211_start_next_roc(struct ieee80211_local *local) +{ +	struct ieee80211_roc_work *roc; + +	lockdep_assert_held(&local->mtx); + +	if (list_empty(&local->roc_list)) { +		ieee80211_run_deferred_scan(local); +		return; +	} + +	roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work, +			       list); + +	if (WARN_ON_ONCE(roc->started)) +		return; + +	if (local->ops->remain_on_channel) { +		int ret, duration = roc->duration; + +		/* XXX: duplicated, see ieee80211_start_roc_work() */ +		if (!duration) +			duration = 10; + +		ret = drv_remain_on_channel(local, roc->sdata, roc->chan, +					    duration, roc->type); + +		roc->started = true; + +		if (ret) { +			wiphy_warn(local->hw.wiphy, +				   "failed to start next HW ROC (%d)\n", ret);  			/* -			 * This may wake up queues even though the driver -			 * currently has them stopped. This is not very -			 * likely, since the driver won't have gotten any -			 * (or hardly any) new packets while we weren't -			 * on the right channel, and even if it happens -			 * it will at most lead to queueing up one more -			 * packet per queue in mac80211 rather than on -			 * the interface qdisc. +			 * queue the work struct again to avoid recursion +			 * when multiple failures occur  			 */ -			netif_tx_wake_all_queues(sdata->dev); +			ieee80211_remain_on_channel_expired(&local->hw);  		} +	} else { +		/* delay it a bit */ +		ieee80211_queue_delayed_work(&local->hw, &roc->work, +					     round_jiffies_relative(HZ/2)); +	} +} -		/* re-enable beaconing */ -		if (enable_beaconing && -		    (sdata->vif.type == NL80211_IFTYPE_AP || -		     sdata->vif.type == NL80211_IFTYPE_ADHOC || -		     sdata->vif.type == NL80211_IFTYPE_MESH_POINT)) -			ieee80211_bss_info_change_notify( -				sdata, BSS_CHANGED_BEACON_ENABLED); +void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free) +{ +	struct ieee80211_roc_work *dep, *tmp; + +	if (WARN_ON(roc->to_be_freed)) +		return; + +	/* was never transmitted */ +	if (roc->frame) { +		cfg80211_mgmt_tx_status(&roc->sdata->wdev, +					(unsigned long)roc->frame, +					roc->frame->data, roc->frame->len, +					false, GFP_KERNEL); +		kfree_skb(roc->frame);  	} -	mutex_unlock(&local->iflist_mtx); + +	if (!roc->mgmt_tx_cookie) +		cfg80211_remain_on_channel_expired(&roc->sdata->wdev, +						   roc->cookie, roc->chan, +						   GFP_KERNEL); + +	list_for_each_entry_safe(dep, tmp, &roc->dependents, list) +		ieee80211_roc_notify_destroy(dep, true); + +	if (free) +		kfree(roc); +	else +		roc->to_be_freed = true; +} + +void ieee80211_sw_roc_work(struct work_struct *work) +{ +	struct ieee80211_roc_work *roc = +		container_of(work, struct ieee80211_roc_work, work.work); +	struct ieee80211_sub_if_data *sdata = roc->sdata; +	struct ieee80211_local *local = sdata->local; +	bool started, on_channel; + +	mutex_lock(&local->mtx); + +	if (roc->to_be_freed) +		goto out_unlock; + +	if (roc->abort) +		goto finish; + +	if (WARN_ON(list_empty(&local->roc_list))) +		goto out_unlock; + +	if (WARN_ON(roc != list_first_entry(&local->roc_list, +					    struct ieee80211_roc_work, +					    list))) +		goto out_unlock; + +	if (!roc->started) { +		struct ieee80211_roc_work *dep; + +		WARN_ON(local->use_chanctx); + +		/* If actually operating on the desired channel (with at least +		 * 20 MHz channel width) don't stop all the operations but still +		 * treat it as though the ROC operation started properly, so +		 * other ROC operations won't interfere with this one. +		 */ +		roc->on_channel = roc->chan == local->_oper_chandef.chan && +				  local->_oper_chandef.width != NL80211_CHAN_WIDTH_5 && +				  local->_oper_chandef.width != NL80211_CHAN_WIDTH_10; + +		/* start this ROC */ +		ieee80211_recalc_idle(local); + +		if (!roc->on_channel) { +			ieee80211_offchannel_stop_vifs(local); + +			local->tmp_channel = roc->chan; +			ieee80211_hw_config(local, 0); +		} + +		/* tell userspace or send frame */ +		ieee80211_handle_roc_started(roc); +		list_for_each_entry(dep, &roc->dependents, list) +			ieee80211_handle_roc_started(dep); + +		/* if it was pure TX, just finish right away */ +		if (!roc->duration) +			goto finish; + +		roc->started = true; +		ieee80211_queue_delayed_work(&local->hw, &roc->work, +					     msecs_to_jiffies(roc->duration)); +	} else { +		/* finish this ROC */ + finish: +		list_del(&roc->list); +		started = roc->started; +		on_channel = roc->on_channel; +		ieee80211_roc_notify_destroy(roc, !roc->abort); + +		if (started && !on_channel) { +			ieee80211_flush_queues(local, NULL); + +			local->tmp_channel = NULL; +			ieee80211_hw_config(local, 0); + +			ieee80211_offchannel_return(local); +		} + +		ieee80211_recalc_idle(local); + +		if (started) +			ieee80211_start_next_roc(local); +		else if (list_empty(&local->roc_list)) +			ieee80211_run_deferred_scan(local); +	} + + out_unlock: +	mutex_unlock(&local->mtx); +} + +static void ieee80211_hw_roc_done(struct work_struct *work) +{ +	struct ieee80211_local *local = +		container_of(work, struct ieee80211_local, hw_roc_done); +	struct ieee80211_roc_work *roc; + +	mutex_lock(&local->mtx); + +	if (list_empty(&local->roc_list)) +		goto out_unlock; + +	roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work, +			       list); + +	if (!roc->started) +		goto out_unlock; + +	list_del(&roc->list); + +	ieee80211_roc_notify_destroy(roc, true); + +	/* if there's another roc, start it now */ +	ieee80211_start_next_roc(local); + + out_unlock: +	mutex_unlock(&local->mtx); +} + +void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw) +{ +	struct ieee80211_local *local = hw_to_local(hw); + +	trace_api_remain_on_channel_expired(local); + +	ieee80211_queue_work(hw, &local->hw_roc_done); +} +EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired); + +void ieee80211_roc_setup(struct ieee80211_local *local) +{ +	INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start); +	INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done); +	INIT_LIST_HEAD(&local->roc_list); +} + +void ieee80211_roc_purge(struct ieee80211_local *local, +			 struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_roc_work *roc, *tmp; +	LIST_HEAD(tmp_list); + +	mutex_lock(&local->mtx); +	list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { +		if (sdata && roc->sdata != sdata) +			continue; + +		if (roc->started && local->ops->remain_on_channel) { +			/* can race, so ignore return value */ +			drv_cancel_remain_on_channel(local); +		} + +		list_move_tail(&roc->list, &tmp_list); +		roc->abort = true; +	} +	mutex_unlock(&local->mtx); + +	list_for_each_entry_safe(roc, tmp, &tmp_list, list) { +		if (local->ops->remain_on_channel) { +			list_del(&roc->list); +			ieee80211_roc_notify_destroy(roc, true); +		} else { +			ieee80211_queue_delayed_work(&local->hw, &roc->work, 0); + +			/* work will clean up etc */ +			flush_delayed_work(&roc->work); +			WARN_ON(!roc->to_be_freed); +			kfree(roc); +		} +	} + +	WARN_ON_ONCE(!list_empty(&tmp_list));  } diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index e37355193ed..d478b880a0a 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -6,20 +6,42 @@  #include "driver-ops.h"  #include "led.h" -int __ieee80211_suspend(struct ieee80211_hw *hw) +int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)  {  	struct ieee80211_local *local = hw_to_local(hw);  	struct ieee80211_sub_if_data *sdata;  	struct sta_info *sta; +	if (!local->open_count) +		goto suspend; +  	ieee80211_scan_cancel(local); +	ieee80211_dfs_cac_cancel(local); + +	ieee80211_roc_purge(local, NULL); + +	ieee80211_del_virtual_monitor(local); + +	if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { +		mutex_lock(&local->sta_mtx); +		list_for_each_entry(sta, &local->sta_list, list) { +			set_sta_flag(sta, WLAN_STA_BLOCK_BA); +			ieee80211_sta_tear_down_BA_sessions( +					sta, AGG_STOP_LOCAL_REQUEST); +		} +		mutex_unlock(&local->sta_mtx); +	} +  	ieee80211_stop_queues_by_reason(hw, -			IEEE80211_QUEUE_STOP_REASON_SUSPEND); +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_SUSPEND);  	/* flush out all packets */  	synchronize_net(); +	ieee80211_flush_queues(local, NULL); +  	local->quiescing = true;  	/* make quiescing visible to timers everywhere */  	mb(); @@ -36,68 +58,75 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)  	cancel_work_sync(&local->dynamic_ps_enable_work);  	del_timer_sync(&local->dynamic_ps_timer); -	/* disable keys */ -	list_for_each_entry(sdata, &local->interfaces, list) -		ieee80211_disable_keys(sdata); +	local->wowlan = wowlan && local->open_count; +	if (local->wowlan) { +		int err = drv_suspend(local, wowlan); +		if (err < 0) { +			local->quiescing = false; +			local->wowlan = false; +			if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { +				mutex_lock(&local->sta_mtx); +				list_for_each_entry(sta, +						    &local->sta_list, list) { +					clear_sta_flag(sta, WLAN_STA_BLOCK_BA); +				} +				mutex_unlock(&local->sta_mtx); +			} +			ieee80211_wake_queues_by_reason(hw, +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_SUSPEND); +			return err; +		} else if (err > 0) { +			WARN_ON(err != 1); +			return err; +		} else { +			goto suspend; +		} +	}  	/* tear down aggregation sessions and remove STAs */  	mutex_lock(&local->sta_mtx);  	list_for_each_entry(sta, &local->sta_list, list) { -		if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { -			set_sta_flags(sta, WLAN_STA_BLOCK_BA); -			ieee80211_sta_tear_down_BA_sessions(sta, true); -		} -  		if (sta->uploaded) { -			sdata = sta->sdata; -			if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) -				sdata = container_of(sdata->bss, -					     struct ieee80211_sub_if_data, -					     u.ap); +			enum ieee80211_sta_state state; -			drv_sta_remove(local, sdata, &sta->sta); +			state = sta->sta_state; +			for (; state > IEEE80211_STA_NOTEXIST; state--) +				WARN_ON(drv_sta_state(local, sta->sdata, sta, +						      state, state - 1));  		} - -		mesh_plink_quiesce(sta);  	}  	mutex_unlock(&local->sta_mtx); -	/* remove all interfaces */ +	/* remove all interfaces that were created in the driver */  	list_for_each_entry(sdata, &local->interfaces, list) { -		cancel_work_sync(&sdata->work); - -		switch(sdata->vif.type) { -		case NL80211_IFTYPE_STATION: -			ieee80211_sta_quiesce(sdata); -			break; -		case NL80211_IFTYPE_ADHOC: -			ieee80211_ibss_quiesce(sdata); -			break; -		case NL80211_IFTYPE_MESH_POINT: -			ieee80211_mesh_quiesce(sdata); -			break; +		if (!ieee80211_sdata_running(sdata)) +			continue; +		switch (sdata->vif.type) {  		case NL80211_IFTYPE_AP_VLAN:  		case NL80211_IFTYPE_MONITOR: -			/* don't tell driver about this */  			continue; +		case NL80211_IFTYPE_STATION: +			ieee80211_mgd_quiesce(sdata); +			break;  		default:  			break;  		} -		if (!ieee80211_sdata_running(sdata)) -			continue; - -		/* disable beaconing */ -		ieee80211_bss_info_change_notify(sdata, -			BSS_CHANGED_BEACON_ENABLED); - -		drv_remove_interface(local, &sdata->vif); +		drv_remove_interface(local, sdata);  	} +	/* +	 * We disconnected on all interfaces before suspend, all channel +	 * contexts should be released. +	 */ +	WARN_ON(!list_empty(&local->chanctx_list)); +  	/* stop hardware - this must stop RX */  	if (local->open_count)  		ieee80211_stop_device(local); + suspend:  	local->suspended = true;  	/* need suspended to be visible before quiescing is false */  	barrier(); @@ -111,3 +140,13 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)   * ieee80211_reconfig(), which is also needed for hardware   * hang/firmware failure/etc. recovery.   */ + +void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif, +				    struct cfg80211_wowlan_wakeup *wakeup, +				    gfp_t gfp) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + +	cfg80211_report_wowlan_wakeup(&sdata->wdev, wakeup, gfp); +} +EXPORT_SYMBOL(ieee80211_report_wowlan_wakeup); diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 3d5a2cb835c..8fdadfd94ba 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -10,6 +10,7 @@  #include <linux/kernel.h>  #include <linux/rtnetlink.h> +#include <linux/module.h>  #include <linux/slab.h>  #include "rate.h"  #include "ieee80211_i.h" @@ -17,7 +18,7 @@  struct rate_control_alg {  	struct list_head list; -	struct rate_control_ops *ops; +	const struct rate_control_ops *ops;  };  static LIST_HEAD(rate_ctrl_algs); @@ -28,7 +29,7 @@ module_param(ieee80211_default_rc_algo, charp, 0644);  MODULE_PARM_DESC(ieee80211_default_rc_algo,  		 "Default rate control algorithm for mac80211 to use"); -int ieee80211_rate_control_register(struct rate_control_ops *ops) +int ieee80211_rate_control_register(const struct rate_control_ops *ops)  {  	struct rate_control_alg *alg; @@ -59,7 +60,7 @@ int ieee80211_rate_control_register(struct rate_control_ops *ops)  }  EXPORT_SYMBOL(ieee80211_rate_control_register); -void ieee80211_rate_control_unregister(struct rate_control_ops *ops) +void ieee80211_rate_control_unregister(const struct rate_control_ops *ops)  {  	struct rate_control_alg *alg; @@ -75,32 +76,31 @@ void ieee80211_rate_control_unregister(struct rate_control_ops *ops)  }  EXPORT_SYMBOL(ieee80211_rate_control_unregister); -static struct rate_control_ops * +static const struct rate_control_ops *  ieee80211_try_rate_control_ops_get(const char *name)  {  	struct rate_control_alg *alg; -	struct rate_control_ops *ops = NULL; +	const struct rate_control_ops *ops = NULL;  	if (!name)  		return NULL;  	mutex_lock(&rate_ctrl_mutex);  	list_for_each_entry(alg, &rate_ctrl_algs, list) { -		if (!strcmp(alg->ops->name, name)) -			if (try_module_get(alg->ops->module)) { -				ops = alg->ops; -				break; -			} +		if (!strcmp(alg->ops->name, name)) { +			ops = alg->ops; +			break; +		}  	}  	mutex_unlock(&rate_ctrl_mutex);  	return ops;  }  /* Get the rate control algorithm. */ -static struct rate_control_ops * +static const struct rate_control_ops *  ieee80211_rate_control_ops_get(const char *name)  { -	struct rate_control_ops *ops; +	const struct rate_control_ops *ops;  	const char *alg_name;  	kparam_block_sysfs_write(ieee80211_default_rc_algo); @@ -110,10 +110,6 @@ ieee80211_rate_control_ops_get(const char *name)  		alg_name = name;  	ops = ieee80211_try_rate_control_ops_get(alg_name); -	if (!ops) { -		request_module("rc80211_%s", alg_name); -		ops = ieee80211_try_rate_control_ops_get(alg_name); -	}  	if (!ops && name)  		/* try default if specific alg requested but not found */  		ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo); @@ -126,11 +122,6 @@ ieee80211_rate_control_ops_get(const char *name)  	return ops;  } -static void ieee80211_rate_control_ops_put(struct rate_control_ops *ops) -{ -	module_put(ops->module); -} -  #ifdef CONFIG_MAC80211_DEBUGFS  static ssize_t rcname_read(struct file *file, char __user *userbuf,  			   size_t count, loff_t *ppos) @@ -144,7 +135,7 @@ static ssize_t rcname_read(struct file *file, char __user *userbuf,  static const struct file_operations rcname_ops = {  	.read = rcname_read, -	.open = mac80211_open_file_generic, +	.open = simple_open,  	.llseek = default_llseek,  };  #endif @@ -157,12 +148,11 @@ static struct rate_control_ref *rate_control_alloc(const char *name,  	ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL);  	if (!ref) -		goto fail_ref; -	kref_init(&ref->kref); +		return NULL;  	ref->local = local;  	ref->ops = ieee80211_rate_control_ops_get(name);  	if (!ref->ops) -		goto fail_ops; +		goto free;  #ifdef CONFIG_MAC80211_DEBUGFS  	debugfsdir = debugfs_create_dir("rc", local->hw.wiphy->debugfsdir); @@ -172,22 +162,16 @@ static struct rate_control_ref *rate_control_alloc(const char *name,  	ref->priv = ref->ops->alloc(&local->hw, debugfsdir);  	if (!ref->priv) -		goto fail_priv; +		goto free;  	return ref; -fail_priv: -	ieee80211_rate_control_ops_put(ref->ops); -fail_ops: +free:  	kfree(ref); -fail_ref:  	return NULL;  } -static void rate_control_release(struct kref *kref) +static void rate_control_free(struct rate_control_ref *ctrl_ref)  { -	struct rate_control_ref *ctrl_ref; - -	ctrl_ref = container_of(kref, struct rate_control_ref, kref);  	ctrl_ref->ops->free(ctrl_ref->priv);  #ifdef CONFIG_MAC80211_DEBUGFS @@ -195,11 +179,10 @@ static void rate_control_release(struct kref *kref)  	ctrl_ref->local->debugfs.rcdir = NULL;  #endif -	ieee80211_rate_control_ops_put(ctrl_ref->ops);  	kfree(ctrl_ref);  } -static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc) +static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc)  {  	struct sk_buff *skb = txrc->skb;  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; @@ -208,10 +191,12 @@ static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc)  	fc = hdr->frame_control; -	return (info->flags & IEEE80211_TX_CTL_NO_ACK) || !ieee80211_is_data(fc); +	return (info->flags & (IEEE80211_TX_CTL_NO_ACK | +			       IEEE80211_TX_CTL_USE_MINRATE)) || +		!ieee80211_is_data(fc);  } -static void rc_send_low_broadcast(s8 *idx, u32 basic_rates, +static void rc_send_low_basicrate(s8 *idx, u32 basic_rates,  				  struct ieee80211_supported_band *sband)  {  	u8 i; @@ -233,38 +218,84 @@ static void rc_send_low_broadcast(s8 *idx, u32 basic_rates,  	/* could not find a basic rate; use original selection */  } -bool rate_control_send_low(struct ieee80211_sta *sta, +static void __rate_control_send_low(struct ieee80211_hw *hw, +				    struct ieee80211_supported_band *sband, +				    struct ieee80211_sta *sta, +				    struct ieee80211_tx_info *info, +				    u32 rate_mask) +{ +	int i; +	u32 rate_flags = +		ieee80211_chandef_rate_flags(&hw->conf.chandef); + +	if ((sband->band == IEEE80211_BAND_2GHZ) && +	    (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) +		rate_flags |= IEEE80211_RATE_ERP_G; + +	info->control.rates[0].idx = 0; +	for (i = 0; i < sband->n_bitrates; i++) { +		if (!(rate_mask & BIT(i))) +			continue; + +		if ((rate_flags & sband->bitrates[i].flags) != rate_flags) +			continue; + +		if (!rate_supported(sta, sband->band, i)) +			continue; + +		info->control.rates[0].idx = i; +		break; +	} +	WARN_ON_ONCE(i == sband->n_bitrates); + +	info->control.rates[0].count = +		(info->flags & IEEE80211_TX_CTL_NO_ACK) ? +		1 : hw->max_rate_tries; + +	info->control.skip_table = 1; +} + + +bool rate_control_send_low(struct ieee80211_sta *pubsta,  			   void *priv_sta,  			   struct ieee80211_tx_rate_control *txrc)  {  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);  	struct ieee80211_supported_band *sband = txrc->sband; +	struct sta_info *sta;  	int mcast_rate; +	bool use_basicrate = false; -	if (!sta || !priv_sta || rc_no_data_or_no_ack(txrc)) { -		info->control.rates[0].idx = rate_lowest_index(txrc->sband, sta); -		info->control.rates[0].count = -			(info->flags & IEEE80211_TX_CTL_NO_ACK) ? -			1 : txrc->hw->max_rate_tries; -		if (!sta && txrc->bss) { +	if (!pubsta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) { +		__rate_control_send_low(txrc->hw, sband, pubsta, info, +					txrc->rate_idx_mask); + +		if (!pubsta && txrc->bss) {  			mcast_rate = txrc->bss_conf->mcast_rate[sband->band];  			if (mcast_rate > 0) {  				info->control.rates[0].idx = mcast_rate - 1;  				return true;  			} +			use_basicrate = true; +		} else if (pubsta) { +			sta = container_of(pubsta, struct sta_info, sta); +			if (ieee80211_vif_is_mesh(&sta->sdata->vif)) +				use_basicrate = true; +		} -			rc_send_low_broadcast(&info->control.rates[0].idx, +		if (use_basicrate) +			rc_send_low_basicrate(&info->control.rates[0].idx,  					      txrc->bss_conf->basic_rates,  					      sband); -		} +  		return true;  	}  	return false;  }  EXPORT_SYMBOL(rate_control_send_low); -static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, -				int n_bitrates, u32 mask) +static bool rate_idx_match_legacy_mask(struct ieee80211_tx_rate *rate, +				       int n_bitrates, u32 mask)  {  	int j; @@ -273,7 +304,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,  		if (mask & (1 << j)) {  			/* Okay, found a suitable rate. Use it. */  			rate->idx = j; -			return; +			return true;  		}  	} @@ -282,6 +313,108 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,  		if (mask & (1 << j)) {  			/* Okay, found a suitable rate. Use it. */  			rate->idx = j; +			return true; +		} +	} +	return false; +} + +static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate, +				    u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]) +{ +	int i, j; +	int ridx, rbit; + +	ridx = rate->idx / 8; +	rbit = rate->idx % 8; + +	/* sanity check */ +	if (ridx < 0 || ridx >= IEEE80211_HT_MCS_MASK_LEN) +		return false; + +	/* See whether the selected rate or anything below it is allowed. */ +	for (i = ridx; i >= 0; i--) { +		for (j = rbit; j >= 0; j--) +			if (mcs_mask[i] & BIT(j)) { +				rate->idx = i * 8 + j; +				return true; +			} +		rbit = 7; +	} + +	/* Try to find a higher rate that would be allowed */ +	ridx = (rate->idx + 1) / 8; +	rbit = (rate->idx + 1) % 8; + +	for (i = ridx; i < IEEE80211_HT_MCS_MASK_LEN; i++) { +		for (j = rbit; j < 8; j++) +			if (mcs_mask[i] & BIT(j)) { +				rate->idx = i * 8 + j; +				return true; +			} +		rbit = 0; +	} +	return false; +} + + + +static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, +				struct ieee80211_supported_band *sband, +				enum nl80211_chan_width chan_width, +				u32 mask, +				u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]) +{ +	struct ieee80211_tx_rate alt_rate; + +	/* handle HT rates */ +	if (rate->flags & IEEE80211_TX_RC_MCS) { +		if (rate_idx_match_mcs_mask(rate, mcs_mask)) +			return; + +		/* also try the legacy rates. */ +		alt_rate.idx = 0; +		/* keep protection flags */ +		alt_rate.flags = rate->flags & +				 (IEEE80211_TX_RC_USE_RTS_CTS | +				  IEEE80211_TX_RC_USE_CTS_PROTECT | +				  IEEE80211_TX_RC_USE_SHORT_PREAMBLE); +		alt_rate.count = rate->count; +		if (rate_idx_match_legacy_mask(&alt_rate, +					       sband->n_bitrates, mask)) { +			*rate = alt_rate; +			return; +		} +	} else { +		/* handle legacy rates */ +		if (rate_idx_match_legacy_mask(rate, sband->n_bitrates, mask)) +			return; + +		/* if HT BSS, and we handle a data frame, also try HT rates */ +		switch (chan_width) { +		case NL80211_CHAN_WIDTH_20_NOHT: +		case NL80211_CHAN_WIDTH_5: +		case NL80211_CHAN_WIDTH_10: +			return; +		default: +			break; +		} + +		alt_rate.idx = 0; +		/* keep protection flags */ +		alt_rate.flags = rate->flags & +				 (IEEE80211_TX_RC_USE_RTS_CTS | +				  IEEE80211_TX_RC_USE_CTS_PROTECT | +				  IEEE80211_TX_RC_USE_SHORT_PREAMBLE); +		alt_rate.count = rate->count; + +		alt_rate.flags |= IEEE80211_TX_RC_MCS; + +		if (chan_width == NL80211_CHAN_WIDTH_40) +			alt_rate.flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + +		if (rate_idx_match_mcs_mask(&alt_rate, mcs_mask)) { +			*rate = alt_rate;  			return;  		}  	} @@ -295,6 +428,236 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,  	 */  } +static void rate_fixup_ratelist(struct ieee80211_vif *vif, +				struct ieee80211_supported_band *sband, +				struct ieee80211_tx_info *info, +				struct ieee80211_tx_rate *rates, +				int max_rates) +{ +	struct ieee80211_rate *rate; +	bool inval = false; +	int i; + +	/* +	 * Set up the RTS/CTS rate as the fastest basic rate +	 * that is not faster than the data rate unless there +	 * is no basic rate slower than the data rate, in which +	 * case we pick the slowest basic rate +	 * +	 * XXX: Should this check all retry rates? +	 */ +	if (!(rates[0].flags & IEEE80211_TX_RC_MCS)) { +		u32 basic_rates = vif->bss_conf.basic_rates; +		s8 baserate = basic_rates ? ffs(basic_rates - 1) : 0; + +		rate = &sband->bitrates[rates[0].idx]; + +		for (i = 0; i < sband->n_bitrates; i++) { +			/* must be a basic rate */ +			if (!(basic_rates & BIT(i))) +				continue; +			/* must not be faster than the data rate */ +			if (sband->bitrates[i].bitrate > rate->bitrate) +				continue; +			/* maximum */ +			if (sband->bitrates[baserate].bitrate < +			     sband->bitrates[i].bitrate) +				baserate = i; +		} + +		info->control.rts_cts_rate_idx = baserate; +	} + +	for (i = 0; i < max_rates; i++) { +		/* +		 * make sure there's no valid rate following +		 * an invalid one, just in case drivers don't +		 * take the API seriously to stop at -1. +		 */ +		if (inval) { +			rates[i].idx = -1; +			continue; +		} +		if (rates[i].idx < 0) { +			inval = true; +			continue; +		} + +		/* +		 * For now assume MCS is already set up correctly, this +		 * needs to be fixed. +		 */ +		if (rates[i].flags & IEEE80211_TX_RC_MCS) { +			WARN_ON(rates[i].idx > 76); + +			if (!(rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) && +			    info->control.use_cts_prot) +				rates[i].flags |= +					IEEE80211_TX_RC_USE_CTS_PROTECT; +			continue; +		} + +		if (rates[i].flags & IEEE80211_TX_RC_VHT_MCS) { +			WARN_ON(ieee80211_rate_get_vht_mcs(&rates[i]) > 9); +			continue; +		} + +		/* set up RTS protection if desired */ +		if (info->control.use_rts) { +			rates[i].flags |= IEEE80211_TX_RC_USE_RTS_CTS; +			info->control.use_cts_prot = false; +		} + +		/* RC is busted */ +		if (WARN_ON_ONCE(rates[i].idx >= sband->n_bitrates)) { +			rates[i].idx = -1; +			continue; +		} + +		rate = &sband->bitrates[rates[i].idx]; + +		/* set up short preamble */ +		if (info->control.short_preamble && +		    rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) +			rates[i].flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE; + +		/* set up G protection */ +		if (!(rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) && +		    info->control.use_cts_prot && +		    rate->flags & IEEE80211_RATE_ERP_G) +			rates[i].flags |= IEEE80211_TX_RC_USE_CTS_PROTECT; +	} +} + + +static void rate_control_fill_sta_table(struct ieee80211_sta *sta, +					struct ieee80211_tx_info *info, +					struct ieee80211_tx_rate *rates, +					int max_rates) +{ +	struct ieee80211_sta_rates *ratetbl = NULL; +	int i; + +	if (sta && !info->control.skip_table) +		ratetbl = rcu_dereference(sta->rates); + +	/* Fill remaining rate slots with data from the sta rate table. */ +	max_rates = min_t(int, max_rates, IEEE80211_TX_RATE_TABLE_SIZE); +	for (i = 0; i < max_rates; i++) { +		if (i < ARRAY_SIZE(info->control.rates) && +		    info->control.rates[i].idx >= 0 && +		    info->control.rates[i].count) { +			if (rates != info->control.rates) +				rates[i] = info->control.rates[i]; +		} else if (ratetbl) { +			rates[i].idx = ratetbl->rate[i].idx; +			rates[i].flags = ratetbl->rate[i].flags; +			if (info->control.use_rts) +				rates[i].count = ratetbl->rate[i].count_rts; +			else if (info->control.use_cts_prot) +				rates[i].count = ratetbl->rate[i].count_cts; +			else +				rates[i].count = ratetbl->rate[i].count; +		} else { +			rates[i].idx = -1; +			rates[i].count = 0; +		} + +		if (rates[i].idx < 0 || !rates[i].count) +			break; +	} +} + +static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata, +				    struct ieee80211_sta *sta, +				    struct ieee80211_supported_band *sband, +				    struct ieee80211_tx_info *info, +				    struct ieee80211_tx_rate *rates, +				    int max_rates) +{ +	enum nl80211_chan_width chan_width; +	u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]; +	bool has_mcs_mask; +	u32 mask; +	u32 rate_flags; +	int i; + +	/* +	 * Try to enforce the rateidx mask the user wanted. skip this if the +	 * default mask (allow all rates) is used to save some processing for +	 * the common case. +	 */ +	mask = sdata->rc_rateidx_mask[info->band]; +	has_mcs_mask = sdata->rc_has_mcs_mask[info->band]; +	rate_flags = +		ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); +	for (i = 0; i < sband->n_bitrates; i++) +		if ((rate_flags & sband->bitrates[i].flags) != rate_flags) +			mask &= ~BIT(i); + +	if (mask == (1 << sband->n_bitrates) - 1 && !has_mcs_mask) +		return; + +	if (has_mcs_mask) +		memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[info->band], +		       sizeof(mcs_mask)); +	else +		memset(mcs_mask, 0xff, sizeof(mcs_mask)); + +	if (sta) { +		/* Filter out rates that the STA does not support */ +		mask &= sta->supp_rates[info->band]; +		for (i = 0; i < sizeof(mcs_mask); i++) +			mcs_mask[i] &= sta->ht_cap.mcs.rx_mask[i]; +	} + +	/* +	 * Make sure the rate index selected for each TX rate is +	 * included in the configured mask and change the rate indexes +	 * if needed. +	 */ +	chan_width = sdata->vif.bss_conf.chandef.width; +	for (i = 0; i < max_rates; i++) { +		/* Skip invalid rates */ +		if (rates[i].idx < 0) +			break; + +		rate_idx_match_mask(&rates[i], sband, chan_width, mask, +				    mcs_mask); +	} +} + +void ieee80211_get_tx_rates(struct ieee80211_vif *vif, +			    struct ieee80211_sta *sta, +			    struct sk_buff *skb, +			    struct ieee80211_tx_rate *dest, +			    int max_rates) +{ +	struct ieee80211_sub_if_data *sdata; +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct ieee80211_supported_band *sband; + +	rate_control_fill_sta_table(sta, info, dest, max_rates); + +	if (!vif) +		return; + +	sdata = vif_to_sdata(vif); +	sband = sdata->local->hw.wiphy->bands[info->band]; + +	if (ieee80211_is_data(hdr->frame_control)) +		rate_control_apply_mask(sdata, sta, sband, info, dest, max_rates); + +	if (dest[0].idx < 0) +		__rate_control_send_low(&sdata->local->hw, sband, sta, info, +					sdata->rc_rateidx_mask[info->band]); + +	if (sta) +		rate_fixup_ratelist(vif, sband, info, dest, max_rates); +} +EXPORT_SYMBOL(ieee80211_get_tx_rates); +  void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,  			   struct sta_info *sta,  			   struct ieee80211_tx_rate_control *txrc) @@ -304,9 +667,8 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_sta *ista = NULL;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);  	int i; -	u32 mask; -	if (sta) { +	if (sta && test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) {  		ista = &sta->sta;  		priv_sta = sta->rate_ctrl_priv;  	} @@ -314,7 +676,7 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,  	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 = 1; +		info->control.rates[i].count = 0;  	}  	if (sdata->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) @@ -322,52 +684,39 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,  	ref->ops->get_rate(ref->priv, ista, priv_sta, txrc); -	/* -	 * Try to enforce the rateidx mask the user wanted. skip this if the -	 * default mask (allow all rates) is used to save some processing for -	 * the common case. -	 */ -	mask = sdata->rc_rateidx_mask[info->band]; -	if (mask != (1 << txrc->sband->n_bitrates) - 1) { -		if (sta) { -			/* Filter out rates that the STA does not support */ -			mask &= sta->sta.supp_rates[info->band]; -		} -		/* -		 * Make sure the rate index selected for each TX rate is -		 * included in the configured mask and change the rate indexes -		 * if needed. -		 */ -		for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { -			/* Skip invalid rates */ -			if (info->control.rates[i].idx < 0) -				break; -			/* Rate masking supports only legacy rates for now */ -			if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS) -				continue; -			rate_idx_match_mask(&info->control.rates[i], -					    txrc->sband->n_bitrates, mask); -		} -	} +	if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_RC_TABLE) +		return; -	BUG_ON(info->control.rates[0].idx < 0); +	ieee80211_get_tx_rates(&sdata->vif, ista, txrc->skb, +			       info->control.rates, +			       ARRAY_SIZE(info->control.rates));  } -struct rate_control_ref *rate_control_get(struct rate_control_ref *ref) +int rate_control_set_rates(struct ieee80211_hw *hw, +			   struct ieee80211_sta *pubsta, +			   struct ieee80211_sta_rates *rates)  { -	kref_get(&ref->kref); -	return ref; -} +	struct ieee80211_sta_rates *old; -void rate_control_put(struct rate_control_ref *ref) -{ -	kref_put(&ref->kref, rate_control_release); +	/* +	 * mac80211 guarantees that this function will not be called +	 * concurrently, so the following RCU access is safe, even without +	 * extra locking. This can not be checked easily, so we just set +	 * the condition to true. +	 */ +	old = rcu_dereference_protected(pubsta->rates, true); +	rcu_assign_pointer(pubsta->rates, rates); +	if (old) +		kfree_rcu(old, rcu_head); + +	return 0;  } +EXPORT_SYMBOL(rate_control_set_rates);  int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,  				 const char *name)  { -	struct rate_control_ref *ref, *old; +	struct rate_control_ref *ref;  	ASSERT_RTNL(); @@ -387,12 +736,8 @@ int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,  		return -ENOENT;  	} -	old = local->rate_ctrl; +	WARN_ON(local->rate_ctrl);  	local->rate_ctrl = ref; -	if (old) { -		rate_control_put(old); -		sta_info_flush(local, NULL); -	}  	wiphy_debug(local->hw.wiphy, "Selected rate control algorithm '%s'\n",  		    ref->ops->name); @@ -410,6 +755,6 @@ void rate_control_deinitialize(struct ieee80211_local *local)  		return;  	local->rate_ctrl = NULL; -	rate_control_put(ref); +	rate_control_free(ref);  } diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index 168427b0ffd..9aa2a1190a8 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -14,23 +14,20 @@  #include <linux/netdevice.h>  #include <linux/skbuff.h>  #include <linux/types.h> -#include <linux/kref.h>  #include <net/mac80211.h>  #include "ieee80211_i.h"  #include "sta_info.h" +#include "driver-ops.h"  struct rate_control_ref {  	struct ieee80211_local *local; -	struct rate_control_ops *ops; +	const struct rate_control_ops *ops;  	void *priv; -	struct kref kref;  };  void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,  			   struct sta_info *sta,  			   struct ieee80211_tx_rate_control *txrc); -struct rate_control_ref *rate_control_get(struct rate_control_ref *ref); -void rate_control_put(struct rate_control_ref *ref);  static inline void rate_control_tx_status(struct ieee80211_local *local,  					  struct ieee80211_supported_band *sband, @@ -41,7 +38,7 @@ static inline void rate_control_tx_status(struct ieee80211_local *local,  	struct ieee80211_sta *ista = &sta->sta;  	void *priv_sta = sta->rate_ctrl_priv; -	if (!ref) +	if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL))  		return;  	ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); @@ -55,27 +52,52 @@ static inline void rate_control_rate_init(struct sta_info *sta)  	struct ieee80211_sta *ista = &sta->sta;  	void *priv_sta = sta->rate_ctrl_priv;  	struct ieee80211_supported_band *sband; +	struct ieee80211_chanctx_conf *chanctx_conf; + +	ieee80211_sta_set_rx_nss(sta);  	if (!ref)  		return; -	sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; +	rcu_read_lock(); + +	chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf); +	if (WARN_ON(!chanctx_conf)) { +		rcu_read_unlock(); +		return; +	} + +	sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band]; -	ref->ops->rate_init(ref->priv, sband, ista, priv_sta); +	ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista, +			    priv_sta); +	rcu_read_unlock(); +	set_sta_flag(sta, WLAN_STA_RATE_CONTROL);  }  static inline void rate_control_rate_update(struct ieee80211_local *local,  				    struct ieee80211_supported_band *sband, -				    struct sta_info *sta, u32 changed, -				    enum nl80211_channel_type oper_chan_type) +				    struct sta_info *sta, u32 changed)  {  	struct rate_control_ref *ref = local->rate_ctrl;  	struct ieee80211_sta *ista = &sta->sta;  	void *priv_sta = sta->rate_ctrl_priv; - -	if (ref && ref->ops->rate_update) -		ref->ops->rate_update(ref->priv, sband, ista, -				      priv_sta, changed, oper_chan_type); +	struct ieee80211_chanctx_conf *chanctx_conf; + +	if (ref && ref->ops->rate_update) { +		rcu_read_lock(); + +		chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf); +		if (WARN_ON(!chanctx_conf)) { +			rcu_read_unlock(); +			return; +		} + +		ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def, +				      ista, priv_sta, changed); +		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, @@ -122,8 +144,8 @@ void rate_control_deinitialize(struct ieee80211_local *local);  /* Rate control algorithms */  #ifdef CONFIG_MAC80211_RC_PID -extern int rc80211_pid_init(void); -extern void rc80211_pid_exit(void); +int rc80211_pid_init(void); +void rc80211_pid_exit(void);  #else  static inline int rc80211_pid_init(void)  { @@ -135,8 +157,8 @@ static inline void rc80211_pid_exit(void)  #endif  #ifdef CONFIG_MAC80211_RC_MINSTREL -extern int rc80211_minstrel_init(void); -extern void rc80211_minstrel_exit(void); +int rc80211_minstrel_init(void); +void rc80211_minstrel_exit(void);  #else  static inline int rc80211_minstrel_init(void)  { @@ -148,8 +170,8 @@ static inline void rc80211_minstrel_exit(void)  #endif  #ifdef CONFIG_MAC80211_RC_MINSTREL_HT -extern int rc80211_minstrel_ht_init(void); -extern void rc80211_minstrel_ht_exit(void); +int rc80211_minstrel_ht_init(void); +void rc80211_minstrel_ht_exit(void);  #else  static inline int rc80211_minstrel_ht_init(void)  { diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 778c604d793..1c1469c36dc 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -55,7 +55,6 @@  #include "rate.h"  #include "rc80211_minstrel.h" -#define SAMPLE_COLUMNS	10  #define SAMPLE_TBL(_mi, _idx, _col) \  		_mi->sample_table[(_idx * SAMPLE_COLUMNS) + _col] @@ -70,16 +69,75 @@ rix_to_ndx(struct minstrel_sta_info *mi, int rix)  	return i;  } +/* find & sort topmost throughput rates */ +static inline void +minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list) +{ +	int j = MAX_THR_RATES; + +	while (j > 0 && mi->r[i].cur_tp > mi->r[tp_list[j - 1]].cur_tp) +		j--; +	if (j < MAX_THR_RATES - 1) +		memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1)); +	if (j < MAX_THR_RATES) +		tp_list[j] = i; +} + +static void +minstrel_set_rate(struct minstrel_sta_info *mi, struct ieee80211_sta_rates *ratetbl, +		  int offset, int idx) +{ +	struct minstrel_rate *r = &mi->r[idx]; + +	ratetbl->rate[offset].idx = r->rix; +	ratetbl->rate[offset].count = r->adjusted_retry_count; +	ratetbl->rate[offset].count_cts = r->retry_count_cts; +	ratetbl->rate[offset].count_rts = r->retry_count_rtscts; +} + +static void +minstrel_update_rates(struct minstrel_priv *mp, struct minstrel_sta_info *mi) +{ +	struct ieee80211_sta_rates *ratetbl; +	int i = 0; + +	ratetbl = kzalloc(sizeof(*ratetbl), GFP_ATOMIC); +	if (!ratetbl) +		return; + +	/* Start with max_tp_rate */ +	minstrel_set_rate(mi, ratetbl, i++, mi->max_tp_rate[0]); + +	if (mp->hw->max_rates >= 3) { +		/* At least 3 tx rates supported, use max_tp_rate2 next */ +		minstrel_set_rate(mi, ratetbl, i++, mi->max_tp_rate[1]); +	} + +	if (mp->hw->max_rates >= 2) { +		/* At least 2 tx rates supported, use max_prob_rate next */ +		minstrel_set_rate(mi, ratetbl, i++, mi->max_prob_rate); +	} + +	/* Use lowest rate last */ +	ratetbl->rate[i].idx = mi->lowest_rix; +	ratetbl->rate[i].count = mp->max_retry; +	ratetbl->rate[i].count_cts = mp->max_retry; +	ratetbl->rate[i].count_rts = mp->max_retry; + +	rate_control_set_rates(mp->hw, mi->sta, ratetbl); +} +  static void  minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)  { -	u32 max_tp = 0, index_max_tp = 0, index_max_tp2 = 0; -	u32 max_prob = 0, index_max_prob = 0; +	u8 tmp_tp_rate[MAX_THR_RATES]; +	u8 tmp_prob_rate = 0;  	u32 usecs; -	u32 p;  	int i; -	mi->stats_update = jiffies; +	for (i = 0; i < MAX_THR_RATES; i++) +	    tmp_tp_rate[i] = 0; +  	for (i = 0; i < mi->n_rates; i++) {  		struct minstrel_rate *mr = &mi->r[i]; @@ -87,27 +145,32 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)  		if (!usecs)  			usecs = 1000000; -		/* To avoid rounding issues, probabilities scale from 0 (0%) -		 * to 18000 (100%) */ -		if (mr->attempts) { -			p = (mr->success * 18000) / mr->attempts; +		if (unlikely(mr->attempts > 0)) { +			mr->sample_skipped = 0; +			mr->cur_prob = MINSTREL_FRAC(mr->success, mr->attempts);  			mr->succ_hist += mr->success;  			mr->att_hist += mr->attempts; -			mr->cur_prob = p; -			p = ((p * (100 - mp->ewma_level)) + (mr->probability * -				mp->ewma_level)) / 100; -			mr->probability = p; -			mr->cur_tp = p * (1000000 / usecs); -		} +			mr->probability = minstrel_ewma(mr->probability, +							mr->cur_prob, +							EWMA_LEVEL); +		} else +			mr->sample_skipped++;  		mr->last_success = mr->success;  		mr->last_attempts = mr->attempts;  		mr->success = 0;  		mr->attempts = 0; +		/* Update throughput per rate, reset thr. below 10% success */ +		if (mr->probability < MINSTREL_FRAC(10, 100)) +			mr->cur_tp = 0; +		else +			mr->cur_tp = mr->probability * (1000000 / usecs); +  		/* Sample less often below the 10% chance of success.  		 * Sample less often above the 95% chance of success. */ -		if ((mr->probability > 17100) || (mr->probability < 1800)) { +		if (mr->probability > MINSTREL_FRAC(95, 100) || +		    mr->probability < MINSTREL_FRAC(10, 100)) {  			mr->adjusted_retry_count = mr->retry_count >> 1;  			if (mr->adjusted_retry_count > 2)  				mr->adjusted_retry_count = 2; @@ -118,42 +181,49 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)  		}  		if (!mr->adjusted_retry_count)  			mr->adjusted_retry_count = 2; -	} -	for (i = 0; i < mi->n_rates; i++) { -		struct minstrel_rate *mr = &mi->r[i]; -		if (max_tp < mr->cur_tp) { -			index_max_tp = i; -			max_tp = mr->cur_tp; -		} -		if (max_prob < mr->probability) { -			index_max_prob = i; -			max_prob = mr->probability; +		minstrel_sort_best_tp_rates(mi, i, tmp_tp_rate); + +		/* To determine the most robust rate (max_prob_rate) used at +		 * 3rd mmr stage we distinct between two cases: +		 * (1) if any success probabilitiy >= 95%, out of those rates +		 * choose the maximum throughput rate as max_prob_rate +		 * (2) if all success probabilities < 95%, the rate with +		 * highest success probability is choosen as max_prob_rate */ +		if (mr->probability >= MINSTREL_FRAC(95, 100)) { +			if (mr->cur_tp >= mi->r[tmp_prob_rate].cur_tp) +				tmp_prob_rate = i; +		} else { +			if (mr->probability >= mi->r[tmp_prob_rate].probability) +				tmp_prob_rate = i;  		}  	} -	max_tp = 0; -	for (i = 0; i < mi->n_rates; i++) { -		struct minstrel_rate *mr = &mi->r[i]; +	/* Assign the new rate set */ +	memcpy(mi->max_tp_rate, tmp_tp_rate, sizeof(mi->max_tp_rate)); +	mi->max_prob_rate = tmp_prob_rate; -		if (i == index_max_tp) -			continue; - -		if (max_tp < mr->cur_tp) { -			index_max_tp2 = i; -			max_tp = mr->cur_tp; -		} +#ifdef CONFIG_MAC80211_DEBUGFS +	/* use fixed index if set */ +	if (mp->fixed_rate_idx != -1) { +		mi->max_tp_rate[0] = mp->fixed_rate_idx; +		mi->max_tp_rate[1] = mp->fixed_rate_idx; +		mi->max_prob_rate = mp->fixed_rate_idx;  	} -	mi->max_tp_rate = index_max_tp; -	mi->max_tp_rate2 = index_max_tp2; -	mi->max_prob_rate = index_max_prob; +#endif + +	/* Reset update timer */ +	mi->stats_update = jiffies; + +	minstrel_update_rates(mp, mi);  }  static void  minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, -                   struct ieee80211_sta *sta, void *priv_sta, +		   struct ieee80211_sta *sta, void *priv_sta,  		   struct sk_buff *skb)  { +	struct minstrel_priv *mp = priv;  	struct minstrel_sta_info *mi = priv_sta;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);  	struct ieee80211_tx_rate *ar = info->status.rates; @@ -181,18 +251,22 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,  	if (mi->sample_deferred > 0)  		mi->sample_deferred--; + +	if (time_after(jiffies, mi->stats_update + +				(mp->update_interval * HZ) / 1000)) +		minstrel_update_stats(mp, mi);  }  static inline unsigned int  minstrel_get_retry_count(struct minstrel_rate *mr, -                         struct ieee80211_tx_info *info) +			 struct ieee80211_tx_info *info)  {  	unsigned int retry = mr->adjusted_retry_count; -	if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) +	if (info->control.use_rts)  		retry = max(2U, min(mr->retry_count_rtscts, retry)); -	else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) +	else if (info->control.use_cts_prot)  		retry = max(2U, min(mr->retry_count_cts, retry));  	return retry;  } @@ -202,10 +276,10 @@ static int  minstrel_get_next_sample(struct minstrel_sta_info *mi)  {  	unsigned int sample_ndx; -	sample_ndx = SAMPLE_TBL(mi, mi->sample_idx, mi->sample_column); -	mi->sample_idx++; -	if ((int) mi->sample_idx > (mi->n_rates - 2)) { -		mi->sample_idx = 0; +	sample_ndx = SAMPLE_TBL(mi, mi->sample_row, mi->sample_column); +	mi->sample_row++; +	if ((int) mi->sample_row >= mi->n_rates) { +		mi->sample_row = 0;  		mi->sample_column++;  		if (mi->sample_column >= SAMPLE_COLUMNS)  			mi->sample_column = 0; @@ -221,189 +295,189 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);  	struct minstrel_sta_info *mi = priv_sta;  	struct minstrel_priv *mp = priv; -	struct ieee80211_tx_rate *ar = info->control.rates; -	unsigned int ndx, sample_ndx = 0; -	bool mrr; -	bool sample_slower = false; -	bool sample = false; -	int i, delta; -	int mrr_ndx[3]; -	int sample_rate; - +	struct ieee80211_tx_rate *rate = &info->control.rates[0]; +	struct minstrel_rate *msr, *mr; +	unsigned int ndx; +	bool mrr_capable; +	bool prev_sample; +	int delta; +	int sampling_ratio; + +	/* management/no-ack frames do not use rate control */  	if (rate_control_send_low(sta, priv_sta, txrc))  		return; -	mrr = mp->has_mrr && !txrc->rts && !txrc->bss_conf->use_cts_prot; - -	if (time_after(jiffies, mi->stats_update + (mp->update_interval * -			HZ) / 1000)) -		minstrel_update_stats(mp, mi); - -	ndx = mi->max_tp_rate; - -	if (mrr) -		sample_rate = mp->lookaround_rate_mrr; +	/* check multi-rate-retry capabilities & adjust lookaround_rate */ +	mrr_capable = mp->has_mrr && +		      !txrc->rts && +		      !txrc->bss_conf->use_cts_prot; +	if (mrr_capable) +		sampling_ratio = mp->lookaround_rate_mrr;  	else -		sample_rate = mp->lookaround_rate; +		sampling_ratio = mp->lookaround_rate; +	/* increase sum packet counter */  	mi->packet_count++; -	delta = (mi->packet_count * sample_rate / 100) - + +#ifdef CONFIG_MAC80211_DEBUGFS +	if (mp->fixed_rate_idx != -1) +		return; +#endif + +	delta = (mi->packet_count * sampling_ratio / 100) -  			(mi->sample_count + mi->sample_deferred / 2); -	/* delta > 0: sampling required */ -	if ((delta > 0) && (mrr || !mi->prev_sample)) { -		struct minstrel_rate *msr; -		if (mi->packet_count >= 10000) { -			mi->sample_deferred = 0; -			mi->sample_count = 0; -			mi->packet_count = 0; -		} else if (delta > mi->n_rates * 2) { -			/* With multi-rate retry, not every planned sample -			 * attempt actually gets used, due to the way the retry -			 * chain is set up - [max_tp,sample,prob,lowest] for -			 * sample_rate < max_tp. -			 * -			 * If there's too much sampling backlog and the link -			 * starts getting worse, minstrel would start bursting -			 * out lots of sampling frames, which would result -			 * in a large throughput loss. */ -			mi->sample_count += (delta - mi->n_rates * 2); -		} +	/* delta < 0: no sampling required */ +	prev_sample = mi->prev_sample; +	mi->prev_sample = false; +	if (delta < 0 || (!mrr_capable && prev_sample)) +		return; -		sample_ndx = minstrel_get_next_sample(mi); -		msr = &mi->r[sample_ndx]; -		sample = true; -		sample_slower = mrr && (msr->perfect_tx_time > -			mi->r[ndx].perfect_tx_time); - -		if (!sample_slower) { -			if (msr->sample_limit != 0) { -				ndx = sample_ndx; -				mi->sample_count++; -				if (msr->sample_limit > 0) -					msr->sample_limit--; -			} else { -				sample = false; -			} -		} else { -			/* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark -			 * packets that have the sampling rate deferred to the -			 * second MRR stage. Increase the sample counter only -			 * if the deferred sample rate was actually used. -			 * Use the sample_deferred counter to make sure that -			 * the sampling is not done in large bursts */ -			info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; -			mi->sample_deferred++; -		} +	if (mi->packet_count >= 10000) { +		mi->sample_deferred = 0; +		mi->sample_count = 0; +		mi->packet_count = 0; +	} else if (delta > mi->n_rates * 2) { +		/* With multi-rate retry, not every planned sample +		 * attempt actually gets used, due to the way the retry +		 * chain is set up - [max_tp,sample,prob,lowest] for +		 * sample_rate < max_tp. +		 * +		 * If there's too much sampling backlog and the link +		 * starts getting worse, minstrel would start bursting +		 * out lots of sampling frames, which would result +		 * in a large throughput loss. */ +		mi->sample_count += (delta - mi->n_rates * 2); +	} + +	/* get next random rate sample */ +	ndx = minstrel_get_next_sample(mi); +	msr = &mi->r[ndx]; +	mr = &mi->r[mi->max_tp_rate[0]]; + +	/* Decide if direct ( 1st mrr stage) or indirect (2nd mrr stage) +	 * rate sampling method should be used. +	 * Respect such rates that are not sampled for 20 interations. +	 */ +	if (mrr_capable && +	    msr->perfect_tx_time > mr->perfect_tx_time && +	    msr->sample_skipped < 20) { +		/* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark +		 * packets that have the sampling rate deferred to the +		 * second MRR stage. Increase the sample counter only +		 * if the deferred sample rate was actually used. +		 * Use the sample_deferred counter to make sure that +		 * the sampling is not done in large bursts */ +		info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; +		rate++; +		mi->sample_deferred++; +	} else { +		if (!msr->sample_limit != 0) +			return; + +		mi->sample_count++; +		if (msr->sample_limit > 0) +			msr->sample_limit--;  	} -	mi->prev_sample = sample;  	/* If we're not using MRR and the sampling rate already  	 * has a probability of >95%, we shouldn't be attempting  	 * to use it, as this only wastes precious airtime */ -	if (!mrr && sample && (mi->r[ndx].probability > 17100)) -		ndx = mi->max_tp_rate; - -	ar[0].idx = mi->r[ndx].rix; -	ar[0].count = minstrel_get_retry_count(&mi->r[ndx], info); - -	if (!mrr) { -		if (!sample) -			ar[0].count = mp->max_retry; -		ar[1].idx = mi->lowest_rix; -		ar[1].count = mp->max_retry; +	if (!mrr_capable && +	   (mi->r[ndx].probability > MINSTREL_FRAC(95, 100)))  		return; -	} -	/* MRR setup */ -	if (sample) { -		if (sample_slower) -			mrr_ndx[0] = sample_ndx; -		else -			mrr_ndx[0] = mi->max_tp_rate; -	} else { -		mrr_ndx[0] = mi->max_tp_rate2; -	} -	mrr_ndx[1] = mi->max_prob_rate; -	mrr_ndx[2] = 0; -	for (i = 1; i < 4; i++) { -		ar[i].idx = mi->r[mrr_ndx[i - 1]].rix; -		ar[i].count = mi->r[mrr_ndx[i - 1]].adjusted_retry_count; -	} +	mi->prev_sample = true; + +	rate->idx = mi->r[ndx].rix; +	rate->count = minstrel_get_retry_count(&mi->r[ndx], info);  }  static void -calc_rate_durations(struct minstrel_sta_info *mi, struct ieee80211_local *local, -                    struct minstrel_rate *d, struct ieee80211_rate *rate) +calc_rate_durations(enum ieee80211_band band, +		    struct minstrel_rate *d, +		    struct ieee80211_rate *rate, +		    struct cfg80211_chan_def *chandef)  {  	int erp = !!(rate->flags & IEEE80211_RATE_ERP_G); - -	d->perfect_tx_time = ieee80211_frame_duration(local, 1200, -			rate->bitrate, erp, 1); -	d->ack_time = ieee80211_frame_duration(local, 10, -			rate->bitrate, erp, 1); +	int shift = ieee80211_chandef_get_shift(chandef); + +	d->perfect_tx_time = ieee80211_frame_duration(band, 1200, +			DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1, +			shift); +	d->ack_time = ieee80211_frame_duration(band, 10, +			DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1, +			shift);  }  static void  init_sample_table(struct minstrel_sta_info *mi)  {  	unsigned int i, col, new_idx; -	unsigned int n_srates = mi->n_rates - 1;  	u8 rnd[8];  	mi->sample_column = 0; -	mi->sample_idx = 0; -	memset(mi->sample_table, 0, SAMPLE_COLUMNS * mi->n_rates); +	mi->sample_row = 0; +	memset(mi->sample_table, 0xff, SAMPLE_COLUMNS * mi->n_rates);  	for (col = 0; col < SAMPLE_COLUMNS; col++) { -		for (i = 0; i < n_srates; i++) { -			get_random_bytes(rnd, sizeof(rnd)); -			new_idx = (i + rnd[i & 7]) % n_srates; - -			while (SAMPLE_TBL(mi, new_idx, col) != 0) -				new_idx = (new_idx + 1) % n_srates; - -			/* Don't sample the slowest rate (i.e. slowest base -			 * rate). We must presume that the slowest rate works -			 * fine, or else other management frames will also be -			 * failing and the link will break */ -			SAMPLE_TBL(mi, new_idx, col) = i + 1; +		prandom_bytes(rnd, sizeof(rnd)); +		for (i = 0; i < mi->n_rates; i++) { +			new_idx = (i + rnd[i & 7]) % mi->n_rates; +			while (SAMPLE_TBL(mi, new_idx, col) != 0xff) +				new_idx = (new_idx + 1) % mi->n_rates; + +			SAMPLE_TBL(mi, new_idx, col) = i;  		}  	}  }  static void  minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, -               struct ieee80211_sta *sta, void *priv_sta) +		   struct cfg80211_chan_def *chandef, +		   struct ieee80211_sta *sta, void *priv_sta)  {  	struct minstrel_sta_info *mi = priv_sta;  	struct minstrel_priv *mp = priv; -	struct ieee80211_local *local = hw_to_local(mp->hw);  	struct ieee80211_rate *ctl_rate;  	unsigned int i, n = 0;  	unsigned int t_slot = 9; /* FIXME: get real slot time */ +	u32 rate_flags; +	mi->sta = sta;  	mi->lowest_rix = rate_lowest_index(sband, sta);  	ctl_rate = &sband->bitrates[mi->lowest_rix]; -	mi->sp_ack_dur = ieee80211_frame_duration(local, 10, ctl_rate->bitrate, -				!!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1); +	mi->sp_ack_dur = ieee80211_frame_duration(sband->band, 10, +				ctl_rate->bitrate, +				!!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1, +				ieee80211_chandef_get_shift(chandef)); + +	rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef); +	memset(mi->max_tp_rate, 0, sizeof(mi->max_tp_rate)); +	mi->max_prob_rate = 0;  	for (i = 0; i < sband->n_bitrates; i++) {  		struct minstrel_rate *mr = &mi->r[n];  		unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0;  		unsigned int tx_time_single;  		unsigned int cw = mp->cw_min; +		int shift;  		if (!rate_supported(sta, sband->band, i))  			continue; +		if ((rate_flags & sband->bitrates[i].flags) != rate_flags) +			continue; +  		n++;  		memset(mr, 0, sizeof(*mr));  		mr->rix = i; -		mr->bitrate = sband->bitrates[i].bitrate / 5; -		calc_rate_durations(mi, local, mr, -				&sband->bitrates[i]); +		shift = ieee80211_chandef_get_shift(chandef); +		mr->bitrate = DIV_ROUND_UP(sband->bitrates[i].bitrate, +					   (1 << shift) * 5); +		calc_rate_durations(sband->band, mr, &sband->bitrates[i], +				    chandef);  		/* calculate maximum number of retransmissions before  		 * fallback (based on maximum segment size) */ @@ -417,8 +491,8 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,  			tx_time_single = mr->ack_time + mr->perfect_tx_time;  			/* contention window */ -			tx_time_single += t_slot + min(cw, mp->cw_max); -			cw = (cw << 1) | 1; +			tx_time_single += (t_slot * cw) >> 1; +			cw = min((cw << 1) | 1, mp->cw_max);  			tx_time += tx_time_single;  			tx_time_cts += tx_time_single + mi->sp_ack_dur; @@ -432,6 +506,8 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,  		} while ((tx_time < mp->segment_size) &&  				(++mr->retry_count < mp->max_retry));  		mr->adjusted_retry_count = mr->retry_count; +		if (!(sband->bitrates[i].flags & IEEE80211_RATE_ERP_G)) +			mr->retry_count_cts = mr->retry_count;  	}  	for (i = n; i < sband->n_bitrates; i++) { @@ -443,6 +519,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,  	mi->stats_update = jiffies;  	init_sample_table(mi); +	minstrel_update_rates(mp, mi);  }  static void * @@ -493,6 +570,37 @@ minstrel_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta)  	kfree(mi);  } +static void +minstrel_init_cck_rates(struct minstrel_priv *mp) +{ +	static const int bitrates[4] = { 10, 20, 55, 110 }; +	struct ieee80211_supported_band *sband; +	u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef); +	int i, j; + +	sband = mp->hw->wiphy->bands[IEEE80211_BAND_2GHZ]; +	if (!sband) +		return; + +	for (i = 0, j = 0; i < sband->n_bitrates; i++) { +		struct ieee80211_rate *rate = &sband->bitrates[i]; + +		if (rate->flags & IEEE80211_RATE_ERP_G) +			continue; + +		if ((rate_flags & sband->bitrates[i].flags) != rate_flags) +			continue; + +		for (j = 0; j < ARRAY_SIZE(bitrates); j++) { +			if (rate->bitrate != bitrates[j]) +				continue; + +			mp->cck_rates[j] = i; +			break; +		} +	} +} +  static void *  minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)  { @@ -514,9 +622,6 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)  	mp->lookaround_rate = 5;  	mp->lookaround_rate_mrr = 10; -	/* moving average weight for EWMA */ -	mp->ewma_level = 75; -  	/* maximum time that the hw is allowed to stay in one MRR segment */  	mp->segment_size = 6000; @@ -532,16 +637,38 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)  	mp->hw = hw;  	mp->update_interval = 100; +#ifdef CONFIG_MAC80211_DEBUGFS +	mp->fixed_rate_idx = (u32) -1; +	mp->dbg_fixed_rate = debugfs_create_u32("fixed_rate_idx", +			S_IRUGO | S_IWUGO, debugfsdir, &mp->fixed_rate_idx); +#endif + +	minstrel_init_cck_rates(mp); +  	return mp;  }  static void  minstrel_free(void *priv)  { +#ifdef CONFIG_MAC80211_DEBUGFS +	debugfs_remove(((struct minstrel_priv *)priv)->dbg_fixed_rate); +#endif  	kfree(priv);  } -struct rate_control_ops mac80211_minstrel = { +static u32 minstrel_get_expected_throughput(void *priv_sta) +{ +	struct minstrel_sta_info *mi = priv_sta; +	int idx = mi->max_tp_rate[0]; + +	/* convert pkt per sec in kbps (1200 is the average pkt size used for +	 * computing cur_tp +	 */ +	return MINSTREL_TRUNC(mi->r[idx].cur_tp) * 1200 * 8 / 1024; +} + +const struct rate_control_ops mac80211_minstrel = {  	.name = "minstrel",  	.tx_status = minstrel_tx_status,  	.get_rate = minstrel_get_rate, @@ -554,6 +681,7 @@ struct rate_control_ops mac80211_minstrel = {  	.add_sta_debugfs = minstrel_add_sta_debugfs,  	.remove_sta_debugfs = minstrel_remove_sta_debugfs,  #endif +	.get_expected_throughput = minstrel_get_expected_throughput,  };  int __init diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 0f5a83370aa..046d1bd598a 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -9,6 +9,29 @@  #ifndef __RC_MINSTREL_H  #define __RC_MINSTREL_H +#define EWMA_LEVEL	96	/* ewma weighting factor [/EWMA_DIV] */ +#define EWMA_DIV	128 +#define SAMPLE_COLUMNS	10	/* number of columns in sample table */ + + +/* scaled fraction values */ +#define MINSTREL_SCALE  16 +#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div) +#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE) + +/* number of highest throughput rates to consider*/ +#define MAX_THR_RATES 4 + +/* + * Perform EWMA (Exponentially Weighted Moving Average) calculation +  */ +static inline int +minstrel_ewma(int old, int new, int weight) +{ +	return (new * (EWMA_DIV - weight) + old * weight) / EWMA_DIV; +} + +  struct minstrel_rate {  	int bitrate;  	int rix; @@ -26,6 +49,7 @@ struct minstrel_rate {  	u32 attempts;  	u32 last_attempts;  	u32 last_success; +	u8 sample_skipped;  	/* parts per thousand */  	u32 cur_prob; @@ -39,20 +63,21 @@ struct minstrel_rate {  };  struct minstrel_sta_info { +	struct ieee80211_sta *sta; +  	unsigned long stats_update;  	unsigned int sp_ack_dur;  	unsigned int rate_avg;  	unsigned int lowest_rix; -	unsigned int max_tp_rate; -	unsigned int max_tp_rate2; -	unsigned int max_prob_rate; +	u8 max_tp_rate[MAX_THR_RATES]; +	u8 max_prob_rate;  	unsigned int packet_count;  	unsigned int sample_count;  	int sample_deferred; -	unsigned int sample_idx; +	unsigned int sample_row;  	unsigned int sample_column;  	int n_rates; @@ -73,11 +98,24 @@ struct minstrel_priv {  	unsigned int cw_min;  	unsigned int cw_max;  	unsigned int max_retry; -	unsigned int ewma_level;  	unsigned int segment_size;  	unsigned int update_interval;  	unsigned int lookaround_rate;  	unsigned int lookaround_rate_mrr; + +	u8 cck_rates[4]; + +#ifdef CONFIG_MAC80211_DEBUGFS +	/* +	 * enable fixed rate processing per RC +	 *   - write static index to debugfs:ieee80211/phyX/rc/fixed_rate_idx +	 *   - write -1 to enable RC processing again +	 *   - setting will be applied on next update +	 */ +	u32 fixed_rate_idx; +	struct dentry *dbg_fixed_rate; +#endif +  };  struct minstrel_debugfs_info { @@ -85,7 +123,7 @@ struct minstrel_debugfs_info {  	char buf[];  }; -extern struct rate_control_ops mac80211_minstrel; +extern const struct rate_control_ops mac80211_minstrel;  void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);  void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index a290ad231d7..fd0b9ca1570 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -50,6 +50,7 @@  #include <linux/debugfs.h>  #include <linux/ieee80211.h>  #include <linux/slab.h> +#include <linux/export.h>  #include <net/mac80211.h>  #include "rc80211_minstrel.h" @@ -67,23 +68,25 @@ minstrel_stats_open(struct inode *inode, struct file *file)  	file->private_data = ms;  	p = ms->buf; -	p += sprintf(p, "rate     throughput  ewma prob   this prob  " +	p += sprintf(p, "rate      throughput  ewma prob  this prob  "  			"this succ/attempt   success    attempts\n");  	for (i = 0; i < mi->n_rates; i++) {  		struct minstrel_rate *mr = &mi->r[i]; -		*(p++) = (i == mi->max_tp_rate) ? 'T' : ' '; -		*(p++) = (i == mi->max_tp_rate2) ? 't' : ' '; +		*(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' '; +		*(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' '; +		*(p++) = (i == mi->max_tp_rate[2]) ? 'C' : ' '; +		*(p++) = (i == mi->max_tp_rate[3]) ? 'D' : ' ';  		*(p++) = (i == mi->max_prob_rate) ? 'P' : ' ';  		p += sprintf(p, "%3u%s", mr->bitrate / 2,  				(mr->bitrate & 1 ? ".5" : "  ")); -		tp = mr->cur_tp / ((18000 << 10) / 96); -		prob = mr->cur_prob / 18; -		eprob = mr->probability / 18; +		tp = MINSTREL_TRUNC(mr->cur_tp / 10); +		prob = MINSTREL_TRUNC(mr->cur_prob * 1000); +		eprob = MINSTREL_TRUNC(mr->probability * 1000);  		p += sprintf(p, "  %6u.%1u   %6u.%1u   %6u.%1u        " -				"%3u(%3u)   %8llu    %8llu\n", +				"   %3u(%3u)  %8llu    %8llu\n",  				tp / 10, tp % 10,  				eprob / 10, eprob % 10,  				prob / 10, prob % 10, diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 4ad7a362fcc..85c1e74b771 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -1,5 +1,5 @@  /* - * Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2010-2013 Felix Fietkau <nbd@openwrt.org>   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License version 2 as @@ -17,27 +17,34 @@  #include "rc80211_minstrel_ht.h"  #define AVG_PKT_SIZE	1200 -#define SAMPLE_COLUMNS	10 -#define EWMA_LEVEL		75  /* Number of bits for an average sized packet */  #define MCS_NBITS (AVG_PKT_SIZE << 3)  /* Number of symbols for a packet with (bps) bits per symbol */ -#define MCS_NSYMS(bps) ((MCS_NBITS + (bps) - 1) / (bps)) +#define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps)) -/* Transmission time for a packet containing (syms) symbols */ +/* Transmission time (nanoseconds) for a packet containing (syms) symbols */  #define MCS_SYMBOL_TIME(sgi, syms)					\  	(sgi ?								\ -	  ((syms) * 18 + 4) / 5 :	/* syms * 3.6 us */		\ -	  (syms) << 2			/* syms * 4 us */		\ +	  ((syms) * 18000 + 4000) / 5 :	/* syms * 3.6 us */		\ +	  ((syms) * 1000) << 2		/* syms * 4 us */		\  	)  /* 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 group sort order: HT40 -> SGI -> #streams + */ +#define GROUP_IDX(_streams, _sgi, _ht40)	\ +	MINSTREL_MAX_STREAMS * 2 * _ht40 +	\ +	MINSTREL_MAX_STREAMS * _sgi +		\ +	_streams - 1 +  /* MCS rate information for an MCS group */ -#define MCS_GROUP(_streams, _sgi, _ht40) {				\ +#define MCS_GROUP(_streams, _sgi, _ht40)				\ +	[GROUP_IDX(_streams, _sgi, _ht40)] = {				\  	.streams = _streams,						\  	.flags =							\  		(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) |			\ @@ -54,10 +61,37 @@  	}								\  } +#define CCK_DURATION(_bitrate, _short, _len)		\ +	(1000 * (10 /* SIFS */ +			\ +	 (_short ? 72 + 24 : 144 + 48) +		\ +	 (8 * (_len + 4) * 10) / (_bitrate))) + +#define CCK_ACK_DURATION(_bitrate, _short)			\ +	(CCK_DURATION((_bitrate > 10 ? 20 : 10), false, 60) +	\ +	 CCK_DURATION(_bitrate, _short, AVG_PKT_SIZE)) + +#define CCK_DURATION_LIST(_short)			\ +	CCK_ACK_DURATION(10, _short),			\ +	CCK_ACK_DURATION(20, _short),			\ +	CCK_ACK_DURATION(55, _short),			\ +	CCK_ACK_DURATION(110, _short) + +#define CCK_GROUP						\ +	[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS] = {	\ +		.streams = 0,					\ +		.duration = {					\ +			CCK_DURATION_LIST(false),		\ +			CCK_DURATION_LIST(true)			\ +		}						\ +	} +  /*   * To enable sufficiently targeted rate sampling, MCS rates are divided into   * groups, based on the number of streams and flags (HT40, SGI) that they   * use. + * + * Sortorder has to be fixed for GROUP_IDX macro to be applicable: + * HT40 -> SGI -> #streams   */  const struct mcs_group minstrel_mcs_groups[] = {  	MCS_GROUP(1, 0, 0), @@ -83,18 +117,17 @@ const struct mcs_group minstrel_mcs_groups[] = {  #if MINSTREL_MAX_STREAMS >= 3  	MCS_GROUP(3, 1, 1),  #endif + +	/* must be last */ +	CCK_GROUP  }; -static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES]; +#define MINSTREL_CCK_GROUP	(ARRAY_SIZE(minstrel_mcs_groups) - 1) -/* - * Perform EWMA (Exponentially Weighted Moving Average) calculation - */ -static int -minstrel_ewma(int old, int new, int weight) -{ -	return (new * (100 - weight) + old * weight) / 100; -} +static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly; + +static void +minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);  /*   * Look up an MCS group index based on mac80211 rate information @@ -102,21 +135,32 @@ minstrel_ewma(int old, int new, int weight)  static int  minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate)  { -	int streams = (rate->idx / MCS_GROUP_RATES) + 1; -	u32 flags = IEEE80211_TX_RC_SHORT_GI | IEEE80211_TX_RC_40_MHZ_WIDTH; -	int i; +	return GROUP_IDX((rate->idx / 8) + 1, +			 !!(rate->flags & IEEE80211_TX_RC_SHORT_GI), +			 !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)); +} -	for (i = 0; i < ARRAY_SIZE(minstrel_mcs_groups); i++) { -		if (minstrel_mcs_groups[i].streams != streams) -			continue; -		if (minstrel_mcs_groups[i].flags != (rate->flags & flags)) -			continue; +static struct minstrel_rate_stats * +minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, +		      struct ieee80211_tx_rate *rate) +{ +	int group, idx; -		return i; -	} +	if (rate->flags & IEEE80211_TX_RC_MCS) { +		group = minstrel_ht_get_group_idx(rate); +		idx = rate->idx % 8; +	} else { +		group = MINSTREL_CCK_GROUP; -	WARN_ON(1); -	return 0; +		for (idx = 0; idx < ARRAY_SIZE(mp->cck_rates); idx++) +			if (rate->idx == mp->cck_rates[idx]) +				break; + +		/* short preamble */ +		if (!(mi->groups[group].supported & BIT(idx))) +			idx += 4; +	} +	return &mi->groups[group].rates[idx];  }  static inline struct minstrel_rate_stats * @@ -130,7 +174,7 @@ minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index)   * Recalculate success probabilities and counters for a rate using EWMA   */  static void -minstrel_calc_rate_ewma(struct minstrel_priv *mp, struct minstrel_rate_stats *mr) +minstrel_calc_rate_ewma(struct minstrel_rate_stats *mr)  {  	if (unlikely(mr->attempts > 0)) {  		mr->sample_skipped = 0; @@ -156,22 +200,36 @@ minstrel_calc_rate_ewma(struct minstrel_priv *mp, struct minstrel_rate_stats *mr   * the expected number of retransmissions and their expected length   */  static void -minstrel_ht_calc_tp(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, -                    int group, int rate) +minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)  {  	struct minstrel_rate_stats *mr; -	unsigned int usecs; +	unsigned int nsecs = 0; +	unsigned int tp; +	unsigned int prob;  	mr = &mi->groups[group].rates[rate]; +	prob = mr->probability; -	if (mr->probability < MINSTREL_FRAC(1, 10)) { +	if (prob < MINSTREL_FRAC(1, 10)) {  		mr->cur_tp = 0;  		return;  	} -	usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len); -	usecs += minstrel_mcs_groups[group].duration[rate]; -	mr->cur_tp = MINSTREL_TRUNC((1000000 / usecs) * mr->probability); +	/* +	 * For the throughput calculation, limit the probability value to 90% to +	 * account for collision related packet error rate fluctuation +	 */ +	if (prob > MINSTREL_FRAC(9, 10)) +		prob = MINSTREL_FRAC(9, 10); + +	if (group != MINSTREL_CCK_GROUP) +		nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len); + +	nsecs += minstrel_mcs_groups[group].duration[rate]; + +	/* prob is scaled - see MINSTREL_FRAC above */ +	tp = 1000000 * ((prob * 1000) / nsecs); +	mr->cur_tp = MINSTREL_TRUNC(tp);  }  /* @@ -190,6 +248,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  	struct minstrel_rate_stats *mr;  	int cur_prob, cur_prob_tp, cur_tp, cur_tp2;  	int group, i, index; +	bool mi_rates_valid = false;  	if (mi->ampdu_packets > 0) {  		mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len, @@ -200,11 +259,10 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  	mi->sample_slow = 0;  	mi->sample_count = 0; -	mi->max_tp_rate = 0; -	mi->max_tp_rate2 = 0; -	mi->max_prob_rate = 0;  	for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { +		bool mg_rates_valid = false; +  		cur_prob = 0;  		cur_prob_tp = 0;  		cur_tp = 0; @@ -214,28 +272,34 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  		if (!mg->supported)  			continue; -		mg->max_tp_rate = 0; -		mg->max_tp_rate2 = 0; -		mg->max_prob_rate = 0;  		mi->sample_count++;  		for (i = 0; i < MCS_GROUP_RATES; i++) {  			if (!(mg->supported & BIT(i)))  				continue; +			index = MCS_GROUP_RATES * group + i; + +			/* initialize rates selections starting indexes */ +			if (!mg_rates_valid) { +				mg->max_tp_rate = mg->max_tp_rate2 = +					mg->max_prob_rate = i; +				if (!mi_rates_valid) { +					mi->max_tp_rate = mi->max_tp_rate2 = +						mi->max_prob_rate = index; +					mi_rates_valid = true; +				} +				mg_rates_valid = true; +			} +  			mr = &mg->rates[i];  			mr->retry_updated = false; -			index = MCS_GROUP_RATES * group + i; -			minstrel_calc_rate_ewma(mp, mr); -			minstrel_ht_calc_tp(mp, mi, group, i); +			minstrel_calc_rate_ewma(mr); +			minstrel_ht_calc_tp(mi, group, i);  			if (!mr->cur_tp)  				continue; -			/* ignore the lowest rate of each single-stream group */ -			if (!i && minstrel_mcs_groups[group].streams == 1) -				continue; -  			if ((mr->cur_tp > cur_prob_tp && mr->probability >  			     MINSTREL_FRAC(3, 4)) || mr->probability > cur_prob) {  				mg->max_prob_rate = index; @@ -259,8 +323,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  		}  	} -	/* try to sample up to half of the availble rates during each interval */ -	mi->sample_count *= 4; +	/* try to sample all available rates during each interval */ +	mi->sample_count *= 8;  	cur_prob = 0;  	cur_prob_tp = 0; @@ -271,18 +335,13 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  		if (!mg->supported)  			continue; -		mr = minstrel_get_ratestats(mi, mg->max_prob_rate); -		if (cur_prob_tp < mr->cur_tp && -		    minstrel_mcs_groups[group].streams == 1) { -			mi->max_prob_rate = mg->max_prob_rate; -			cur_prob = mr->cur_prob; -			cur_prob_tp = mr->cur_tp; -		} -  		mr = minstrel_get_ratestats(mi, mg->max_tp_rate);  		if (cur_tp < mr->cur_tp) { +			mi->max_tp_rate2 = mi->max_tp_rate; +			cur_tp2 = cur_tp;  			mi->max_tp_rate = mg->max_tp_rate;  			cur_tp = mr->cur_tp; +			mi->max_prob_streams = minstrel_mcs_groups[group].streams - 1;  		}  		mr = minstrel_get_ratestats(mi, mg->max_tp_rate2); @@ -292,19 +351,50 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  		}  	} +	if (mi->max_prob_streams < 1) +		mi->max_prob_streams = 1; + +	for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { +		mg = &mi->groups[group]; +		if (!mg->supported) +			continue; +		mr = minstrel_get_ratestats(mi, mg->max_prob_rate); +		if (cur_prob_tp < mr->cur_tp && +		    minstrel_mcs_groups[group].streams <= mi->max_prob_streams) { +			mi->max_prob_rate = mg->max_prob_rate; +			cur_prob = mr->cur_prob; +			cur_prob_tp = mr->cur_tp; +		} +	} + +#ifdef CONFIG_MAC80211_DEBUGFS +	/* use fixed index if set */ +	if (mp->fixed_rate_idx != -1) { +		mi->max_tp_rate = mp->fixed_rate_idx; +		mi->max_tp_rate2 = mp->fixed_rate_idx; +		mi->max_prob_rate = mp->fixed_rate_idx; +	} +#endif +  	mi->stats_update = jiffies;  }  static bool -minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate) +minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rate)  { -	if (!rate->count) +	if (rate->idx < 0)  		return false; -	if (rate->idx < 0) +	if (!rate->count)  		return false; -	return !!(rate->flags & IEEE80211_TX_RC_MCS); +	if (rate->flags & IEEE80211_TX_RC_MCS) +		return true; + +	return rate->idx == mp->cck_rates[0] || +	       rate->idx == mp->cck_rates[1] || +	       rate->idx == mp->cck_rates[2] || +	       rate->idx == mp->cck_rates[3];  }  static void @@ -355,7 +445,7 @@ minstrel_downgrade_rate(struct minstrel_ht_sta *mi, unsigned int *idx,  }  static void -minstrel_aggr_check(struct minstrel_priv *mp, struct ieee80211_sta *pubsta, struct sk_buff *skb) +minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb)  {  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;  	struct sta_info *sta = container_of(pubsta, struct sta_info, sta); @@ -374,7 +464,7 @@ minstrel_aggr_check(struct minstrel_priv *mp, struct ieee80211_sta *pubsta, stru  	if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)  		return; -	ieee80211_start_tx_ba_session(pubsta, tid); +	ieee80211_start_tx_ba_session(pubsta, tid, 5000);  }  static void @@ -388,9 +478,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,  	struct ieee80211_tx_rate *ar = info->status.rates;  	struct minstrel_rate_stats *rate, *rate2;  	struct minstrel_priv *mp = priv; -	bool last = false; -	int group; -	int i = 0; +	bool last, update = false; +	int i;  	if (!msp->is_ht)  		return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb); @@ -411,24 +500,19 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,  	if (!mi->sample_wait && !mi->sample_tries && mi->sample_count > 0) {  		mi->sample_wait = 16 + 2 * MINSTREL_TRUNC(mi->avg_ampdu_len); -		mi->sample_tries = 2; +		mi->sample_tries = 1;  		mi->sample_count--;  	} -	if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) { +	if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)  		mi->sample_packets += info->status.ampdu_len; -		minstrel_next_sample_idx(mi); -	} +	last = !minstrel_ht_txstat_valid(mp, &ar[0]);  	for (i = 0; !last; i++) {  		last = (i == IEEE80211_TX_MAX_RATES - 1) || -		       !minstrel_ht_txstat_valid(&ar[i + 1]); - -		if (!minstrel_ht_txstat_valid(&ar[i])) -			break; +		       !minstrel_ht_txstat_valid(mp, &ar[i + 1]); -		group = minstrel_ht_get_group_idx(&ar[i]); -		rate = &mi->groups[group].rates[ar[i].idx % 8]; +		rate = minstrel_ht_get_stats(mp, mi, &ar[i]);  		if (last)  			rate->success += info->status.ampdu_ack_len; @@ -443,19 +527,29 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,  	rate = minstrel_get_ratestats(mi, mi->max_tp_rate);  	if (rate->attempts > 30 &&  	    MINSTREL_FRAC(rate->success, rate->attempts) < -	    MINSTREL_FRAC(20, 100)) +	    MINSTREL_FRAC(20, 100)) {  		minstrel_downgrade_rate(mi, &mi->max_tp_rate, true); +		update = true; +	}  	rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate2);  	if (rate2->attempts > 30 &&  	    MINSTREL_FRAC(rate2->success, rate2->attempts) < -	    MINSTREL_FRAC(20, 100)) +	    MINSTREL_FRAC(20, 100)) {  		minstrel_downgrade_rate(mi, &mi->max_tp_rate2, false); +		update = true; +	}  	if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) { +		update = true;  		minstrel_ht_update_stats(mp, mi); -		minstrel_aggr_check(mp, sta, skb); +		if (!(info->flags & IEEE80211_TX_CTL_AMPDU) && +		    mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP) +			minstrel_aggr_check(sta, skb);  	} + +	if (update) +		minstrel_ht_update_rates(mp, mi);  }  static void @@ -466,8 +560,10 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,  	const struct mcs_group *group;  	unsigned int tx_time, tx_time_rtscts, tx_time_data;  	unsigned int cw = mp->cw_min; +	unsigned int ctime = 0;  	unsigned int t_slot = 9; /* FIXME */  	unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len); +	unsigned int overhead = 0, overhead_rtscts = 0;  	mr = minstrel_get_ratestats(mi, index);  	if (mr->probability < MINSTREL_FRAC(1, 10)) { @@ -481,14 +577,33 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,  	mr->retry_updated = true;  	group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; -	tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len; -	tx_time = 2 * (t_slot + mi->overhead + tx_time_data); -	tx_time_rtscts = 2 * (t_slot + mi->overhead_rtscts + tx_time_data); +	tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len / 1000; + +	/* Contention time for first 2 tries */ +	ctime = (t_slot * cw) >> 1; +	cw = min((cw << 1) | 1, mp->cw_max); +	ctime += (t_slot * cw) >> 1; +	cw = min((cw << 1) | 1, mp->cw_max); + +	if (index / MCS_GROUP_RATES != MINSTREL_CCK_GROUP) { +		overhead = mi->overhead; +		overhead_rtscts = mi->overhead_rtscts; +	} + +	/* Total TX time for data and Contention after first 2 tries */ +	tx_time = ctime + 2 * (overhead + tx_time_data); +	tx_time_rtscts = ctime + 2 * (overhead_rtscts + tx_time_data); + +	/* See how many more tries we can fit inside segment size */  	do { -		cw = (cw << 1) | 1; -		cw = min(cw, mp->cw_max); -		tx_time += cw + t_slot + mi->overhead; -		tx_time_rtscts += cw + t_slot + mi->overhead_rtscts; +		/* Contention time for this try */ +		ctime = (t_slot * cw) >> 1; +		cw = min((cw << 1) | 1, mp->cw_max); + +		/* Total TX time after this try */ +		tx_time += ctime + overhead + tx_time_data; +		tx_time_rtscts += ctime + overhead_rtscts + tx_time_data; +  		if (tx_time_rtscts < mp->segment_size)  			mr->retry_count_rtscts++;  	} while ((tx_time < mp->segment_size) && @@ -498,32 +613,70 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,  static void  minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, -                     struct ieee80211_tx_rate *rate, int index, -                     struct ieee80211_tx_rate_control *txrc, -                     bool sample, bool rtscts) +                     struct ieee80211_sta_rates *ratetbl, int offset, int index)  {  	const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];  	struct minstrel_rate_stats *mr; +	u8 idx; +	u16 flags;  	mr = minstrel_get_ratestats(mi, index);  	if (!mr->retry_updated)  		minstrel_calc_retransmit(mp, mi, index); -	if (sample) -		rate->count = 1; -	else if (mr->probability < MINSTREL_FRAC(20, 100)) -		rate->count = 2; -	else if (rtscts) -		rate->count = mr->retry_count_rtscts; -	else -		rate->count = mr->retry_count; - -	rate->flags = IEEE80211_TX_RC_MCS | group->flags; -	if (txrc->short_preamble) -		rate->flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE; -	if (txrc->rts || rtscts) -		rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS; -	rate->idx = index % MCS_GROUP_RATES + (group->streams - 1) * MCS_GROUP_RATES; +	if (mr->probability < MINSTREL_FRAC(20, 100) || !mr->retry_count) { +		ratetbl->rate[offset].count = 2; +		ratetbl->rate[offset].count_rts = 2; +		ratetbl->rate[offset].count_cts = 2; +	} else { +		ratetbl->rate[offset].count = mr->retry_count; +		ratetbl->rate[offset].count_cts = mr->retry_count; +		ratetbl->rate[offset].count_rts = mr->retry_count_rtscts; +	} + +	if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) { +		idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)]; +		flags = 0; +	} else { +		idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8; +		flags = IEEE80211_TX_RC_MCS | group->flags; +	} + +	if (offset > 0) { +		ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts; +		flags |= IEEE80211_TX_RC_USE_RTS_CTS; +	} + +	ratetbl->rate[offset].idx = idx; +	ratetbl->rate[offset].flags = flags; +} + +static void +minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) +{ +	struct ieee80211_sta_rates *rates; +	int i = 0; + +	rates = kzalloc(sizeof(*rates), GFP_ATOMIC); +	if (!rates) +		return; + +	/* Start with max_tp_rate */ +	minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate); + +	if (mp->hw->max_rates >= 3) { +		/* At least 3 tx rates supported, use max_tp_rate2 next */ +		minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate2); +	} + +	if (mp->hw->max_rates >= 2) { +		/* +		 * At least 2 tx rates supported, use max_prob_rate next */ +		minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_prob_rate); +	} + +	rates->rate[i].idx = -1; +	rate_control_set_rates(mp->hw, mi->sta, rates);  }  static inline int @@ -538,6 +691,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  {  	struct minstrel_rate_stats *mr;  	struct minstrel_mcs_group_data *mg; +	unsigned int sample_dur, sample_group;  	int sample_idx = 0;  	if (mi->sample_wait > 0) { @@ -548,50 +702,81 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)  	if (!mi->sample_tries)  		return -1; -	mi->sample_tries--; -	mg = &mi->groups[mi->sample_group]; +	sample_group = mi->sample_group; +	mg = &mi->groups[sample_group];  	sample_idx = sample_table[mg->column][mg->index]; +	minstrel_next_sample_idx(mi); + +	if (!(mg->supported & BIT(sample_idx))) +		return -1; +  	mr = &mg->rates[sample_idx]; -	sample_idx += mi->sample_group * MCS_GROUP_RATES; +	sample_idx += sample_group * MCS_GROUP_RATES;  	/* -	 * When not using MRR, do not sample if the probability is already -	 * higher than 95% to avoid wasting airtime +	 * Sampling might add some overhead (RTS, no aggregation) +	 * to the frame. Hence, don't use sampling for the currently +	 * used rates.  	 */ -	if (!mp->has_mrr && (mr->probability > MINSTREL_FRAC(95, 100))) -		goto next; +	if (sample_idx == mi->max_tp_rate || +	    sample_idx == mi->max_tp_rate2 || +	    sample_idx == mi->max_prob_rate) +		return -1; + +	/* +	 * Do not sample if the probability is already higher than 95% +	 * to avoid wasting airtime. +	 */ +	if (mr->probability > MINSTREL_FRAC(95, 100)) +		return -1;  	/*  	 * Make sure that lower rates get sampled only occasionally,  	 * if the link is working perfectly.  	 */ -	if (minstrel_get_duration(sample_idx) > -	    minstrel_get_duration(mi->max_tp_rate)) { +	sample_dur = minstrel_get_duration(sample_idx); +	if (sample_dur >= minstrel_get_duration(mi->max_tp_rate2) && +	    (mi->max_prob_streams < +	     minstrel_mcs_groups[sample_group].streams || +	     sample_dur >= minstrel_get_duration(mi->max_prob_rate))) {  		if (mr->sample_skipped < 20) -			goto next; +			return -1;  		if (mi->sample_slow++ > 2) -			goto next; +			return -1;  	} +	mi->sample_tries--;  	return sample_idx; +} -next: -	minstrel_next_sample_idx(mi); -	return -1; +static void +minstrel_ht_check_cck_shortpreamble(struct minstrel_priv *mp, +				    struct minstrel_ht_sta *mi, bool val) +{ +	u8 supported = mi->groups[MINSTREL_CCK_GROUP].supported; + +	if (!supported || !mi->cck_supported_short) +		return; + +	if (supported & (mi->cck_supported_short << (val * 4))) +		return; + +	supported ^= mi->cck_supported_short | (mi->cck_supported_short << 4); +	mi->groups[MINSTREL_CCK_GROUP].supported = supported;  }  static void  minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,                       struct ieee80211_tx_rate_control *txrc)  { +	const struct mcs_group *sample_group;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb); -	struct ieee80211_tx_rate *ar = info->status.rates; +	struct ieee80211_tx_rate *rate = &info->status.rates[0];  	struct minstrel_ht_sta_priv *msp = priv_sta;  	struct minstrel_ht_sta *mi = &msp->ht;  	struct minstrel_priv *mp = priv;  	int sample_idx; -	bool sample = false;  	if (rate_control_send_low(sta, priv_sta, txrc))  		return; @@ -600,24 +785,19 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,  		return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc);  	info->flags |= mi->tx_flags; -	sample_idx = minstrel_get_sample_rate(mp, mi); -	if (sample_idx >= 0) { -		sample = true; -		minstrel_ht_set_rate(mp, mi, &ar[0], sample_idx, -			txrc, true, false); -		minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate, -			txrc, false, false); -		info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; -	} else { -		minstrel_ht_set_rate(mp, mi, &ar[0], mi->max_tp_rate, -			txrc, false, false); -		minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate2, -			txrc, false, true); -	} -	minstrel_ht_set_rate(mp, mi, &ar[2], mi->max_prob_rate, txrc, false, !sample); +	minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble); + +#ifdef CONFIG_MAC80211_DEBUGFS +	if (mp->fixed_rate_idx != -1) +		return; +#endif -	ar[3].count = 0; -	ar[3].idx = -1; +	/* Don't use EAPOL frames for sampling on non-mrr hw */ +	if (mp->hw->max_rates == 1 && +	    (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) +		sample_idx = -1; +	else +		sample_idx = minstrel_get_sample_rate(mp, mi);  	mi->total_packets++; @@ -626,41 +806,84 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,  		mi->total_packets = 0;  		mi->sample_packets = 0;  	} + +	if (sample_idx < 0) +		return; + +	sample_group = &minstrel_mcs_groups[sample_idx / MCS_GROUP_RATES]; +	info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; +	rate->count = 1; + +	if (sample_idx / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) { +		int idx = sample_idx % ARRAY_SIZE(mp->cck_rates); +		rate->idx = mp->cck_rates[idx]; +		rate->flags = 0; +		return; +	} + +	rate->idx = sample_idx % MCS_GROUP_RATES + +		    (sample_group->streams - 1) * 8; +	rate->flags = IEEE80211_TX_RC_MCS | sample_group->flags; +} + +static void +minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, +		       struct ieee80211_supported_band *sband, +		       struct ieee80211_sta *sta) +{ +	int i; + +	if (sband->band != IEEE80211_BAND_2GHZ) +		return; + +	if (!(mp->hw->flags & IEEE80211_HW_SUPPORTS_HT_CCK_RATES)) +		return; + +	mi->cck_supported = 0; +	mi->cck_supported_short = 0; +	for (i = 0; i < 4; i++) { +		if (!rate_supported(sta, sband->band, mp->cck_rates[i])) +			continue; + +		mi->cck_supported |= BIT(i); +		if (sband->bitrates[i].flags & IEEE80211_RATE_SHORT_PREAMBLE) +			mi->cck_supported_short |= BIT(i); +	} + +	mi->groups[MINSTREL_CCK_GROUP].supported = mi->cck_supported;  }  static void  minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, -                        struct ieee80211_sta *sta, void *priv_sta, -			enum nl80211_channel_type oper_chan_type) +			struct cfg80211_chan_def *chandef, +                        struct ieee80211_sta *sta, void *priv_sta)  {  	struct minstrel_priv *mp = priv;  	struct minstrel_ht_sta_priv *msp = priv_sta;  	struct minstrel_ht_sta *mi = &msp->ht;  	struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs; -	struct ieee80211_local *local = hw_to_local(mp->hw);  	u16 sta_cap = sta->ht_cap.cap; +	int n_supported = 0;  	int ack_dur;  	int stbc;  	int i;  	/* fall back to the old minstrel for legacy stations */ -	if (!sta->ht_cap.ht_supported) { -		msp->is_ht = false; -		memset(&msp->legacy, 0, sizeof(msp->legacy)); -		msp->legacy.r = msp->ratelist; -		msp->legacy.sample_table = msp->sample_table; -		return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy); -	} +	if (!sta->ht_cap.ht_supported) +		goto use_legacy;  	BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != -		MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS); +		MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1);  	msp->is_ht = true;  	memset(mi, 0, sizeof(*mi)); + +	mi->sta = sta;  	mi->stats_update = jiffies; -	ack_dur = ieee80211_frame_duration(local, 10, 60, 1, 1); -	mi->overhead = ieee80211_frame_duration(local, 0, 60, 1, 1) + ack_dur; +	ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0); +	mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1, 0); +	mi->overhead += ack_dur;  	mi->overhead_rtscts = mi->overhead + 2 * ack_dur;  	mi->avg_ampdu_len = MINSTREL_FRAC(1, 1); @@ -682,47 +905,72 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,  	if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)  		mi->tx_flags |= IEEE80211_TX_CTL_LDPC; -	if (oper_chan_type != NL80211_CHAN_HT40MINUS && -	    oper_chan_type != NL80211_CHAN_HT40PLUS) -		sta_cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; -  	for (i = 0; i < ARRAY_SIZE(mi->groups); i++) { -		u16 req = 0; -  		mi->groups[i].supported = 0; +		if (i == MINSTREL_CCK_GROUP) { +			minstrel_ht_update_cck(mp, mi, sband, sta); +			continue; +		} +  		if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) { -			if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) -				req |= IEEE80211_HT_CAP_SGI_40; -			else -				req |= IEEE80211_HT_CAP_SGI_20; +			if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { +				if (!(sta_cap & IEEE80211_HT_CAP_SGI_40)) +					continue; +			} else { +				if (!(sta_cap & IEEE80211_HT_CAP_SGI_20)) +					continue; +			}  		} -		if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) -			req |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; +		if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH && +		    sta->bandwidth < IEEE80211_STA_RX_BW_40) +			continue; -		if ((sta_cap & req) != req) +		/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */ +		if (sta->smps_mode == IEEE80211_SMPS_STATIC && +		    minstrel_mcs_groups[i].streams > 1)  			continue;  		mi->groups[i].supported =  			mcs->rx_mask[minstrel_mcs_groups[i].streams - 1]; + +		if (mi->groups[i].supported) +			n_supported++;  	} + +	if (!n_supported) +		goto use_legacy; + +	/* create an initial rate table with the lowest supported rates */ +	minstrel_ht_update_stats(mp, mi); +	minstrel_ht_update_rates(mp, mi); + +	return; + +use_legacy: +	msp->is_ht = false; +	memset(&msp->legacy, 0, sizeof(msp->legacy)); +	msp->legacy.r = msp->ratelist; +	msp->legacy.sample_table = msp->sample_table; +	return mac80211_minstrel.rate_init(priv, sband, chandef, sta, +					   &msp->legacy);  }  static void  minstrel_ht_rate_init(void *priv, struct ieee80211_supported_band *sband, +		      struct cfg80211_chan_def *chandef,                        struct ieee80211_sta *sta, void *priv_sta)  { -	struct minstrel_priv *mp = priv; - -	minstrel_ht_update_caps(priv, sband, sta, priv_sta, mp->hw->conf.channel_type); +	minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta);  }  static void  minstrel_ht_rate_update(void *priv, struct ieee80211_supported_band *sband, +			struct cfg80211_chan_def *chandef,                          struct ieee80211_sta *sta, void *priv_sta, -                        u32 changed, enum nl80211_channel_type oper_chan_type) +                        u32 changed)  { -	minstrel_ht_update_caps(priv, sband, sta, priv_sta, oper_chan_type); +	minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta);  }  static void * @@ -741,7 +989,7 @@ minstrel_ht_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp)  			max_rates = sband->n_bitrates;  	} -	msp = kzalloc(sizeof(struct minstrel_ht_sta), gfp); +	msp = kzalloc(sizeof(*msp), gfp);  	if (!msp)  		return NULL; @@ -784,7 +1032,23 @@ minstrel_ht_free(void *priv)  	mac80211_minstrel.free(priv);  } -static struct rate_control_ops mac80211_minstrel_ht = { +static u32 minstrel_ht_get_expected_throughput(void *priv_sta) +{ +	struct minstrel_ht_sta_priv *msp = priv_sta; +	struct minstrel_ht_sta *mi = &msp->ht; +	int i, j; + +	if (!msp->is_ht) +		return mac80211_minstrel.get_expected_throughput(priv_sta); + +	i = mi->max_tp_rate / MCS_GROUP_RATES; +	j = mi->max_tp_rate % MCS_GROUP_RATES; + +	/* convert cur_tp from pkt per second in kbps */ +	return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024; +} + +static const struct rate_control_ops mac80211_minstrel_ht = {  	.name = "minstrel_ht",  	.tx_status = minstrel_ht_tx_status,  	.get_rate = minstrel_ht_get_rate, @@ -798,21 +1062,20 @@ static struct rate_control_ops mac80211_minstrel_ht = {  	.add_sta_debugfs = minstrel_ht_add_sta_debugfs,  	.remove_sta_debugfs = minstrel_ht_remove_sta_debugfs,  #endif +	.get_expected_throughput = minstrel_ht_get_expected_throughput,  }; -static void -init_sample_table(void) +static void __init init_sample_table(void)  {  	int col, i, new_idx;  	u8 rnd[MCS_GROUP_RATES];  	memset(sample_table, 0xff, sizeof(sample_table));  	for (col = 0; col < SAMPLE_COLUMNS; col++) { +		prandom_bytes(rnd, sizeof(rnd));  		for (i = 0; i < MCS_GROUP_RATES; i++) { -			get_random_bytes(rnd, sizeof(rnd));  			new_idx = (i + rnd[i]) % MCS_GROUP_RATES; -  			while (sample_table[col][new_idx] != 0xff)  				new_idx = (new_idx + 1) % MCS_GROUP_RATES; diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h index 462d2b227ed..d655586773a 100644 --- a/net/mac80211/rc80211_minstrel_ht.h +++ b/net/mac80211/rc80211_minstrel_ht.h @@ -16,11 +16,6 @@  #define MINSTREL_MAX_STREAMS	3  #define MINSTREL_STREAM_GROUPS	4 -/* scaled fraction values */ -#define MINSTREL_SCALE	16 -#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div) -#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE) -  #define MCS_GROUP_RATES	8  struct mcs_group { @@ -70,6 +65,8 @@ struct minstrel_mcs_group_data {  };  struct minstrel_ht_sta { +	struct ieee80211_sta *sta; +  	/* ampdu length (average, per sampling interval) */  	unsigned int ampdu_len;  	unsigned int ampdu_packets; @@ -85,6 +82,7 @@ struct minstrel_ht_sta {  	/* best probability rate */  	unsigned int max_prob_rate; +	unsigned int max_prob_streams;  	/* time of last status update */  	unsigned long stats_update; @@ -107,8 +105,11 @@ struct minstrel_ht_sta {  	/* current MCS group to be sampled */  	u8 sample_group; +	u8 cck_supported; +	u8 cck_supported_short; +  	/* MCS rate group info and statistics */ -	struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS]; +	struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1];  };  struct minstrel_ht_sta_priv { diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index cefcb5d2dae..3e7d793de0c 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -10,17 +10,80 @@  #include <linux/skbuff.h>  #include <linux/debugfs.h>  #include <linux/ieee80211.h> +#include <linux/export.h>  #include <net/mac80211.h>  #include "rc80211_minstrel.h"  #include "rc80211_minstrel_ht.h" +static char * +minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) +{ +	unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; +	const struct mcs_group *mg; +	unsigned int j, tp, prob, eprob; +	char htmode = '2'; +	char gimode = 'L'; + +	if (!mi->groups[i].supported) +		return p; + +	mg = &minstrel_mcs_groups[i]; +	if (mg->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) +		htmode = '4'; +	if (mg->flags & IEEE80211_TX_RC_SHORT_GI) +		gimode = 'S'; + +	for (j = 0; j < MCS_GROUP_RATES; j++) { +		struct minstrel_rate_stats *mr = &mi->groups[i].rates[j]; +		static const int bitrates[4] = { 10, 20, 55, 110 }; +		int idx = i * MCS_GROUP_RATES + j; + +		if (!(mi->groups[i].supported & BIT(j))) +			continue; + +		if (i == max_mcs) +			p += sprintf(p, "CCK/%cP   ", j < 4 ? 'L' : 'S'); +		else +			p += sprintf(p, "HT%c0/%cGI ", htmode, gimode); + +		*(p++) = (idx == mi->max_tp_rate) ? 'T' : ' '; +		*(p++) = (idx == mi->max_tp_rate2) ? 't' : ' '; +		*(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; + +		if (i == max_mcs) { +			int r = bitrates[j % 4]; +			p += sprintf(p, " %2u.%1uM", r / 10, r % 10); +		} else { +			p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j); +		} + +		tp = mr->cur_tp / 10; +		prob = MINSTREL_TRUNC(mr->cur_prob * 1000); +		eprob = MINSTREL_TRUNC(mr->probability * 1000); + +		p += sprintf(p, "      %6u.%1u   %6u.%1u    %6u.%1u    " +				"%3u            %3u(%3u)  %8llu    %8llu\n", +				tp / 10, tp % 10, +				eprob / 10, eprob % 10, +				prob / 10, prob % 10, +				mr->retry_count, +				mr->last_success, +				mr->last_attempts, +				(unsigned long long)mr->succ_hist, +				(unsigned long long)mr->att_hist); +	} + +	return p; +} +  static int  minstrel_ht_stats_open(struct inode *inode, struct file *file)  {  	struct minstrel_ht_sta_priv *msp = inode->i_private;  	struct minstrel_ht_sta *mi = &msp->ht;  	struct minstrel_debugfs_info *ms; -	unsigned int i, j, tp, prob, eprob; +	unsigned int i; +	unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS;  	char *p;  	int ret; @@ -37,50 +100,13 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)  	file->private_data = ms;  	p = ms->buf; -	p += sprintf(p, "type      rate     throughput  ewma prob   this prob  " -			"this succ/attempt   success    attempts\n"); -	for (i = 0; i < MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; i++) { -		char htmode = '2'; -		char gimode = 'L'; - -		if (!mi->groups[i].supported) -			continue; - -		if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) -			htmode = '4'; -		if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) -			gimode = 'S'; +	p += sprintf(p, "type         rate     throughput  ewma prob   this prob  " +			"retry   this succ/attempt   success    attempts\n"); -		for (j = 0; j < MCS_GROUP_RATES; j++) { -			struct minstrel_rate_stats *mr = &mi->groups[i].rates[j]; -			int idx = i * MCS_GROUP_RATES + j; +	p = minstrel_ht_stats_dump(mi, max_mcs, p); +	for (i = 0; i < max_mcs; i++) +		p = minstrel_ht_stats_dump(mi, i, p); -			if (!(mi->groups[i].supported & BIT(j))) -				continue; - -			p += sprintf(p, "HT%c0/%cGI ", htmode, gimode); - -			*(p++) = (idx == mi->max_tp_rate) ? 'T' : ' '; -			*(p++) = (idx == mi->max_tp_rate2) ? 't' : ' '; -			*(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; -			p += sprintf(p, "MCS%-2u", (minstrel_mcs_groups[i].streams - 1) * -					MCS_GROUP_RATES + j); - -			tp = mr->cur_tp / 10; -			prob = MINSTREL_TRUNC(mr->cur_prob * 1000); -			eprob = MINSTREL_TRUNC(mr->probability * 1000); - -			p += sprintf(p, "  %6u.%1u   %6u.%1u   %6u.%1u        " -					"%3u(%3u)   %8llu    %8llu\n", -					tp / 10, tp % 10, -					eprob / 10, eprob % 10, -					prob / 10, prob % 10, -					mr->last_success, -					mr->last_attempts, -					(unsigned long long)mr->succ_hist, -					(unsigned long long)mr->att_hist); -		} -	}  	p += sprintf(p, "\nTotal packet count::    ideal %d      "  			"lookaround %d\n",  			max(0, (int) mi->total_packets - (int) mi->sample_packets), diff --git a/net/mac80211/rc80211_pid.h b/net/mac80211/rc80211_pid.h index 1a873f00691..19111c7bf45 100644 --- a/net/mac80211/rc80211_pid.h +++ b/net/mac80211/rc80211_pid.h @@ -24,9 +24,6 @@  /* Fixed point arithmetic shifting amount. */  #define RC_PID_ARITH_SHIFT		8 -/* Fixed point arithmetic factor. */ -#define RC_PID_ARITH_FACTOR		(1 << RC_PID_ARITH_SHIFT) -  /* Proportional PID component coefficient. */  #define RC_PID_COEFF_P			15  /* Integral PID component coefficient. */ @@ -80,7 +77,7 @@ union rc_pid_event_data {  };  struct rc_pid_event { -	/* The time when the event occured */ +	/* The time when the event occurred */  	unsigned long timestamp;  	/* Event ID number */ diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index aeda65466f3..d0da2a70fe6 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -293,6 +293,7 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,  static void  rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, +			   struct cfg80211_chan_def *chandef,  			   struct ieee80211_sta *sta, void *priv_sta)  {  	struct rc_pid_sta_info *spinfo = priv_sta; @@ -318,7 +319,7 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,  			rinfo[i].diff = i * pinfo->norm_offset;  	}  	for (i = 1; i < sband->n_bitrates; i++) { -		s = 0; +		s = false;  		for (j = 0; j < sband->n_bitrates - i; j++)  			if (unlikely(sband->bitrates[rinfo[j].index].bitrate >  				     sband->bitrates[rinfo[j + 1].index].bitrate)) { @@ -327,7 +328,7 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,  				rinfo[j + 1].index = tmp;  				rinfo[rinfo[j].index].rev_index = j;  				rinfo[rinfo[j + 1].index].rev_index = j + 1; -				s = 1; +				s = true;  			}  		if (!s)  			break; @@ -451,7 +452,7 @@ static void rate_control_pid_free_sta(void *priv, struct ieee80211_sta *sta,  	kfree(priv_sta);  } -static struct rate_control_ops mac80211_rcpid = { +static const struct rate_control_ops mac80211_rcpid = {  	.name = "pid",  	.tx_status = rate_control_pid_tx_status,  	.get_rate = rate_control_pid_get_rate, diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c index 4851e9e2dae..6ff134650a8 100644 --- a/net/mac80211/rc80211_pid_debugfs.c +++ b/net/mac80211/rc80211_pid_debugfs.c @@ -13,6 +13,7 @@  #include <linux/types.h>  #include <linux/skbuff.h>  #include <linux/slab.h> +#include <linux/export.h>  #include <net/mac80211.h>  #include "rate.h" @@ -166,29 +167,29 @@ static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,  	 * provide large enough buffers. */  	length = length < RC_PID_PRINT_BUF_SIZE ?  		 length : RC_PID_PRINT_BUF_SIZE; -	p = snprintf(pb, length, "%u %lu ", ev->id, ev->timestamp); +	p = scnprintf(pb, length, "%u %lu ", ev->id, ev->timestamp);  	switch (ev->type) {  	case RC_PID_EVENT_TYPE_TX_STATUS: -		p += snprintf(pb + p, length - p, "tx_status %u %u", -			      !(ev->data.flags & IEEE80211_TX_STAT_ACK), -			      ev->data.tx_status.status.rates[0].idx); +		p += scnprintf(pb + p, length - p, "tx_status %u %u", +			       !(ev->data.flags & IEEE80211_TX_STAT_ACK), +			       ev->data.tx_status.status.rates[0].idx);  		break;  	case RC_PID_EVENT_TYPE_RATE_CHANGE: -		p += snprintf(pb + p, length - p, "rate_change %d %d", -			      ev->data.index, ev->data.rate); +		p += scnprintf(pb + p, length - p, "rate_change %d %d", +			       ev->data.index, ev->data.rate);  		break;  	case RC_PID_EVENT_TYPE_TX_RATE: -		p += snprintf(pb + p, length - p, "tx_rate %d %d", -			      ev->data.index, ev->data.rate); +		p += scnprintf(pb + p, length - p, "tx_rate %d %d", +			       ev->data.index, ev->data.rate);  		break;  	case RC_PID_EVENT_TYPE_PF_SAMPLE: -		p += snprintf(pb + p, length - p, -			      "pf_sample %d %d %d %d", -			      ev->data.pf_sample, ev->data.prop_err, -			      ev->data.int_err, ev->data.der_err); +		p += scnprintf(pb + p, length - p, +			       "pf_sample %d %d %d %d", +			       ev->data.pf_sample, ev->data.prop_err, +			       ev->data.int_err, ev->data.der_err);  		break;  	} -	p += snprintf(pb + p, length - p, "\n"); +	p += scnprintf(pb + p, length - p, "\n");  	spin_unlock_irqrestore(&events->lock, status); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index d2fcd22ab06..394e201cde6 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -16,8 +16,10 @@  #include <linux/netdevice.h>  #include <linux/etherdevice.h>  #include <linux/rcupdate.h> +#include <linux/export.h>  #include <net/mac80211.h>  #include <net/ieee80211_radiotap.h> +#include <asm/unaligned.h>  #include "ieee80211_i.h"  #include "driver-ops.h" @@ -27,6 +29,7 @@  #include "wpa.h"  #include "tkip.h"  #include "wme.h" +#include "rate.h"  /*   * monitor mode reception @@ -44,46 +47,78 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,  			/* driver bug */  			WARN_ON(1);  			dev_kfree_skb(skb); -			skb = NULL; +			return NULL;  		}  	}  	return skb;  } -static inline int should_drop_frame(struct sk_buff *skb, -				    int present_fcs_len) +static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len)  {  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); -	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; +	struct ieee80211_hdr *hdr = (void *)skb->data; + +	if (status->flag & (RX_FLAG_FAILED_FCS_CRC | +			    RX_FLAG_FAILED_PLCP_CRC | +			    RX_FLAG_AMPDU_IS_ZEROLEN)) +		return true; -	if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) -		return 1;  	if (unlikely(skb->len < 16 + present_fcs_len)) -		return 1; +		return true; +  	if (ieee80211_is_ctl(hdr->frame_control) &&  	    !ieee80211_is_pspoll(hdr->frame_control) &&  	    !ieee80211_is_back_req(hdr->frame_control)) -		return 1; -	return 0; +		return true; + +	return false;  }  static int -ieee80211_rx_radiotap_len(struct ieee80211_local *local, -			  struct ieee80211_rx_status *status) +ieee80211_rx_radiotap_space(struct ieee80211_local *local, +			    struct ieee80211_rx_status *status)  {  	int len;  	/* always present fields */ -	len = sizeof(struct ieee80211_radiotap_header) + 9; +	len = sizeof(struct ieee80211_radiotap_header) + 8; + +	/* allocate extra bitmaps */ +	if (status->chains) +		len += 4 * hweight8(status->chains); -	if (status->flag & RX_FLAG_TSFT) +	if (ieee80211_have_rx_timestamp(status)) { +		len = ALIGN(len, 8);  		len += 8; +	}  	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)  		len += 1; -	if (len & 1) /* padding for RX_FLAGS if necessary */ -		len++; +	/* antenna field, if we don't have per-chain info */ +	if (!status->chains) +		len += 1; + +	/* padding for RX_FLAGS if necessary */ +	len = ALIGN(len, 2); + +	if (status->flag & RX_FLAG_HT) /* HT info */ +		len += 3; + +	if (status->flag & RX_FLAG_AMPDU_DETAILS) { +		len = ALIGN(len, 4); +		len += 8; +	} + +	if (status->flag & RX_FLAG_VHT) { +		len = ALIGN(len, 2); +		len += 12; +	} + +	if (status->chains) { +		/* antenna and antenna signal fields */ +		len += 2 * hweight8(status->chains); +	}  	return len;  } @@ -97,38 +132,66 @@ static void  ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,  				 struct sk_buff *skb,  				 struct ieee80211_rate *rate, -				 int rtap_len) +				 int rtap_len, bool has_fcs)  {  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);  	struct ieee80211_radiotap_header *rthdr;  	unsigned char *pos; +	__le32 *it_present; +	u32 it_present_val;  	u16 rx_flags = 0; +	u16 channel_flags = 0; +	int mpdulen, chain; +	unsigned long chains = status->chains; + +	mpdulen = skb->len; +	if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS))) +		mpdulen += FCS_LEN;  	rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);  	memset(rthdr, 0, rtap_len); +	it_present = &rthdr->it_present;  	/* radiotap header, set always present flags */ -	rthdr->it_present = -		cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | -			    (1 << IEEE80211_RADIOTAP_CHANNEL) | -			    (1 << IEEE80211_RADIOTAP_ANTENNA) | -			    (1 << IEEE80211_RADIOTAP_RX_FLAGS));  	rthdr->it_len = cpu_to_le16(rtap_len); +	it_present_val = BIT(IEEE80211_RADIOTAP_FLAGS) | +			 BIT(IEEE80211_RADIOTAP_CHANNEL) | +			 BIT(IEEE80211_RADIOTAP_RX_FLAGS); -	pos = (unsigned char *)(rthdr+1); +	if (!status->chains) +		it_present_val |= BIT(IEEE80211_RADIOTAP_ANTENNA); + +	for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) { +		it_present_val |= +			BIT(IEEE80211_RADIOTAP_EXT) | +			BIT(IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE); +		put_unaligned_le32(it_present_val, it_present); +		it_present++; +		it_present_val = BIT(IEEE80211_RADIOTAP_ANTENNA) | +				 BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL); +	} + +	put_unaligned_le32(it_present_val, it_present); + +	pos = (void *)(it_present + 1);  	/* the order of the following fields is important */  	/* IEEE80211_RADIOTAP_TSFT */ -	if (status->flag & RX_FLAG_TSFT) { -		put_unaligned_le64(status->mactime, pos); -		rthdr->it_present |= -			cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT); +	if (ieee80211_have_rx_timestamp(status)) { +		/* padding */ +		while ((pos - (u8 *)rthdr) & 7) +			*pos++ = 0; +		put_unaligned_le64( +			ieee80211_calculate_rx_timestamp(local, status, +							 mpdulen, 0), +			pos); +		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);  		pos += 8;  	}  	/* IEEE80211_RADIOTAP_FLAGS */ -	if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) +	if (has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS))  		*pos |= IEEE80211_RADIOTAP_F_FCS;  	if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))  		*pos |= IEEE80211_RADIOTAP_F_BADFCS; @@ -137,40 +200,49 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,  	pos++;  	/* IEEE80211_RADIOTAP_RATE */ -	if (status->flag & RX_FLAG_HT) { +	if (!rate || status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) {  		/* -		 * TODO: add following information into radiotap header once -		 * suitable fields are defined for it: -		 * - MCS index (status->rate_idx) -		 * - HT40 (status->flag & RX_FLAG_40MHZ) -		 * - short-GI (status->flag & RX_FLAG_SHORT_GI) +		 * Without rate information don't add it. If we have, +		 * MCS information is a separate field in radiotap, +		 * added below. The byte here is needed as padding +		 * for the channel though, so initialise it to 0.  		 */  		*pos = 0;  	} else { +		int shift = 0;  		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); -		*pos = rate->bitrate / 5; +		if (status->flag & RX_FLAG_10MHZ) +			shift = 1; +		else if (status->flag & RX_FLAG_5MHZ) +			shift = 2; +		*pos = DIV_ROUND_UP(rate->bitrate, 5 * (1 << shift));  	}  	pos++;  	/* IEEE80211_RADIOTAP_CHANNEL */  	put_unaligned_le16(status->freq, pos);  	pos += 2; +	if (status->flag & RX_FLAG_10MHZ) +		channel_flags |= IEEE80211_CHAN_HALF; +	else if (status->flag & RX_FLAG_5MHZ) +		channel_flags |= IEEE80211_CHAN_QUARTER; +  	if (status->band == IEEE80211_BAND_5GHZ) -		put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ, -				   pos); -	else if (status->flag & RX_FLAG_HT) -		put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ, -				   pos); -	else if (rate->flags & IEEE80211_RATE_ERP_G) -		put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ, -				   pos); +		channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ; +	else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) +		channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; +	else if (rate && rate->flags & IEEE80211_RATE_ERP_G) +		channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ; +	else if (rate) +		channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;  	else -		put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ, -				   pos); +		channel_flags |= IEEE80211_CHAN_2GHZ; +	put_unaligned_le16(channel_flags, pos);  	pos += 2;  	/* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */ -	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { +	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM && +	    !(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {  		*pos = status->signal;  		rthdr->it_present |=  			cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL); @@ -179,20 +251,120 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,  	/* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */ -	/* IEEE80211_RADIOTAP_ANTENNA */ -	*pos = status->antenna; -	pos++; +	if (!status->chains) { +		/* IEEE80211_RADIOTAP_ANTENNA */ +		*pos = status->antenna; +		pos++; +	}  	/* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */  	/* IEEE80211_RADIOTAP_RX_FLAGS */  	/* ensure 2 byte alignment for the 2 byte field as required */  	if ((pos - (u8 *)rthdr) & 1) -		pos++; +		*pos++ = 0;  	if (status->flag & RX_FLAG_FAILED_PLCP_CRC)  		rx_flags |= IEEE80211_RADIOTAP_F_RX_BADPLCP;  	put_unaligned_le16(rx_flags, pos);  	pos += 2; + +	if (status->flag & RX_FLAG_HT) { +		unsigned int stbc; + +		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); +		*pos++ = local->hw.radiotap_mcs_details; +		*pos = 0; +		if (status->flag & RX_FLAG_SHORT_GI) +			*pos |= IEEE80211_RADIOTAP_MCS_SGI; +		if (status->flag & RX_FLAG_40MHZ) +			*pos |= IEEE80211_RADIOTAP_MCS_BW_40; +		if (status->flag & RX_FLAG_HT_GF) +			*pos |= IEEE80211_RADIOTAP_MCS_FMT_GF; +		if (status->flag & RX_FLAG_LDPC) +			*pos |= IEEE80211_RADIOTAP_MCS_FEC_LDPC; +		stbc = (status->flag & RX_FLAG_STBC_MASK) >> RX_FLAG_STBC_SHIFT; +		*pos |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT; +		pos++; +		*pos++ = status->rate_idx; +	} + +	if (status->flag & RX_FLAG_AMPDU_DETAILS) { +		u16 flags = 0; + +		/* ensure 4 byte alignment */ +		while ((pos - (u8 *)rthdr) & 3) +			pos++; +		rthdr->it_present |= +			cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS); +		put_unaligned_le32(status->ampdu_reference, pos); +		pos += 4; +		if (status->flag & RX_FLAG_AMPDU_REPORT_ZEROLEN) +			flags |= IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN; +		if (status->flag & RX_FLAG_AMPDU_IS_ZEROLEN) +			flags |= IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN; +		if (status->flag & RX_FLAG_AMPDU_LAST_KNOWN) +			flags |= IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN; +		if (status->flag & RX_FLAG_AMPDU_IS_LAST) +			flags |= IEEE80211_RADIOTAP_AMPDU_IS_LAST; +		if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_ERROR) +			flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR; +		if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN) +			flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN; +		put_unaligned_le16(flags, pos); +		pos += 2; +		if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN) +			*pos++ = status->ampdu_delimiter_crc; +		else +			*pos++ = 0; +		*pos++ = 0; +	} + +	if (status->flag & RX_FLAG_VHT) { +		u16 known = local->hw.radiotap_vht_details; + +		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT); +		/* known field - how to handle 80+80? */ +		if (status->vht_flag & RX_VHT_FLAG_80P80MHZ) +			known &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH; +		put_unaligned_le16(known, pos); +		pos += 2; +		/* flags */ +		if (status->flag & RX_FLAG_SHORT_GI) +			*pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI; +		/* in VHT, STBC is binary */ +		if (status->flag & RX_FLAG_STBC_MASK) +			*pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC; +		if (status->vht_flag & RX_VHT_FLAG_BF) +			*pos |= IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED; +		pos++; +		/* bandwidth */ +		if (status->vht_flag & RX_VHT_FLAG_80MHZ) +			*pos++ = 4; +		else if (status->vht_flag & RX_VHT_FLAG_80P80MHZ) +			*pos++ = 0; /* marked not known above */ +		else if (status->vht_flag & RX_VHT_FLAG_160MHZ) +			*pos++ = 11; +		else if (status->flag & RX_FLAG_40MHZ) +			*pos++ = 1; +		else /* 20 MHz */ +			*pos++ = 0; +		/* MCS/NSS */ +		*pos = (status->rate_idx << 4) | status->vht_nss; +		pos += 4; +		/* coding field */ +		if (status->flag & RX_FLAG_LDPC) +			*pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0; +		pos++; +		/* group ID */ +		pos++; +		/* partial_aid */ +		pos += 2; +	} + +	for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) { +		*pos++ = status->chain_signal[chain]; +		*pos++ = chain; +	}  }  /* @@ -206,7 +378,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,  {  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);  	struct ieee80211_sub_if_data *sdata; -	int needed_headroom = 0; +	int needed_headroom;  	struct sk_buff *skb, *skb2;  	struct net_device *prev_dev = NULL;  	int present_fcs_len = 0; @@ -220,13 +392,10 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,  	 * the SKB because it has a bad FCS/PLCP checksum.  	 */ -	/* room for the radiotap header based on driver features */ -	needed_headroom = ieee80211_rx_radiotap_len(local, status); -  	if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)  		present_fcs_len = FCS_LEN; -	/* make sure hdr->frame_control is on the linear part */ +	/* ensure hdr->frame_control is in skb head */  	if (!pskb_may_pull(origskb, 2)) {  		dev_kfree_skb(origskb);  		return NULL; @@ -241,6 +410,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,  		return remove_monitor_info(local, origskb);  	} +	/* room for the radiotap header based on driver features */ +	needed_headroom = ieee80211_rx_radiotap_space(local, status); +  	if (should_drop_frame(origskb, present_fcs_len)) {  		/* only need to expand headroom if necessary */  		skb = origskb; @@ -272,7 +444,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,  	}  	/* prepend radiotap information */ -	ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom); +	ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom, +					 true);  	skb_reset_mac_header(skb);  	skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -311,20 +484,22 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,  	return origskb;  } -  static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)  {  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); -	int tid; +	int tid, seqno_idx, security_idx;  	/* does the frame have a qos control field? */  	if (ieee80211_is_data_qos(hdr->frame_control)) {  		u8 *qc = ieee80211_get_qos_ctl(hdr);  		/* frame has qos control */  		tid = *qc & IEEE80211_QOS_CTL_TID_MASK; -		if (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) +		if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT)  			status->rx_flags |= IEEE80211_RX_AMSDU; + +		seqno_idx = tid; +		security_idx = tid;  	} else {  		/*  		 * IEEE 802.11-2007, 7.1.3.4.1 ("Sequence Number field"): @@ -337,10 +512,15 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)  		 *  		 * We also use that counter for non-QoS STAs.  		 */ -		tid = NUM_RX_DATA_QUEUES - 1; +		seqno_idx = IEEE80211_NUM_TIDS; +		security_idx = 0; +		if (ieee80211_is_mgmt(hdr->frame_control)) +			security_idx = IEEE80211_NUM_TIDS; +		tid = 0;  	} -	rx->queue = tid; +	rx->seqno_idx = seqno_idx; +	rx->security_idx = security_idx;  	/* Set skb->priority to 1d tag if highest order bit of TID is not set.  	 * For now, set skb->priority to 0 for other cases. */  	rx->skb->priority = (tid > 7) ? 0 : tid; @@ -367,7 +547,7 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)   * specs were sane enough this time around to require padding each A-MSDU   * subframe to a length that is a multiple of four.   * - * Padding like Atheros hardware adds which is inbetween the 802.11 header and + * Padding like Atheros hardware adds which is between the 802.11 header and   * the payload is not supported, the driver is required to move the 802.11   * header to be directly in front of the payload in that case.   */ @@ -382,40 +562,14 @@ static void ieee80211_verify_alignment(struct ieee80211_rx_data *rx)  /* rx handlers */ -static ieee80211_rx_result debug_noinline -ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx) -{ -	struct ieee80211_local *local = rx->local; -	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); -	struct sk_buff *skb = rx->skb; - -	if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN))) -		return RX_CONTINUE; - -	if (test_bit(SCAN_HW_SCANNING, &local->scanning)) -		return ieee80211_scan_rx(rx->sdata, skb); - -	if (test_bit(SCAN_SW_SCANNING, &local->scanning)) { -		/* drop all the other packets during a software scan anyway */ -		if (ieee80211_scan_rx(rx->sdata, skb) != RX_QUEUED) -			dev_kfree_skb(skb); -		return RX_QUEUED; -	} - -	/* scanning finished during invoking of handlers */ -	I802_DEBUG_INC(local->rx_handlers_drop_passive_scan); -	return RX_DROP_UNUSABLE; -} - -  static int ieee80211_is_unicast_robust_mgmt_frame(struct sk_buff *skb)  {  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; -	if (skb->len < 24 || is_multicast_ether_addr(hdr->addr1)) +	if (is_multicast_ether_addr(hdr->addr1))  		return 0; -	return ieee80211_is_robust_mgmt_frame(hdr); +	return ieee80211_is_robust_mgmt_frame(skb);  } @@ -423,10 +577,10 @@ static int ieee80211_is_multicast_robust_mgmt_frame(struct sk_buff *skb)  {  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; -	if (skb->len < 24 || !is_multicast_ether_addr(hdr->addr1)) +	if (!is_multicast_ether_addr(hdr->addr1))  		return 0; -	return ieee80211_is_robust_mgmt_frame(hdr); +	return ieee80211_is_robust_mgmt_frame(skb);  } @@ -436,11 +590,10 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)  	struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data;  	struct ieee80211_mmie *mmie; -	if (skb->len < 24 + sizeof(*mmie) || -	    !is_multicast_ether_addr(hdr->da)) +	if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da))  		return -1; -	if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr)) +	if (!ieee80211_is_robust_mgmt_frame(skb))  		return -1; /* not a robust management frame */  	mmie = (struct ieee80211_mmie *) @@ -452,25 +605,43 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)  	return le16_to_cpu(mmie->key_id);  } +static int iwl80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs, +				 struct sk_buff *skb) +{ +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; +	__le16 fc; +	int hdrlen; +	u8 keyid; + +	fc = hdr->frame_control; +	hdrlen = ieee80211_hdrlen(fc); -static ieee80211_rx_result -ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) +	if (skb->len < hdrlen + cs->hdr_len) +		return -EINVAL; + +	skb_copy_bits(skb, hdrlen + cs->key_idx_off, &keyid, 1); +	keyid &= cs->key_idx_mask; +	keyid >>= cs->key_idx_shift; + +	return keyid; +} + +static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)  {  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; -	unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);  	char *dev_addr = rx->sdata->vif.addr;  	if (ieee80211_is_data(hdr->frame_control)) {  		if (is_multicast_ether_addr(hdr->addr1)) {  			if (ieee80211_has_tods(hdr->frame_control) || -				!ieee80211_has_fromds(hdr->frame_control)) +			    !ieee80211_has_fromds(hdr->frame_control))  				return RX_DROP_MONITOR; -			if (memcmp(hdr->addr3, dev_addr, ETH_ALEN) == 0) +			if (ether_addr_equal(hdr->addr3, dev_addr))  				return RX_DROP_MONITOR;  		} else {  			if (!ieee80211_has_a4(hdr->frame_control))  				return RX_DROP_MONITOR; -			if (memcmp(hdr->addr4, dev_addr, ETH_ALEN) == 0) +			if (ether_addr_equal(hdr->addr4, dev_addr))  				return RX_DROP_MONITOR;  		}  	} @@ -479,64 +650,48 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)  	 * establisment frame, beacon or probe, drop the frame.  	 */ -	if (!rx->sta || sta_plink_state(rx->sta) != PLINK_ESTAB) { +	if (!rx->sta || sta_plink_state(rx->sta) != NL80211_PLINK_ESTAB) {  		struct ieee80211_mgmt *mgmt;  		if (!ieee80211_is_mgmt(hdr->frame_control))  			return RX_DROP_MONITOR;  		if (ieee80211_is_action(hdr->frame_control)) { +			u8 category; + +			/* make sure category field is present */ +			if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE) +				return RX_DROP_MONITOR; +  			mgmt = (struct ieee80211_mgmt *)hdr; -			if (mgmt->u.action.category != WLAN_CATEGORY_MESH_PLINK) +			category = mgmt->u.action.category; +			if (category != WLAN_CATEGORY_MESH_ACTION && +			    category != WLAN_CATEGORY_SELF_PROTECTED)  				return RX_DROP_MONITOR;  			return RX_CONTINUE;  		}  		if (ieee80211_is_probe_req(hdr->frame_control) ||  		    ieee80211_is_probe_resp(hdr->frame_control) || -		    ieee80211_is_beacon(hdr->frame_control)) +		    ieee80211_is_beacon(hdr->frame_control) || +		    ieee80211_is_auth(hdr->frame_control))  			return RX_CONTINUE;  		return RX_DROP_MONITOR; -  	} -#define msh_h_get(h, l) ((struct ieee80211s_hdr *) ((u8 *)h + l)) - -	if (ieee80211_is_data(hdr->frame_control) && -	    is_multicast_ether_addr(hdr->addr1) && -	    mesh_rmc_check(hdr->addr3, msh_h_get(hdr, hdrlen), rx->sdata)) -		return RX_DROP_MONITOR; -#undef msh_h_get -  	return RX_CONTINUE;  } -#define SEQ_MODULO 0x1000 -#define SEQ_MASK   0xfff - -static inline int seq_less(u16 sq1, u16 sq2) -{ -	return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1); -} - -static inline u16 seq_inc(u16 sq) -{ -	return (sq + 1) & SEQ_MASK; -} - -static inline u16 seq_sub(u16 sq1, u16 sq2) -{ -	return (sq1 - sq2) & SEQ_MASK; -} - - -static void ieee80211_release_reorder_frame(struct ieee80211_hw *hw, +static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,  					    struct tid_ampdu_rx *tid_agg_rx,  					    int index,  					    struct sk_buff_head *frames)  {  	struct sk_buff *skb = tid_agg_rx->reorder_buf[index]; +	struct ieee80211_rx_status *status; + +	lockdep_assert_held(&tid_agg_rx->reorder_lock);  	if (!skb)  		goto no_frame; @@ -544,23 +699,27 @@ static void ieee80211_release_reorder_frame(struct ieee80211_hw *hw,  	/* release the frame from the reorder ring buffer */  	tid_agg_rx->stored_mpdu_num--;  	tid_agg_rx->reorder_buf[index] = NULL; +	status = IEEE80211_SKB_RXCB(skb); +	status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;  	__skb_queue_tail(frames, skb);  no_frame: -	tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num); +	tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);  } -static void ieee80211_release_reorder_frames(struct ieee80211_hw *hw, +static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata,  					     struct tid_ampdu_rx *tid_agg_rx,  					     u16 head_seq_num,  					     struct sk_buff_head *frames)  {  	int index; -	while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) { -		index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % -							tid_agg_rx->buf_size; -		ieee80211_release_reorder_frame(hw, tid_agg_rx, index, frames); +	lockdep_assert_held(&tid_agg_rx->reorder_lock); + +	while (ieee80211_sn_less(tid_agg_rx->head_seq_num, head_seq_num)) { +		index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size; +		ieee80211_release_reorder_frame(sdata, tid_agg_rx, index, +						frames);  	}  } @@ -575,17 +734,18 @@ static void ieee80211_release_reorder_frames(struct ieee80211_hw *hw,   */  #define HT_RX_REORDER_BUF_TIMEOUT (HZ / 10) -static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw, +static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,  					  struct tid_ampdu_rx *tid_agg_rx,  					  struct sk_buff_head *frames)  {  	int index, j; +	lockdep_assert_held(&tid_agg_rx->reorder_lock); +  	/* release the buffer until next missing frame */ -	index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % -						tid_agg_rx->buf_size; +	index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;  	if (!tid_agg_rx->reorder_buf[index] && -	    tid_agg_rx->stored_mpdu_num > 1) { +	    tid_agg_rx->stored_mpdu_num) {  		/*  		 * No buffers ready to be released, but check whether any  		 * frames in the reorder buffer have timed out. @@ -597,54 +757,32 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw,  				skipped++;  				continue;  			} -			if (!time_after(jiffies, tid_agg_rx->reorder_time[j] + +			if (skipped && +			    !time_after(jiffies, tid_agg_rx->reorder_time[j] +  					HT_RX_REORDER_BUF_TIMEOUT))  				goto set_release_timer; -#ifdef CONFIG_MAC80211_HT_DEBUG -			if (net_ratelimit()) -				wiphy_debug(hw->wiphy, -					    "release an RX reorder frame due to timeout on earlier frames\n"); -#endif -			ieee80211_release_reorder_frame(hw, tid_agg_rx, -							j, frames); +			ht_dbg_ratelimited(sdata, +					   "release an RX reorder frame due to timeout on earlier frames\n"); +			ieee80211_release_reorder_frame(sdata, tid_agg_rx, j, +							frames);  			/*  			 * Increment the head seq# also for the skipped slots.  			 */  			tid_agg_rx->head_seq_num = -				(tid_agg_rx->head_seq_num + skipped) & SEQ_MASK; +				(tid_agg_rx->head_seq_num + +				 skipped) & IEEE80211_SN_MASK;  			skipped = 0;  		}  	} else while (tid_agg_rx->reorder_buf[index]) { -		ieee80211_release_reorder_frame(hw, tid_agg_rx, index, frames); -		index =	seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % -							tid_agg_rx->buf_size; +		ieee80211_release_reorder_frame(sdata, tid_agg_rx, index, +						frames); +		index =	tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;  	} -	/* -	 * Disable the reorder release timer for now. -	 * -	 * The current implementation lacks a proper locking scheme -	 * which would protect vital statistic and debug counters -	 * from being updated by two different but concurrent BHs. -	 * -	 * More information about the topic is available from: -	 * - thread: http://marc.info/?t=128635927000001 -	 * -	 * What was wrong: -	 * =>  http://marc.info/?l=linux-wireless&m=128636170811964 -	 * "Basically the thing is that until your patch, the data -	 *  in the struct didn't actually need locking because it -	 *  was accessed by the RX path only which is not concurrent." -	 * -	 * List of what needs to be fixed: -	 * => http://marc.info/?l=linux-wireless&m=128656352920957 -	 * -  	if (tid_agg_rx->stored_mpdu_num) { -		j = index = seq_sub(tid_agg_rx->head_seq_num, -				    tid_agg_rx->ssn) % tid_agg_rx->buf_size; +		j = index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;  		for (; j != (index - 1) % tid_agg_rx->buf_size;  		     j = (j + 1) % tid_agg_rx->buf_size) { @@ -655,15 +793,11 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw,   set_release_timer:  		mod_timer(&tid_agg_rx->reorder_timer, -			  tid_agg_rx->reorder_time[j] + +			  tid_agg_rx->reorder_time[j] + 1 +  			  HT_RX_REORDER_BUF_TIMEOUT);  	} else {  		del_timer(&tid_agg_rx->reorder_timer);  	} -	*/ - -set_release_timer: -	return;  }  /* @@ -671,7 +805,7 @@ set_release_timer:   * rcu_read_lock protection. It returns false if the frame   * can be processed immediately, true if it was consumed.   */ -static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, +static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata,  					     struct tid_ampdu_rx *tid_agg_rx,  					     struct sk_buff *skb,  					     struct sk_buff_head *frames) @@ -683,12 +817,13 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,  	int index;  	bool ret = true; +	spin_lock(&tid_agg_rx->reorder_lock); +  	buf_size = tid_agg_rx->buf_size;  	head_seq_num = tid_agg_rx->head_seq_num; -	spin_lock(&tid_agg_rx->reorder_lock);  	/* frame with out of date sequence number */ -	if (seq_less(mpdu_seq_num, head_seq_num)) { +	if (ieee80211_sn_less(mpdu_seq_num, head_seq_num)) {  		dev_kfree_skb(skb);  		goto out;  	} @@ -697,16 +832,17 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,  	 * If frame the sequence number exceeds our buffering window  	 * size release some previous frames to make room for this one.  	 */ -	if (!seq_less(mpdu_seq_num, head_seq_num + buf_size)) { -		head_seq_num = seq_inc(seq_sub(mpdu_seq_num, buf_size)); +	if (!ieee80211_sn_less(mpdu_seq_num, head_seq_num + buf_size)) { +		head_seq_num = ieee80211_sn_inc( +				ieee80211_sn_sub(mpdu_seq_num, buf_size));  		/* release stored frames up to new head to stack */ -		ieee80211_release_reorder_frames(hw, tid_agg_rx, head_seq_num, -						 frames); +		ieee80211_release_reorder_frames(sdata, tid_agg_rx, +						 head_seq_num, frames);  	}  	/* Now the new frame is always in the range of the reordering buffer */ -	index = seq_sub(mpdu_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; +	index = mpdu_seq_num % tid_agg_rx->buf_size;  	/* check if we already stored this frame */  	if (tid_agg_rx->reorder_buf[index]) { @@ -717,10 +853,13 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,  	/*  	 * If the current MPDU is in the right order and nothing else  	 * is stored we can process it directly, no need to buffer it. +	 * If it is first but there's something stored, we may be able +	 * to release frames after this one.  	 */  	if (mpdu_seq_num == tid_agg_rx->head_seq_num &&  	    tid_agg_rx->stored_mpdu_num == 0) { -		tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num); +		tid_agg_rx->head_seq_num = +			ieee80211_sn_inc(tid_agg_rx->head_seq_num);  		ret = false;  		goto out;  	} @@ -729,7 +868,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,  	tid_agg_rx->reorder_buf[index] = skb;  	tid_agg_rx->reorder_time[index] = jiffies;  	tid_agg_rx->stored_mpdu_num++; -	ieee80211_sta_reorder_release(hw, tid_agg_rx, frames); +	ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);   out:  	spin_unlock(&tid_agg_rx->reorder_lock); @@ -745,14 +884,15 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,  {  	struct sk_buff *skb = rx->skb;  	struct ieee80211_local *local = rx->local; -	struct ieee80211_hw *hw = &local->hw;  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; +	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);  	struct sta_info *sta = rx->sta;  	struct tid_ampdu_rx *tid_agg_rx;  	u16 sc; -	int tid; +	u8 tid, ack_policy; -	if (!ieee80211_is_data_qos(hdr->frame_control)) +	if (!ieee80211_is_data_qos(hdr->frame_control) || +	    is_multicast_ether_addr(hdr->addr1))  		goto dont_reorder;  	/* @@ -763,6 +903,8 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,  	if (!sta)  		goto dont_reorder; +	ack_policy = *ieee80211_get_qos_ctl(hdr) & +		     IEEE80211_QOS_CTL_ACK_POLICY_MASK;  	tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;  	tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); @@ -773,12 +915,20 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,  	if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))  		goto dont_reorder; +	/* not part of a BA session */ +	if (ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK && +	    ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_NORMAL) +		goto dont_reorder; + +	/* not actually part of this BA session */ +	if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) +		goto dont_reorder; +  	/* new, potentially un-ordered, ampdu frame - process it */  	/* reset session timer */  	if (tid_agg_rx->timeout) -		mod_timer(&tid_agg_rx->session_timer, -			  TU_TO_EXP_TIME(tid_agg_rx->timeout)); +		tid_agg_rx->last_rx = jiffies;  	/* if this mpdu is fragmented - terminate rx aggregation session */  	sc = le16_to_cpu(hdr->seq_ctrl); @@ -796,7 +946,8 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,  	 * sure that we cannot get to it any more before doing  	 * anything with it.  	 */ -	if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames)) +	if (ieee80211_sta_manage_reorder_buf(rx->sdata, tid_agg_rx, skb, +					     frames))  		return;   dont_reorder: @@ -809,18 +960,25 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); -	/* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */ -	if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) { +	/* +	 * Drop duplicate 802.11 retransmissions +	 * (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery") +	 */ +	if (rx->skb->len >= 24 && rx->sta && +	    !ieee80211_is_ctl(hdr->frame_control) && +	    !ieee80211_is_qos_nullfunc(hdr->frame_control) && +	    !is_multicast_ether_addr(hdr->addr1)) {  		if (unlikely(ieee80211_has_retry(hdr->frame_control) && -			     rx->sta->last_seq_ctrl[rx->queue] == +			     rx->sta->last_seq_ctrl[rx->seqno_idx] ==  			     hdr->seq_ctrl)) {  			if (status->rx_flags & IEEE80211_RX_RA_MATCH) {  				rx->local->dot11FrameDuplicateCount++;  				rx->sta->num_duplicates++;  			} -			return RX_DROP_MONITOR; -		} else -			rx->sta->last_seq_ctrl[rx->queue] = hdr->seq_ctrl; +			return RX_DROP_UNUSABLE; +		} else if (!(status->flag & RX_FLAG_AMSDU_MORE)) { +			rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl; +		}  	}  	if (unlikely(rx->skb->len < 16)) { @@ -844,16 +1002,33 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)  		      ieee80211_is_pspoll(hdr->frame_control)) &&  		     rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&  		     rx->sdata->vif.type != NL80211_IFTYPE_WDS && -		     (!rx->sta || !test_sta_flags(rx->sta, WLAN_STA_ASSOC)))) { -		if ((!ieee80211_has_fromds(hdr->frame_control) && -		     !ieee80211_has_tods(hdr->frame_control) && -		     ieee80211_is_data(hdr->frame_control)) || -		    !(status->rx_flags & IEEE80211_RX_RA_MATCH)) { -			/* Drop IBSS frames and frames for other hosts -			 * silently. */ -			return RX_DROP_MONITOR; +		     (!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) { +		/* +		 * accept port control frames from the AP even when it's not +		 * yet marked ASSOC to prevent a race where we don't set the +		 * assoc bit quickly enough before it sends the first frame +		 */ +		if (rx->sta && rx->sdata->vif.type == NL80211_IFTYPE_STATION && +		    ieee80211_is_data_present(hdr->frame_control)) { +			unsigned int hdrlen; +			__be16 ethertype; + +			hdrlen = ieee80211_hdrlen(hdr->frame_control); + +			if (rx->skb->len < hdrlen + 8) +				return RX_DROP_MONITOR; + +			skb_copy_bits(rx->skb, hdrlen + 6, ðertype, 2); +			if (ethertype == rx->sdata->control_port_protocol) +				return RX_CONTINUE;  		} +		if (rx->sdata->vif.type == NL80211_IFTYPE_AP && +		    cfg80211_rx_spurious_frame(rx->sdata->dev, +					       hdr->addr2, +					       GFP_ATOMIC)) +			return RX_DROP_UNUSABLE; +  		return RX_DROP_MONITOR;  	} @@ -862,6 +1037,314 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)  static ieee80211_rx_result debug_noinline +ieee80211_rx_h_check_more_data(struct ieee80211_rx_data *rx) +{ +	struct ieee80211_local *local; +	struct ieee80211_hdr *hdr; +	struct sk_buff *skb; + +	local = rx->local; +	skb = rx->skb; +	hdr = (struct ieee80211_hdr *) skb->data; + +	if (!local->pspolling) +		return RX_CONTINUE; + +	if (!ieee80211_has_fromds(hdr->frame_control)) +		/* this is not from AP */ +		return RX_CONTINUE; + +	if (!ieee80211_is_data(hdr->frame_control)) +		return RX_CONTINUE; + +	if (!ieee80211_has_moredata(hdr->frame_control)) { +		/* AP has no more frames buffered for us */ +		local->pspolling = false; +		return RX_CONTINUE; +	} + +	/* more data bit is set, let's request a new frame from the AP */ +	ieee80211_send_pspoll(local, rx->sdata); + +	return RX_CONTINUE; +} + +static void sta_ps_start(struct sta_info *sta) +{ +	struct ieee80211_sub_if_data *sdata = sta->sdata; +	struct ieee80211_local *local = sdata->local; +	struct ps_data *ps; + +	if (sta->sdata->vif.type == NL80211_IFTYPE_AP || +	    sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +		ps = &sdata->bss->ps; +	else +		return; + +	atomic_inc(&ps->num_sta_ps); +	set_sta_flag(sta, WLAN_STA_PS_STA); +	if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) +		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); +} + +static void sta_ps_end(struct sta_info *sta) +{ +	ps_dbg(sta->sdata, "STA %pM aid %d exits power save mode\n", +	       sta->sta.addr, sta->sta.aid); + +	if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { +		/* +		 * Clear the flag only if the other one is still set +		 * so that the TX path won't start TX'ing new frames +		 * directly ... In the case that the driver flag isn't +		 * set ieee80211_sta_ps_deliver_wakeup() will clear it. +		 */ +		clear_sta_flag(sta, WLAN_STA_PS_STA); +		ps_dbg(sta->sdata, "STA %pM aid %d driver-ps-blocked\n", +		       sta->sta.addr, sta->sta.aid); +		return; +	} + +	ieee80211_sta_ps_deliver_wakeup(sta); +} + +int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start) +{ +	struct sta_info *sta_inf = container_of(sta, struct sta_info, sta); +	bool in_ps; + +	WARN_ON(!(sta_inf->local->hw.flags & IEEE80211_HW_AP_LINK_PS)); + +	/* Don't let the same PS state be set twice */ +	in_ps = test_sta_flag(sta_inf, WLAN_STA_PS_STA); +	if ((start && in_ps) || (!start && !in_ps)) +		return -EINVAL; + +	if (start) +		sta_ps_start(sta_inf); +	else +		sta_ps_end(sta_inf); + +	return 0; +} +EXPORT_SYMBOL(ieee80211_sta_ps_transition); + +static ieee80211_rx_result debug_noinline +ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx) +{ +	struct ieee80211_sub_if_data *sdata = rx->sdata; +	struct ieee80211_hdr *hdr = (void *)rx->skb->data; +	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); +	int tid, ac; + +	if (!rx->sta || !(status->rx_flags & IEEE80211_RX_RA_MATCH)) +		return RX_CONTINUE; + +	if (sdata->vif.type != NL80211_IFTYPE_AP && +	    sdata->vif.type != NL80211_IFTYPE_AP_VLAN) +		return RX_CONTINUE; + +	/* +	 * The device handles station powersave, so don't do anything about +	 * uAPSD and PS-Poll frames (the latter shouldn't even come up from +	 * it to mac80211 since they're handled.) +	 */ +	if (sdata->local->hw.flags & IEEE80211_HW_AP_LINK_PS) +		return RX_CONTINUE; + +	/* +	 * Don't do anything if the station isn't already asleep. In +	 * the uAPSD case, the station will probably be marked asleep, +	 * in the PS-Poll case the station must be confused ... +	 */ +	if (!test_sta_flag(rx->sta, WLAN_STA_PS_STA)) +		return RX_CONTINUE; + +	if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) { +		if (!test_sta_flag(rx->sta, WLAN_STA_SP)) { +			if (!test_sta_flag(rx->sta, WLAN_STA_PS_DRIVER)) +				ieee80211_sta_ps_deliver_poll_response(rx->sta); +			else +				set_sta_flag(rx->sta, WLAN_STA_PSPOLL); +		} + +		/* Free PS Poll skb here instead of returning RX_DROP that would +		 * count as an dropped frame. */ +		dev_kfree_skb(rx->skb); + +		return RX_QUEUED; +	} else if (!ieee80211_has_morefrags(hdr->frame_control) && +		   !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && +		   ieee80211_has_pm(hdr->frame_control) && +		   (ieee80211_is_data_qos(hdr->frame_control) || +		    ieee80211_is_qos_nullfunc(hdr->frame_control))) { +		tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; +		ac = ieee802_1d_to_ac[tid & 7]; + +		/* +		 * If this AC is not trigger-enabled do nothing. +		 * +		 * NB: This could/should check a separate bitmap of trigger- +		 * enabled queues, but for now we only implement uAPSD w/o +		 * TSPEC changes to the ACs, so they're always the same. +		 */ +		if (!(rx->sta->sta.uapsd_queues & BIT(ac))) +			return RX_CONTINUE; + +		/* if we are in a service period, do nothing */ +		if (test_sta_flag(rx->sta, WLAN_STA_SP)) +			return RX_CONTINUE; + +		if (!test_sta_flag(rx->sta, WLAN_STA_PS_DRIVER)) +			ieee80211_sta_ps_deliver_uapsd(rx->sta); +		else +			set_sta_flag(rx->sta, WLAN_STA_UAPSD); +	} + +	return RX_CONTINUE; +} + +static ieee80211_rx_result debug_noinline +ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) +{ +	struct sta_info *sta = rx->sta; +	struct sk_buff *skb = rx->skb; +	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; +	int i; + +	if (!sta) +		return RX_CONTINUE; + +	/* +	 * Update last_rx only for IBSS packets which are for the current +	 * BSSID and for station already AUTHORIZED to avoid keeping the +	 * current IBSS network alive in cases where other STAs start +	 * using different BSSID. This will also give the station another +	 * chance to restart the authentication/authorization in case +	 * something went wrong the first time. +	 */ +	if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) { +		u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len, +						NL80211_IFTYPE_ADHOC); +		if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid) && +		    test_sta_flag(sta, WLAN_STA_AUTHORIZED)) { +			sta->last_rx = jiffies; +			if (ieee80211_is_data(hdr->frame_control) && +			    !is_multicast_ether_addr(hdr->addr1)) { +				sta->last_rx_rate_idx = status->rate_idx; +				sta->last_rx_rate_flag = status->flag; +				sta->last_rx_rate_vht_flag = status->vht_flag; +				sta->last_rx_rate_vht_nss = status->vht_nss; +			} +		} +	} else if (!is_multicast_ether_addr(hdr->addr1)) { +		/* +		 * Mesh beacons will update last_rx when if they are found to +		 * match the current local configuration when processed. +		 */ +		sta->last_rx = jiffies; +		if (ieee80211_is_data(hdr->frame_control)) { +			sta->last_rx_rate_idx = status->rate_idx; +			sta->last_rx_rate_flag = status->flag; +			sta->last_rx_rate_vht_flag = status->vht_flag; +			sta->last_rx_rate_vht_nss = status->vht_nss; +		} +	} + +	if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) +		return RX_CONTINUE; + +	if (rx->sdata->vif.type == NL80211_IFTYPE_STATION) +		ieee80211_sta_rx_notify(rx->sdata, hdr); + +	sta->rx_fragments++; +	sta->rx_bytes += rx->skb->len; +	if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) { +		sta->last_signal = status->signal; +		ewma_add(&sta->avg_signal, -status->signal); +	} + +	if (status->chains) { +		sta->chains = status->chains; +		for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) { +			int signal = status->chain_signal[i]; + +			if (!(status->chains & BIT(i))) +				continue; + +			sta->chain_signal_last[i] = signal; +			ewma_add(&sta->chain_signal_avg[i], -signal); +		} +	} + +	/* +	 * Change STA power saving mode only at the end of a frame +	 * exchange sequence. +	 */ +	if (!(sta->local->hw.flags & IEEE80211_HW_AP_LINK_PS) && +	    !ieee80211_has_morefrags(hdr->frame_control) && +	    !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && +	    (rx->sdata->vif.type == NL80211_IFTYPE_AP || +	     rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && +	    /* PM bit is only checked in frames where it isn't reserved, +	     * in AP mode it's reserved in non-bufferable management frames +	     * (cf. IEEE 802.11-2012 8.2.4.1.7 Power Management field) +	     */ +	    (!ieee80211_is_mgmt(hdr->frame_control) || +	     ieee80211_is_bufferable_mmpdu(hdr->frame_control))) { +		if (test_sta_flag(sta, WLAN_STA_PS_STA)) { +			if (!ieee80211_has_pm(hdr->frame_control)) +				sta_ps_end(sta); +		} else { +			if (ieee80211_has_pm(hdr->frame_control)) +				sta_ps_start(sta); +		} +	} + +	/* mesh power save support */ +	if (ieee80211_vif_is_mesh(&rx->sdata->vif)) +		ieee80211_mps_rx_h_sta_process(sta, hdr); + +	/* +	 * Drop (qos-)data::nullfunc frames silently, since they +	 * are used only to control station power saving mode. +	 */ +	if (ieee80211_is_nullfunc(hdr->frame_control) || +	    ieee80211_is_qos_nullfunc(hdr->frame_control)) { +		I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc); + +		/* +		 * If we receive a 4-addr nullfunc frame from a STA +		 * that was not moved to a 4-addr STA vlan yet send +		 * the event to userspace and for older hostapd drop +		 * the frame to the monitor interface. +		 */ +		if (ieee80211_has_a4(hdr->frame_control) && +		    (rx->sdata->vif.type == NL80211_IFTYPE_AP || +		     (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && +		      !rx->sdata->u.vlan.sta))) { +			if (!test_and_set_sta_flag(sta, WLAN_STA_4ADDR_EVENT)) +				cfg80211_rx_unexpected_4addr_frame( +					rx->sdata->dev, sta->sta.addr, +					GFP_ATOMIC); +			return RX_DROP_MONITOR; +		} +		/* +		 * Update counter and free packet here to avoid +		 * counting this as a dropped packed. +		 */ +		sta->rx_packets++; +		dev_kfree_skb(rx->skb); +		return RX_QUEUED; +	} + +	return RX_CONTINUE; +} /* ieee80211_rx_h_sta_process */ + +static ieee80211_rx_result debug_noinline  ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)  {  	struct sk_buff *skb = rx->skb; @@ -873,6 +1356,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)  	struct ieee80211_key *sta_ptk = NULL;  	int mmie_keyidx = -1;  	__le16 fc; +	const struct ieee80211_cipher_scheme *cs = NULL;  	/*  	 * Key selection 101 @@ -910,11 +1394,19 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)  	/* start without a key */  	rx->key = NULL; +	fc = hdr->frame_control; -	if (rx->sta) -		sta_ptk = rcu_dereference(rx->sta->ptk); +	if (rx->sta) { +		int keyid = rx->sta->ptk_idx; -	fc = hdr->frame_control; +		if (ieee80211_has_protected(fc) && rx->sta->cipher_scheme) { +			cs = rx->sta->cipher_scheme; +			keyid = iwl80211_get_cs_keyid(cs, rx->skb); +			if (unlikely(keyid < 0)) +				return RX_DROP_UNUSABLE; +		} +		sta_ptk = rcu_dereference(rx->sta->ptk[keyid]); +	}  	if (!ieee80211_has_protected(fc))  		mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb); @@ -948,15 +1440,35 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)  		 * have been expected.  		 */  		struct ieee80211_key *key = NULL; +		struct ieee80211_sub_if_data *sdata = rx->sdata; +		int i; +  		if (ieee80211_is_mgmt(fc) &&  		    is_multicast_ether_addr(hdr->addr1) &&  		    (key = rcu_dereference(rx->sdata->default_mgmt_key)))  			rx->key = key; -		else if ((key = rcu_dereference(rx->sdata->default_key))) -			rx->key = key; +		else { +			if (rx->sta) { +				for (i = 0; i < NUM_DEFAULT_KEYS; i++) { +					key = rcu_dereference(rx->sta->gtk[i]); +					if (key) +						break; +				} +			} +			if (!key) { +				for (i = 0; i < NUM_DEFAULT_KEYS; i++) { +					key = rcu_dereference(sdata->keys[i]); +					if (key) +						break; +				} +			} +			if (key) +				rx->key = key; +		}  		return RX_CONTINUE;  	} else {  		u8 keyid; +  		/*  		 * The device doesn't give us the IV so we won't be  		 * able to look up the key. That's ok though, we @@ -972,15 +1484,21 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)  		hdrlen = ieee80211_hdrlen(fc); -		if (rx->skb->len < 8 + hdrlen) -			return RX_DROP_UNUSABLE; /* TODO: count this? */ +		if (cs) { +			keyidx = iwl80211_get_cs_keyid(cs, rx->skb); -		/* -		 * no need to call ieee80211_wep_get_keyidx, -		 * it verifies a bunch of things we've done already -		 */ -		skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1); -		keyidx = keyid >> 6; +			if (unlikely(keyidx < 0)) +				return RX_DROP_UNUSABLE; +		} else { +			if (rx->skb->len < 8 + hdrlen) +				return RX_DROP_UNUSABLE; /* TODO: count this? */ +			/* +			 * no need to call ieee80211_wep_get_keyidx, +			 * it verifies a bunch of things we've done already +			 */ +			skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1); +			keyidx = keyid >> 6; +		}  		/* check per-station GTK first, if multicast packet */  		if (is_multicast_ether_addr(hdr->addr1) && rx->sta) @@ -1004,26 +1522,18 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)  	}  	if (rx->key) { +		if (unlikely(rx->key->flags & KEY_FLAG_TAINTED)) +			return RX_DROP_MONITOR; +  		rx->key->tx_rx_count++;  		/* TODO: add threshold stuff again */  	} else {  		return RX_DROP_MONITOR;  	} -	if (skb_linearize(rx->skb)) -		return RX_DROP_UNUSABLE; -	/* the hdr variable is invalid now! */ -  	switch (rx->key->conf.cipher) {  	case WLAN_CIPHER_SUITE_WEP40:  	case WLAN_CIPHER_SUITE_WEP104: -		/* Check for weak IVs if possible */ -		if (rx->sta && ieee80211_is_data(fc) && -		    (!(status->flag & RX_FLAG_IV_STRIPPED) || -		     !(status->flag & RX_FLAG_DECRYPTED)) && -		    ieee80211_wep_is_weak_iv(rx->skb, rx->key)) -			rx->sta->wep_weak_iv_count++; -  		result = ieee80211_crypto_wep_decrypt(rx);  		break;  	case WLAN_CIPHER_SUITE_TKIP: @@ -1036,209 +1546,30 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)  		result = ieee80211_crypto_aes_cmac_decrypt(rx);  		break;  	default: -		/* -		 * We can reach here only with HW-only algorithms -		 * but why didn't it decrypt the frame?! -		 */ -		return RX_DROP_UNUSABLE; +		result = ieee80211_crypto_hw_decrypt(rx);  	} +	/* the hdr variable is invalid after the decrypt handlers */ +  	/* either the frame has been decrypted or will be dropped */  	status->flag |= RX_FLAG_DECRYPTED;  	return result;  } -static ieee80211_rx_result debug_noinline -ieee80211_rx_h_check_more_data(struct ieee80211_rx_data *rx) -{ -	struct ieee80211_local *local; -	struct ieee80211_hdr *hdr; -	struct sk_buff *skb; - -	local = rx->local; -	skb = rx->skb; -	hdr = (struct ieee80211_hdr *) skb->data; - -	if (!local->pspolling) -		return RX_CONTINUE; - -	if (!ieee80211_has_fromds(hdr->frame_control)) -		/* this is not from AP */ -		return RX_CONTINUE; - -	if (!ieee80211_is_data(hdr->frame_control)) -		return RX_CONTINUE; - -	if (!ieee80211_has_moredata(hdr->frame_control)) { -		/* AP has no more frames buffered for us */ -		local->pspolling = false; -		return RX_CONTINUE; -	} - -	/* more data bit is set, let's request a new frame from the AP */ -	ieee80211_send_pspoll(local, rx->sdata); - -	return RX_CONTINUE; -} - -static void ap_sta_ps_start(struct sta_info *sta) -{ -	struct ieee80211_sub_if_data *sdata = sta->sdata; -	struct ieee80211_local *local = sdata->local; - -	atomic_inc(&sdata->bss->num_sta_ps); -	set_sta_flags(sta, WLAN_STA_PS_STA); -	drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG -	printk(KERN_DEBUG "%s: STA %pM aid %d enters power save mode\n", -	       sdata->name, sta->sta.addr, sta->sta.aid); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ -} - -static void ap_sta_ps_end(struct sta_info *sta) -{ -	struct ieee80211_sub_if_data *sdata = sta->sdata; - -	atomic_dec(&sdata->bss->num_sta_ps); - -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG -	printk(KERN_DEBUG "%s: STA %pM aid %d exits power save mode\n", -	       sdata->name, sta->sta.addr, sta->sta.aid); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ - -	if (test_sta_flags(sta, WLAN_STA_PS_DRIVER)) { -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG -		printk(KERN_DEBUG "%s: STA %pM aid %d driver-ps-blocked\n", -		       sdata->name, sta->sta.addr, sta->sta.aid); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ -		return; -	} - -	ieee80211_sta_ps_deliver_wakeup(sta); -} - -static ieee80211_rx_result debug_noinline -ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) -{ -	struct sta_info *sta = rx->sta; -	struct sk_buff *skb = rx->skb; -	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); -	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - -	if (!sta) -		return RX_CONTINUE; - -	/* -	 * Update last_rx only for IBSS packets which are for the current -	 * BSSID to avoid keeping the current IBSS network alive in cases -	 * where other STAs start using different BSSID. -	 */ -	if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) { -		u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len, -						NL80211_IFTYPE_ADHOC); -		if (compare_ether_addr(bssid, rx->sdata->u.ibss.bssid) == 0) -			sta->last_rx = jiffies; -	} else if (!is_multicast_ether_addr(hdr->addr1)) { -		/* -		 * Mesh beacons will update last_rx when if they are found to -		 * match the current local configuration when processed. -		 */ -		sta->last_rx = jiffies; -	} - -	if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) -		return RX_CONTINUE; - -	if (rx->sdata->vif.type == NL80211_IFTYPE_STATION) -		ieee80211_sta_rx_notify(rx->sdata, hdr); - -	sta->rx_fragments++; -	sta->rx_bytes += rx->skb->len; -	sta->last_signal = status->signal; - -	/* -	 * Change STA power saving mode only at the end of a frame -	 * exchange sequence. -	 */ -	if (!ieee80211_has_morefrags(hdr->frame_control) && -	    (rx->sdata->vif.type == NL80211_IFTYPE_AP || -	     rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) { -		if (test_sta_flags(sta, WLAN_STA_PS_STA)) { -			/* -			 * Ignore doze->wake transitions that are -			 * indicated by non-data frames, the standard -			 * is unclear here, but for example going to -			 * PS mode and then scanning would cause a -			 * doze->wake transition for the probe request, -			 * and that is clearly undesirable. -			 */ -			if (ieee80211_is_data(hdr->frame_control) && -			    !ieee80211_has_pm(hdr->frame_control)) -				ap_sta_ps_end(sta); -		} else { -			if (ieee80211_has_pm(hdr->frame_control)) -				ap_sta_ps_start(sta); -		} -	} - -	/* -	 * Drop (qos-)data::nullfunc frames silently, since they -	 * are used only to control station power saving mode. -	 */ -	if (ieee80211_is_nullfunc(hdr->frame_control) || -	    ieee80211_is_qos_nullfunc(hdr->frame_control)) { -		I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc); - -		/* -		 * If we receive a 4-addr nullfunc frame from a STA -		 * that was not moved to a 4-addr STA vlan yet, drop -		 * the frame to the monitor interface, to make sure -		 * that hostapd sees it -		 */ -		if (ieee80211_has_a4(hdr->frame_control) && -		    (rx->sdata->vif.type == NL80211_IFTYPE_AP || -		     (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && -		      !rx->sdata->u.vlan.sta))) -			return RX_DROP_MONITOR; -		/* -		 * Update counter and free packet here to avoid -		 * counting this as a dropped packed. -		 */ -		sta->rx_packets++; -		dev_kfree_skb(rx->skb); -		return RX_QUEUED; -	} - -	return RX_CONTINUE; -} /* ieee80211_rx_h_sta_process */ -  static inline struct ieee80211_fragment_entry *  ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,  			 unsigned int frag, unsigned int seq, int rx_queue,  			 struct sk_buff **skb)  {  	struct ieee80211_fragment_entry *entry; -	int idx; -	idx = sdata->fragment_next;  	entry = &sdata->fragments[sdata->fragment_next++];  	if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX)  		sdata->fragment_next = 0; -	if (!skb_queue_empty(&entry->skb_list)) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -		struct ieee80211_hdr *hdr = -			(struct ieee80211_hdr *) entry->skb_list.next->data; -		printk(KERN_DEBUG "%s: RX reassembly removed oldest " -		       "fragment entry (idx=%d age=%lu seq=%d last_frag=%d " -		       "addr1=%pM addr2=%pM\n", -		       sdata->name, idx, -		       jiffies - entry->first_frag_time, entry->seq, -		       entry->last_frag, hdr->addr1, hdr->addr2); -#endif +	if (!skb_queue_empty(&entry->skb_list))  		__skb_queue_purge(&entry->skb_list); -	}  	__skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */  	*skb = NULL; @@ -1281,8 +1612,8 @@ ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,  		 */  		if (((hdr->frame_control ^ f_hdr->frame_control) &  		     cpu_to_le16(IEEE80211_FCTL_FTYPE)) || -		    compare_ether_addr(hdr->addr1, f_hdr->addr1) != 0 || -		    compare_ether_addr(hdr->addr2, f_hdr->addr2) != 0) +		    !ether_addr_equal(hdr->addr1, f_hdr->addr1) || +		    !ether_addr_equal(hdr->addr2, f_hdr->addr2))  			continue;  		if (time_after(jiffies, entry->first_frag_time + 2 * HZ)) { @@ -1308,11 +1639,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)  	hdr = (struct ieee80211_hdr *)rx->skb->data;  	fc = hdr->frame_control; + +	if (ieee80211_is_ctl(fc)) +		return RX_CONTINUE; +  	sc = le16_to_cpu(hdr->seq_ctrl);  	frag = sc & IEEE80211_SCTL_FRAG;  	if (likely((!ieee80211_has_morefrags(fc) && frag == 0) || -		   (rx->skb)->len < 24 ||  		   is_multicast_ether_addr(hdr->addr1))) {  		/* not fragmented */  		goto out; @@ -1333,17 +1667,16 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)  	if (frag == 0) {  		/* This is the first fragment of a new frame. */  		entry = ieee80211_reassemble_add(rx->sdata, frag, seq, -						 rx->queue, &(rx->skb)); +						 rx->seqno_idx, &(rx->skb));  		if (rx->key && rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP &&  		    ieee80211_has_protected(fc)) { -			int queue = ieee80211_is_mgmt(fc) ? -				NUM_RX_DATA_QUEUES : rx->queue; +			int queue = rx->security_idx;  			/* Store CCMP PN so that we can verify that the next  			 * fragment has a sequential PN value. */  			entry->ccmp = 1;  			memcpy(entry->last_pn,  			       rx->key->u.ccmp.rx_pn[queue], -			       CCMP_PN_LEN); +			       IEEE80211_CCMP_PN_LEN);  		}  		return RX_QUEUED;  	} @@ -1351,7 +1684,8 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)  	/* This is a fragment for a frame that should already be pending in  	 * fragment cache. Add this fragment to the end of the pending entry.  	 */ -	entry = ieee80211_reassemble_find(rx->sdata, frag, seq, rx->queue, hdr); +	entry = ieee80211_reassemble_find(rx->sdata, frag, seq, +					  rx->seqno_idx, hdr);  	if (!entry) {  		I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);  		return RX_DROP_MONITOR; @@ -1361,22 +1695,21 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)  	 * (IEEE 802.11i, 8.3.3.4.5) */  	if (entry->ccmp) {  		int i; -		u8 pn[CCMP_PN_LEN], *rpn; +		u8 pn[IEEE80211_CCMP_PN_LEN], *rpn;  		int queue;  		if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP)  			return RX_DROP_UNUSABLE; -		memcpy(pn, entry->last_pn, CCMP_PN_LEN); -		for (i = CCMP_PN_LEN - 1; i >= 0; i--) { +		memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN); +		for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) {  			pn[i]++;  			if (pn[i])  				break;  		} -		queue = ieee80211_is_mgmt(fc) ? -			NUM_RX_DATA_QUEUES : rx->queue; +		queue = rx->security_idx;  		rpn = rx->key->u.ccmp.rx_pn[queue]; -		if (memcmp(pn, rpn, CCMP_PN_LEN)) +		if (memcmp(pn, rpn, IEEE80211_CCMP_PN_LEN))  			return RX_DROP_UNUSABLE; -		memcpy(entry->last_pn, pn, CCMP_PN_LEN); +		memcpy(entry->last_pn, pn, IEEE80211_CCMP_PN_LEN);  	}  	skb_pull(rx->skb, ieee80211_hdrlen(fc)); @@ -1417,64 +1750,15 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)  	return RX_CONTINUE;  } -static ieee80211_rx_result debug_noinline -ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx) -{ -	struct ieee80211_sub_if_data *sdata = rx->sdata; -	__le16 fc = ((struct ieee80211_hdr *)rx->skb->data)->frame_control; -	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); - -	if (likely(!rx->sta || !ieee80211_is_pspoll(fc) || -		   !(status->rx_flags & IEEE80211_RX_RA_MATCH))) -		return RX_CONTINUE; - -	if ((sdata->vif.type != NL80211_IFTYPE_AP) && -	    (sdata->vif.type != NL80211_IFTYPE_AP_VLAN)) -		return RX_DROP_UNUSABLE; - -	if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) -		ieee80211_sta_ps_deliver_poll_response(rx->sta); -	else -		set_sta_flags(rx->sta, WLAN_STA_PSPOLL); - -	/* Free PS Poll skb here instead of returning RX_DROP that would -	 * count as an dropped frame. */ -	dev_kfree_skb(rx->skb); - -	return RX_QUEUED; -} - -static ieee80211_rx_result debug_noinline -ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx) -{ -	u8 *data = rx->skb->data; -	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)data; - -	if (!ieee80211_is_data_qos(hdr->frame_control)) -		return RX_CONTINUE; - -	/* remove the qos control field, update frame type and meta-data */ -	memmove(data + IEEE80211_QOS_CTL_LEN, data, -		ieee80211_hdrlen(hdr->frame_control) - IEEE80211_QOS_CTL_LEN); -	hdr = (struct ieee80211_hdr *)skb_pull(rx->skb, IEEE80211_QOS_CTL_LEN); -	/* change frame type to non QOS */ -	hdr->frame_control &= ~cpu_to_le16(IEEE80211_STYPE_QOS_DATA); - -	return RX_CONTINUE; -} - -static int -ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx) +static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)  { -	if (unlikely(!rx->sta || -	    !test_sta_flags(rx->sta, WLAN_STA_AUTHORIZED))) +	if (unlikely(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED)))  		return -EACCES;  	return 0;  } -static int -ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) +static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)  {  	struct sk_buff *skb = rx->skb;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); @@ -1496,8 +1780,7 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)  	return 0;  } -static int -ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) +static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)  {  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); @@ -1510,22 +1793,33 @@ ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)  	if (status->flag & RX_FLAG_DECRYPTED)  		return 0; -	if (rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP)) { +	if (rx->sta && test_sta_flag(rx->sta, WLAN_STA_MFP)) {  		if (unlikely(!ieee80211_has_protected(fc) &&  			     ieee80211_is_unicast_robust_mgmt_frame(rx->skb) && -			     rx->key)) +			     rx->key)) { +			if (ieee80211_is_deauth(fc) || +			    ieee80211_is_disassoc(fc)) +				cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev, +							     rx->skb->data, +							     rx->skb->len);  			return -EACCES; +		}  		/* BIP does not use Protected field, so need to check MMIE */  		if (unlikely(ieee80211_is_multicast_robust_mgmt_frame(rx->skb) && -			     ieee80211_get_mmie_keyidx(rx->skb) < 0)) +			     ieee80211_get_mmie_keyidx(rx->skb) < 0)) { +			if (ieee80211_is_deauth(fc) || +			    ieee80211_is_disassoc(fc)) +				cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev, +							     rx->skb->data, +							     rx->skb->len);  			return -EACCES; +		}  		/*  		 * When using MFP, Action frames are not allowed prior to  		 * having configured keys.  		 */  		if (unlikely(ieee80211_is_action(fc) && !rx->key && -			     ieee80211_is_robust_mgmt_frame( -				     (struct ieee80211_hdr *) rx->skb->data))) +			     ieee80211_is_robust_mgmt_frame(rx->skb)))  			return -EACCES;  	} @@ -1533,21 +1827,43 @@ ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)  }  static int -__ieee80211_data_to_8023(struct ieee80211_rx_data *rx) +__ieee80211_data_to_8023(struct ieee80211_rx_data *rx, bool *port_control)  {  	struct ieee80211_sub_if_data *sdata = rx->sdata;  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; +	bool check_port_control = false; +	struct ethhdr *ehdr; +	int ret; +	*port_control = false;  	if (ieee80211_has_a4(hdr->frame_control) &&  	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !sdata->u.vlan.sta)  		return -1; +	if (sdata->vif.type == NL80211_IFTYPE_STATION && +	    !!sdata->u.mgd.use_4addr != !!ieee80211_has_a4(hdr->frame_control)) { + +		if (!sdata->u.mgd.use_4addr) +			return -1; +		else +			check_port_control = true; +	} +  	if (is_multicast_ether_addr(hdr->addr1) && -	    ((sdata->vif.type == NL80211_IFTYPE_AP_VLAN && sdata->u.vlan.sta) || -	     (sdata->vif.type == NL80211_IFTYPE_STATION && sdata->u.mgd.use_4addr))) +	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN && sdata->u.vlan.sta) +		return -1; + +	ret = ieee80211_data_to_8023(rx->skb, sdata->vif.addr, sdata->vif.type); +	if (ret < 0) +		return ret; + +	ehdr = (struct ethhdr *) rx->skb->data; +	if (ehdr->h_proto == rx->sdata->control_port_protocol) +		*port_control = true; +	else if (check_port_control)  		return -1; -	return ieee80211_data_to_8023(rx->skb, sdata->vif.addr, sdata->vif.type); +	return 0;  }  /* @@ -1564,8 +1880,8 @@ static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc)  	 * of whether the frame was encrypted or not.  	 */  	if (ehdr->h_proto == rx->sdata->control_port_protocol && -	    (compare_ether_addr(ehdr->h_dest, rx->sdata->vif.addr) == 0 || -	     compare_ether_addr(ehdr->h_dest, pae_group_addr) == 0)) +	    (ether_addr_equal(ehdr->h_dest, rx->sdata->vif.addr) || +	     ether_addr_equal(ehdr->h_dest, pae_group_addr)))  		return true;  	if (ieee80211_802_1x_port_control(rx) || @@ -1602,9 +1918,9 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)  			 * local net stack and back to the wireless medium  			 */  			xmit_skb = skb_copy(skb, GFP_ATOMIC); -			if (!xmit_skb && net_ratelimit()) -				printk(KERN_DEBUG "%s: failed to clone " -				       "multicast frame\n", dev->name); +			if (!xmit_skb) +				net_info_ratelimited("%s: failed to clone multicast frame\n", +						    dev->name);  		} else {  			dsta = sta_info_get(sdata, skb->data);  			if (dsta) { @@ -1620,18 +1936,17 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)  		}  	} -	if (skb) { -		int align __maybe_unused; -  #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS -		/* -		 * 'align' will only take the values 0 or 2 here -		 * since all frames are required to be aligned -		 * to 2-byte boundaries when being passed to -		 * mac80211. That also explains the __skb_push() -		 * below. +	if (skb) { +		/* 'align' will only take the values 0 or 2 here since all +		 * frames are required to be aligned to 2-byte boundaries +		 * when being passed to mac80211; the code here works just +		 * as well if that isn't true, but mac80211 assumes it can +		 * access fields as 2-byte aligned (e.g. for ether_addr_equal)  		 */ -		align = ((unsigned long)(skb->data + sizeof(struct ethhdr))) & 3; +		int align; + +		align = (unsigned long)(skb->data + sizeof(struct ethhdr)) & 3;  		if (align) {  			if (WARN_ON(skb_headroom(skb) < 3)) {  				dev_kfree_skb(skb); @@ -1644,18 +1959,26 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)  				skb_set_tail_pointer(skb, len);  			}  		} +	}  #endif -		if (skb) { -			/* deliver to local stack */ -			skb->protocol = eth_type_trans(skb, dev); -			memset(skb->cb, 0, sizeof(skb->cb)); +	if (skb) { +		/* deliver to local stack */ +		skb->protocol = eth_type_trans(skb, dev); +		memset(skb->cb, 0, sizeof(skb->cb)); +		if (rx->local->napi) +			napi_gro_receive(rx->local->napi, skb); +		else  			netif_receive_skb(skb); -		}  	}  	if (xmit_skb) { -		/* send to wireless media */ +		/* +		 * Send to wireless media and increase priority by 256 to +		 * keep the received priority instead of reclassifying +		 * the frame (see cfg80211_classify8021d). +		 */ +		xmit_skb->priority += 256;  		xmit_skb->protocol = htons(ETH_P_802_3);  		skb_reset_network_header(xmit_skb);  		skb_reset_mac_header(xmit_skb); @@ -1702,7 +2025,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)  	ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,  				 rx->sdata->vif.type, -				 rx->local->hw.extra_tx_headroom); +				 rx->local->hw.extra_tx_headroom, true);  	while (!skb_queue_empty(&frame_list)) {  		rx->skb = __skb_dequeue(&frame_list); @@ -1724,23 +2047,45 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)  static ieee80211_rx_result  ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)  { -	struct ieee80211_hdr *hdr; +	struct ieee80211_hdr *fwd_hdr, *hdr; +	struct ieee80211_tx_info *info;  	struct ieee80211s_hdr *mesh_hdr; -	unsigned int hdrlen;  	struct sk_buff *skb = rx->skb, *fwd_skb;  	struct ieee80211_local *local = rx->local;  	struct ieee80211_sub_if_data *sdata = rx->sdata;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	u16 q, hdrlen;  	hdr = (struct ieee80211_hdr *) skb->data;  	hdrlen = ieee80211_hdrlen(hdr->frame_control); + +	/* make sure fixed part of mesh header is there, also checks skb len */ +	if (!pskb_may_pull(rx->skb, hdrlen + 6)) +		return RX_DROP_MONITOR; +  	mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); -	if (!ieee80211_is_data(hdr->frame_control)) +	/* make sure full mesh header is there, also checks skb len */ +	if (!pskb_may_pull(rx->skb, +			   hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr))) +		return RX_DROP_MONITOR; + +	/* reload pointers */ +	hdr = (struct ieee80211_hdr *) skb->data; +	mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); + +	/* frame is in RMC, don't forward */ +	if (ieee80211_is_data(hdr->frame_control) && +	    is_multicast_ether_addr(hdr->addr1) && +	    mesh_rmc_check(rx->sdata, hdr->addr3, mesh_hdr)) +		return RX_DROP_MONITOR; + +	if (!ieee80211_is_data(hdr->frame_control) || +	    !(status->rx_flags & IEEE80211_RX_RA_MATCH))  		return RX_CONTINUE;  	if (!mesh_hdr->ttl) -		/* illegal frame */  		return RX_DROP_MONITOR;  	if (mesh_hdr->flags & MESH_FLAGS_AE) { @@ -1751,18 +2096,21 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)  		if (is_multicast_ether_addr(hdr->addr1)) {  			mpp_addr = hdr->addr3;  			proxied_addr = mesh_hdr->eaddr1; -		} else { +		} else if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6) { +			/* has_a4 already checked in ieee80211_rx_mesh_check */  			mpp_addr = hdr->addr4;  			proxied_addr = mesh_hdr->eaddr2; +		} else { +			return RX_DROP_MONITOR;  		}  		rcu_read_lock(); -		mppath = mpp_path_lookup(proxied_addr, sdata); +		mppath = mpp_path_lookup(sdata, proxied_addr);  		if (!mppath) { -			mpp_path_add(proxied_addr, mpp_addr, sdata); +			mpp_path_add(sdata, proxied_addr, mpp_addr);  		} else {  			spin_lock_bh(&mppath->state_lock); -			if (compare_ether_addr(mppath->mpp, mpp_addr) != 0) +			if (!ether_addr_equal(mppath->mpp, mpp_addr))  				memcpy(mppath->mpp, mpp_addr, ETH_ALEN);  			spin_unlock_bh(&mppath->state_lock);  		} @@ -1771,61 +2119,60 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)  	/* Frame has reached destination.  Don't forward */  	if (!is_multicast_ether_addr(hdr->addr1) && -	    compare_ether_addr(sdata->vif.addr, hdr->addr3) == 0) +	    ether_addr_equal(sdata->vif.addr, hdr->addr3))  		return RX_CONTINUE; -	mesh_hdr->ttl--; +	q = ieee80211_select_queue_80211(sdata, skb, hdr); +	if (ieee80211_queue_stopped(&local->hw, q)) { +		IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion); +		return RX_DROP_MONITOR; +	} +	skb_set_queue_mapping(skb, q); -	if (status->rx_flags & IEEE80211_RX_RA_MATCH) { -		if (!mesh_hdr->ttl) -			IEEE80211_IFSTA_MESH_CTR_INC(&rx->sdata->u.mesh, -						     dropped_frames_ttl); -		else { -			struct ieee80211_hdr *fwd_hdr; -			struct ieee80211_tx_info *info; - -			fwd_skb = skb_copy(skb, GFP_ATOMIC); - -			if (!fwd_skb && net_ratelimit()) -				printk(KERN_DEBUG "%s: failed to clone mesh frame\n", -						   sdata->name); - -			fwd_hdr =  (struct ieee80211_hdr *) fwd_skb->data; -			memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); -			info = IEEE80211_SKB_CB(fwd_skb); -			memset(info, 0, sizeof(*info)); -			info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; -			info->control.vif = &rx->sdata->vif; -			skb_set_queue_mapping(skb, -				ieee80211_select_queue(rx->sdata, fwd_skb)); -			ieee80211_set_qos_hdr(local, skb); -			if (is_multicast_ether_addr(fwd_hdr->addr1)) -				IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, -								fwded_mcast); -			else { -				int err; -				/* -				 * Save TA to addr1 to send TA a path error if a -				 * suitable next hop is not found -				 */ -				memcpy(fwd_hdr->addr1, fwd_hdr->addr2, -						ETH_ALEN); -				err = mesh_nexthop_lookup(fwd_skb, sdata); -				/* Failed to immediately resolve next hop: -				 * fwded frame was dropped or will be added -				 * later to the pending skb queue.  */ -				if (err) -					return RX_DROP_MONITOR; - -				IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, -								fwded_unicast); -			} -			IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, -						     fwded_frames); -			ieee80211_add_pending_skb(local, fwd_skb); -		} +	if (!--mesh_hdr->ttl) { +		IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl); +		goto out;  	} +	if (!ifmsh->mshcfg.dot11MeshForwarding) +		goto out; + +	fwd_skb = skb_copy(skb, GFP_ATOMIC); +	if (!fwd_skb) { +		net_info_ratelimited("%s: failed to clone mesh frame\n", +				    sdata->name); +		goto out; +	} + +	fwd_hdr =  (struct ieee80211_hdr *) fwd_skb->data; +	fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY); +	info = IEEE80211_SKB_CB(fwd_skb); +	memset(info, 0, sizeof(*info)); +	info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; +	info->control.vif = &rx->sdata->vif; +	info->control.jiffies = jiffies; +	if (is_multicast_ether_addr(fwd_hdr->addr1)) { +		IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast); +		memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); +		/* update power mode indication when forwarding */ +		ieee80211_mps_set_frame_flags(sdata, NULL, fwd_hdr); +	} else if (!mesh_nexthop_lookup(sdata, fwd_skb)) { +		/* mesh power mode flags updated in mesh_nexthop_lookup */ +		IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast); +	} else { +		/* unable to resolve next hop */ +		mesh_path_error_tx(sdata, ifmsh->mshcfg.element_ttl, +				   fwd_hdr->addr3, 0, +				   WLAN_REASON_MESH_PATH_NOFORWARD, +				   fwd_hdr->addr2); +		IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route); +		kfree_skb(fwd_skb); +		return RX_DROP_MONITOR; +	} + +	IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames); +	ieee80211_add_pending_skb(local, fwd_skb); + out:  	if (is_multicast_ether_addr(hdr->addr1) ||  	    sdata->dev->flags & IFF_PROMISC)  		return RX_CONTINUE; @@ -1842,6 +2189,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)  	struct net_device *dev = sdata->dev;  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;  	__le16 fc = hdr->frame_control; +	bool port_control;  	int err;  	if (unlikely(!ieee80211_is_data(hdr->frame_control))) @@ -1851,28 +2199,43 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)  		return RX_DROP_MONITOR;  	/* -	 * Allow the cooked monitor interface of an AP to see 4-addr frames so -	 * that a 4-addr station can be detected and moved into a separate VLAN +	 * Send unexpected-4addr-frame event to hostapd. For older versions, +	 * also drop the frame to cooked monitor interfaces.  	 */  	if (ieee80211_has_a4(hdr->frame_control) && -	    sdata->vif.type == NL80211_IFTYPE_AP) +	    sdata->vif.type == NL80211_IFTYPE_AP) { +		if (rx->sta && +		    !test_and_set_sta_flag(rx->sta, WLAN_STA_4ADDR_EVENT)) +			cfg80211_rx_unexpected_4addr_frame( +				rx->sdata->dev, rx->sta->sta.addr, GFP_ATOMIC);  		return RX_DROP_MONITOR; +	} -	err = __ieee80211_data_to_8023(rx); +	err = __ieee80211_data_to_8023(rx, &port_control);  	if (unlikely(err))  		return RX_DROP_UNUSABLE;  	if (!ieee80211_frame_allowed(rx, fc))  		return RX_DROP_MONITOR; +	if (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && +	    unlikely(port_control) && sdata->bss) { +		sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, +				     u.ap); +		dev = sdata->dev; +		rx->sdata = sdata; +	} +  	rx->skb->dev = dev;  	dev->stats.rx_packets++;  	dev->stats.rx_bytes += rx->skb->len; -	if (ieee80211_is_data(hdr->frame_control) && -	    !is_multicast_ether_addr(hdr->addr1) && -	    local->hw.conf.dynamic_ps_timeout > 0 && local->ps_sdata) { +	if (local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 && +	    !is_multicast_ether_addr( +		    ((struct ethhdr *)rx->skb->data)->h_dest) && +	    (!local->scanning && +	     !test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))) {  			mod_timer(&local->dynamic_ps_timer, jiffies +  			 msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));  	} @@ -1885,8 +2248,6 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)  static ieee80211_rx_result debug_noinline  ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)  { -	struct ieee80211_local *local = rx->local; -	struct ieee80211_hw *hw = &local->hw;  	struct sk_buff *skb = rx->skb;  	struct ieee80211_bar *bar = (struct ieee80211_bar *)skb->data;  	struct tid_ampdu_rx *tid_agg_rx; @@ -1921,9 +2282,12 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)  			mod_timer(&tid_agg_rx->session_timer,  				  TU_TO_EXP_TIME(tid_agg_rx->timeout)); +		spin_lock(&tid_agg_rx->reorder_lock);  		/* release stored frames up to start of BAR */ -		ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num, -						 frames); +		ieee80211_release_reorder_frames(rx->sdata, tid_agg_rx, +						 start_seq_num, frames); +		spin_unlock(&tid_agg_rx->reorder_lock); +  		kfree_skb(skb);  		return RX_QUEUED;  	} @@ -1944,13 +2308,13 @@ static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata,  	struct sk_buff *skb;  	struct ieee80211_mgmt *resp; -	if (compare_ether_addr(mgmt->da, sdata->vif.addr) != 0) { +	if (!ether_addr_equal(mgmt->da, sdata->vif.addr)) {  		/* Not to own unicast address */  		return;  	} -	if (compare_ether_addr(mgmt->sa, sdata->u.mgd.bssid) != 0 || -	    compare_ether_addr(mgmt->bssid, sdata->u.mgd.bssid) != 0) { +	if (!ether_addr_equal(mgmt->sa, sdata->u.mgd.bssid) || +	    !ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid)) {  		/* Not from the current AP or not associated yet. */  		return;  	} @@ -1999,6 +2363,20 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)  	if (!ieee80211_is_mgmt(mgmt->frame_control))  		return RX_DROP_MONITOR; +	if (rx->sdata->vif.type == NL80211_IFTYPE_AP && +	    ieee80211_is_beacon(mgmt->frame_control) && +	    !(rx->flags & IEEE80211_RX_BEACON_REPORTED)) { +		int sig = 0; + +		if (rx->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) +			sig = status->signal; + +		cfg80211_report_obss_beacon(rx->local->hw.wiphy, +					    rx->skb->data, rx->skb->len, +					    status->freq, sig); +		rx->flags |= IEEE80211_RX_BEACON_REPORTED; +	} +  	if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))  		return RX_DROP_MONITOR; @@ -2024,23 +2402,144 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  	if (len < IEEE80211_MIN_ACTION_SIZE)  		return RX_DROP_UNUSABLE; -	if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) +	if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC && +	    mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED && +	    mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT)  		return RX_DROP_UNUSABLE;  	if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))  		return RX_DROP_UNUSABLE;  	switch (mgmt->u.action.category) { +	case WLAN_CATEGORY_HT: +		/* reject HT action frames from stations not supporting HT */ +		if (!rx->sta->sta.ht_cap.ht_supported) +			goto invalid; + +		if (sdata->vif.type != NL80211_IFTYPE_STATION && +		    sdata->vif.type != NL80211_IFTYPE_MESH_POINT && +		    sdata->vif.type != NL80211_IFTYPE_AP_VLAN && +		    sdata->vif.type != NL80211_IFTYPE_AP && +		    sdata->vif.type != NL80211_IFTYPE_ADHOC) +			break; + +		/* verify action & smps_control/chanwidth are present */ +		if (len < IEEE80211_MIN_ACTION_SIZE + 2) +			goto invalid; + +		switch (mgmt->u.action.u.ht_smps.action) { +		case WLAN_HT_ACTION_SMPS: { +			struct ieee80211_supported_band *sband; +			enum ieee80211_smps_mode smps_mode; + +			/* convert to HT capability */ +			switch (mgmt->u.action.u.ht_smps.smps_control) { +			case WLAN_HT_SMPS_CONTROL_DISABLED: +				smps_mode = IEEE80211_SMPS_OFF; +				break; +			case WLAN_HT_SMPS_CONTROL_STATIC: +				smps_mode = IEEE80211_SMPS_STATIC; +				break; +			case WLAN_HT_SMPS_CONTROL_DYNAMIC: +				smps_mode = IEEE80211_SMPS_DYNAMIC; +				break; +			default: +				goto invalid; +			} + +			/* if no change do nothing */ +			if (rx->sta->sta.smps_mode == smps_mode) +				goto handled; +			rx->sta->sta.smps_mode = smps_mode; + +			sband = rx->local->hw.wiphy->bands[status->band]; + +			rate_control_rate_update(local, sband, rx->sta, +						 IEEE80211_RC_SMPS_CHANGED); +			goto handled; +		} +		case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: { +			struct ieee80211_supported_band *sband; +			u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth; +			enum ieee80211_sta_rx_bandwidth new_bw; + +			/* If it doesn't support 40 MHz it can't change ... */ +			if (!(rx->sta->sta.ht_cap.cap & +					IEEE80211_HT_CAP_SUP_WIDTH_20_40)) +				goto handled; + +			if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ) +				new_bw = IEEE80211_STA_RX_BW_20; +			else +				new_bw = ieee80211_sta_cur_vht_bw(rx->sta); + +			if (rx->sta->sta.bandwidth == new_bw) +				goto handled; + +			sband = rx->local->hw.wiphy->bands[status->band]; + +			rate_control_rate_update(local, sband, rx->sta, +						 IEEE80211_RC_BW_CHANGED); +			goto handled; +		} +		default: +			goto invalid; +		} + +		break; +	case WLAN_CATEGORY_PUBLIC: +		if (len < IEEE80211_MIN_ACTION_SIZE + 1) +			goto invalid; +		if (sdata->vif.type != NL80211_IFTYPE_STATION) +			break; +		if (!rx->sta) +			break; +		if (!ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid)) +			break; +		if (mgmt->u.action.u.ext_chan_switch.action_code != +				WLAN_PUB_ACTION_EXT_CHANSW_ANN) +			break; +		if (len < offsetof(struct ieee80211_mgmt, +				   u.action.u.ext_chan_switch.variable)) +			goto invalid; +		goto queue; +	case WLAN_CATEGORY_VHT: +		if (sdata->vif.type != NL80211_IFTYPE_STATION && +		    sdata->vif.type != NL80211_IFTYPE_MESH_POINT && +		    sdata->vif.type != NL80211_IFTYPE_AP_VLAN && +		    sdata->vif.type != NL80211_IFTYPE_AP && +		    sdata->vif.type != NL80211_IFTYPE_ADHOC) +			break; + +		/* verify action code is present */ +		if (len < IEEE80211_MIN_ACTION_SIZE + 1) +			goto invalid; + +		switch (mgmt->u.action.u.vht_opmode_notif.action_code) { +		case WLAN_VHT_ACTION_OPMODE_NOTIF: { +			u8 opmode; + +			/* verify opmode is present */ +			if (len < IEEE80211_MIN_ACTION_SIZE + 2) +				goto invalid; + +			opmode = mgmt->u.action.u.vht_opmode_notif.operating_mode; + +			ieee80211_vht_handle_opmode(rx->sdata, rx->sta, +						    opmode, status->band, +						    false); +			goto handled; +		} +		default: +			break; +		} +		break;  	case WLAN_CATEGORY_BACK: -		/* -		 * The aggregation code is not prepared to handle -		 * anything but STA/AP due to the BSSID handling; -		 * IBSS could work in the code but isn't supported -		 * by drivers or the standard. -		 */  		if (sdata->vif.type != NL80211_IFTYPE_STATION && +		    sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&  		    sdata->vif.type != NL80211_IFTYPE_AP_VLAN && -		    sdata->vif.type != NL80211_IFTYPE_AP) +		    sdata->vif.type != NL80211_IFTYPE_AP && +		    sdata->vif.type != NL80211_IFTYPE_ADHOC)  			break;  		/* verify action_code is present */ @@ -2069,35 +2568,49 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  		goto queue;  	case WLAN_CATEGORY_SPECTRUM_MGMT: -		if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ) -			break; - -		if (sdata->vif.type != NL80211_IFTYPE_STATION) -			break; -  		/* verify action_code is present */  		if (len < IEEE80211_MIN_ACTION_SIZE + 1)  			break;  		switch (mgmt->u.action.u.measurement.action_code) {  		case WLAN_ACTION_SPCT_MSR_REQ: +			if (status->band != IEEE80211_BAND_5GHZ) +				break; +  			if (len < (IEEE80211_MIN_ACTION_SIZE +  				   sizeof(mgmt->u.action.u.measurement)))  				break; + +			if (sdata->vif.type != NL80211_IFTYPE_STATION) +				break; +  			ieee80211_process_measurement_req(sdata, mgmt, len);  			goto handled; -		case WLAN_ACTION_SPCT_CHL_SWITCH: +		case WLAN_ACTION_SPCT_CHL_SWITCH: { +			u8 *bssid;  			if (len < (IEEE80211_MIN_ACTION_SIZE +  				   sizeof(mgmt->u.action.u.chan_switch)))  				break; -			if (sdata->vif.type != NL80211_IFTYPE_STATION) +			if (sdata->vif.type != NL80211_IFTYPE_STATION && +			    sdata->vif.type != NL80211_IFTYPE_ADHOC && +			    sdata->vif.type != NL80211_IFTYPE_MESH_POINT) +				break; + +			if (sdata->vif.type == NL80211_IFTYPE_STATION) +				bssid = sdata->u.mgd.bssid; +			else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) +				bssid = sdata->u.ibss.bssid; +			else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) +				bssid = mgmt->sa; +			else  				break; -			if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN)) +			if (!ether_addr_equal(mgmt->bssid, bssid))  				break;  			goto queue; +			}  		}  		break;  	case WLAN_CATEGORY_SA_QUERY: @@ -2113,10 +2626,38 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)  			goto handled;  		}  		break; -	case WLAN_CATEGORY_MESH_PLINK: -	case WLAN_CATEGORY_MESH_PATH_SEL: +	case WLAN_CATEGORY_SELF_PROTECTED: +		if (len < (IEEE80211_MIN_ACTION_SIZE + +			   sizeof(mgmt->u.action.u.self_prot.action_code))) +			break; + +		switch (mgmt->u.action.u.self_prot.action_code) { +		case WLAN_SP_MESH_PEERING_OPEN: +		case WLAN_SP_MESH_PEERING_CLOSE: +		case WLAN_SP_MESH_PEERING_CONFIRM: +			if (!ieee80211_vif_is_mesh(&sdata->vif)) +				goto invalid; +			if (sdata->u.mesh.user_mpm) +				/* userspace handles this frame */ +				break; +			goto queue; +		case WLAN_SP_MGK_INFORM: +		case WLAN_SP_MGK_ACK: +			if (!ieee80211_vif_is_mesh(&sdata->vif)) +				goto invalid; +			break; +		} +		break; +	case WLAN_CATEGORY_MESH_ACTION: +		if (len < (IEEE80211_MIN_ACTION_SIZE + +			   sizeof(mgmt->u.action.u.mesh_action.action_code))) +			break; +  		if (!ieee80211_vif_is_mesh(&sdata->vif))  			break; +		if (mesh_action_is_path_sel(mgmt) && +		    !mesh_path_sel_is_hwmp(sdata)) +			break;  		goto queue;  	} @@ -2146,6 +2687,7 @@ static ieee80211_rx_result debug_noinline  ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)  {  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); +	int sig = 0;  	/* skip known-bad action frames and return them in the next handler */  	if (status->rx_flags & IEEE80211_RX_MALFORMED_ACTION_FRM) @@ -2158,16 +2700,17 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)  	 * it transmitted were processed or returned.  	 */ -	if (cfg80211_rx_mgmt(rx->sdata->dev, status->freq, -			     rx->skb->data, rx->skb->len, -			     GFP_ATOMIC)) { +	if (rx->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) +		sig = status->signal; + +	if (cfg80211_rx_mgmt(&rx->sdata->wdev, status->freq, sig, +			     rx->skb->data, rx->skb->len, 0, GFP_ATOMIC)) {  		if (rx->sta)  			rx->sta->rx_packets++;  		dev_kfree_skb(rx->skb);  		return RX_QUEUED;  	} -  	return RX_CONTINUE;  } @@ -2188,7 +2731,7 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)  	 * frames that we didn't handle, including returning unknown  	 * ones. For all other modes we will return them to the sender,  	 * setting the 0x80 bit in the action category, as required by -	 * 802.11-2007 7.3.1.11. +	 * 802.11-2012 9.24.4.  	 * Newer versions of hostapd shall also use the management frame  	 * registration mechanisms, but older ones still use cooked  	 * monitor interfaces so push all frames there. @@ -2198,6 +2741,9 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)  	     sdata->vif.type == NL80211_IFTYPE_AP_VLAN))  		return RX_DROP_MONITOR; +	if (is_multicast_ether_addr(mgmt->da)) +		return RX_DROP_MONITOR; +  	/* do not return rejected action frames */  	if (mgmt->u.action.category & 0x80)  		return RX_DROP_UNUSABLE; @@ -2213,7 +2759,19 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)  		memset(nskb->cb, 0, sizeof(nskb->cb)); -		ieee80211_tx_skb(rx->sdata, nskb); +		if (rx->sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) { +			struct ieee80211_tx_info *info = IEEE80211_SKB_CB(nskb); + +			info->flags = IEEE80211_TX_CTL_TX_OFFCHAN | +				      IEEE80211_TX_INTFL_OFFCHAN_TX_OK | +				      IEEE80211_TX_CTL_NO_CCK_RATE; +			if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) +				info->hw_queue = +					local->hw.offchannel_tx_hw_queue; +		} + +		__ieee80211_tx_skb_tid_band(rx->sdata, nskb, 7, +					    status->band);  	}  	dev_kfree_skb(rx->skb);  	return RX_QUEUED; @@ -2223,14 +2781,9 @@ static ieee80211_rx_result debug_noinline  ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)  {  	struct ieee80211_sub_if_data *sdata = rx->sdata; -	ieee80211_rx_result rxs;  	struct ieee80211_mgmt *mgmt = (void *)rx->skb->data;  	__le16 stype; -	rxs = ieee80211_work_rx_mgmt(rx->sdata, rx->skb); -	if (rxs != RX_CONTINUE) -		return rxs; -  	stype = mgmt->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE);  	if (!ieee80211_vif_is_mesh(&sdata->vif) && @@ -2239,20 +2792,27 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)  		return RX_DROP_MONITOR;  	switch (stype) { +	case cpu_to_le16(IEEE80211_STYPE_AUTH):  	case cpu_to_le16(IEEE80211_STYPE_BEACON):  	case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP):  		/* process for all: mesh, mlme, ibss */  		break; +	case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP): +	case cpu_to_le16(IEEE80211_STYPE_REASSOC_RESP):  	case cpu_to_le16(IEEE80211_STYPE_DEAUTH):  	case cpu_to_le16(IEEE80211_STYPE_DISASSOC): +		if (is_multicast_ether_addr(mgmt->da) && +		    !is_broadcast_ether_addr(mgmt->da)) +			return RX_DROP_MONITOR; +  		/* process only for station */  		if (sdata->vif.type != NL80211_IFTYPE_STATION)  			return RX_DROP_MONITOR;  		break;  	case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): -	case cpu_to_le16(IEEE80211_STYPE_AUTH): -		/* process only for ibss */ -		if (sdata->vif.type != NL80211_IFTYPE_ADHOC) +		/* process only for ibss and mesh */ +		if (sdata->vif.type != NL80211_IFTYPE_ADHOC && +		    sdata->vif.type != NL80211_IFTYPE_MESH_POINT)  			return RX_DROP_MONITOR;  		break;  	default: @@ -2269,63 +2829,16 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)  	return RX_QUEUED;  } -static void ieee80211_rx_michael_mic_report(struct ieee80211_hdr *hdr, -					    struct ieee80211_rx_data *rx) -{ -	int keyidx; -	unsigned int hdrlen; - -	hdrlen = ieee80211_hdrlen(hdr->frame_control); -	if (rx->skb->len >= hdrlen + 4) -		keyidx = rx->skb->data[hdrlen + 3] >> 6; -	else -		keyidx = -1; - -	if (!rx->sta) { -		/* -		 * Some hardware seem to generate incorrect Michael MIC -		 * reports; ignore them to avoid triggering countermeasures. -		 */ -		return; -	} - -	if (!ieee80211_has_protected(hdr->frame_control)) -		return; - -	if (rx->sdata->vif.type == NL80211_IFTYPE_AP && keyidx) { -		/* -		 * APs with pairwise keys should never receive Michael MIC -		 * errors for non-zero keyidx because these are reserved for -		 * group keys and only the AP is sending real multicast -		 * frames in the BSS. -		 */ -		return; -	} - -	if (!ieee80211_is_data(hdr->frame_control) && -	    !ieee80211_is_auth(hdr->frame_control)) -		return; - -	mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL, -					GFP_ATOMIC); -} -  /* TODO: use IEEE80211_RX_FRAGMENTED */  static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,  					struct ieee80211_rate *rate)  {  	struct ieee80211_sub_if_data *sdata;  	struct ieee80211_local *local = rx->local; -	struct ieee80211_rtap_hdr { -		struct ieee80211_radiotap_header hdr; -		u8 flags; -		u8 rate_or_pad; -		__le16 chan_freq; -		__le16 chan_flags; -	} __packed *rthdr;  	struct sk_buff *skb = rx->skb, *skb2;  	struct net_device *prev_dev = NULL;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); +	int needed_headroom;  	/*  	 * If cooked monitor has been processed already, then @@ -2335,30 +2848,20 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,  		goto out_free_skb;  	rx->flags |= IEEE80211_RX_CMNTR; -	if (skb_headroom(skb) < sizeof(*rthdr) && -	    pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) +	/* If there are no cooked monitor interfaces, just free the SKB */ +	if (!local->cooked_mntrs)  		goto out_free_skb; -	rthdr = (void *)skb_push(skb, sizeof(*rthdr)); -	memset(rthdr, 0, sizeof(*rthdr)); -	rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr)); -	rthdr->hdr.it_present = -		cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | -			    (1 << IEEE80211_RADIOTAP_CHANNEL)); +	/* room for the radiotap header based on driver features */ +	needed_headroom = ieee80211_rx_radiotap_space(local, status); -	if (rate) { -		rthdr->rate_or_pad = rate->bitrate / 5; -		rthdr->hdr.it_present |= -			cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); -	} -	rthdr->chan_freq = cpu_to_le16(status->freq); +	if (skb_headroom(skb) < needed_headroom && +	    pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) +		goto out_free_skb; -	if (status->band == IEEE80211_BAND_5GHZ) -		rthdr->chan_flags = cpu_to_le16(IEEE80211_CHAN_OFDM | -						IEEE80211_CHAN_5GHZ); -	else -		rthdr->chan_flags = cpu_to_le16(IEEE80211_CHAN_DYN | -						IEEE80211_CHAN_2GHZ); +	/* prepend radiotap information */ +	ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom, +					 false);  	skb_set_mac_header(skb, 0);  	skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -2413,7 +2916,8 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,  		status = IEEE80211_SKB_RXCB((rx->skb));  		sband = rx->local->hw.wiphy->bands[status->band]; -		if (!(status->flag & RX_FLAG_HT)) +		if (!(status->flag & RX_FLAG_HT) && +		    !(status->flag & RX_FLAG_VHT))  			rate = &sband->bitrates[status->rate_idx];  		ieee80211_rx_cooked_monitor(rx, rate); @@ -2444,6 +2948,8 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,  			goto rxh_next;  \  	} while (0); +	spin_lock_bh(&rx->local->rx_path_lock); +  	while ((skb = __skb_dequeue(frames))) {  		/*  		 * all the other fields are valid across frames @@ -2451,21 +2957,19 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,  		 * same TID from the same station  		 */  		rx->skb = skb; -		rx->flags = 0; -		CALL_RXH(ieee80211_rx_h_decrypt)  		CALL_RXH(ieee80211_rx_h_check_more_data) +		CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll)  		CALL_RXH(ieee80211_rx_h_sta_process) +		CALL_RXH(ieee80211_rx_h_decrypt)  		CALL_RXH(ieee80211_rx_h_defragment) -		CALL_RXH(ieee80211_rx_h_ps_poll)  		CALL_RXH(ieee80211_rx_h_michael_mic_verify)  		/* must be after MMIC verify so header is counted in MPDU mic */ -		CALL_RXH(ieee80211_rx_h_remove_qos_control) -		CALL_RXH(ieee80211_rx_h_amsdu)  #ifdef CONFIG_MAC80211_MESH  		if (ieee80211_vif_is_mesh(&rx->sdata->vif))  			CALL_RXH(ieee80211_rx_h_mesh_fwding);  #endif +		CALL_RXH(ieee80211_rx_h_amsdu)  		CALL_RXH(ieee80211_rx_h_data)  		/* special treatment -- needs the queue */ @@ -2484,6 +2988,8 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,  #undef CALL_RXH  	} + +	spin_unlock_bh(&rx->local->rx_path_lock);  }  static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx) @@ -2500,7 +3006,6 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)  			goto rxh_next;  \  	} while (0); -	CALL_RXH(ieee80211_rx_h_passive_scan)  	CALL_RXH(ieee80211_rx_h_check)  	ieee80211_rx_reorder_ampdu(rx, &reorder_release); @@ -2515,9 +3020,8 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)  }  /* - * This function makes calls into the RX path. Therefore the - * caller must hold the sta_info->lock and everything has to - * be under rcu_read_lock protection as well. + * This function makes calls into the RX path, therefore + * it has to be invoked under RCU read lock.   */  void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)  { @@ -2526,7 +3030,10 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)  		.sta = sta,  		.sdata = sta->sdata,  		.local = sta->local, -		.queue = tid, +		/* This is OK -- must be QoS data frame */ +		.security_idx = tid, +		.seqno_idx = tid, +		.flags = 0,  	};  	struct tid_ampdu_rx *tid_agg_rx; @@ -2537,7 +3044,7 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)  	__skb_queue_head_init(&frames);  	spin_lock(&tid_agg_rx->reorder_lock); -	ieee80211_sta_reorder_release(&sta->local->hw, tid_agg_rx, &frames); +	ieee80211_sta_reorder_release(sta->sdata, tid_agg_rx, &frames);  	spin_unlock(&tid_agg_rx->reorder_lock);  	ieee80211_rx_handlers(&rx, &frames); @@ -2545,8 +3052,8 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)  /* main receive path */ -static int prepare_for_handlers(struct ieee80211_rx_data *rx, -				struct ieee80211_hdr *hdr) +static bool prepare_for_handlers(struct ieee80211_rx_data *rx, +				 struct ieee80211_hdr *hdr)  {  	struct ieee80211_sub_if_data *sdata = rx->sdata;  	struct sk_buff *skb = rx->skb; @@ -2557,46 +3064,45 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,  	switch (sdata->vif.type) {  	case NL80211_IFTYPE_STATION:  		if (!bssid && !sdata->u.mgd.use_4addr) -			return 0; +			return false;  		if (!multicast && -		    compare_ether_addr(sdata->vif.addr, hdr->addr1) != 0) { -			if (!(sdata->dev->flags & IFF_PROMISC)) -				return 0; +		    !ether_addr_equal(sdata->vif.addr, hdr->addr1)) { +			if (!(sdata->dev->flags & IFF_PROMISC) || +			    sdata->u.mgd.use_4addr) +				return false;  			status->rx_flags &= ~IEEE80211_RX_RA_MATCH;  		}  		break;  	case NL80211_IFTYPE_ADHOC:  		if (!bssid) -			return 0; +			return false; +		if (ether_addr_equal(sdata->vif.addr, hdr->addr2) || +		    ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2)) +			return false;  		if (ieee80211_is_beacon(hdr->frame_control)) { -			return 1; -		} -		else if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid)) { -			if (!(status->rx_flags & IEEE80211_RX_IN_SCAN)) -				return 0; -			status->rx_flags &= ~IEEE80211_RX_RA_MATCH; +			return true; +		} else if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid)) { +			return false;  		} else if (!multicast && -			   compare_ether_addr(sdata->vif.addr, -					      hdr->addr1) != 0) { +			   !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {  			if (!(sdata->dev->flags & IFF_PROMISC)) -				return 0; +				return false;  			status->rx_flags &= ~IEEE80211_RX_RA_MATCH;  		} else if (!rx->sta) {  			int rate_idx; -			if (status->flag & RX_FLAG_HT) -				rate_idx = 0; /* TODO: HT rates */ +			if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) +				rate_idx = 0; /* TODO: HT/VHT rates */  			else  				rate_idx = status->rate_idx; -			rx->sta = ieee80211_ibss_add_sta(sdata, bssid, -					hdr->addr2, BIT(rate_idx), GFP_ATOMIC); +			ieee80211_ibss_rx_no_sta(sdata, bssid, hdr->addr2, +						 BIT(rate_idx));  		}  		break;  	case NL80211_IFTYPE_MESH_POINT:  		if (!multicast && -		    compare_ether_addr(sdata->vif.addr, -				       hdr->addr1) != 0) { +		    !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {  			if (!(sdata->dev->flags & IFF_PROMISC)) -				return 0; +				return false;  			status->rx_flags &= ~IEEE80211_RX_RA_MATCH;  		} @@ -2604,29 +3110,48 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,  	case NL80211_IFTYPE_AP_VLAN:  	case NL80211_IFTYPE_AP:  		if (!bssid) { -			if (compare_ether_addr(sdata->vif.addr, -					       hdr->addr1)) -				return 0; -		} else if (!ieee80211_bssid_match(bssid, -					sdata->vif.addr)) { -			if (!(status->rx_flags & IEEE80211_RX_IN_SCAN)) -				return 0; +			if (!ether_addr_equal(sdata->vif.addr, hdr->addr1)) +				return false; +		} else if (!ieee80211_bssid_match(bssid, sdata->vif.addr)) { +			/* +			 * Accept public action frames even when the +			 * BSSID doesn't match, this is used for P2P +			 * and location updates. Note that mac80211 +			 * itself never looks at these frames. +			 */ +			if (!multicast && +			    !ether_addr_equal(sdata->vif.addr, hdr->addr1)) +				return false; +			if (ieee80211_is_public_action(hdr, skb->len)) +				return true; +			if (!ieee80211_is_beacon(hdr->frame_control)) +				return false;  			status->rx_flags &= ~IEEE80211_RX_RA_MATCH;  		}  		break;  	case NL80211_IFTYPE_WDS:  		if (bssid || !ieee80211_is_data(hdr->frame_control)) -			return 0; -		if (compare_ether_addr(sdata->u.wds.remote_addr, hdr->addr2)) -			return 0; +			return false; +		if (!ether_addr_equal(sdata->u.wds.remote_addr, hdr->addr2)) +			return false; +		break; +	case NL80211_IFTYPE_P2P_DEVICE: +		if (!ieee80211_is_public_action(hdr, skb->len) && +		    !ieee80211_is_probe_req(hdr->frame_control) && +		    !ieee80211_is_probe_resp(hdr->frame_control) && +		    !ieee80211_is_beacon(hdr->frame_control)) +			return false; +		if (!ether_addr_equal(sdata->vif.addr, hdr->addr1) && +		    !multicast) +			status->rx_flags &= ~IEEE80211_RX_RA_MATCH;  		break;  	default:  		/* should never get here */ -		WARN_ON(1); +		WARN_ON_ONCE(1);  		break;  	} -	return 1; +	return true;  }  /* @@ -2642,27 +3167,19 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,  	struct ieee80211_sub_if_data *sdata = rx->sdata;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);  	struct ieee80211_hdr *hdr = (void *)skb->data; -	int prepares;  	rx->skb = skb;  	status->rx_flags |= IEEE80211_RX_RA_MATCH; -	prepares = prepare_for_handlers(rx, hdr); -	if (!prepares) +	if (!prepare_for_handlers(rx, hdr))  		return false; -	if (status->flag & RX_FLAG_MMIC_ERROR) { -		if (status->rx_flags & IEEE80211_RX_RA_MATCH) -			ieee80211_rx_michael_mic_report(hdr, rx); -		return false; -	} -  	if (!consume) {  		skb = skb_copy(skb, GFP_ATOMIC);  		if (!skb) {  			if (net_ratelimit())  				wiphy_debug(local->hw.wiphy, -					"failed to copy multicast frame for %s\n", +					"failed to copy skb for %s\n",  					sdata->name);  			return true;  		} @@ -2675,13 +3192,12 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,  }  /* - * This is the actual Rx frames handler. as it blongs to Rx path it must + * This is the actual Rx frames handler. as it belongs to Rx path it must   * be called with rcu_read_lock protection.   */  static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,  					 struct sk_buff *skb)  { -	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);  	struct ieee80211_local *local = hw_to_local(hw);  	struct ieee80211_sub_if_data *sdata;  	struct ieee80211_hdr *hdr; @@ -2699,14 +3215,15 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,  	if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))  		local->dot11ReceivedFragmentCount++; -	if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) || -		     test_bit(SCAN_OFF_CHANNEL, &local->scanning))) -		status->rx_flags |= IEEE80211_RX_IN_SCAN; - -	if (ieee80211_is_mgmt(fc)) -		err = skb_linearize(skb); -	else +	if (ieee80211_is_mgmt(fc)) { +		/* drop frame if too short for header */ +		if (skb->len < ieee80211_hdrlen(fc)) +			err = -ENOBUFS; +		else +			err = skb_linearize(skb); +	} else {  		err = !pskb_may_pull(skb, ieee80211_hdrlen(fc)); +	}  	if (err) {  		dev_kfree_skb(skb); @@ -2717,6 +3234,10 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,  	ieee80211_parse_qos(&rx);  	ieee80211_verify_alignment(&rx); +	if (unlikely(ieee80211_is_probe_resp(hdr->frame_control) || +		     ieee80211_is_beacon(hdr->frame_control))) +		ieee80211_scan_rx(local, skb); +  	if (ieee80211_is_data(fc)) {  		prev_sta = NULL; @@ -2739,6 +3260,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,  			if (ieee80211_prepare_and_rx_handle(&rx, skb, true))  				return; +			goto out;  		}  	} @@ -2778,6 +3300,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,  			return;  	} + out:  	dev_kfree_skb(skb);  } @@ -2794,8 +3317,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)  	WARN_ON_ONCE(softirq_count() == 0); -	if (WARN_ON(status->band < 0 || -		    status->band >= IEEE80211_NUM_BANDS)) +	if (WARN_ON(status->band >= IEEE80211_NUM_BANDS))  		goto drop;  	sband = local->hw.wiphy->bands[status->band]; @@ -2812,6 +3334,10 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)  	if (unlikely(local->quiescing || local->suspended))  		goto drop; +	/* We might be during a HW reconfig, prevent Rx for the same reason */ +	if (unlikely(local->in_reconfig)) +		goto drop; +  	/*  	 * The same happens when we're not even started,  	 * but that's worth a warning. @@ -2836,17 +3362,22 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)  			 * hardware error. The driver should catch hardware  			 * errors.  			 */ -			if (WARN((status->rate_idx < 0 || -				 status->rate_idx > 76), +			if (WARN(status->rate_idx > 76,  				 "Rate marked as an HT rate but passed "  				 "status->rate_idx is not "  				 "an MCS index [0-76]: %d (0x%02x)\n",  				 status->rate_idx,  				 status->rate_idx))  				goto drop; +		} else if (status->flag & RX_FLAG_VHT) { +			if (WARN_ONCE(status->rate_idx > 9 || +				      !status->vht_nss || +				      status->vht_nss > 8, +				      "Rate marked as a VHT rate but data is invalid: MCS: %d, NSS: %d\n", +				      status->rate_idx, status->vht_nss)) +				goto drop;  		} else { -			if (WARN_ON(status->rate_idx < 0 || -				    status->rate_idx >= sband->n_bitrates)) +			if (WARN_ON(status->rate_idx >= sband->n_bitrates))  				goto drop;  			rate = &sband->bitrates[status->rate_idx];  		} @@ -2873,6 +3404,9 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)  		return;  	} +	ieee80211_tpt_led_trig_rx(local, +			((struct ieee80211_hdr *)skb->data)->frame_control, +			skb->len);  	__ieee80211_rx_handle_packet(hw, skb);  	rcu_read_unlock(); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index fb274db77e3..f40661eb75b 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -13,10 +13,12 @@   */  #include <linux/if_arp.h> +#include <linux/etherdevice.h>  #include <linux/rtnetlink.h> -#include <linux/pm_qos_params.h> +#include <linux/pm_qos.h>  #include <net/sch_generic.h>  #include <linux/slab.h> +#include <linux/export.h>  #include <net/mac80211.h>  #include "ieee80211_i.h" @@ -25,36 +27,15 @@  #define IEEE80211_PROBE_DELAY (HZ / 33)  #define IEEE80211_CHANNEL_TIME (HZ / 33) -#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 8) - -struct ieee80211_bss * -ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, -		     u8 *ssid, u8 ssid_len) -{ -	struct cfg80211_bss *cbss; - -	cbss = cfg80211_get_bss(local->hw.wiphy, -				ieee80211_get_channel(local->hw.wiphy, freq), -				bssid, ssid, ssid_len, 0, 0); -	if (!cbss) -		return NULL; -	return (void *)cbss->priv; -} - -static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss) -{ -	struct ieee80211_bss *bss = (void *)cbss->priv; - -	kfree(bss_mesh_id(bss)); -	kfree(bss_mesh_cfg(bss)); -} +#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 9)  void ieee80211_rx_bss_put(struct ieee80211_local *local,  			  struct ieee80211_bss *bss)  {  	if (!bss)  		return; -	cfg80211_put_bss(container_of((void *)bss, struct cfg80211_bss, priv)); +	cfg80211_put_bss(local->hw.wiphy, +			 container_of((void *)bss, struct cfg80211_bss, priv));  }  static bool is_uapsd_supported(struct ieee802_11_elems *elems) @@ -77,15 +58,15 @@ static bool is_uapsd_supported(struct ieee802_11_elems *elems)  struct ieee80211_bss *  ieee80211_bss_info_update(struct ieee80211_local *local,  			  struct ieee80211_rx_status *rx_status, -			  struct ieee80211_mgmt *mgmt, -			  size_t len, +			  struct ieee80211_mgmt *mgmt, size_t len,  			  struct ieee802_11_elems *elems, -			  struct ieee80211_channel *channel, -			  bool beacon) +			  struct ieee80211_channel *channel)  { +	bool beacon = ieee80211_is_beacon(mgmt->frame_control);  	struct cfg80211_bss *cbss;  	struct ieee80211_bss *bss;  	int clen, srlen; +	enum nl80211_bss_scan_width scan_width;  	s32 signal = 0;  	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) @@ -93,135 +74,173 @@ ieee80211_bss_info_update(struct ieee80211_local *local,  	else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)  		signal = (rx_status->signal * 100) / local->hw.max_signal; -	cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel, -					 mgmt, len, signal, GFP_ATOMIC); +	scan_width = NL80211_BSS_CHAN_WIDTH_20; +	if (rx_status->flag & RX_FLAG_5MHZ) +		scan_width = NL80211_BSS_CHAN_WIDTH_5; +	if (rx_status->flag & RX_FLAG_10MHZ) +		scan_width = NL80211_BSS_CHAN_WIDTH_10; +	cbss = cfg80211_inform_bss_width_frame(local->hw.wiphy, channel, +					       scan_width, mgmt, len, signal, +					       GFP_ATOMIC);  	if (!cbss)  		return NULL; -	cbss->free_priv = ieee80211_rx_bss_free;  	bss = (void *)cbss->priv; -	/* save the ERP value so that it is available at association time */ -	if (elems->erp_info && elems->erp_info_len >= 1) { -		bss->erp_value = elems->erp_info[0]; -		bss->has_erp_value = 1; -	} +	if (beacon) +		bss->device_ts_beacon = rx_status->device_timestamp; +	else +		bss->device_ts_presp = rx_status->device_timestamp; -	if (elems->tim) { -		struct ieee80211_tim_ie *tim_ie = -			(struct ieee80211_tim_ie *)elems->tim; -		bss->dtim_period = tim_ie->dtim_period; +	if (elems->parse_error) { +		if (beacon) +			bss->corrupt_data |= IEEE80211_BSS_CORRUPT_BEACON; +		else +			bss->corrupt_data |= IEEE80211_BSS_CORRUPT_PROBE_RESP; +	} else { +		if (beacon) +			bss->corrupt_data &= ~IEEE80211_BSS_CORRUPT_BEACON; +		else +			bss->corrupt_data &= ~IEEE80211_BSS_CORRUPT_PROBE_RESP;  	} -	/* If the beacon had no TIM IE, or it was invalid, use 1 */ -	if (beacon && !bss->dtim_period) -		bss->dtim_period = 1; +	/* save the ERP value so that it is available at association time */ +	if (elems->erp_info && (!elems->parse_error || +				!(bss->valid_data & IEEE80211_BSS_VALID_ERP))) { +		bss->erp_value = elems->erp_info[0]; +		bss->has_erp_value = true; +		if (!elems->parse_error) +			bss->valid_data |= IEEE80211_BSS_VALID_ERP; +	}  	/* replace old supported rates if we get new values */ -	srlen = 0; -	if (elems->supp_rates) { -		clen = IEEE80211_MAX_SUPP_RATES; -		if (clen > elems->supp_rates_len) -			clen = elems->supp_rates_len; -		memcpy(bss->supp_rates, elems->supp_rates, clen); -		srlen += clen; -	} -	if (elems->ext_supp_rates) { -		clen = IEEE80211_MAX_SUPP_RATES - srlen; -		if (clen > elems->ext_supp_rates_len) -			clen = elems->ext_supp_rates_len; -		memcpy(bss->supp_rates + srlen, elems->ext_supp_rates, clen); -		srlen += clen; +	if (!elems->parse_error || +	    !(bss->valid_data & IEEE80211_BSS_VALID_RATES)) { +		srlen = 0; +		if (elems->supp_rates) { +			clen = IEEE80211_MAX_SUPP_RATES; +			if (clen > elems->supp_rates_len) +				clen = elems->supp_rates_len; +			memcpy(bss->supp_rates, elems->supp_rates, clen); +			srlen += clen; +		} +		if (elems->ext_supp_rates) { +			clen = IEEE80211_MAX_SUPP_RATES - srlen; +			if (clen > elems->ext_supp_rates_len) +				clen = elems->ext_supp_rates_len; +			memcpy(bss->supp_rates + srlen, elems->ext_supp_rates, +			       clen); +			srlen += clen; +		} +		if (srlen) { +			bss->supp_rates_len = srlen; +			if (!elems->parse_error) +				bss->valid_data |= IEEE80211_BSS_VALID_RATES; +		}  	} -	if (srlen) -		bss->supp_rates_len = srlen; -	bss->wmm_used = elems->wmm_param || elems->wmm_info; -	bss->uapsd_supported = is_uapsd_supported(elems); +	if (!elems->parse_error || +	    !(bss->valid_data & IEEE80211_BSS_VALID_WMM)) { +		bss->wmm_used = elems->wmm_param || elems->wmm_info; +		bss->uapsd_supported = is_uapsd_supported(elems); +		if (!elems->parse_error) +			bss->valid_data |= IEEE80211_BSS_VALID_WMM; +	} -	if (!beacon) -		bss->last_probe_resp = jiffies; +	if (beacon) { +		struct ieee80211_supported_band *sband = +			local->hw.wiphy->bands[rx_status->band]; +		if (!(rx_status->flag & RX_FLAG_HT) && +		    !(rx_status->flag & RX_FLAG_VHT)) +			bss->beacon_rate = +				&sband->bitrates[rx_status->rate_idx]; +	}  	return bss;  } -ieee80211_rx_result -ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) +void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)  {  	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); -	struct ieee80211_mgmt *mgmt; +	struct ieee80211_sub_if_data *sdata1, *sdata2; +	struct ieee80211_mgmt *mgmt = (void *)skb->data;  	struct ieee80211_bss *bss;  	u8 *elements;  	struct ieee80211_channel *channel;  	size_t baselen; -	int freq; -	__le16 fc; -	bool presp, beacon = false;  	struct ieee802_11_elems elems; -	if (skb->len < 2) -		return RX_DROP_UNUSABLE; - -	mgmt = (struct ieee80211_mgmt *) skb->data; -	fc = mgmt->frame_control; +	if (skb->len < 24 || +	    (!ieee80211_is_probe_resp(mgmt->frame_control) && +	     !ieee80211_is_beacon(mgmt->frame_control))) +		return; -	if (ieee80211_is_ctl(fc)) -		return RX_CONTINUE; +	sdata1 = rcu_dereference(local->scan_sdata); +	sdata2 = rcu_dereference(local->sched_scan_sdata); -	if (skb->len < 24) -		return RX_DROP_MONITOR; +	if (likely(!sdata1 && !sdata2)) +		return; -	presp = ieee80211_is_probe_resp(fc); -	if (presp) { +	if (ieee80211_is_probe_resp(mgmt->frame_control)) {  		/* ignore ProbeResp to foreign address */ -		if (memcmp(mgmt->da, sdata->vif.addr, ETH_ALEN)) -			return RX_DROP_MONITOR; +		if ((!sdata1 || !ether_addr_equal(mgmt->da, sdata1->vif.addr)) && +		    (!sdata2 || !ether_addr_equal(mgmt->da, sdata2->vif.addr))) +			return; -		presp = true;  		elements = mgmt->u.probe_resp.variable;  		baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);  	} else { -		beacon = ieee80211_is_beacon(fc);  		baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);  		elements = mgmt->u.beacon.variable;  	} -	if (!presp && !beacon) -		return RX_CONTINUE; -  	if (baselen > skb->len) -		return RX_DROP_MONITOR; - -	ieee802_11_parse_elems(elements, skb->len - baselen, &elems); +		return; -	if (elems.ds_params && elems.ds_params_len == 1) -		freq = ieee80211_channel_to_frequency(elems.ds_params[0]); -	else -		freq = rx_status->freq; +	ieee802_11_parse_elems(elements, skb->len - baselen, false, &elems); -	channel = ieee80211_get_channel(sdata->local->hw.wiphy, freq); +	channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);  	if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) -		return RX_DROP_MONITOR; +		return; -	bss = ieee80211_bss_info_update(sdata->local, rx_status, +	bss = ieee80211_bss_info_update(local, rx_status,  					mgmt, skb->len, &elems, -					channel, beacon); +					channel);  	if (bss) -		ieee80211_rx_bss_put(sdata->local, bss); +		ieee80211_rx_bss_put(local, bss); +} -	dev_kfree_skb(skb); -	return RX_QUEUED; +static void +ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef, +			       enum nl80211_bss_scan_width scan_width) +{ +	memset(chandef, 0, sizeof(*chandef)); +	switch (scan_width) { +	case NL80211_BSS_CHAN_WIDTH_5: +		chandef->width = NL80211_CHAN_WIDTH_5; +		break; +	case NL80211_BSS_CHAN_WIDTH_10: +		chandef->width = NL80211_CHAN_WIDTH_10; +		break; +	default: +		chandef->width = NL80211_CHAN_WIDTH_20_NOHT; +		break; +	}  }  /* return false if no more work */  static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)  {  	struct cfg80211_scan_request *req = local->scan_req; +	struct cfg80211_chan_def chandef;  	enum ieee80211_band band;  	int i, ielen, n_chans; +	if (test_bit(SCAN_HW_CANCELLED, &local->scanning)) +		return false; +  	do {  		if (local->hw_scan_band == IEEE80211_NUM_BANDS)  			return false; @@ -240,19 +259,23 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)  	} while (!n_chans);  	local->hw_scan_req->n_channels = n_chans; +	ieee80211_prepare_scan_chandef(&chandef, req->scan_width);  	ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, -					 req->ie, req->ie_len, band, (u32) -1, -					 0); +					 local->hw_scan_ies_bufsize, +					 req->ie, req->ie_len, band, +					 req->rates[band], &chandef);  	local->hw_scan_req->ie_len = ielen; +	local->hw_scan_req->no_cck = req->no_cck;  	return true;  } -static bool __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, -				       bool was_hw_scan) +static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)  {  	struct ieee80211_local *local = hw_to_local(hw); +	bool hw_scan = local->ops->hw_scan; +	bool was_scanning = local->scanning;  	lockdep_assert_held(&local->mtx); @@ -266,12 +289,18 @@ static bool __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,  		aborted = true;  	if (WARN_ON(!local->scan_req)) -		return false; +		return; + +	if (hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { +		int rc; + +		rc = drv_hw_scan(local, +			rcu_dereference_protected(local->scan_sdata, +						  lockdep_is_held(&local->mtx)), +			local->hw_scan_req); -	if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { -		int rc = drv_hw_scan(local, local->scan_sdata, local->hw_scan_req);  		if (rc == 0) -			return false; +			return;  	}  	kfree(local->hw_scan_req); @@ -280,34 +309,27 @@ static bool __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,  	if (local->scan_req != local->int_scan_req)  		cfg80211_scan_done(local->scan_req, aborted);  	local->scan_req = NULL; -	local->scan_sdata = NULL; +	RCU_INIT_POINTER(local->scan_sdata, NULL);  	local->scanning = 0; -	local->scan_channel = NULL; +	local->scan_chandef.chan = NULL; -	return true; -} +	/* Set power back to normal operating levels. */ +	ieee80211_hw_config(local, 0); -static void __ieee80211_scan_completed_finish(struct ieee80211_hw *hw, -					      bool was_hw_scan) -{ -	struct ieee80211_local *local = hw_to_local(hw); - -	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); -	if (!was_hw_scan) { +	if (!hw_scan) {  		ieee80211_configure_filter(local);  		drv_sw_scan_complete(local); -		ieee80211_offchannel_return(local, true); +		ieee80211_offchannel_return(local);  	} -	mutex_lock(&local->mtx);  	ieee80211_recalc_idle(local); -	mutex_unlock(&local->mtx);  	ieee80211_mlme_notify_scan_completed(local);  	ieee80211_ibss_notify_scan_completed(local);  	ieee80211_mesh_notify_scan_completed(local); -	ieee80211_queue_work(&local->hw, &local->work_work); +	if (was_scanning) +		ieee80211_start_next_roc(local);  }  void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) @@ -325,6 +347,10 @@ EXPORT_SYMBOL(ieee80211_scan_completed);  static int ieee80211_start_sw_scan(struct ieee80211_local *local)  { +	/* Software scan is not supported in multi-channel cases */ +	if (local->use_chanctx) +		return -EOPNOTSUPP; +  	/*  	 * Hardware/driver doesn't support hw_scan, so use software  	 * scanning instead. First send a nullfunc frame with power save @@ -340,23 +366,90 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)  	 */  	drv_sw_scan_start(local); -	ieee80211_offchannel_stop_beaconing(local); - -	local->leave_oper_channel_time = 0; +	local->leave_oper_channel_time = jiffies;  	local->next_scan_state = SCAN_DECISION;  	local->scan_channel_idx = 0; -	drv_flush(local, false); +	ieee80211_offchannel_stop_vifs(local); + +	/* ensure nullfunc is transmitted before leaving operating channel */ +	ieee80211_flush_queues(local, NULL);  	ieee80211_configure_filter(local); +	/* We need to set power level at maximum rate for scanning. */ +	ieee80211_hw_config(local, 0); +  	ieee80211_queue_delayed_work(&local->hw, -				     &local->scan_work, -				     IEEE80211_CHANNEL_TIME); +				     &local->scan_work, 0);  	return 0;  } +static bool ieee80211_can_scan(struct ieee80211_local *local, +			       struct ieee80211_sub_if_data *sdata) +{ +	if (local->radar_detect_enabled) +		return false; + +	if (!list_empty(&local->roc_list)) +		return false; + +	if (sdata->vif.type == NL80211_IFTYPE_STATION && +	    sdata->u.mgd.flags & IEEE80211_STA_CONNECTION_POLL) +		return false; + +	return true; +} + +void ieee80211_run_deferred_scan(struct ieee80211_local *local) +{ +	lockdep_assert_held(&local->mtx); + +	if (!local->scan_req || local->scanning) +		return; + +	if (!ieee80211_can_scan(local, +				rcu_dereference_protected( +					local->scan_sdata, +					lockdep_is_held(&local->mtx)))) +		return; + +	ieee80211_queue_delayed_work(&local->hw, &local->scan_work, +				     round_jiffies_relative(0)); +} + +static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, +					    unsigned long *next_delay) +{ +	int i; +	struct ieee80211_sub_if_data *sdata; +	enum ieee80211_band band = local->hw.conf.chandef.chan->band; +	u32 tx_flags; + +	tx_flags = IEEE80211_TX_INTFL_OFFCHAN_TX_OK; +	if (local->scan_req->no_cck) +		tx_flags |= IEEE80211_TX_CTL_NO_CCK_RATE; + +	sdata = rcu_dereference_protected(local->scan_sdata, +					  lockdep_is_held(&local->mtx)); + +	for (i = 0; i < local->scan_req->n_ssids; i++) +		ieee80211_send_probe_req( +			sdata, NULL, +			local->scan_req->ssids[i].ssid, +			local->scan_req->ssids[i].ssid_len, +			local->scan_req->ie, local->scan_req->ie_len, +			local->scan_req->rates[band], false, +			tx_flags, local->hw.conf.chandef.chan, true); + +	/* +	 * After sending probe requests, wait for probe responses +	 * on the channel. +	 */ +	*next_delay = IEEE80211_CHANNEL_TIME; +	local->next_scan_state = SCAN_DECISION; +}  static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,  				  struct cfg80211_scan_request *req) @@ -369,21 +462,21 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,  	if (local->scan_req)  		return -EBUSY; -	if (!list_empty(&local->work_list)) { +	if (!ieee80211_can_scan(local, sdata)) {  		/* wait for the work to finish/time out */  		local->scan_req = req; -		local->scan_sdata = sdata; +		rcu_assign_pointer(local->scan_sdata, sdata);  		return 0;  	}  	if (local->ops->hw_scan) {  		u8 *ies; +		local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len;  		local->hw_scan_req = kmalloc(  				sizeof(*local->hw_scan_req) +  				req->n_channels * sizeof(req->channels[0]) + -				2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len + -				req->ie_len, GFP_KERNEL); +				local->hw_scan_ies_bufsize, GFP_KERNEL);  		if (!local->hw_scan_req)  			return -ENOMEM; @@ -393,6 +486,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,  			sizeof(*local->hw_scan_req) +  			req->n_channels * sizeof(req->channels[0]);  		local->hw_scan_req->ie = ies; +		local->hw_scan_req->flags = req->flags;  		local->hw_scan_band = 0; @@ -406,12 +500,48 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,  	}  	local->scan_req = req; -	local->scan_sdata = sdata; +	rcu_assign_pointer(local->scan_sdata, sdata); -	if (local->ops->hw_scan) +	if (local->ops->hw_scan) {  		__set_bit(SCAN_HW_SCANNING, &local->scanning); -	else +	} else if ((req->n_channels == 1) && +		   (req->channels[0] == local->_oper_chandef.chan)) { +		/* +		 * If we are scanning only on the operating channel +		 * then we do not need to stop normal activities +		 */ +		unsigned long next_delay; + +		__set_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning); + +		ieee80211_recalc_idle(local); + +		/* Notify driver scan is starting, keep order of operations +		 * same as normal software scan, in case that matters. */ +		drv_sw_scan_start(local); + +		ieee80211_configure_filter(local); /* accept probe-responses */ + +		/* We need to ensure power level is at max for scanning. */ +		ieee80211_hw_config(local, 0); + +		if ((req->channels[0]->flags & +		     IEEE80211_CHAN_NO_IR) || +		    !local->scan_req->n_ssids) { +			next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; +		} else { +			ieee80211_scan_state_send_probe(local, &next_delay); +			next_delay = IEEE80211_CHANNEL_TIME; +		} + +		/* Now, just wait a bit and we are all done! */ +		ieee80211_queue_delayed_work(&local->hw, &local->scan_work, +					     next_delay); +		return 0; +	} else { +		/* Do normal software scan */  		__set_bit(SCAN_SW_SCANNING, &local->scanning); +	}  	ieee80211_recalc_idle(local); @@ -429,7 +559,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,  		ieee80211_recalc_idle(local);  		local->scan_req = NULL; -		local->scan_sdata = NULL; +		RCU_INIT_POINTER(local->scan_sdata, NULL);  	}  	return rc; @@ -442,7 +572,7 @@ ieee80211_scan_get_channel_time(struct ieee80211_channel *chan)  	 * TODO: channel switching also consumes quite some time,  	 * add that delay as well to get a better estimation  	 */ -	if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) +	if (chan->flags & IEEE80211_CHAN_NO_IR)  		return IEEE80211_PASSIVE_CHANNEL_TIME;  	return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME;  } @@ -453,10 +583,9 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,  	bool associated = false;  	bool tx_empty = true;  	bool bad_latency; -	bool listen_int_exceeded; -	unsigned long min_beacon_int = 0;  	struct ieee80211_sub_if_data *sdata;  	struct ieee80211_channel *next_chan; +	enum mac80211_scan_state next_scan_state;  	/*  	 * check if at least one STA interface is associated, @@ -472,11 +601,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,  			if (sdata->u.mgd.associated) {  				associated = true; -				if (sdata->vif.bss_conf.beacon_int < -				    min_beacon_int || min_beacon_int == 0) -					min_beacon_int = -						sdata->vif.bss_conf.beacon_int; -  				if (!qdisc_all_tx_empty(sdata->dev)) {  					tx_empty = false;  					break; @@ -486,91 +610,34 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,  	}  	mutex_unlock(&local->iflist_mtx); -	if (local->scan_channel) { -		/* -		 * we're currently scanning a different channel, let's -		 * see if we can scan another channel without interfering -		 * with the current traffic situation. -		 * -		 * Since we don't know if the AP has pending frames for us -		 * we can only check for our tx queues and use the current -		 * pm_qos requirements for rx. Hence, if no tx traffic occurs -		 * at all we will scan as many channels in a row as the pm_qos -		 * latency allows us to. Additionally we also check for the -		 * currently negotiated listen interval to prevent losing -		 * frames unnecessarily. -		 * -		 * Otherwise switch back to the operating channel. -		 */ -		next_chan = local->scan_req->channels[local->scan_channel_idx]; - -		bad_latency = time_after(jiffies + -				ieee80211_scan_get_channel_time(next_chan), -				local->leave_oper_channel_time + -				usecs_to_jiffies(pm_qos_request(PM_QOS_NETWORK_LATENCY))); - -		listen_int_exceeded = time_after(jiffies + -				ieee80211_scan_get_channel_time(next_chan), -				local->leave_oper_channel_time + -				usecs_to_jiffies(min_beacon_int * 1024) * -				local->hw.conf.listen_interval); - -		if (associated && ( !tx_empty || bad_latency || -		    listen_int_exceeded)) -			local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; -		else -			local->next_scan_state = SCAN_SET_CHANNEL; -	} else { -		/* -		 * we're on the operating channel currently, let's -		 * leave that channel now to scan another one -		 */ -		local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL; -	} - -	*next_delay = 0; -} - -static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, -						    unsigned long *next_delay) -{ -	ieee80211_offchannel_stop_station(local); - -	__set_bit(SCAN_OFF_CHANNEL, &local->scanning); +	next_chan = local->scan_req->channels[local->scan_channel_idx];  	/* -	 * What if the nullfunc frames didn't arrive? +	 * we're currently scanning a different channel, let's +	 * see if we can scan another channel without interfering +	 * with the current traffic situation. +	 * +	 * Keep good latency, do not stay off-channel more than 125 ms.  	 */ -	drv_flush(local, false); -	if (local->ops->flush) -		*next_delay = 0; -	else -		*next_delay = HZ / 10; -	/* remember when we left the operating channel */ -	local->leave_oper_channel_time = jiffies; - -	/* advance to the next channel to be scanned */ -	local->next_scan_state = SCAN_SET_CHANNEL; -} - -static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local, -						    unsigned long *next_delay) -{ -	/* switch back to the operating channel */ -	local->scan_channel = NULL; -	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); +	bad_latency = time_after(jiffies + +				 ieee80211_scan_get_channel_time(next_chan), +				 local->leave_oper_channel_time + HZ / 8); -	/* -	 * Only re-enable station mode interface now; beaconing will be -	 * re-enabled once the full scan has been completed. -	 */ -	ieee80211_offchannel_return(local, false); +	if (associated && !tx_empty) { +		if (local->scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) +			next_scan_state = SCAN_ABORT; +		else +			next_scan_state = SCAN_SUSPEND; +	} else if (associated && bad_latency) { +		next_scan_state = SCAN_SUSPEND; +	} else { +		next_scan_state = SCAN_SET_CHANNEL; +	} -	__clear_bit(SCAN_OFF_CHANNEL, &local->scanning); +	local->next_scan_state = next_scan_state; -	*next_delay = HZ / 5; -	local->next_scan_state = SCAN_DECISION; +	*next_delay = 0;  }  static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, @@ -578,11 +645,35 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,  {  	int skip;  	struct ieee80211_channel *chan; +	enum nl80211_bss_scan_width oper_scan_width;  	skip = 0;  	chan = local->scan_req->channels[local->scan_channel_idx]; -	local->scan_channel = chan; +	local->scan_chandef.chan = chan; +	local->scan_chandef.center_freq1 = chan->center_freq; +	local->scan_chandef.center_freq2 = 0; +	switch (local->scan_req->scan_width) { +	case NL80211_BSS_CHAN_WIDTH_5: +		local->scan_chandef.width = NL80211_CHAN_WIDTH_5; +		break; +	case NL80211_BSS_CHAN_WIDTH_10: +		local->scan_chandef.width = NL80211_CHAN_WIDTH_10; +		break; +	case NL80211_BSS_CHAN_WIDTH_20: +		/* If scanning on oper channel, use whatever channel-type +		 * is currently in use. +		 */ +		oper_scan_width = cfg80211_chandef_to_scan_width( +					&local->_oper_chandef); +		if (chan == local->_oper_chandef.chan && +		    oper_scan_width == local->scan_req->scan_width) +			local->scan_chandef = local->_oper_chandef; +		else +			local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT; +		break; +	} +  	if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))  		skip = 1; @@ -605,7 +696,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,  	 *  	 * In any case, it is not necessary for a passive scan.  	 */ -	if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN || +	if (chan->flags & IEEE80211_CHAN_NO_IR ||  	    !local->scan_req->n_ssids) {  		*next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;  		local->next_scan_state = SCAN_DECISION; @@ -617,37 +708,58 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,  	local->next_scan_state = SCAN_SEND_PROBE;  } -static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, -					    unsigned long *next_delay) +static void ieee80211_scan_state_suspend(struct ieee80211_local *local, +					 unsigned long *next_delay)  { -	int i; -	struct ieee80211_sub_if_data *sdata = local->scan_sdata; +	/* switch back to the operating channel */ +	local->scan_chandef.chan = NULL; +	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); -	for (i = 0; i < local->scan_req->n_ssids; i++) -		ieee80211_send_probe_req( -			sdata, NULL, -			local->scan_req->ssids[i].ssid, -			local->scan_req->ssids[i].ssid_len, -			local->scan_req->ie, local->scan_req->ie_len); +	/* disable PS */ +	ieee80211_offchannel_return(local); -	/* -	 * After sending probe requests, wait for probe responses -	 * on the channel. -	 */ -	*next_delay = IEEE80211_CHANNEL_TIME; -	local->next_scan_state = SCAN_DECISION; +	*next_delay = HZ / 5; +	/* afterwards, resume scan & go to next channel */ +	local->next_scan_state = SCAN_RESUME; +} + +static void ieee80211_scan_state_resume(struct ieee80211_local *local, +					unsigned long *next_delay) +{ +	ieee80211_offchannel_stop_vifs(local); + +	if (local->ops->flush) { +		ieee80211_flush_queues(local, NULL); +		*next_delay = 0; +	} else +		*next_delay = HZ / 10; + +	/* remember when we left the operating channel */ +	local->leave_oper_channel_time = jiffies; + +	/* advance to the next channel to be scanned */ +	local->next_scan_state = SCAN_SET_CHANNEL;  }  void ieee80211_scan_work(struct work_struct *work)  {  	struct ieee80211_local *local =  		container_of(work, struct ieee80211_local, scan_work.work); -	struct ieee80211_sub_if_data *sdata = local->scan_sdata; +	struct ieee80211_sub_if_data *sdata;  	unsigned long next_delay = 0; -	bool aborted, hw_scan, finish; +	bool aborted;  	mutex_lock(&local->mtx); +	sdata = rcu_dereference_protected(local->scan_sdata, +					  lockdep_is_held(&local->mtx)); + +	/* When scanning on-channel, the first-callback means completed. */ +	if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) { +		aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning); +		goto out_complete; +	} +  	if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) {  		aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning);  		goto out_complete; @@ -661,7 +773,7 @@ void ieee80211_scan_work(struct work_struct *work)  		int rc;  		local->scan_req = NULL; -		local->scan_sdata = NULL; +		RCU_INIT_POINTER(local->scan_sdata, NULL);  		rc = __ieee80211_start_scan(sdata, req);  		if (rc) { @@ -674,18 +786,15 @@ void ieee80211_scan_work(struct work_struct *work)  	}  	/* -	 * Avoid re-scheduling when the sdata is going away. -	 */ -	if (!ieee80211_sdata_running(sdata)) { -		aborted = true; -		goto out_complete; -	} - -	/*  	 * as long as no delay is required advance immediately  	 * without scheduling a new work  	 */  	do { +		if (!ieee80211_sdata_running(sdata)) { +			aborted = true; +			goto out_complete; +		} +  		switch (local->next_scan_state) {  		case SCAN_DECISION:  			/* if no more bands/channels left, complete scan */ @@ -701,27 +810,23 @@ void ieee80211_scan_work(struct work_struct *work)  		case SCAN_SEND_PROBE:  			ieee80211_scan_state_send_probe(local, &next_delay);  			break; -		case SCAN_LEAVE_OPER_CHANNEL: -			ieee80211_scan_state_leave_oper_channel(local, &next_delay); +		case SCAN_SUSPEND: +			ieee80211_scan_state_suspend(local, &next_delay);  			break; -		case SCAN_ENTER_OPER_CHANNEL: -			ieee80211_scan_state_enter_oper_channel(local, &next_delay); +		case SCAN_RESUME: +			ieee80211_scan_state_resume(local, &next_delay);  			break; +		case SCAN_ABORT: +			aborted = true; +			goto out_complete;  		}  	} while (next_delay == 0);  	ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay); -	mutex_unlock(&local->mtx); -	return; +	goto out;  out_complete: -	hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); -	finish = __ieee80211_scan_completed(&local->hw, aborted, hw_scan); -	mutex_unlock(&local->mtx); -	if (finish) -		__ieee80211_scan_completed_finish(&local->hw, hw_scan); -	return; - +	__ieee80211_scan_completed(&local->hw, aborted);  out:  	mutex_unlock(&local->mtx);  } @@ -738,9 +843,10 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,  	return res;  } -int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, -				    const u8 *ssid, u8 ssid_len, -				    struct ieee80211_channel *chan) +int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, +				const u8 *ssid, u8 ssid_len, +				struct ieee80211_channel *chan, +				enum nl80211_bss_scan_width scan_width)  {  	struct ieee80211_local *local = sdata->local;  	int ret = -EBUSY; @@ -754,28 +860,43 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,  	/* fill internal scan request */  	if (!chan) { -		int i, nchan = 0; +		int i, max_n; +		int n_ch = 0;  		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {  			if (!local->hw.wiphy->bands[band])  				continue; -			for (i = 0; -			     i < local->hw.wiphy->bands[band]->n_channels; -			     i++) { -				local->int_scan_req->channels[nchan] = + +			max_n = local->hw.wiphy->bands[band]->n_channels; +			for (i = 0; i < max_n; i++) { +				struct ieee80211_channel *tmp_ch =  				    &local->hw.wiphy->bands[band]->channels[i]; -				nchan++; + +				if (tmp_ch->flags & (IEEE80211_CHAN_NO_IR | +						     IEEE80211_CHAN_DISABLED)) +					continue; + +				local->int_scan_req->channels[n_ch] = tmp_ch; +				n_ch++;  			}  		} -		local->int_scan_req->n_channels = nchan; +		if (WARN_ON_ONCE(n_ch == 0)) +			goto unlock; + +		local->int_scan_req->n_channels = n_ch;  	} else { +		if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IR | +						IEEE80211_CHAN_DISABLED))) +			goto unlock; +  		local->int_scan_req->channels[0] = chan;  		local->int_scan_req->n_channels = 1;  	}  	local->int_scan_req->ssids = &local->scan_ssid;  	local->int_scan_req->n_ssids = 1; +	local->int_scan_req->scan_width = scan_width;  	memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);  	local->int_scan_req->ssids[0].ssid_len = ssid_len; @@ -790,11 +911,8 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,   */  void ieee80211_scan_cancel(struct ieee80211_local *local)  { -	bool abortscan; -	bool finish = false; -  	/* -	 * We are only canceling software scan, or deferred scan that was not +	 * We are canceling software scan, or deferred scan that was not  	 * yet really started (see __ieee80211_start_scan ).  	 *  	 * Regarding hardware scan: @@ -806,19 +924,192 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)  	 * - we can not cancel scan_work since driver can schedule it  	 *   by ieee80211_scan_completed(..., true) to finish scan  	 * -	 * Hence low lever driver is responsible for canceling HW scan. +	 * Hence we only call the cancel_hw_scan() callback, but the low-level +	 * driver is still responsible for calling ieee80211_scan_completed() +	 * after the scan was completed/aborted.  	 */  	mutex_lock(&local->mtx); -	abortscan = local->scan_req && !test_bit(SCAN_HW_SCANNING, &local->scanning); -	if (abortscan) -		finish = __ieee80211_scan_completed(&local->hw, true, false); +	if (!local->scan_req) +		goto out; + +	/* +	 * We have a scan running and the driver already reported completion, +	 * but the worker hasn't run yet or is stuck on the mutex - mark it as +	 * cancelled. +	 */ +	if (test_bit(SCAN_HW_SCANNING, &local->scanning) && +	    test_bit(SCAN_COMPLETED, &local->scanning)) { +		set_bit(SCAN_HW_CANCELLED, &local->scanning); +		goto out; +	} + +	if (test_bit(SCAN_HW_SCANNING, &local->scanning)) { +		/* +		 * Make sure that __ieee80211_scan_completed doesn't trigger a +		 * scan on another band. +		 */ +		set_bit(SCAN_HW_CANCELLED, &local->scanning); +		if (local->ops->cancel_hw_scan) +			drv_cancel_hw_scan(local, +				rcu_dereference_protected(local->scan_sdata, +						lockdep_is_held(&local->mtx))); +		goto out; +	} + +	/* +	 * If the work is currently running, it must be blocked on +	 * the mutex, but we'll set scan_sdata = NULL and it'll +	 * simply exit once it acquires the mutex. +	 */ +	cancel_delayed_work(&local->scan_work); +	/* and clean up */ +	__ieee80211_scan_completed(&local->hw, true); +out:  	mutex_unlock(&local->mtx); +} -	if (abortscan) { -		/* The scan is canceled, but stop work from being pending */ -		cancel_delayed_work_sync(&local->scan_work); +int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, +					struct cfg80211_sched_scan_request *req) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_sched_scan_ies sched_scan_ies = {}; +	struct cfg80211_chan_def chandef; +	int ret, i, iebufsz; + +	iebufsz = local->scan_ies_len + req->ie_len; + +	lockdep_assert_held(&local->mtx); + +	if (!local->ops->sched_scan_start) +		return -ENOTSUPP; + +	for (i = 0; i < IEEE80211_NUM_BANDS; i++) { +		if (!local->hw.wiphy->bands[i]) +			continue; + +		sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL); +		if (!sched_scan_ies.ie[i]) { +			ret = -ENOMEM; +			goto out_free; +		} + +		ieee80211_prepare_scan_chandef(&chandef, req->scan_width); + +		sched_scan_ies.len[i] = +			ieee80211_build_preq_ies(local, sched_scan_ies.ie[i], +						 iebufsz, req->ie, req->ie_len, +						 i, (u32) -1, &chandef); +	} + +	ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); +	if (ret == 0) { +		rcu_assign_pointer(local->sched_scan_sdata, sdata); +		local->sched_scan_req = req; +	} + +out_free: +	while (i > 0) +		kfree(sched_scan_ies.ie[--i]); + +	if (ret) { +		/* Clean in case of failure after HW restart or upon resume. */ +		RCU_INIT_POINTER(local->sched_scan_sdata, NULL); +		local->sched_scan_req = NULL; +	} + +	return ret; +} + +int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, +				       struct cfg80211_sched_scan_request *req) +{ +	struct ieee80211_local *local = sdata->local; +	int ret; + +	mutex_lock(&local->mtx); + +	if (rcu_access_pointer(local->sched_scan_sdata)) { +		mutex_unlock(&local->mtx); +		return -EBUSY; +	} + +	ret = __ieee80211_request_sched_scan_start(sdata, req); + +	mutex_unlock(&local->mtx); +	return ret; +} + +int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local; +	int ret = 0; + +	mutex_lock(&local->mtx); + +	if (!local->ops->sched_scan_stop) { +		ret = -ENOTSUPP; +		goto out; +	} + +	/* We don't want to restart sched scan anymore. */ +	local->sched_scan_req = NULL; + +	if (rcu_access_pointer(local->sched_scan_sdata)) { +		ret = drv_sched_scan_stop(local, sdata); +		if (!ret) +			rcu_assign_pointer(local->sched_scan_sdata, NULL); +	} +out: +	mutex_unlock(&local->mtx); + +	return ret; +} + +void ieee80211_sched_scan_results(struct ieee80211_hw *hw) +{ +	struct ieee80211_local *local = hw_to_local(hw); + +	trace_api_sched_scan_results(local); + +	cfg80211_sched_scan_results(hw->wiphy); +} +EXPORT_SYMBOL(ieee80211_sched_scan_results); + +void ieee80211_sched_scan_end(struct ieee80211_local *local) +{ +	mutex_lock(&local->mtx); + +	if (!rcu_access_pointer(local->sched_scan_sdata)) { +		mutex_unlock(&local->mtx); +		return;  	} -	if (finish) -		__ieee80211_scan_completed_finish(&local->hw, false); + +	RCU_INIT_POINTER(local->sched_scan_sdata, NULL); + +	/* If sched scan was aborted by the driver. */ +	local->sched_scan_req = NULL; + +	mutex_unlock(&local->mtx); + +	cfg80211_sched_scan_stopped(local->hw.wiphy); +} + +void ieee80211_sched_scan_stopped_work(struct work_struct *work) +{ +	struct ieee80211_local *local = +		container_of(work, struct ieee80211_local, +			     sched_scan_stopped_work); + +	ieee80211_sched_scan_end(local); +} + +void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw) +{ +	struct ieee80211_local *local = hw_to_local(hw); + +	trace_api_sched_scan_stopped(local); + +	schedule_work(&local->sched_scan_stopped_work);  } +EXPORT_SYMBOL(ieee80211_sched_scan_stopped); diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 7733f66ee2c..6ab00907008 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -21,6 +21,177 @@  #include "sta_info.h"  #include "wme.h" +int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, +				 struct ieee802_11_elems *elems, bool beacon, +				 enum ieee80211_band current_band, +				 u32 sta_flags, u8 *bssid, +				 struct ieee80211_csa_ie *csa_ie) +{ +	enum ieee80211_band new_band; +	int new_freq; +	u8 new_chan_no; +	struct ieee80211_channel *new_chan; +	struct cfg80211_chan_def new_vht_chandef = {}; +	const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; +	const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; +	const struct ieee80211_ht_operation *ht_oper; +	int secondary_channel_offset = -1; + +	sec_chan_offs = elems->sec_chan_offs; +	wide_bw_chansw_ie = elems->wide_bw_chansw_ie; +	ht_oper = elems->ht_operation; + +	if (sta_flags & (IEEE80211_STA_DISABLE_HT | +			 IEEE80211_STA_DISABLE_40MHZ)) { +		sec_chan_offs = NULL; +		wide_bw_chansw_ie = NULL; +		/* only used for bandwidth here */ +		ht_oper = NULL; +	} + +	if (sta_flags & IEEE80211_STA_DISABLE_VHT) +		wide_bw_chansw_ie = NULL; + +	if (elems->ext_chansw_ie) { +		if (!ieee80211_operating_class_to_band( +				elems->ext_chansw_ie->new_operating_class, +				&new_band)) { +			sdata_info(sdata, +				   "cannot understand ECSA IE operating class %d, disconnecting\n", +				   elems->ext_chansw_ie->new_operating_class); +			return -EINVAL; +		} +		new_chan_no = elems->ext_chansw_ie->new_ch_num; +		csa_ie->count = elems->ext_chansw_ie->count; +		csa_ie->mode = elems->ext_chansw_ie->mode; +	} else if (elems->ch_switch_ie) { +		new_band = current_band; +		new_chan_no = elems->ch_switch_ie->new_ch_num; +		csa_ie->count = elems->ch_switch_ie->count; +		csa_ie->mode = elems->ch_switch_ie->mode; +	} else { +		/* nothing here we understand */ +		return 1; +	} + +	/* Mesh Channel Switch Parameters Element */ +	if (elems->mesh_chansw_params_ie) { +		csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl; +		csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags; +		csa_ie->pre_value = le16_to_cpu( +				elems->mesh_chansw_params_ie->mesh_pre_value); +	} + +	new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); +	new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); +	if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) { +		sdata_info(sdata, +			   "BSS %pM switches to unsupported channel (%d MHz), disconnecting\n", +			   bssid, new_freq); +		return -EINVAL; +	} + +	if (!beacon && sec_chan_offs) { +		secondary_channel_offset = sec_chan_offs->sec_chan_offs; +	} else if (beacon && ht_oper) { +		secondary_channel_offset = +			ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET; +	} else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) { +		/* If it's not a beacon, HT is enabled and the IE not present, +		 * it's 20 MHz, 802.11-2012 8.5.2.6: +		 *	This element [the Secondary Channel Offset Element] is +		 *	present when switching to a 40 MHz channel. It may be +		 *	present when switching to a 20 MHz channel (in which +		 *	case the secondary channel offset is set to SCN). +		 */ +		secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; +	} + +	switch (secondary_channel_offset) { +	default: +		/* secondary_channel_offset was present but is invalid */ +	case IEEE80211_HT_PARAM_CHA_SEC_NONE: +		cfg80211_chandef_create(&csa_ie->chandef, new_chan, +					NL80211_CHAN_HT20); +		break; +	case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: +		cfg80211_chandef_create(&csa_ie->chandef, new_chan, +					NL80211_CHAN_HT40PLUS); +		break; +	case IEEE80211_HT_PARAM_CHA_SEC_BELOW: +		cfg80211_chandef_create(&csa_ie->chandef, new_chan, +					NL80211_CHAN_HT40MINUS); +		break; +	case -1: +		cfg80211_chandef_create(&csa_ie->chandef, new_chan, +					NL80211_CHAN_NO_HT); +		/* keep width for 5/10 MHz channels */ +		switch (sdata->vif.bss_conf.chandef.width) { +		case NL80211_CHAN_WIDTH_5: +		case NL80211_CHAN_WIDTH_10: +			csa_ie->chandef.width = +				sdata->vif.bss_conf.chandef.width; +			break; +		default: +			break; +		} +		break; +	} + +	if (wide_bw_chansw_ie) { +		new_vht_chandef.chan = new_chan; +		new_vht_chandef.center_freq1 = +			ieee80211_channel_to_frequency( +				wide_bw_chansw_ie->new_center_freq_seg0, +				new_band); + +		switch (wide_bw_chansw_ie->new_channel_width) { +		default: +			/* hmmm, ignore VHT and use HT if present */ +		case IEEE80211_VHT_CHANWIDTH_USE_HT: +			new_vht_chandef.chan = NULL; +			break; +		case IEEE80211_VHT_CHANWIDTH_80MHZ: +			new_vht_chandef.width = NL80211_CHAN_WIDTH_80; +			break; +		case IEEE80211_VHT_CHANWIDTH_160MHZ: +			new_vht_chandef.width = NL80211_CHAN_WIDTH_160; +			break; +		case IEEE80211_VHT_CHANWIDTH_80P80MHZ: +			/* field is otherwise reserved */ +			new_vht_chandef.center_freq2 = +				ieee80211_channel_to_frequency( +					wide_bw_chansw_ie->new_center_freq_seg1, +					new_band); +			new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80; +			break; +		} +		if (sta_flags & IEEE80211_STA_DISABLE_80P80MHZ && +		    new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80) +			ieee80211_chandef_downgrade(&new_vht_chandef); +		if (sta_flags & IEEE80211_STA_DISABLE_160MHZ && +		    new_vht_chandef.width == NL80211_CHAN_WIDTH_160) +			ieee80211_chandef_downgrade(&new_vht_chandef); +		if (sta_flags & IEEE80211_STA_DISABLE_40MHZ && +		    new_vht_chandef.width > NL80211_CHAN_WIDTH_20) +			ieee80211_chandef_downgrade(&new_vht_chandef); +	} + +	/* if VHT data is there validate & use it */ +	if (new_vht_chandef.chan) { +		if (!cfg80211_chandef_compatible(&new_vht_chandef, +						 &csa_ie->chandef)) { +			sdata_info(sdata, +				   "BSS %pM: CSA has inconsistent channel data, disconnecting\n", +				   bssid); +			return -EINVAL; +		} +		csa_ie->chandef = new_vht_chandef; +	} + +	return 0; +} +  static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,  					struct ieee80211_msrment_ie *request_ie,  					const u8 *da, const u8 *bssid, @@ -32,12 +203,8 @@ static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_da  	skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom +  				sizeof(struct ieee80211_msrment_ie)); - -	if (!skb) { -		printk(KERN_ERR "%s: failed to allocate buffer for " -				"measurement report frame\n", sdata->name); +	if (!skb)  		return; -	}  	skb_reserve(skb, local->hw.extra_tx_headroom);  	msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index eff58571fd7..a9b46d8ea22 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -9,6 +9,7 @@  #include <linux/module.h>  #include <linux/init.h> +#include <linux/etherdevice.h>  #include <linux/netdevice.h>  #include <linux/types.h>  #include <linux/slab.h> @@ -24,6 +25,7 @@  #include "sta_info.h"  #include "debugfs_sta.h"  #include "mesh.h" +#include "wme.h"  /**   * DOC: STA information lifetime rules @@ -47,9 +49,9 @@   * Station entries are added by mac80211 when you establish a link with a   * peer. This means different things for the different type of interfaces   * we support. For a regular station this mean we add the AP sta when we - * receive an assocation response from the AP. For IBSS this occurs when + * receive an association response from the AP. For IBSS this occurs when   * get to know about a peer on the same IBSS. For WDS we add the sta for - * the peer imediately upon device open. When using AP mode we add stations + * the peer immediately upon device open. When using AP mode we add stations   * for each respective station upon request from userspace through nl80211.   *   * In order to remove a STA info structure, various sta_info_destroy_*() @@ -61,13 +63,14 @@   * freed before they are done using it.   */ -/* Caller must hold local->sta_lock */ +/* Caller must hold local->sta_mtx */  static int sta_info_hash_del(struct ieee80211_local *local,  			     struct sta_info *sta)  {  	struct sta_info *s; -	s = local->sta_hash[STA_HASH(sta->sta.addr)]; +	s = rcu_dereference_protected(local->sta_hash[STA_HASH(sta->sta.addr)], +				      lockdep_is_held(&local->sta_mtx));  	if (!s)  		return -ENOENT;  	if (s == sta) { @@ -76,9 +79,11 @@ static int sta_info_hash_del(struct ieee80211_local *local,  		return 0;  	} -	while (s->hnext && s->hnext != sta) -		s = s->hnext; -	if (s->hnext) { +	while (rcu_access_pointer(s->hnext) && +	       rcu_access_pointer(s->hnext) != sta) +		s = rcu_dereference_protected(s->hnext, +					lockdep_is_held(&local->sta_mtx)); +	if (rcu_access_pointer(s->hnext)) {  		rcu_assign_pointer(s->hnext, sta->hnext);  		return 0;  	} @@ -86,6 +91,67 @@ static int sta_info_hash_del(struct ieee80211_local *local,  	return -ENOENT;  } +static void __cleanup_single_sta(struct sta_info *sta) +{ +	int ac, i; +	struct tid_ampdu_tx *tid_tx; +	struct ieee80211_sub_if_data *sdata = sta->sdata; +	struct ieee80211_local *local = sdata->local; +	struct ps_data *ps; + +	if (test_sta_flag(sta, WLAN_STA_PS_STA) || +	    test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { +		if (sta->sdata->vif.type == NL80211_IFTYPE_AP || +		    sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +			ps = &sdata->bss->ps; +		else if (ieee80211_vif_is_mesh(&sdata->vif)) +			ps = &sdata->u.mesh.ps; +		else +			return; + +		clear_sta_flag(sta, WLAN_STA_PS_STA); +		clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + +		atomic_dec(&ps->num_sta_ps); +		sta_info_recalc_tim(sta); +	} + +	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]); +		ieee80211_purge_tx_queue(&local->hw, &sta->tx_filtered[ac]); +	} + +	if (ieee80211_vif_is_mesh(&sdata->vif)) +		mesh_sta_cleanup(sta); + +	cancel_work_sync(&sta->drv_unblock_wk); + +	/* +	 * Destroy aggregation state here. It would be nice to wait for the +	 * driver to finish aggregation stop and then clean up, but for now +	 * drivers have to handle aggregation stop being requested, followed +	 * directly by station destruction. +	 */ +	for (i = 0; i < IEEE80211_NUM_TIDS; i++) { +		kfree(sta->ampdu_mlme.tid_start_tx[i]); +		tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]); +		if (!tid_tx) +			continue; +		ieee80211_purge_tx_queue(&local->hw, &tid_tx->pending); +		kfree(tid_tx); +	} +} + +static void cleanup_single_sta(struct sta_info *sta) +{ +	struct ieee80211_sub_if_data *sdata = sta->sdata; +	struct ieee80211_local *local = sdata->local; + +	__cleanup_single_sta(sta); +	sta_info_free(local, sta); +} +  /* protected by RCU */  struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,  			      const u8 *addr) @@ -94,16 +160,12 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,  	struct sta_info *sta;  	sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], -				    rcu_read_lock_held() || -				    lockdep_is_held(&local->sta_lock) ||  				    lockdep_is_held(&local->sta_mtx));  	while (sta) {  		if (sta->sdata == sdata && -		    memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) +		    ether_addr_equal(sta->sta.addr, addr))  			break;  		sta = rcu_dereference_check(sta->hnext, -					    rcu_read_lock_held() || -					    lockdep_is_held(&local->sta_lock) ||  					    lockdep_is_held(&local->sta_mtx));  	}  	return sta; @@ -120,17 +182,13 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,  	struct sta_info *sta;  	sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], -				    rcu_read_lock_held() || -				    lockdep_is_held(&local->sta_lock) ||  				    lockdep_is_held(&local->sta_mtx));  	while (sta) {  		if ((sta->sdata == sdata ||  		     (sta->sdata->bss && sta->sdata->bss == sdata->bss)) && -		    memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) +		    ether_addr_equal(sta->sta.addr, addr))  			break;  		sta = rcu_dereference_check(sta->hnext, -					    rcu_read_lock_held() || -					    lockdep_is_held(&local->sta_lock) ||  					    lockdep_is_held(&local->sta_mtx));  	}  	return sta; @@ -157,33 +215,40 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,  }  /** - * __sta_info_free - internal STA free helper + * sta_info_free - free STA   *   * @local: pointer to the global information   * @sta: STA info to free   *   * This function must undo everything done by sta_info_alloc() - * that may happen before sta_info_insert(). + * that may happen before sta_info_insert(). It may only be + * called when sta_info_insert() has not been attempted (and + * if that fails, the station is freed anyway.)   */ -static void __sta_info_free(struct ieee80211_local *local, -			    struct sta_info *sta) +void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)  { -	if (sta->rate_ctrl) { +	int i; + +	if (sta->rate_ctrl)  		rate_control_free_sta(sta); -		rate_control_put(sta->rate_ctrl); + +	if (sta->tx_lat) { +		for (i = 0; i < IEEE80211_NUM_TIDS; i++) +			kfree(sta->tx_lat[i].bins); +		kfree(sta->tx_lat);  	} -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -	wiphy_debug(local->hw.wiphy, "Destroyed STA %pM\n", sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ +	sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); +	kfree(rcu_dereference_raw(sta->sta.rates));  	kfree(sta);  } -/* Caller must hold local->sta_lock */ +/* Caller must hold local->sta_mtx */  static void sta_info_hash_add(struct ieee80211_local *local,  			      struct sta_info *sta)  { +	lockdep_assert_held(&local->sta_mtx);  	sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)];  	rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);  } @@ -197,13 +262,24 @@ static void sta_unblock(struct work_struct *wk)  	if (sta->dead)  		return; -	if (!test_sta_flags(sta, WLAN_STA_PS_STA)) +	if (!test_sta_flag(sta, WLAN_STA_PS_STA)) { +		local_bh_disable();  		ieee80211_sta_ps_deliver_wakeup(sta); -	else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) { -		clear_sta_flags(sta, WLAN_STA_PS_DRIVER); +		local_bh_enable(); +	} else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) { +		clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + +		local_bh_disable();  		ieee80211_sta_ps_deliver_poll_response(sta); +		local_bh_enable(); +	} else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD)) { +		clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + +		local_bh_disable(); +		ieee80211_sta_ps_deliver_uapsd(sta); +		local_bh_enable();  	} else -		clear_sta_flags(sta, WLAN_STA_PS_DRIVER); +		clear_sta_flag(sta, WLAN_STA_PS_DRIVER);  }  static int sta_prepare_rate_control(struct ieee80211_local *local, @@ -212,44 +288,86 @@ static int sta_prepare_rate_control(struct ieee80211_local *local,  	if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)  		return 0; -	sta->rate_ctrl = rate_control_get(local->rate_ctrl); +	sta->rate_ctrl = local->rate_ctrl;  	sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl,  						     &sta->sta, gfp); -	if (!sta->rate_ctrl_priv) { -		rate_control_put(sta->rate_ctrl); +	if (!sta->rate_ctrl_priv)  		return -ENOMEM; -	}  	return 0;  }  struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, -				u8 *addr, gfp_t gfp) +				const u8 *addr, gfp_t gfp)  {  	struct ieee80211_local *local = sdata->local;  	struct sta_info *sta; +	struct timespec uptime; +	struct ieee80211_tx_latency_bin_ranges *tx_latency;  	int i;  	sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);  	if (!sta)  		return NULL; +	rcu_read_lock(); +	tx_latency = rcu_dereference(local->tx_latency); +	/* init stations Tx latency statistics && TID bins */ +	if (tx_latency) { +		sta->tx_lat = kzalloc(IEEE80211_NUM_TIDS * +				      sizeof(struct ieee80211_tx_latency_stat), +				      GFP_ATOMIC); +		if (!sta->tx_lat) { +			rcu_read_unlock(); +			goto free; +		} + +		if (tx_latency->n_ranges) { +			for (i = 0; i < IEEE80211_NUM_TIDS; i++) { +				/* size of bins is size of the ranges +1 */ +				sta->tx_lat[i].bin_count = +					tx_latency->n_ranges + 1; +				sta->tx_lat[i].bins = +					kcalloc(sta->tx_lat[i].bin_count, +						sizeof(u32), GFP_ATOMIC); +				if (!sta->tx_lat[i].bins) { +					rcu_read_unlock(); +					goto free; +				} +			} +		} +	} +	rcu_read_unlock(); +  	spin_lock_init(&sta->lock); -	spin_lock_init(&sta->flaglock); +	spin_lock_init(&sta->ps_lock);  	INIT_WORK(&sta->drv_unblock_wk, sta_unblock);  	INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);  	mutex_init(&sta->ampdu_mlme.mtx); +#ifdef CONFIG_MAC80211_MESH +	if (ieee80211_vif_is_mesh(&sdata->vif) && +	    !sdata->u.mesh.user_mpm) +		init_timer(&sta->plink_timer); +	sta->nonpeer_pm = NL80211_MESH_POWER_ACTIVE; +#endif  	memcpy(sta->sta.addr, addr, ETH_ALEN);  	sta->local = local;  	sta->sdata = sdata; +	sta->last_rx = jiffies; -	if (sta_prepare_rate_control(local, sta, gfp)) { -		kfree(sta); -		return NULL; -	} +	sta->sta_state = IEEE80211_STA_NONE; + +	do_posix_clock_monotonic_gettime(&uptime); +	sta->last_connected = uptime.tv_sec; +	ewma_init(&sta->avg_signal, 1024, 8); +	for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) +		ewma_init(&sta->chain_signal_avg[i], 1024, 8); -	for (i = 0; i < STA_TID_NUM; i++) { +	if (sta_prepare_rate_control(local, sta, gfp)) +		goto free; + +	for (i = 0; i < IEEE80211_NUM_TIDS; i++) {  		/*  		 * timer_to_tid must be initialized with identity mapping  		 * to enable session_timer's data differentiation. See @@ -257,224 +375,203 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,  		 */  		sta->timer_to_tid[i] = i;  	} -	skb_queue_head_init(&sta->ps_tx_buf); -	skb_queue_head_init(&sta->tx_filtered); +	for (i = 0; i < IEEE80211_NUM_ACS; i++) { +		skb_queue_head_init(&sta->ps_tx_buf[i]); +		skb_queue_head_init(&sta->tx_filtered[i]); +	} -	for (i = 0; i < NUM_RX_DATA_QUEUES; i++) +	for (i = 0; i < IEEE80211_NUM_TIDS; i++)  		sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX); -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -	wiphy_debug(local->hw.wiphy, "Allocated STA %pM\n", sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ - -#ifdef CONFIG_MAC80211_MESH -	sta->plink_state = PLINK_LISTEN; -	init_timer(&sta->plink_timer); -#endif +	sta->sta.smps_mode = IEEE80211_SMPS_OFF; +	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)]; +		u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> +				IEEE80211_HT_CAP_SM_PS_SHIFT; +		/* +		 * Assume that hostapd advertises our caps in the beacon and +		 * this is the known_smps_mode for a station that just assciated +		 */ +		switch (smps) { +		case WLAN_HT_SMPS_CONTROL_DISABLED: +			sta->known_smps_mode = IEEE80211_SMPS_OFF; +			break; +		case WLAN_HT_SMPS_CONTROL_STATIC: +			sta->known_smps_mode = IEEE80211_SMPS_STATIC; +			break; +		case WLAN_HT_SMPS_CONTROL_DYNAMIC: +			sta->known_smps_mode = IEEE80211_SMPS_DYNAMIC; +			break; +		default: +			WARN_ON(1); +		} +	} +	sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);  	return sta; + +free: +	if (sta->tx_lat) { +		for (i = 0; i < IEEE80211_NUM_TIDS; i++) +			kfree(sta->tx_lat[i].bins); +		kfree(sta->tx_lat); +	} +	kfree(sta); +	return NULL;  } -static int sta_info_finish_insert(struct sta_info *sta, bool async) +static int sta_info_insert_check(struct sta_info *sta)  { -	struct ieee80211_local *local = sta->local;  	struct ieee80211_sub_if_data *sdata = sta->sdata; -	struct station_info sinfo; -	unsigned long flags; -	int err = 0; - -	lockdep_assert_held(&local->sta_mtx); - -	/* notify driver */ -	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) -		sdata = container_of(sdata->bss, -				     struct ieee80211_sub_if_data, -				     u.ap); -	err = drv_sta_add(local, sdata, &sta->sta); -	if (err) { -		if (!async) -			return err; -		printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to driver (%d)" -				  " - keeping it anyway.\n", -		       sdata->name, sta->sta.addr, err); -	} else { -		sta->uploaded = true; -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -		if (async) -			wiphy_debug(local->hw.wiphy, -				    "Finished adding IBSS STA %pM\n", -				    sta->sta.addr); -#endif -	} - -	sdata = sta->sdata; - -	if (!async) { -		local->num_sta++; -		local->sta_generation++; -		smp_mb(); - -		/* make the station visible */ -		spin_lock_irqsave(&local->sta_lock, flags); -		sta_info_hash_add(local, sta); -		spin_unlock_irqrestore(&local->sta_lock, flags); -	} - -	list_add(&sta->list, &local->sta_list); -	ieee80211_sta_debugfs_add(sta); -	rate_control_add_sta_debugfs(sta); - -	sinfo.filled = 0; -	sinfo.generation = local->sta_generation; -	cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); +	/* +	 * Can't be a WARN_ON because it can be triggered through a race: +	 * something inserts a STA (on one CPU) without holding the RTNL +	 * and another CPU turns off the net device. +	 */ +	if (unlikely(!ieee80211_sdata_running(sdata))) +		return -ENETDOWN; +	if (WARN_ON(ether_addr_equal(sta->sta.addr, sdata->vif.addr) || +		    is_multicast_ether_addr(sta->sta.addr))) +		return -EINVAL;  	return 0;  } -static void sta_info_finish_pending(struct ieee80211_local *local) +static int sta_info_insert_drv_state(struct ieee80211_local *local, +				     struct ieee80211_sub_if_data *sdata, +				     struct sta_info *sta)  { -	struct sta_info *sta; -	unsigned long flags; +	enum ieee80211_sta_state state; +	int err = 0; -	spin_lock_irqsave(&local->sta_lock, flags); -	while (!list_empty(&local->sta_pending_list)) { -		sta = list_first_entry(&local->sta_pending_list, -				       struct sta_info, list); -		list_del(&sta->list); -		spin_unlock_irqrestore(&local->sta_lock, flags); +	for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state; state++) { +		err = drv_sta_state(local, sdata, sta, state, state + 1); +		if (err) +			break; +	} -		sta_info_finish_insert(sta, true); +	if (!err) { +		/* +		 * Drivers using legacy sta_add/sta_remove callbacks only +		 * get uploaded set to true after sta_add is called. +		 */ +		if (!local->ops->sta_add) +			sta->uploaded = true; +		return 0; +	} -		spin_lock_irqsave(&local->sta_lock, flags); +	if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { +		sdata_info(sdata, +			   "failed to move IBSS STA %pM to state %d (%d) - keeping it anyway\n", +			   sta->sta.addr, state + 1, err); +		err = 0;  	} -	spin_unlock_irqrestore(&local->sta_lock, flags); -} -static void sta_info_finish_work(struct work_struct *work) -{ -	struct ieee80211_local *local = -		container_of(work, struct ieee80211_local, sta_finish_work); +	/* unwind on error */ +	for (; state > IEEE80211_STA_NOTEXIST; state--) +		WARN_ON(drv_sta_state(local, sdata, sta, state, state - 1)); -	mutex_lock(&local->sta_mtx); -	sta_info_finish_pending(local); -	mutex_unlock(&local->sta_mtx); +	return err;  } -int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) +/* + * should be called with sta_mtx locked + * this function replaces the mutex lock + * with a RCU lock + */ +static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)  {  	struct ieee80211_local *local = sta->local;  	struct ieee80211_sub_if_data *sdata = sta->sdata; -	unsigned long flags; +	struct station_info sinfo;  	int err = 0; -	/* -	 * Can't be a WARN_ON because it can be triggered through a race: -	 * something inserts a STA (on one CPU) without holding the RTNL -	 * and another CPU turns off the net device. -	 */ -	if (unlikely(!ieee80211_sdata_running(sdata))) { -		err = -ENETDOWN; -		rcu_read_lock(); -		goto out_free; -	} +	lockdep_assert_held(&local->sta_mtx); -	if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 || -		    is_multicast_ether_addr(sta->sta.addr))) { -		err = -EINVAL; -		rcu_read_lock(); -		goto out_free; +	/* check if STA exists already */ +	if (sta_info_get_bss(sdata, sta->sta.addr)) { +		err = -EEXIST; +		goto out_err;  	} -	/* -	 * In ad-hoc mode, we sometimes need to insert stations -	 * from tasklet context from the RX path. To avoid races, -	 * always do so in that case -- see the comment below. -	 */ -	if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { -		spin_lock_irqsave(&local->sta_lock, flags); -		/* check if STA exists already */ -		if (sta_info_get_bss(sdata, sta->sta.addr)) { -			spin_unlock_irqrestore(&local->sta_lock, flags); -			rcu_read_lock(); -			err = -EEXIST; -			goto out_free; -		} +	local->num_sta++; +	local->sta_generation++; +	smp_mb(); -		local->num_sta++; -		local->sta_generation++; -		smp_mb(); -		sta_info_hash_add(local, sta); +	/* simplify things and don't accept BA sessions yet */ +	set_sta_flag(sta, WLAN_STA_BLOCK_BA); -		list_add_tail(&sta->list, &local->sta_pending_list); +	/* make the station visible */ +	sta_info_hash_add(local, sta); -		rcu_read_lock(); -		spin_unlock_irqrestore(&local->sta_lock, flags); +	list_add_rcu(&sta->list, &local->sta_list); -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -		wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n", -			    sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ +	/* notify driver */ +	err = sta_info_insert_drv_state(local, sdata, sta); +	if (err) +		goto out_remove; -		ieee80211_queue_work(&local->hw, &local->sta_finish_work); +	set_sta_flag(sta, WLAN_STA_INSERTED); +	/* accept BA sessions now */ +	clear_sta_flag(sta, WLAN_STA_BLOCK_BA); -		return 0; -	} +	ieee80211_recalc_min_chandef(sdata); +	ieee80211_sta_debugfs_add(sta); +	rate_control_add_sta_debugfs(sta); -	/* -	 * On first glance, this will look racy, because the code -	 * below this point, which inserts a station with sleeping, -	 * unlocks the sta_lock between checking existence in the -	 * hash table and inserting into it. -	 * -	 * However, it is not racy against itself because it keeps -	 * the mutex locked. It still seems to race against the -	 * above code that atomically inserts the station... That, -	 * however, is not true because the above code can only -	 * be invoked for IBSS interfaces, and the below code will -	 * not be -- and the two do not race against each other as -	 * the hash table also keys off the interface. -	 */ +	memset(&sinfo, 0, sizeof(sinfo)); +	sinfo.filled = 0; +	sinfo.generation = local->sta_generation; +	cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); -	might_sleep(); +	sta_dbg(sdata, "Inserted STA %pM\n", sta->sta.addr); -	mutex_lock(&local->sta_mtx); +	/* move reference to rcu-protected */ +	rcu_read_lock(); +	mutex_unlock(&local->sta_mtx); -	spin_lock_irqsave(&local->sta_lock, flags); -	/* check if STA exists already */ -	if (sta_info_get_bss(sdata, sta->sta.addr)) { -		spin_unlock_irqrestore(&local->sta_lock, flags); -		mutex_unlock(&local->sta_mtx); -		rcu_read_lock(); -		err = -EEXIST; -		goto out_free; -	} +	if (ieee80211_vif_is_mesh(&sdata->vif)) +		mesh_accept_plinks_update(sdata); -	spin_unlock_irqrestore(&local->sta_lock, flags); +	return 0; + out_remove: +	sta_info_hash_del(local, sta); +	list_del_rcu(&sta->list); +	local->num_sta--; +	synchronize_net(); +	__cleanup_single_sta(sta); + out_err: +	mutex_unlock(&local->sta_mtx); +	rcu_read_lock(); +	return err; +} + +int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) +{ +	struct ieee80211_local *local = sta->local; +	int err; -	err = sta_info_finish_insert(sta, false); +	might_sleep(); + +	err = sta_info_insert_check(sta);  	if (err) { -		mutex_unlock(&local->sta_mtx);  		rcu_read_lock();  		goto out_free;  	} -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -	wiphy_debug(local->hw.wiphy, "Inserted STA %pM\n", sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ - -	/* move reference to rcu-protected */ -	rcu_read_lock(); -	mutex_unlock(&local->sta_mtx); +	mutex_lock(&local->sta_mtx); -	if (ieee80211_vif_is_mesh(&sdata->vif)) -		mesh_accept_plinks_update(sdata); +	err = sta_info_insert_finish(sta); +	if (err) +		goto out_free;  	return 0;   out_free: -	BUG_ON(!err); -	__sta_info_free(local, sta); +	sta_info_free(local, sta);  	return err;  } @@ -487,82 +584,138 @@ int sta_info_insert(struct sta_info *sta)  	return err;  } -static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid) +static inline void __bss_tim_set(u8 *tim, u16 id)  {  	/*  	 * This format has been mandated by the IEEE specifications,  	 * so this line may not be changed to use the __set_bit() format.  	 */ -	bss->tim[aid / 8] |= (1 << (aid % 8)); +	tim[id / 8] |= (1 << (id % 8));  } -static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid) +static inline void __bss_tim_clear(u8 *tim, u16 id)  {  	/*  	 * This format has been mandated by the IEEE specifications,  	 * so this line may not be changed to use the __clear_bit() format.  	 */ -	bss->tim[aid / 8] &= ~(1 << (aid % 8)); +	tim[id / 8] &= ~(1 << (id % 8));  } -static void __sta_info_set_tim_bit(struct ieee80211_if_ap *bss, -				   struct sta_info *sta) +static inline bool __bss_tim_get(u8 *tim, u16 id)  { -	BUG_ON(!bss); - -	__bss_tim_set(bss, sta->sta.aid); +	/* +	 * This format has been mandated by the IEEE specifications, +	 * so this line may not be changed to use the test_bit() format. +	 */ +	return tim[id / 8] & (1 << (id % 8)); +} -	if (sta->local->ops->set_tim) { -		sta->local->tim_in_locked_section = true; -		drv_set_tim(sta->local, &sta->sta, true); -		sta->local->tim_in_locked_section = false; +static unsigned long ieee80211_tids_for_ac(int ac) +{ +	/* If we ever support TIDs > 7, this obviously needs to be adjusted */ +	switch (ac) { +	case IEEE80211_AC_VO: +		return BIT(6) | BIT(7); +	case IEEE80211_AC_VI: +		return BIT(4) | BIT(5); +	case IEEE80211_AC_BE: +		return BIT(0) | BIT(3); +	case IEEE80211_AC_BK: +		return BIT(1) | BIT(2); +	default: +		WARN_ON(1); +		return 0;  	}  } -void sta_info_set_tim_bit(struct sta_info *sta) +void sta_info_recalc_tim(struct sta_info *sta)  { -	unsigned long flags; +	struct ieee80211_local *local = sta->local; +	struct ps_data *ps; +	bool indicate_tim = false; +	u8 ignore_for_tim = sta->sta.uapsd_queues; +	int ac; +	u16 id; + +	if (sta->sdata->vif.type == NL80211_IFTYPE_AP || +	    sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { +		if (WARN_ON_ONCE(!sta->sdata->bss)) +			return; + +		ps = &sta->sdata->bss->ps; +		id = sta->sta.aid; +#ifdef CONFIG_MAC80211_MESH +	} else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) { +		ps = &sta->sdata->u.mesh.ps; +		/* TIM map only for 1 <= PLID <= IEEE80211_MAX_AID */ +		id = sta->plid % (IEEE80211_MAX_AID + 1); +#endif +	} else { +		return; +	} + +	/* No need to do anything if the driver does all */ +	if (local->hw.flags & IEEE80211_HW_AP_LINK_PS) +		return; -	BUG_ON(!sta->sdata->bss); +	if (sta->dead) +		goto done; -	spin_lock_irqsave(&sta->local->sta_lock, flags); -	__sta_info_set_tim_bit(sta->sdata->bss, sta); -	spin_unlock_irqrestore(&sta->local->sta_lock, flags); -} +	/* +	 * If all ACs are delivery-enabled then we should build +	 * the TIM bit for all ACs anyway; if only some are then +	 * we ignore those and build the TIM bit using only the +	 * non-enabled ones. +	 */ +	if (ignore_for_tim == BIT(IEEE80211_NUM_ACS) - 1) +		ignore_for_tim = 0; -static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss, -				     struct sta_info *sta) -{ -	BUG_ON(!bss); +	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { +		unsigned long tids; -	__bss_tim_clear(bss, sta->sta.aid); +		if (ignore_for_tim & BIT(ac)) +			continue; + +		indicate_tim |= !skb_queue_empty(&sta->tx_filtered[ac]) || +				!skb_queue_empty(&sta->ps_tx_buf[ac]); +		if (indicate_tim) +			break; + +		tids = ieee80211_tids_for_ac(ac); -	if (sta->local->ops->set_tim) { -		sta->local->tim_in_locked_section = true; -		drv_set_tim(sta->local, &sta->sta, false); -		sta->local->tim_in_locked_section = false; +		indicate_tim |= +			sta->driver_buffered_tids & tids;  	} -} -void sta_info_clear_tim_bit(struct sta_info *sta) -{ -	unsigned long flags; + done: +	spin_lock_bh(&local->tim_lock); -	BUG_ON(!sta->sdata->bss); +	if (indicate_tim == __bss_tim_get(ps->tim, id)) +		goto out_unlock; -	spin_lock_irqsave(&sta->local->sta_lock, flags); -	__sta_info_clear_tim_bit(sta->sdata->bss, sta); -	spin_unlock_irqrestore(&sta->local->sta_lock, flags); +	if (indicate_tim) +		__bss_tim_set(ps->tim, id); +	else +		__bss_tim_clear(ps->tim, id); + +	if (local->ops->set_tim) { +		local->tim_in_locked_section = true; +		drv_set_tim(local, &sta->sta, indicate_tim); +		local->tim_in_locked_section = false; +	} + +out_unlock: +	spin_unlock_bh(&local->tim_lock);  } -static int sta_info_buffer_expired(struct sta_info *sta, -				   struct sk_buff *skb) +static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb)  {  	struct ieee80211_tx_info *info;  	int timeout;  	if (!skb) -		return 0; +		return false;  	info = IEEE80211_SKB_CB(skb); @@ -576,50 +729,107 @@ static int sta_info_buffer_expired(struct sta_info *sta,  } -static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, -					     struct sta_info *sta) +static bool sta_info_cleanup_expire_buffered_ac(struct ieee80211_local *local, +						struct sta_info *sta, int ac)  {  	unsigned long flags;  	struct sk_buff *skb; -	struct ieee80211_sub_if_data *sdata; -	if (skb_queue_empty(&sta->ps_tx_buf)) -		return false; +	/* +	 * First check for frames that should expire on the filtered +	 * queue. Frames here were rejected by the driver and are on +	 * a separate queue to avoid reordering with normal PS-buffered +	 * frames. They also aren't accounted for right now in the +	 * total_ps_buffered counter. +	 */ +	for (;;) { +		spin_lock_irqsave(&sta->tx_filtered[ac].lock, flags); +		skb = skb_peek(&sta->tx_filtered[ac]); +		if (sta_info_buffer_expired(sta, skb)) +			skb = __skb_dequeue(&sta->tx_filtered[ac]); +		else +			skb = NULL; +		spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags); + +		/* +		 * Frames are queued in order, so if this one +		 * hasn't expired yet we can stop testing. If +		 * we actually reached the end of the queue we +		 * also need to stop, of course. +		 */ +		if (!skb) +			break; +		ieee80211_free_txskb(&local->hw, skb); +	} +	/* +	 * Now also check the normal PS-buffered queue, this will +	 * only find something if the filtered queue was emptied +	 * since the filtered frames are all before the normal PS +	 * buffered frames. +	 */  	for (;;) { -		spin_lock_irqsave(&sta->ps_tx_buf.lock, flags); -		skb = skb_peek(&sta->ps_tx_buf); +		spin_lock_irqsave(&sta->ps_tx_buf[ac].lock, flags); +		skb = skb_peek(&sta->ps_tx_buf[ac]);  		if (sta_info_buffer_expired(sta, skb)) -			skb = __skb_dequeue(&sta->ps_tx_buf); +			skb = __skb_dequeue(&sta->ps_tx_buf[ac]);  		else  			skb = NULL; -		spin_unlock_irqrestore(&sta->ps_tx_buf.lock, flags); +		spin_unlock_irqrestore(&sta->ps_tx_buf[ac].lock, flags); +		/* +		 * frames are queued in order, so if this one +		 * hasn't expired yet (or we reached the end of +		 * the queue) we can stop testing +		 */  		if (!skb)  			break; -		sdata = sta->sdata;  		local->total_ps_buffered--; -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG -		printk(KERN_DEBUG "Buffered frame expired (STA %pM)\n", +		ps_dbg(sta->sdata, "Buffered frame expired (STA %pM)\n",  		       sta->sta.addr); -#endif -		dev_kfree_skb(skb); - -		if (skb_queue_empty(&sta->ps_tx_buf)) -			sta_info_clear_tim_bit(sta); +		ieee80211_free_txskb(&local->hw, skb);  	} -	return true; +	/* +	 * Finally, recalculate the TIM bit for this station -- it might +	 * now be clear because the station was too slow to retrieve its +	 * frames. +	 */ +	sta_info_recalc_tim(sta); + +	/* +	 * Return whether there are any frames still buffered, this is +	 * used to check whether the cleanup timer still needs to run, +	 * if there are no frames we don't need to rearm the timer. +	 */ +	return !(skb_queue_empty(&sta->ps_tx_buf[ac]) && +		 skb_queue_empty(&sta->tx_filtered[ac]));  } -static int __must_check __sta_info_destroy(struct sta_info *sta) +static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, +					     struct sta_info *sta) +{ +	bool have_buffered = false; +	int ac; + +	/* This is only necessary for stations on BSS/MBSS interfaces */ +	if (!sta->sdata->bss && +	    !ieee80211_vif_is_mesh(&sta->sdata->vif)) +		return false; + +	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) +		have_buffered |= +			sta_info_cleanup_expire_buffered_ac(local, sta, ac); + +	return have_buffered; +} + +static int __must_check __sta_info_destroy_part1(struct sta_info *sta)  {  	struct ieee80211_local *local;  	struct ieee80211_sub_if_data *sdata; -	struct sk_buff *skb; -	unsigned long flags; -	int ret, i; +	int ret;  	might_sleep(); @@ -629,91 +839,89 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)  	local = sta->local;  	sdata = sta->sdata; +	lockdep_assert_held(&local->sta_mtx); +  	/*  	 * Before removing the station from the driver and  	 * rate control, it might still start new aggregation  	 * sessions -- block that to make sure the tear-down  	 * will be sufficient.  	 */ -	set_sta_flags(sta, WLAN_STA_BLOCK_BA); -	ieee80211_sta_tear_down_BA_sessions(sta, true); +	set_sta_flag(sta, WLAN_STA_BLOCK_BA); +	ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA); -	spin_lock_irqsave(&local->sta_lock, flags);  	ret = sta_info_hash_del(local, sta); -	/* this might still be the pending list ... which is fine */ -	if (!ret) -		list_del(&sta->list); -	spin_unlock_irqrestore(&local->sta_lock, flags); -	if (ret) +	if (WARN_ON(ret))  		return ret; -	for (i = 0; i < NUM_DEFAULT_KEYS; i++) -		ieee80211_key_free(local, sta->gtk[i]); -	if (sta->ptk) -		ieee80211_key_free(local, sta->ptk); +	list_del_rcu(&sta->list); -	sta->dead = true; +	drv_sta_pre_rcu_remove(local, sta->sdata, sta); -	if (test_and_clear_sta_flags(sta, -				WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) { -		BUG_ON(!sdata->bss); +	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && +	    rcu_access_pointer(sdata->u.vlan.sta) == sta) +		RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); -		atomic_dec(&sdata->bss->num_sta_ps); -		__sta_info_clear_tim_bit(sdata->bss, sta); -	} +	return 0; +} + +static void __sta_info_destroy_part2(struct sta_info *sta) +{ +	struct ieee80211_local *local = sta->local; +	struct ieee80211_sub_if_data *sdata = sta->sdata; +	int ret; + +	/* +	 * NOTE: This assumes at least synchronize_net() was done +	 *	 after _part1 and before _part2! +	 */ + +	might_sleep(); +	lockdep_assert_held(&local->sta_mtx); + +	/* now keys can no longer be reached */ +	ieee80211_free_sta_keys(local, sta); + +	sta->dead = true;  	local->num_sta--;  	local->sta_generation++; -	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) -		rcu_assign_pointer(sdata->u.vlan.sta, NULL); +	while (sta->sta_state > IEEE80211_STA_NONE) { +		ret = sta_info_move_state(sta, sta->sta_state - 1); +		if (ret) { +			WARN_ON_ONCE(1); +			break; +		} +	}  	if (sta->uploaded) { -		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) -			sdata = container_of(sdata->bss, -					     struct ieee80211_sub_if_data, -					     u.ap); -		drv_sta_remove(local, sdata, &sta->sta); -		sdata = sta->sdata; +		ret = drv_sta_state(local, sdata, sta, IEEE80211_STA_NONE, +				    IEEE80211_STA_NOTEXIST); +		WARN_ON_ONCE(ret != 0);  	} -	/* -	 * At this point, after we wait for an RCU grace period, -	 * neither mac80211 nor the driver can reference this -	 * sta struct any more except by still existing timers -	 * associated with this station that we clean up below. -	 */ -	synchronize_rcu(); - -#ifdef CONFIG_MAC80211_MESH -	if (ieee80211_vif_is_mesh(&sdata->vif)) -		mesh_accept_plinks_update(sdata); -#endif +	sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr); -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -	wiphy_debug(local->hw.wiphy, "Removed STA %pM\n", sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ -	cancel_work_sync(&sta->drv_unblock_wk); +	cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL);  	rate_control_remove_sta_debugfs(sta);  	ieee80211_sta_debugfs_remove(sta); +	ieee80211_recalc_min_chandef(sdata); -#ifdef CONFIG_MAC80211_MESH -	if (ieee80211_vif_is_mesh(&sta->sdata->vif)) { -		mesh_plink_deactivate(sta); -		del_timer_sync(&sta->plink_timer); -	} -#endif +	cleanup_single_sta(sta); +} -	while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { -		local->total_ps_buffered--; -		dev_kfree_skb_any(skb); -	} +int __must_check __sta_info_destroy(struct sta_info *sta) +{ +	int err = __sta_info_destroy_part1(sta); -	while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) -		dev_kfree_skb_any(skb); +	if (err) +		return err; -	__sta_info_free(local, sta); +	synchronize_net(); + +	__sta_info_destroy_part2(sta);  	return 0;  } @@ -763,60 +971,52 @@ static void sta_info_cleanup(unsigned long data)  	if (!timer_needed)  		return; -	local->sta_cleanup.expires = -		round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL); -	add_timer(&local->sta_cleanup); +	mod_timer(&local->sta_cleanup, +		  round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL));  }  void sta_info_init(struct ieee80211_local *local)  { -	spin_lock_init(&local->sta_lock); +	spin_lock_init(&local->tim_lock);  	mutex_init(&local->sta_mtx);  	INIT_LIST_HEAD(&local->sta_list); -	INIT_LIST_HEAD(&local->sta_pending_list); -	INIT_WORK(&local->sta_finish_work, sta_info_finish_work);  	setup_timer(&local->sta_cleanup, sta_info_cleanup,  		    (unsigned long)local); -	local->sta_cleanup.expires = -		round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL); -} - -int sta_info_start(struct ieee80211_local *local) -{ -	add_timer(&local->sta_cleanup); -	return 0;  }  void sta_info_stop(struct ieee80211_local *local)  { -	del_timer(&local->sta_cleanup); -	sta_info_flush(local, NULL); +	del_timer_sync(&local->sta_cleanup);  } -/** - * sta_info_flush - flush matching STA entries from the STA table - * - * Returns the number of removed STA entries. - * - * @local: local interface data - * @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs - */ -int sta_info_flush(struct ieee80211_local *local, -		   struct ieee80211_sub_if_data *sdata) + +int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans)  { +	struct ieee80211_local *local = sdata->local;  	struct sta_info *sta, *tmp; +	LIST_HEAD(free_list);  	int ret = 0;  	might_sleep(); -	mutex_lock(&local->sta_mtx); - -	sta_info_finish_pending(local); +	WARN_ON(vlans && sdata->vif.type != NL80211_IFTYPE_AP); +	WARN_ON(vlans && !sdata->bss); +	mutex_lock(&local->sta_mtx);  	list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { -		if (!sdata || sdata == sta->sdata) -			WARN_ON(__sta_info_destroy(sta)); +		if (sdata == sta->sdata || +		    (vlans && sdata->bss == sta->sdata->bss)) { +			if (!WARN_ON(__sta_info_destroy_part1(sta))) +				list_add(&sta->free_list, &free_list); +			ret++; +		} +	} + +	if (!list_empty(&free_list)) { +		synchronize_net(); +		list_for_each_entry_safe(sta, tmp, &free_list, free_list) +			__sta_info_destroy_part2(sta);  	}  	mutex_unlock(&local->sta_mtx); @@ -830,14 +1030,23 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,  	struct sta_info *sta, *tmp;  	mutex_lock(&local->sta_mtx); -	list_for_each_entry_safe(sta, tmp, &local->sta_list, list) + +	list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { +		if (sdata != sta->sdata) +			continue; +  		if (time_after(jiffies, sta->last_rx + exp_time)) { -#ifdef CONFIG_MAC80211_IBSS_DEBUG -			printk(KERN_DEBUG "%s: expiring inactive STA %pM\n", -			       sdata->name, sta->sta.addr); -#endif +			sta_dbg(sta->sdata, "expiring inactive STA %pM\n", +				sta->sta.addr); + +			if (ieee80211_vif_is_mesh(&sdata->vif) && +			    test_sta_flag(sta, WLAN_STA_PS_STA)) +				atomic_dec(&sdata->u.mesh.ps.num_sta_ps); +  			WARN_ON(__sta_info_destroy(sta));  		} +	} +  	mutex_unlock(&local->sta_mtx);  } @@ -853,7 +1062,7 @@ struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw,  	 */  	for_each_sta_info(hw_to_local(hw), addr, sta, nxt) {  		if (localaddr && -		    compare_ether_addr(sta->sdata->vif.addr, localaddr) != 0) +		    !ether_addr_equal(sta->sdata->vif.addr, localaddr))  			continue;  		if (!sta->uploaded)  			return NULL; @@ -883,96 +1092,448 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif,  }  EXPORT_SYMBOL(ieee80211_find_sta); -static void clear_sta_ps_flags(void *_sta) -{ -	struct sta_info *sta = _sta; - -	clear_sta_flags(sta, WLAN_STA_PS_DRIVER | WLAN_STA_PS_STA); -} -  /* powersave support code */  void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)  {  	struct ieee80211_sub_if_data *sdata = sta->sdata;  	struct ieee80211_local *local = sdata->local; -	int sent, buffered; +	struct sk_buff_head pending; +	int filtered = 0, buffered = 0, ac; +	unsigned long flags; +	struct ps_data *ps; + +	if (sdata->vif.type == NL80211_IFTYPE_AP || +	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +		ps = &sdata->bss->ps; +	else if (ieee80211_vif_is_mesh(&sdata->vif)) +		ps = &sdata->u.mesh.ps; +	else +		return; + +	clear_sta_flag(sta, WLAN_STA_SP); -	drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); +	BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1); +	sta->driver_buffered_tids = 0; -	if (!skb_queue_empty(&sta->ps_tx_buf)) -		sta_info_clear_tim_bit(sta); +	if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) +		drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); +	skb_queue_head_init(&pending); + +	/* sync with ieee80211_tx_h_unicast_ps_buf */ +	spin_lock(&sta->ps_lock);  	/* Send all buffered frames to the station */ -	sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered); -	buffered = ieee80211_add_pending_skbs_fn(local, &sta->ps_tx_buf, -						 clear_sta_ps_flags, sta); -	sent += buffered; +	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { +		int count = skb_queue_len(&pending), tmp; + +		spin_lock_irqsave(&sta->tx_filtered[ac].lock, flags); +		skb_queue_splice_tail_init(&sta->tx_filtered[ac], &pending); +		spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags); +		tmp = skb_queue_len(&pending); +		filtered += tmp - count; +		count = tmp; + +		spin_lock_irqsave(&sta->ps_tx_buf[ac].lock, flags); +		skb_queue_splice_tail_init(&sta->ps_tx_buf[ac], &pending); +		spin_unlock_irqrestore(&sta->ps_tx_buf[ac].lock, flags); +		tmp = skb_queue_len(&pending); +		buffered += tmp - count; +	} + +	ieee80211_add_pending_skbs(local, &pending); +	clear_sta_flag(sta, WLAN_STA_PS_DRIVER); +	clear_sta_flag(sta, WLAN_STA_PS_STA); +	spin_unlock(&sta->ps_lock); + +	atomic_dec(&ps->num_sta_ps); + +	/* This station just woke up and isn't aware of our SMPS state */ +	if (!ieee80211_vif_is_mesh(&sdata->vif) && +	    !ieee80211_smps_is_restrictive(sta->known_smps_mode, +					   sdata->smps_mode) && +	    sta->known_smps_mode != sdata->bss->req_smps && +	    sta_info_tx_streams(sta) != 1) { +		ht_dbg(sdata, +		       "%pM just woke up and MIMO capable - update SMPS\n", +		       sta->sta.addr); +		ieee80211_send_smps_action(sdata, sdata->bss->req_smps, +					   sta->sta.addr, +					   sdata->vif.bss_conf.bssid); +	} +  	local->total_ps_buffered -= buffered; -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG -	printk(KERN_DEBUG "%s: STA %pM aid %d sending %d filtered/%d PS frames " -	       "since STA not sleeping anymore\n", sdata->name, -	       sta->sta.addr, sta->sta.aid, sent - buffered, buffered); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ +	sta_info_recalc_tim(sta); + +	ps_dbg(sdata, +	       "STA %pM aid %d sending %d filtered/%d PS frames since STA not sleeping anymore\n", +	       sta->sta.addr, sta->sta.aid, filtered, buffered);  } -void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) +static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, +					 struct sta_info *sta, int tid, +					 enum ieee80211_frame_release_type reason, +					 bool call_driver)  { -	struct ieee80211_sub_if_data *sdata = sta->sdata;  	struct ieee80211_local *local = sdata->local; +	struct ieee80211_qos_hdr *nullfunc;  	struct sk_buff *skb; -	int no_pending_pkts; +	int size = sizeof(*nullfunc); +	__le16 fc; +	bool qos = test_sta_flag(sta, WLAN_STA_WME); +	struct ieee80211_tx_info *info; +	struct ieee80211_chanctx_conf *chanctx_conf; -	skb = skb_dequeue(&sta->tx_filtered); -	if (!skb) { -		skb = skb_dequeue(&sta->ps_tx_buf); -		if (skb) -			local->total_ps_buffered--; +	if (qos) { +		fc = cpu_to_le16(IEEE80211_FTYPE_DATA | +				 IEEE80211_STYPE_QOS_NULLFUNC | +				 IEEE80211_FCTL_FROMDS); +	} else { +		size -= 2; +		fc = cpu_to_le16(IEEE80211_FTYPE_DATA | +				 IEEE80211_STYPE_NULLFUNC | +				 IEEE80211_FCTL_FROMDS);  	} -	no_pending_pkts = skb_queue_empty(&sta->tx_filtered) && -		skb_queue_empty(&sta->ps_tx_buf); -	if (skb) { -		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); -		struct ieee80211_hdr *hdr = -			(struct ieee80211_hdr *) skb->data; +	skb = dev_alloc_skb(local->hw.extra_tx_headroom + size); +	if (!skb) +		return; + +	skb_reserve(skb, local->hw.extra_tx_headroom); + +	nullfunc = (void *) skb_put(skb, size); +	nullfunc->frame_control = fc; +	nullfunc->duration_id = 0; +	memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN); +	memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); +	memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN); +	nullfunc->seq_ctrl = 0; + +	skb->priority = tid; +	skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]); +	if (qos) { +		nullfunc->qos_ctrl = cpu_to_le16(tid); + +		if (reason == IEEE80211_FRAME_RELEASE_UAPSD) +			nullfunc->qos_ctrl |= +				cpu_to_le16(IEEE80211_QOS_CTL_EOSP); +	} + +	info = IEEE80211_SKB_CB(skb); + +	/* +	 * Tell TX path to send this frame even though the +	 * STA may still remain is PS mode after this frame +	 * exchange. Also set EOSP to indicate this packet +	 * ends the poll/service period. +	 */ +	info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER | +		       IEEE80211_TX_CTL_PS_RESPONSE | +		       IEEE80211_TX_STATUS_EOSP | +		       IEEE80211_TX_CTL_REQ_TX_STATUS; + +	if (call_driver) +		drv_allow_buffered_frames(local, sta, BIT(tid), 1, +					  reason, false); + +	skb->dev = sdata->dev; + +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	if (WARN_ON(!chanctx_conf)) { +		rcu_read_unlock(); +		kfree_skb(skb); +		return; +	} + +	ieee80211_xmit(sdata, skb, chanctx_conf->def.chan->band); +	rcu_read_unlock(); +} + +static int find_highest_prio_tid(unsigned long tids) +{ +	/* lower 3 TIDs aren't ordered perfectly */ +	if (tids & 0xF8) +		return fls(tids) - 1; +	/* TID 0 is BE just like TID 3 */ +	if (tids & BIT(0)) +		return 0; +	return fls(tids) - 1; +} + +static void +ieee80211_sta_ps_deliver_response(struct sta_info *sta, +				  int n_frames, u8 ignored_acs, +				  enum ieee80211_frame_release_type reason) +{ +	struct ieee80211_sub_if_data *sdata = sta->sdata; +	struct ieee80211_local *local = sdata->local; +	bool more_data = false; +	int ac; +	unsigned long driver_release_tids = 0; +	struct sk_buff_head frames; + +	/* Service or PS-Poll period starts */ +	set_sta_flag(sta, WLAN_STA_SP); + +	__skb_queue_head_init(&frames); + +	/* Get response frame(s) and more data bit for the last one. */ +	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { +		unsigned long tids; + +		if (ignored_acs & BIT(ac)) +			continue; + +		tids = ieee80211_tids_for_ac(ac); + +		/* if we already have frames from software, then we can't also +		 * release from hardware queues +		 */ +		if (skb_queue_empty(&frames)) +			driver_release_tids |= sta->driver_buffered_tids & tids; + +		if (driver_release_tids) { +			/* If the driver has data on more than one TID then +			 * certainly there's more data if we release just a +			 * single frame now (from a single TID). This will +			 * only happen for PS-Poll. +			 */ +			if (reason == IEEE80211_FRAME_RELEASE_PSPOLL && +			    hweight16(driver_release_tids) > 1) { +				more_data = true; +				driver_release_tids = +					BIT(find_highest_prio_tid( +						driver_release_tids)); +				break; +			} +		} else { +			struct sk_buff *skb; + +			while (n_frames > 0) { +				skb = skb_dequeue(&sta->tx_filtered[ac]); +				if (!skb) { +					skb = skb_dequeue( +						&sta->ps_tx_buf[ac]); +					if (skb) +						local->total_ps_buffered--; +				} +				if (!skb) +					break; +				n_frames--; +				__skb_queue_tail(&frames, skb); +			} +		} + +		/* If we have more frames buffered on this AC, then set the +		 * more-data bit and abort the loop since we can't send more +		 * data from other ACs before the buffered frames from this. +		 */ +		if (!skb_queue_empty(&sta->tx_filtered[ac]) || +		    !skb_queue_empty(&sta->ps_tx_buf[ac])) { +			more_data = true; +			break; +		} +	} + +	if (skb_queue_empty(&frames) && !driver_release_tids) { +		int tid;  		/* -		 * Tell TX path to send this frame even though the STA may -		 * still remain is PS mode after this frame exchange. +		 * For PS-Poll, this can only happen due to a race condition +		 * when we set the TIM bit and the station notices it, but +		 * before it can poll for the frame we expire it. +		 * +		 * For uAPSD, this is said in the standard (11.2.1.5 h): +		 *	At each unscheduled SP for a non-AP STA, the AP shall +		 *	attempt to transmit at least one MSDU or MMPDU, but no +		 *	more than the value specified in the Max SP Length field +		 *	in the QoS Capability element from delivery-enabled ACs, +		 *	that are destined for the non-AP STA. +		 * +		 * Since we have no other MSDU/MMPDU, transmit a QoS null frame.  		 */ -		info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE; - -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG -		printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n", -		       sta->sta.addr, sta->sta.aid, -		       skb_queue_len(&sta->ps_tx_buf)); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ - -		/* Use MoreData flag to indicate whether there are more -		 * buffered frames for this STA */ -		if (no_pending_pkts) -			hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA); -		else -			hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); -		ieee80211_add_pending_skb(local, skb); +		/* This will evaluate to 1, 3, 5 or 7. */ +		tid = 7 - ((ffs(~ignored_acs) - 1) << 1); + +		ieee80211_send_null_response(sdata, sta, tid, reason, true); +	} else if (!driver_release_tids) { +		struct sk_buff_head pending; +		struct sk_buff *skb; +		int num = 0; +		u16 tids = 0; +		bool need_null = false; + +		skb_queue_head_init(&pending); + +		while ((skb = __skb_dequeue(&frames))) { +			struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +			struct ieee80211_hdr *hdr = (void *) skb->data; +			u8 *qoshdr = NULL; + +			num++; + +			/* +			 * Tell TX path to send this frame even though the +			 * STA may still remain is PS mode after this frame +			 * exchange. +			 */ +			info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER | +				       IEEE80211_TX_CTL_PS_RESPONSE; + +			/* +			 * Use MoreData flag to indicate whether there are +			 * more buffered frames for this STA +			 */ +			if (more_data || !skb_queue_empty(&frames)) +				hdr->frame_control |= +					cpu_to_le16(IEEE80211_FCTL_MOREDATA); +			else +				hdr->frame_control &= +					cpu_to_le16(~IEEE80211_FCTL_MOREDATA); + +			if (ieee80211_is_data_qos(hdr->frame_control) || +			    ieee80211_is_qos_nullfunc(hdr->frame_control)) +				qoshdr = ieee80211_get_qos_ctl(hdr); + +			tids |= BIT(skb->priority); + +			__skb_queue_tail(&pending, skb); + +			/* end service period after last frame or add one */ +			if (!skb_queue_empty(&frames)) +				continue; + +			if (reason != IEEE80211_FRAME_RELEASE_UAPSD) { +				/* for PS-Poll, there's only one frame */ +				info->flags |= IEEE80211_TX_STATUS_EOSP | +					       IEEE80211_TX_CTL_REQ_TX_STATUS; +				break; +			} + +			/* For uAPSD, things are a bit more complicated. If the +			 * last frame has a QoS header (i.e. is a QoS-data or +			 * QoS-nulldata frame) then just set the EOSP bit there +			 * and be done. +			 * If the frame doesn't have a QoS header (which means +			 * it should be a bufferable MMPDU) then we can't set +			 * the EOSP bit in the QoS header; add a QoS-nulldata +			 * frame to the list to send it after the MMPDU. +			 * +			 * Note that this code is only in the mac80211-release +			 * code path, we assume that the driver will not buffer +			 * anything but QoS-data frames, or if it does, will +			 * create the QoS-nulldata frame by itself if needed. +			 * +			 * Cf. 802.11-2012 10.2.1.10 (c). +			 */ +			if (qoshdr) { +				*qoshdr |= IEEE80211_QOS_CTL_EOSP; + +				info->flags |= IEEE80211_TX_STATUS_EOSP | +					       IEEE80211_TX_CTL_REQ_TX_STATUS; +			} else { +				/* The standard isn't completely clear on this +				 * as it says the more-data bit should be set +				 * if there are more BUs. The QoS-Null frame +				 * we're about to send isn't buffered yet, we +				 * only create it below, but let's pretend it +				 * was buffered just in case some clients only +				 * expect more-data=0 when eosp=1. +				 */ +				hdr->frame_control |= +					cpu_to_le16(IEEE80211_FCTL_MOREDATA); +				need_null = true; +				num++; +			} +			break; +		} + +		drv_allow_buffered_frames(local, sta, tids, num, +					  reason, more_data); -		if (no_pending_pkts) -			sta_info_clear_tim_bit(sta); -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG +		ieee80211_add_pending_skbs(local, &pending); + +		if (need_null) +			ieee80211_send_null_response( +				sdata, sta, find_highest_prio_tid(tids), +				reason, false); + +		sta_info_recalc_tim(sta);  	} else {  		/* -		 * FIXME: This can be the result of a race condition between -		 *	  us expiring a frame and the station polling for it. -		 *	  Should we send it a null-func frame indicating we -		 *	  have nothing buffered for it? +		 * We need to release a frame that is buffered somewhere in the +		 * driver ... it'll have to handle that. +		 * Note that the driver also has to check the number of frames +		 * on the TIDs we're releasing from - if there are more than +		 * n_frames it has to set the more-data bit (if we didn't ask +		 * it to set it anyway due to other buffered frames); if there +		 * are fewer than n_frames it has to make sure to adjust that +		 * to allow the service period to end properly.  		 */ -		printk(KERN_DEBUG "%s: STA %pM sent PS Poll even " -		       "though there are no buffered frames for it\n", -		       sdata->name, sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ +		drv_release_buffered_frames(local, sta, driver_release_tids, +					    n_frames, reason, more_data); + +		/* +		 * Note that we don't recalculate the TIM bit here as it would +		 * most likely have no effect at all unless the driver told us +		 * 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. +		 */ +	} +} + +void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta) +{ +	u8 ignore_for_response = sta->sta.uapsd_queues; + +	/* +	 * If all ACs are delivery-enabled then we should reply +	 * from any of them, if only some are enabled we reply +	 * only from the non-enabled ones. +	 */ +	if (ignore_for_response == BIT(IEEE80211_NUM_ACS) - 1) +		ignore_for_response = 0; + +	ieee80211_sta_ps_deliver_response(sta, 1, ignore_for_response, +					  IEEE80211_FRAME_RELEASE_PSPOLL); +} + +void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta) +{ +	int n_frames = sta->sta.max_sp; +	u8 delivery_enabled = sta->sta.uapsd_queues; + +	/* +	 * If we ever grow support for TSPEC this might happen if +	 * the TSPEC update from hostapd comes in between a trigger +	 * frame setting WLAN_STA_UAPSD in the RX path and this +	 * actually getting called. +	 */ +	if (!delivery_enabled) +		return; + +	switch (sta->sta.max_sp) { +	case 1: +		n_frames = 2; +		break; +	case 2: +		n_frames = 4; +		break; +	case 3: +		n_frames = 6; +		break; +	case 0: +		/* XXX: what is a good value? */ +		n_frames = 8; +		break;  	} + +	ieee80211_sta_ps_deliver_response(sta, n_frames, ~delivery_enabled, +					  IEEE80211_FRAME_RELEASE_UAPSD);  }  void ieee80211_sta_block_awake(struct ieee80211_hw *hw, @@ -983,8 +1544,163 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,  	trace_api_sta_block_awake(sta->local, pubsta, block);  	if (block) -		set_sta_flags(sta, WLAN_STA_PS_DRIVER); -	else if (test_sta_flags(sta, WLAN_STA_PS_DRIVER)) +		set_sta_flag(sta, WLAN_STA_PS_DRIVER); +	else if (test_sta_flag(sta, WLAN_STA_PS_DRIVER))  		ieee80211_queue_work(hw, &sta->drv_unblock_wk);  }  EXPORT_SYMBOL(ieee80211_sta_block_awake); + +void ieee80211_sta_eosp(struct ieee80211_sta *pubsta) +{ +	struct sta_info *sta = container_of(pubsta, struct sta_info, sta); +	struct ieee80211_local *local = sta->local; + +	trace_api_eosp(local, pubsta); + +	clear_sta_flag(sta, WLAN_STA_SP); +} +EXPORT_SYMBOL(ieee80211_sta_eosp); + +void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, +				u8 tid, bool buffered) +{ +	struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + +	if (WARN_ON(tid >= IEEE80211_NUM_TIDS)) +		return; + +	trace_api_sta_set_buffered(sta->local, pubsta, tid, buffered); + +	if (buffered) +		set_bit(tid, &sta->driver_buffered_tids); +	else +		clear_bit(tid, &sta->driver_buffered_tids); + +	sta_info_recalc_tim(sta); +} +EXPORT_SYMBOL(ieee80211_sta_set_buffered); + +int sta_info_move_state(struct sta_info *sta, +			enum ieee80211_sta_state new_state) +{ +	might_sleep(); + +	if (sta->sta_state == new_state) +		return 0; + +	/* check allowed transitions first */ + +	switch (new_state) { +	case IEEE80211_STA_NONE: +		if (sta->sta_state != IEEE80211_STA_AUTH) +			return -EINVAL; +		break; +	case IEEE80211_STA_AUTH: +		if (sta->sta_state != IEEE80211_STA_NONE && +		    sta->sta_state != IEEE80211_STA_ASSOC) +			return -EINVAL; +		break; +	case IEEE80211_STA_ASSOC: +		if (sta->sta_state != IEEE80211_STA_AUTH && +		    sta->sta_state != IEEE80211_STA_AUTHORIZED) +			return -EINVAL; +		break; +	case IEEE80211_STA_AUTHORIZED: +		if (sta->sta_state != IEEE80211_STA_ASSOC) +			return -EINVAL; +		break; +	default: +		WARN(1, "invalid state %d", new_state); +		return -EINVAL; +	} + +	sta_dbg(sta->sdata, "moving STA %pM to state %d\n", +		sta->sta.addr, new_state); + +	/* +	 * notify the driver before the actual changes so it can +	 * fail the transition +	 */ +	if (test_sta_flag(sta, WLAN_STA_INSERTED)) { +		int err = drv_sta_state(sta->local, sta->sdata, sta, +					sta->sta_state, new_state); +		if (err) +			return err; +	} + +	/* reflect the change in all state variables */ + +	switch (new_state) { +	case IEEE80211_STA_NONE: +		if (sta->sta_state == IEEE80211_STA_AUTH) +			clear_bit(WLAN_STA_AUTH, &sta->_flags); +		break; +	case IEEE80211_STA_AUTH: +		if (sta->sta_state == IEEE80211_STA_NONE) +			set_bit(WLAN_STA_AUTH, &sta->_flags); +		else if (sta->sta_state == IEEE80211_STA_ASSOC) +			clear_bit(WLAN_STA_ASSOC, &sta->_flags); +		break; +	case IEEE80211_STA_ASSOC: +		if (sta->sta_state == IEEE80211_STA_AUTH) { +			set_bit(WLAN_STA_ASSOC, &sta->_flags); +		} else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) { +			if (sta->sdata->vif.type == NL80211_IFTYPE_AP || +			    (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && +			     !sta->sdata->u.vlan.sta)) +				atomic_dec(&sta->sdata->bss->num_mcast_sta); +			clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags); +		} +		break; +	case IEEE80211_STA_AUTHORIZED: +		if (sta->sta_state == IEEE80211_STA_ASSOC) { +			if (sta->sdata->vif.type == NL80211_IFTYPE_AP || +			    (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && +			     !sta->sdata->u.vlan.sta)) +				atomic_inc(&sta->sdata->bss->num_mcast_sta); +			set_bit(WLAN_STA_AUTHORIZED, &sta->_flags); +		} +		break; +	default: +		break; +	} + +	sta->sta_state = new_state; + +	return 0; +} + +u8 sta_info_tx_streams(struct sta_info *sta) +{ +	struct ieee80211_sta_ht_cap *ht_cap = &sta->sta.ht_cap; +	u8 rx_streams; + +	if (!sta->sta.ht_cap.ht_supported) +		return 1; + +	if (sta->sta.vht_cap.vht_supported) { +		int i; +		u16 tx_mcs_map = +			le16_to_cpu(sta->sta.vht_cap.vht_mcs.tx_mcs_map); + +		for (i = 7; i >= 0; i--) +			if ((tx_mcs_map & (0x3 << (i * 2))) != +			    IEEE80211_VHT_MCS_NOT_SUPPORTED) +				return i + 1; +	} + +	if (ht_cap->mcs.rx_mask[3]) +		rx_streams = 4; +	else if (ht_cap->mcs.rx_mask[2]) +		rx_streams = 3; +	else if (ht_cap->mcs.rx_mask[1]) +		rx_streams = 2; +	else +		rx_streams = 1; + +	if (!(ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_RX_DIFF)) +		return rx_streams; + +	return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) +			>> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; +} diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index b562d9b6a70..4acc5fc402f 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -13,12 +13,15 @@  #include <linux/types.h>  #include <linux/if_ether.h>  #include <linux/workqueue.h> +#include <linux/average.h> +#include <linux/etherdevice.h>  #include "key.h"  /**   * enum ieee80211_sta_info_flags - Stations flags   * - * These flags are used with &struct sta_info's @flags member. + * These flags are used with &struct sta_info's @flags member, but + * only indirectly with set_sta_flag() and friends.   *   * @WLAN_STA_AUTH: Station is authenticated.   * @WLAN_STA_ASSOC: Station is associated. @@ -28,7 +31,6 @@   *	when virtual port control is not in use.   * @WLAN_STA_SHORT_PREAMBLE: Station is capable of receiving short-preamble   *	frames. - * @WLAN_STA_ASSOC_AP: We're associated to that station, it is an AP.   * @WLAN_STA_WME: Station is a QoS-STA.   * @WLAN_STA_WDS: Station is one of our WDS peers.   * @WLAN_STA_CLEAR_PS_FILT: Clear PS filter in hardware (using the @@ -42,26 +44,50 @@   *	be in the queues   * @WLAN_STA_PSPOLL: Station sent PS-poll while driver was keeping   *	station in power-save mode, reply when the driver unblocks. + * @WLAN_STA_TDLS_PEER: Station is a TDLS peer. + * @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct + *	packets. This means the link is enabled. + * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was + *	keeping station in power-save mode, reply when the driver + *	unblocks the station. + * @WLAN_STA_SP: Station is in a service period, so don't try to + *	reply to other uAPSD trigger frames or PS-Poll. + * @WLAN_STA_4ADDR_EVENT: 4-addr event was already sent for this frame. + * @WLAN_STA_INSERTED: This station is inserted into the hash table. + * @WLAN_STA_RATE_CONTROL: rate control was initialized for this station. + * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid. + * @WLAN_STA_MPSP_OWNER: local STA is owner of a mesh Peer Service Period. + * @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP.   */  enum ieee80211_sta_info_flags { -	WLAN_STA_AUTH		= 1<<0, -	WLAN_STA_ASSOC		= 1<<1, -	WLAN_STA_PS_STA		= 1<<2, -	WLAN_STA_AUTHORIZED	= 1<<3, -	WLAN_STA_SHORT_PREAMBLE	= 1<<4, -	WLAN_STA_ASSOC_AP	= 1<<5, -	WLAN_STA_WME		= 1<<6, -	WLAN_STA_WDS		= 1<<7, -	WLAN_STA_CLEAR_PS_FILT	= 1<<9, -	WLAN_STA_MFP		= 1<<10, -	WLAN_STA_BLOCK_BA	= 1<<11, -	WLAN_STA_PS_DRIVER	= 1<<12, -	WLAN_STA_PSPOLL		= 1<<13, +	WLAN_STA_AUTH, +	WLAN_STA_ASSOC, +	WLAN_STA_PS_STA, +	WLAN_STA_AUTHORIZED, +	WLAN_STA_SHORT_PREAMBLE, +	WLAN_STA_WME, +	WLAN_STA_WDS, +	WLAN_STA_CLEAR_PS_FILT, +	WLAN_STA_MFP, +	WLAN_STA_BLOCK_BA, +	WLAN_STA_PS_DRIVER, +	WLAN_STA_PSPOLL, +	WLAN_STA_TDLS_PEER, +	WLAN_STA_TDLS_PEER_AUTH, +	WLAN_STA_UAPSD, +	WLAN_STA_SP, +	WLAN_STA_4ADDR_EVENT, +	WLAN_STA_INSERTED, +	WLAN_STA_RATE_CONTROL, +	WLAN_STA_TOFFSET_KNOWN, +	WLAN_STA_MPSP_OWNER, +	WLAN_STA_MPSP_RECIPIENT,  }; -#define STA_TID_NUM 16  #define ADDBA_RESP_INTERVAL HZ -#define HT_AGG_MAX_RETRIES		0x3 +#define HT_AGG_MAX_RETRIES		15 +#define HT_AGG_BURST_RETRIES		3 +#define HT_AGG_RETRIES_PERIOD		(15 * HZ)  #define HT_AGG_STATE_DRV_READY		0  #define HT_AGG_STATE_RESPONSE_RECEIVED	1 @@ -70,33 +96,54 @@ enum ieee80211_sta_info_flags {  #define HT_AGG_STATE_WANT_START		4  #define HT_AGG_STATE_WANT_STOP		5 +enum ieee80211_agg_stop_reason { +	AGG_STOP_DECLINED, +	AGG_STOP_LOCAL_REQUEST, +	AGG_STOP_PEER_REQUEST, +	AGG_STOP_DESTROY_STA, +}; +  /**   * struct tid_ampdu_tx - TID aggregation information (Tx).   *   * @rcu_head: rcu head for freeing structure + * @session_timer: check if we keep Tx-ing on the TID (by timeout value)   * @addba_resp_timer: timer for peer's response to addba request   * @pending: pending frames queue -- use sta's spinlock to protect   * @dialog_token: dialog token for aggregation session + * @timeout: session timeout value to be filled in ADDBA requests   * @state: session state (see above) + * @last_tx: jiffies of last tx activity   * @stop_initiator: initiator of a session stop   * @tx_stop: TX DelBA frame when stopping + * @buf_size: reorder buffer size at receiver + * @failed_bar_ssn: ssn of the last failed BAR tx attempt + * @bar_pending: BAR needs to be re-sent + * + * This structure's lifetime is managed by RCU, assignments to + * the array holding it must hold the aggregation mutex.   * - * This structure is protected by RCU and the per-station - * spinlock. Assignments to the array holding it must hold - * the spinlock, only the TX path can access it under RCU - * lock-free if, and only if, the state has  the flag - * %HT_AGG_STATE_OPERATIONAL set. Otherwise, the TX path - * must also acquire the spinlock and re-check the state, - * see comments in the tx code touching it. + * The TX path can access it under RCU lock-free if, and + * only if, the state has the flag %HT_AGG_STATE_OPERATIONAL + * set. Otherwise, the TX path must also acquire the spinlock + * and re-check the state, see comments in the tx code + * touching it.   */  struct tid_ampdu_tx {  	struct rcu_head rcu_head; +	struct timer_list session_timer;  	struct timer_list addba_resp_timer;  	struct sk_buff_head pending;  	unsigned long state; +	unsigned long last_tx; +	u16 timeout;  	u8 dialog_token;  	u8 stop_initiator;  	bool tx_stop; +	u8 buf_size; + +	u16 failed_bar_ssn; +	bool bar_pending;  };  /** @@ -106,6 +153,7 @@ struct tid_ampdu_tx {   * @reorder_time: jiffies when skb was added   * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)   * @reorder_timer: releases expired frames from the reorder buffer. + * @last_rx: jiffies of last rx activity   * @head_seq_num: head sequence number in reordering buffer.   * @stored_mpdu_num: number of MPDUs in reordering buffer   * @ssn: Starting Sequence Number expected to be aggregated. @@ -115,15 +163,13 @@ struct tid_ampdu_tx {   * @rcu_head: RCU head used for freeing this struct   * @reorder_lock: serializes access to reorder buffer, see below.   * - * This structure is protected by RCU and the per-station - * spinlock. Assignments to the array holding it must hold - * the spinlock. + * This structure's lifetime is managed by RCU, assignments to + * the array holding it must hold the aggregation mutex.   * - * The @reorder_lock is used to protect the variables and - * arrays such as @reorder_buf, @reorder_time, @head_seq_num, - * @stored_mpdu_num and @reorder_time from being corrupted by - * concurrent access of the RX path and the expired frame - * release timer. + * The @reorder_lock is used to protect the members of this + * struct, except for @timeout, @buf_size and @dialog_token, + * which are constant across the lifetime of the struct (the + * dialog token being used only for debugging).   */  struct tid_ampdu_rx {  	struct rcu_head rcu_head; @@ -132,6 +178,7 @@ struct tid_ampdu_rx {  	unsigned long *reorder_time;  	struct timer_list session_timer;  	struct timer_list reorder_timer; +	unsigned long last_rx;  	u16 head_seq_num;  	u16 stored_mpdu_num;  	u16 ssn; @@ -145,48 +192,52 @@ struct tid_ampdu_rx {   *   * @tid_rx: aggregation info for Rx per TID -- RCU protected   * @tid_tx: aggregation info for Tx per TID + * @tid_start_tx: sessions where start was requested   * @addba_req_num: number of times addBA request has been sent. + * @last_addba_req_time: timestamp of the last addBA request.   * @dialog_token_allocator: dialog token enumerator for each new session;   * @work: work struct for starting/stopping aggregation   * @tid_rx_timer_expired: bitmap indicating on which TIDs the   *	RX timer expired until the work for it runs + * @tid_rx_stop_requested:  bitmap indicating which BA sessions per TID the + *	driver requested to close until the work for it runs   * @mtx: mutex to protect all TX data (except non-NULL assignments   *	to tid_tx[idx], which are protected by the sta spinlock) + *	tid_start_tx is also protected by sta->lock.   */  struct sta_ampdu_mlme {  	struct mutex mtx;  	/* rx */ -	struct tid_ampdu_rx *tid_rx[STA_TID_NUM]; -	unsigned long tid_rx_timer_expired[BITS_TO_LONGS(STA_TID_NUM)]; +	struct tid_ampdu_rx __rcu *tid_rx[IEEE80211_NUM_TIDS]; +	unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; +	unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];  	/* tx */  	struct work_struct work; -	struct tid_ampdu_tx *tid_tx[STA_TID_NUM]; -	u8 addba_req_num[STA_TID_NUM]; +	struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS]; +	struct tid_ampdu_tx *tid_start_tx[IEEE80211_NUM_TIDS]; +	unsigned long last_addba_req_time[IEEE80211_NUM_TIDS]; +	u8 addba_req_num[IEEE80211_NUM_TIDS];  	u8 dialog_token_allocator;  }; - -/** - * enum plink_state - state of a mesh peer link finite state machine +/* + * struct ieee80211_tx_latency_stat - Tx latency statistics + * + * Measures TX latency and jitter for a station per TID.   * - * @PLINK_LISTEN: initial state, considered the implicit state of non existant - * 	mesh peer links - * @PLINK_OPN_SNT: mesh plink open frame has been sent to this mesh peer - * @PLINK_OPN_RCVD: mesh plink open frame has been received from this mesh peer - * @PLINK_CNF_RCVD: mesh plink confirm frame has been received from this mesh - * 	peer - * @PLINK_ESTAB: mesh peer link is established - * @PLINK_HOLDING: mesh peer link is being closed or cancelled - * @PLINK_BLOCKED: all frames transmitted from this mesh plink are discarded + * @max: worst case latency + * @sum: sum of all latencies + * @counter: amount of Tx frames sent from interface + * @bins: each bin counts how many frames transmitted within a certain + * latency range. when disabled it is NULL. + * @bin_count: amount of bins.   */ -enum plink_state { -	PLINK_LISTEN, -	PLINK_OPN_SNT, -	PLINK_OPN_RCVD, -	PLINK_CNF_RCVD, -	PLINK_ESTAB, -	PLINK_HOLDING, -	PLINK_BLOCKED +struct ieee80211_tx_latency_stat { +	u32 max; +	u32 sum; +	u32 counter; +	u32 *bins; +	u32 bin_count;  };  /** @@ -196,34 +247,46 @@ enum plink_state {   * mac80211 is communicating with.   *   * @list: global linked list entry + * @free_list: list entry for keeping track of stations to free   * @hnext: hash table linked list pointer   * @local: pointer to the global information   * @sdata: virtual interface this station belongs to - * @ptk: peer key negotiated with this station, if any + * @ptk: peer keys negotiated with this station, if any + * @ptk_idx: last installed peer key index   * @gtk: group keys negotiated with this station, if any + * @gtk_idx: last installed group key index   * @rate_ctrl: rate control algorithm reference   * @rate_ctrl_priv: rate control private per-STA pointer   * @last_tx_rate: rate used for last transmit, to report to userspace as   *	"the" transmit rate + * @last_rx_rate_idx: rx status rate index of the last data packet + * @last_rx_rate_flag: rx status flag of the last data packet + * @last_rx_rate_vht_flag: rx status vht flag of the last data packet + * @last_rx_rate_vht_nss: rx status nss of last data packet   * @lock: used for locking all fields that require locking, see comments   *	in the header file. - * @flaglock: spinlock for flags accesses   * @drv_unblock_wk: used for driver PS unblocking   * @listen_interval: listen interval of this station, when we're acting as AP - * @flags: STA flags, see &enum ieee80211_sta_info_flags - * @ps_tx_buf: buffer of frames to transmit to this station - *	when it leaves power saving state - * @tx_filtered: buffer of frames we already tried to transmit - *	but were filtered by hardware due to STA having entered - *	power saving state + * @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly + * @ps_lock: used for powersave (when mac80211 is the AP) related locking + * @ps_tx_buf: buffers (per AC) of frames to transmit to this station + *	when it leaves power saving state or polls + * @tx_filtered: buffers (per AC) of frames we already tried to + *	transmit but were filtered by hardware due to STA having + *	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   * @rx_packets: Number of MSDUs received from this STA   * @rx_bytes: Number of bytes received from this STA   * @wep_weak_iv_count: number of weak WEP IVs received from this station   * @last_rx: time (in jiffies) when last frame was received from this STA + * @last_connected: time (in seconds) when a station got connected   * @num_duplicates: number of duplicate frames received from this STA   * @rx_fragments: number of received MPDUs   * @rx_dropped: number of dropped MPDUs from this STA   * @last_signal: signal of last received frame from this STA + * @avg_signal: moving average of signal of received frames from this STA + * @last_ack_signal: signal of last received Ack frame from this STA   * @last_seq_ctrl: last received seq/frag number from this STA (per RX queue)   * @tx_filtered_count: number of frames the hardware filtered for this STA   * @tx_retry_failed: number of frames that failed retry @@ -235,6 +298,7 @@ enum plink_state {   * @tid_seq: per-TID sequence numbers for sending to this STA   * @ampdu_mlme: A-MPDU state machine state   * @timer_to_tid: identity mapping to ID timers + * @tx_lat: Tx latency statistics   * @llid: Local link ID   * @plid: Peer link ID   * @reason: Cancel reason on PLINK_HOLDING state @@ -243,25 +307,43 @@ enum plink_state {   * @plink_state: peer link state   * @plink_timeout: timeout of peer link   * @plink_timer: peer link watch timer - * @plink_timer_was_running: used by suspend/resume to restore timers + * @t_offset: timing offset relative to this host + * @t_offset_setpoint: reference timing offset of this sta to be used when + * 	calculating clockdrift + * @local_pm: local link-specific power save mode + * @peer_pm: peer-specific power save mode towards local STA + * @nonpeer_pm: STA power save mode towards non-peer neighbors   * @debugfs: debug filesystem info - * @sta: station information we share with the driver   * @dead: set to true when sta is unlinked   * @uploaded: set to true when sta is uploaded to the driver   * @lost_packets: number of consecutive lost packets + * @sta: station information we share with the driver + * @sta_state: duplicates information about station state (for debug) + * @beacon_loss_count: number of times beacon loss has triggered + * @rcu_head: RCU head used for freeing this station struct + * @cur_max_bandwidth: maximum bandwidth to use for TX to the station, + *	taken from HT/VHT capabilities or VHT operating mode notification + * @chains: chains ever used for RX from this station + * @chain_signal_last: last signal (per chain) + * @chain_signal_avg: signal average (per chain) + * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for + *	AP only. + * @cipher_scheme: optional cipher scheme for this station   */  struct sta_info {  	/* General information, mostly static */ -	struct list_head list; -	struct sta_info *hnext; +	struct list_head list, free_list; +	struct rcu_head rcu_head; +	struct sta_info __rcu *hnext;  	struct ieee80211_local *local;  	struct ieee80211_sub_if_data *sdata; -	struct ieee80211_key *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; -	struct ieee80211_key *ptk; +	struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; +	struct ieee80211_key __rcu *ptk[NUM_DEFAULT_KEYS]; +	u8 gtk_idx; +	u8 ptk_idx;  	struct rate_control_ref *rate_ctrl;  	void *rate_ctrl_priv;  	spinlock_t lock; -	spinlock_t flaglock;  	struct work_struct drv_unblock_wk; @@ -271,28 +353,36 @@ struct sta_info {  	bool uploaded; -	/* -	 * frequently updated, locked with own spinlock (flaglock), -	 * use the accessors defined below -	 */ -	u32 flags; +	enum ieee80211_sta_state sta_state; -	/* -	 * STA powersave frame queues, no more than the internal -	 * locking required. -	 */ -	struct sk_buff_head ps_tx_buf; -	struct sk_buff_head tx_filtered; +	/* use the accessors defined below */ +	unsigned long _flags; + +	/* STA powersave lock and frame queues */ +	spinlock_t ps_lock; +	struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS]; +	struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS]; +	unsigned long driver_buffered_tids;  	/* Updated from RX path only, no locking requirements */ -	unsigned long rx_packets, rx_bytes; +	unsigned long rx_packets; +	u64 rx_bytes;  	unsigned long wep_weak_iv_count;  	unsigned long last_rx; +	long last_connected;  	unsigned long num_duplicates;  	unsigned long rx_fragments;  	unsigned long rx_dropped;  	int last_signal; -	__le16 last_seq_ctrl[NUM_RX_DATA_QUEUES]; +	struct ewma avg_signal; +	int last_ack_signal; + +	u8 chains; +	s8 chain_signal_last[IEEE80211_MAX_CHAINS]; +	struct ewma chain_signal_avg[IEEE80211_MAX_CHAINS]; + +	/* Plus 1 for non-QoS frames */ +	__le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];  	/* Updated from TX status path only, no locking requirements */  	unsigned long tx_filtered_count; @@ -301,32 +391,43 @@ struct sta_info {  	unsigned int fail_avg;  	/* Updated from TX path only, no locking requirements */ -	unsigned long tx_packets; -	unsigned long tx_bytes; -	unsigned long tx_fragments; +	u32 tx_fragments; +	u64 tx_packets[IEEE80211_NUM_ACS]; +	u64 tx_bytes[IEEE80211_NUM_ACS];  	struct ieee80211_tx_rate last_tx_rate; +	int last_rx_rate_idx; +	u32 last_rx_rate_flag; +	u32 last_rx_rate_vht_flag; +	u8 last_rx_rate_vht_nss;  	u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];  	/*  	 * Aggregation information, locked with lock.  	 */  	struct sta_ampdu_mlme ampdu_mlme; -	u8 timer_to_tid[STA_TID_NUM]; +	u8 timer_to_tid[IEEE80211_NUM_TIDS]; + +	struct ieee80211_tx_latency_stat *tx_lat;  #ifdef CONFIG_MAC80211_MESH  	/*  	 * Mesh peer link attributes  	 * TODO: move to a sub-structure that is referenced with pointer?  	 */ -	__le16 llid; -	__le16 plid; -	__le16 reason; +	u16 llid; +	u16 plid; +	u16 reason;  	u8 plink_retries;  	bool ignore_plink_timer; -	bool plink_timer_was_running; -	enum plink_state plink_state; +	enum nl80211_plink_state plink_state;  	u32 plink_timeout;  	struct timer_list plink_timer; +	s64 t_offset; +	s64 t_offset_setpoint; +	/* mesh power save */ +	enum nl80211_mesh_power_mode local_pm; +	enum nl80211_mesh_power_mode peer_pm; +	enum nl80211_mesh_power_mode nonpeer_pm;  #endif  #ifdef CONFIG_MAC80211_DEBUGFS @@ -336,84 +437,100 @@ struct sta_info {  	} debugfs;  #endif +	enum ieee80211_sta_rx_bandwidth cur_max_bandwidth; +  	unsigned int lost_packets; +	unsigned int beacon_loss_count; + +	enum ieee80211_smps_mode known_smps_mode; +	const struct ieee80211_cipher_scheme *cipher_scheme;  	/* keep last! */  	struct ieee80211_sta sta;  }; -static inline enum plink_state sta_plink_state(struct sta_info *sta) +static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta)  {  #ifdef CONFIG_MAC80211_MESH  	return sta->plink_state;  #endif -	return PLINK_LISTEN; +	return NL80211_PLINK_LISTEN;  } -static inline void set_sta_flags(struct sta_info *sta, const u32 flags) +static inline void set_sta_flag(struct sta_info *sta, +				enum ieee80211_sta_info_flags flag)  { -	unsigned long irqfl; - -	spin_lock_irqsave(&sta->flaglock, irqfl); -	sta->flags |= flags; -	spin_unlock_irqrestore(&sta->flaglock, irqfl); +	WARN_ON(flag == WLAN_STA_AUTH || +		flag == WLAN_STA_ASSOC || +		flag == WLAN_STA_AUTHORIZED); +	set_bit(flag, &sta->_flags);  } -static inline void clear_sta_flags(struct sta_info *sta, const u32 flags) +static inline void clear_sta_flag(struct sta_info *sta, +				  enum ieee80211_sta_info_flags flag)  { -	unsigned long irqfl; - -	spin_lock_irqsave(&sta->flaglock, irqfl); -	sta->flags &= ~flags; -	spin_unlock_irqrestore(&sta->flaglock, irqfl); +	WARN_ON(flag == WLAN_STA_AUTH || +		flag == WLAN_STA_ASSOC || +		flag == WLAN_STA_AUTHORIZED); +	clear_bit(flag, &sta->_flags);  } -static inline u32 test_sta_flags(struct sta_info *sta, const u32 flags) +static inline int test_sta_flag(struct sta_info *sta, +				enum ieee80211_sta_info_flags flag)  { -	u32 ret; -	unsigned long irqfl; - -	spin_lock_irqsave(&sta->flaglock, irqfl); -	ret = sta->flags & flags; -	spin_unlock_irqrestore(&sta->flaglock, irqfl); - -	return ret; +	return test_bit(flag, &sta->_flags);  } -static inline u32 test_and_clear_sta_flags(struct sta_info *sta, -					   const u32 flags) +static inline int test_and_clear_sta_flag(struct sta_info *sta, +					  enum ieee80211_sta_info_flags flag)  { -	u32 ret; -	unsigned long irqfl; - -	spin_lock_irqsave(&sta->flaglock, irqfl); -	ret = sta->flags & flags; -	sta->flags &= ~flags; -	spin_unlock_irqrestore(&sta->flaglock, irqfl); +	WARN_ON(flag == WLAN_STA_AUTH || +		flag == WLAN_STA_ASSOC || +		flag == WLAN_STA_AUTHORIZED); +	return test_and_clear_bit(flag, &sta->_flags); +} -	return ret; +static inline int test_and_set_sta_flag(struct sta_info *sta, +					enum ieee80211_sta_info_flags flag) +{ +	WARN_ON(flag == WLAN_STA_AUTH || +		flag == WLAN_STA_ASSOC || +		flag == WLAN_STA_AUTHORIZED); +	return test_and_set_bit(flag, &sta->_flags);  } -static inline u32 get_sta_flags(struct sta_info *sta) +int sta_info_move_state(struct sta_info *sta, +			enum ieee80211_sta_state new_state); + +static inline void sta_info_pre_move_state(struct sta_info *sta, +					   enum ieee80211_sta_state new_state)  { -	u32 ret; -	unsigned long irqfl; +	int ret; -	spin_lock_irqsave(&sta->flaglock, irqfl); -	ret = sta->flags; -	spin_unlock_irqrestore(&sta->flaglock, irqfl); +	WARN_ON_ONCE(test_sta_flag(sta, WLAN_STA_INSERTED)); -	return ret; +	ret = sta_info_move_state(sta, new_state); +	WARN_ON_ONCE(ret);  } +void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, +			     struct tid_ampdu_tx *tid_tx); + +static inline struct tid_ampdu_tx * +rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid) +{ +	return rcu_dereference_protected(sta->ampdu_mlme.tid_tx[tid], +					 lockdep_is_held(&sta->lock) || +					 lockdep_is_held(&sta->ampdu_mlme.mtx)); +}  #define STA_HASH_SIZE 256  #define STA_HASH(sta) (sta[5]) -/* Maximum number of frames to buffer per power saving station */ -#define STA_MAX_TX_BUFFER 128 +/* Maximum number of frames to buffer per power saving station per AC */ +#define STA_MAX_TX_BUFFER	64  /* Minimum buffered frame expiry time. If STA uses listen interval that is   * smaller than this value, the minimum value here is used instead. */ @@ -440,7 +557,7 @@ void for_each_sta_info_type_check(struct ieee80211_local *local,  {  } -#define for_each_sta_info(local, _addr, _sta, nxt) 			\ +#define for_each_sta_info(local, _addr, _sta, nxt)			\  	for (	/* initialise loop */					\  		_sta = rcu_dereference(local->sta_hash[STA_HASH(_addr)]),\  		nxt = _sta ? rcu_dereference(_sta->hnext) : NULL;	\ @@ -453,7 +570,7 @@ void for_each_sta_info_type_check(struct ieee80211_local *local,  		nxt = _sta ? rcu_dereference(_sta->hnext) : NULL	\  	     )								\  	/* compare address and run code only if it matches */		\ -	if (memcmp(_sta->sta.addr, (_addr), ETH_ALEN) == 0) +	if (ether_addr_equal(_sta->sta.addr, (_addr)))  /*   * Get STA info by index, BROKEN! @@ -465,7 +582,10 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,   * until sta_info_insert().   */  struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, -				u8 *addr, gfp_t gfp); +				const u8 *addr, gfp_t gfp); + +void sta_info_free(struct ieee80211_local *local, struct sta_info *sta); +  /*   * Insert STA info into hash table/list, returns zero or a   * -EEXIST if (if the same MAC address is already present). @@ -476,25 +596,44 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,   */  int sta_info_insert(struct sta_info *sta);  int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU); -int sta_info_insert_atomic(struct sta_info *sta); +int __must_check __sta_info_destroy(struct sta_info *sta);  int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata,  			  const u8 *addr);  int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,  			      const u8 *addr); -void sta_info_set_tim_bit(struct sta_info *sta); -void sta_info_clear_tim_bit(struct sta_info *sta); +void sta_info_recalc_tim(struct sta_info *sta);  void sta_info_init(struct ieee80211_local *local); -int sta_info_start(struct ieee80211_local *local);  void sta_info_stop(struct ieee80211_local *local); -int sta_info_flush(struct ieee80211_local *local, -		   struct ieee80211_sub_if_data *sdata); + +/** + * sta_info_flush - flush matching STA entries from the STA table + * + * Returns the number of removed STA entries. + * + * @sdata: sdata to remove all stations from + * @vlans: if the given interface is an AP interface, also flush VLANs + */ +int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans); + +static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata) +{ +	return __sta_info_flush(sdata, false); +} + +void sta_set_rate_info_tx(struct sta_info *sta, +			  const struct ieee80211_tx_rate *rate, +			  struct rate_info *rinfo); +void sta_set_rate_info_rx(struct sta_info *sta, +			  struct rate_info *rinfo);  void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,  			  unsigned long exp_time); +u8 sta_info_tx_streams(struct sta_info *sta);  void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);  void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); +void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta);  #endif /* STA_INFO_H */ diff --git a/net/mac80211/status.c b/net/mac80211/status.c index bed7e32ed90..ba29ebc8614 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -9,11 +9,16 @@   * published by the Free Software Foundation.   */ +#include <linux/export.h> +#include <linux/etherdevice.h> +#include <linux/time.h>  #include <net/mac80211.h> +#include <asm/unaligned.h>  #include "ieee80211_i.h"  #include "rate.h"  #include "mesh.h"  #include "led.h" +#include "wme.h"  void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, @@ -30,7 +35,7 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,  		skb_queue_len(&local->skb_queue_unreliable);  	while (tmp > IEEE80211_IRQSAFE_QUEUE_LIMIT &&  	       (skb = skb_dequeue(&local->skb_queue_unreliable))) { -		dev_kfree_skb_irq(skb); +		ieee80211_free_txskb(hw, skb);  		tmp--;  		I802_DEBUG_INC(local->tx_status_drop);  	} @@ -43,6 +48,8 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,  					    struct sk_buff *skb)  {  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct ieee80211_hdr *hdr = (void *)skb->data; +	int ac;  	/*  	 * This skb 'survived' a round-trip through the driver, and @@ -63,11 +70,37 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,  	sta->tx_filtered_count++;  	/* +	 * Clear more-data bit on filtered frames, it might be set +	 * but later frames might time out so it might have to be +	 * clear again ... It's all rather unlikely (this frame +	 * should time out first, right?) but let's not confuse +	 * peers unnecessarily. +	 */ +	if (hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_MOREDATA)) +		hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_MOREDATA); + +	if (ieee80211_is_data_qos(hdr->frame_control)) { +		u8 *p = ieee80211_get_qos_ctl(hdr); +		int tid = *p & IEEE80211_QOS_CTL_TID_MASK; + +		/* +		 * Clear EOSP if set, this could happen e.g. +		 * if an absence period (us being a P2P GO) +		 * shortens the SP. +		 */ +		if (*p & IEEE80211_QOS_CTL_EOSP) +			*p &= ~IEEE80211_QOS_CTL_EOSP; +		ac = ieee802_1d_to_ac[tid & 7]; +	} else { +		ac = IEEE80211_AC_BE; +	} + +	/*  	 * Clear the TX filter mask for this STA when sending the next  	 * packet. If the STA went to power save mode, this will happen  	 * when it wakes up for the next time.  	 */ -	set_sta_flags(sta, WLAN_STA_CLEAR_PS_FILT); +	set_sta_flag(sta, WLAN_STA_CLEAR_PS_FILT);  	/*  	 * This code races in the following way: @@ -98,14 +131,24 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,  	 *  (b) always process RX events before TX status events if ordering  	 *      can be unknown, for example with different interrupt status  	 *	bits. +	 *  (c) if PS mode transitions are manual (i.e. the flag +	 *      %IEEE80211_HW_AP_LINK_PS is set), always process PS state +	 *      changes before calling TX status events if ordering can be +	 *	unknown.  	 */ -	if (test_sta_flags(sta, WLAN_STA_PS_STA) && -	    skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) { -		skb_queue_tail(&sta->tx_filtered, skb); +	if (test_sta_flag(sta, WLAN_STA_PS_STA) && +	    skb_queue_len(&sta->tx_filtered[ac]) < STA_MAX_TX_BUFFER) { +		skb_queue_tail(&sta->tx_filtered[ac], skb); +		sta_info_recalc_tim(sta); + +		if (!timer_pending(&local->sta_cleanup)) +			mod_timer(&local->sta_cleanup, +				  round_jiffies(jiffies + +						STA_INFO_CLEANUP_INTERVAL));  		return;  	} -	if (!test_sta_flags(sta, WLAN_STA_PS_STA) && +	if (!test_sta_flag(sta, WLAN_STA_PS_STA) &&  	    !(info->flags & IEEE80211_TX_INTFL_RETRIED)) {  		/* Software retry the packet once */  		info->flags |= IEEE80211_TX_INTFL_RETRIED; @@ -113,14 +156,23 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,  		return;  	} -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG -	if (net_ratelimit()) -		wiphy_debug(local->hw.wiphy, -			    "dropped TX filtered frame, queue_len=%d PS=%d @%lu\n", -			    skb_queue_len(&sta->tx_filtered), -			    !!test_sta_flags(sta, WLAN_STA_PS_STA), jiffies); -#endif -	dev_kfree_skb(skb); +	ps_dbg_ratelimited(sta->sdata, +			   "dropped TX filtered frame, queue_len=%d PS=%d @%lu\n", +			   skb_queue_len(&sta->tx_filtered[ac]), +			   !!test_sta_flag(sta, WLAN_STA_PS_STA), jiffies); +	ieee80211_free_txskb(&local->hw, skb); +} + +static void ieee80211_check_pending_bar(struct sta_info *sta, u8 *addr, u8 tid) +{ +	struct tid_ampdu_tx *tid_tx; + +	tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); +	if (!tid_tx || !tid_tx->bar_pending) +		return; + +	tid_tx->bar_pending = false; +	ieee80211_send_bar(&sta->sdata->vif, addr, tid, tid_tx->failed_bar_ssn);  }  static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) @@ -129,36 +181,355 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)  	struct ieee80211_local *local = sta->local;  	struct ieee80211_sub_if_data *sdata = sta->sdata; +	if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) +		sta->last_rx = jiffies; + +	if (ieee80211_is_data_qos(mgmt->frame_control)) { +		struct ieee80211_hdr *hdr = (void *) skb->data; +		u8 *qc = ieee80211_get_qos_ctl(hdr); +		u16 tid = qc[0] & 0xf; + +		ieee80211_check_pending_bar(sta, hdr->addr1, tid); +	} +  	if (ieee80211_is_action(mgmt->frame_control) && -	    sdata->vif.type == NL80211_IFTYPE_STATION &&  	    mgmt->u.action.category == WLAN_CATEGORY_HT && -	    mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) { -		/* -		 * This update looks racy, but isn't -- if we come -		 * here we've definitely got a station that we're -		 * talking to, and on a managed interface that can -		 * only be the AP. And the only other place updating -		 * this variable is before we're associated. -		 */ +	    mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS && +	    ieee80211_sdata_running(sdata)) { +		enum ieee80211_smps_mode smps_mode; +  		switch (mgmt->u.action.u.ht_smps.smps_control) {  		case WLAN_HT_SMPS_CONTROL_DYNAMIC: -			sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_DYNAMIC; +			smps_mode = IEEE80211_SMPS_DYNAMIC;  			break;  		case WLAN_HT_SMPS_CONTROL_STATIC: -			sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_STATIC; +			smps_mode = IEEE80211_SMPS_STATIC;  			break;  		case WLAN_HT_SMPS_CONTROL_DISABLED:  		default: /* shouldn't happen since we don't send that */ -			sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_OFF; +			smps_mode = IEEE80211_SMPS_OFF;  			break;  		} -		ieee80211_queue_work(&local->hw, &local->recalc_smps); +		if (sdata->vif.type == NL80211_IFTYPE_STATION) { +			/* +			 * This update looks racy, but isn't -- if we come +			 * here we've definitely got a station that we're +			 * talking to, and on a managed interface that can +			 * only be the AP. And the only other place updating +			 * this variable in managed mode is before association. +			 */ +			sdata->smps_mode = smps_mode; +			ieee80211_queue_work(&local->hw, &sdata->recalc_smps); +		} else if (sdata->vif.type == NL80211_IFTYPE_AP || +			   sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { +			sta->known_smps_mode = smps_mode; +		} +	} +} + +static void ieee80211_set_bar_pending(struct sta_info *sta, u8 tid, u16 ssn) +{ +	struct tid_ampdu_tx *tid_tx; + +	tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); +	if (!tid_tx) +		return; + +	tid_tx->failed_bar_ssn = ssn; +	tid_tx->bar_pending = true; +} + +static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info) +{ +	int len = sizeof(struct ieee80211_radiotap_header); + +	/* IEEE80211_RADIOTAP_RATE rate */ +	if (info->status.rates[0].idx >= 0 && +	    !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS | +					     IEEE80211_TX_RC_VHT_MCS))) +		len += 2; + +	/* IEEE80211_RADIOTAP_TX_FLAGS */ +	len += 2; + +	/* IEEE80211_RADIOTAP_DATA_RETRIES */ +	len += 1; + +	/* IEEE80211_RADIOTAP_MCS +	 * IEEE80211_RADIOTAP_VHT */ +	if (info->status.rates[0].idx >= 0) { +		if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS) +			len += 3; +		else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS) +			len = ALIGN(len, 2) + 12; +	} + +	return len; +} + +static void +ieee80211_add_tx_radiotap_header(struct ieee80211_local *local, +				 struct ieee80211_supported_band *sband, +				 struct sk_buff *skb, int retry_count, +				 int rtap_len, int shift) +{ +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; +	struct ieee80211_radiotap_header *rthdr; +	unsigned char *pos; +	u16 txflags; + +	rthdr = (struct ieee80211_radiotap_header *) skb_push(skb, rtap_len); + +	memset(rthdr, 0, rtap_len); +	rthdr->it_len = cpu_to_le16(rtap_len); +	rthdr->it_present = +		cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) | +			    (1 << IEEE80211_RADIOTAP_DATA_RETRIES)); +	pos = (unsigned char *)(rthdr + 1); + +	/* +	 * XXX: Once radiotap gets the bitmap reset thing the vendor +	 *	extensions proposal contains, we can actually report +	 *	the whole set of tries we did. +	 */ + +	/* IEEE80211_RADIOTAP_RATE */ +	if (info->status.rates[0].idx >= 0 && +	    !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS | +					     IEEE80211_TX_RC_VHT_MCS))) { +		u16 rate; + +		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); +		rate = sband->bitrates[info->status.rates[0].idx].bitrate; +		*pos = DIV_ROUND_UP(rate, 5 * (1 << shift)); +		/* padding for tx flags */ +		pos += 2; +	} + +	/* IEEE80211_RADIOTAP_TX_FLAGS */ +	txflags = 0; +	if (!(info->flags & IEEE80211_TX_STAT_ACK) && +	    !is_multicast_ether_addr(hdr->addr1)) +		txflags |= IEEE80211_RADIOTAP_F_TX_FAIL; + +	if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) +		txflags |= IEEE80211_RADIOTAP_F_TX_CTS; +	if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) +		txflags |= IEEE80211_RADIOTAP_F_TX_RTS; + +	put_unaligned_le16(txflags, pos); +	pos += 2; + +	/* IEEE80211_RADIOTAP_DATA_RETRIES */ +	/* for now report the total retry_count */ +	*pos = retry_count; +	pos++; + +	if (info->status.rates[0].idx < 0) +		return; + +	/* IEEE80211_RADIOTAP_MCS +	 * IEEE80211_RADIOTAP_VHT */ +	if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS) { +		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); +		pos[0] = IEEE80211_RADIOTAP_MCS_HAVE_MCS | +			 IEEE80211_RADIOTAP_MCS_HAVE_GI | +			 IEEE80211_RADIOTAP_MCS_HAVE_BW; +		if (info->status.rates[0].flags & IEEE80211_TX_RC_SHORT_GI) +			pos[1] |= IEEE80211_RADIOTAP_MCS_SGI; +		if (info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) +			pos[1] |= IEEE80211_RADIOTAP_MCS_BW_40; +		if (info->status.rates[0].flags & IEEE80211_TX_RC_GREEN_FIELD) +			pos[1] |= IEEE80211_RADIOTAP_MCS_FMT_GF; +		pos[2] = info->status.rates[0].idx; +		pos += 3; +	} else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS) { +		u16 known = local->hw.radiotap_vht_details & +			(IEEE80211_RADIOTAP_VHT_KNOWN_GI | +			 IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH); + +		rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT); + +		/* required alignment from rthdr */ +		pos = (u8 *)rthdr + ALIGN(pos - (u8 *)rthdr, 2); + +		/* u16 known - IEEE80211_RADIOTAP_VHT_KNOWN_* */ +		put_unaligned_le16(known, pos); +		pos += 2; + +		/* u8 flags - IEEE80211_RADIOTAP_VHT_FLAG_* */ +		if (info->status.rates[0].flags & IEEE80211_TX_RC_SHORT_GI) +			*pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI; +		pos++; + +		/* u8 bandwidth */ +		if (info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) +			*pos = 1; +		else if (info->status.rates[0].flags & IEEE80211_TX_RC_80_MHZ_WIDTH) +			*pos = 4; +		else if (info->status.rates[0].flags & IEEE80211_TX_RC_160_MHZ_WIDTH) +			*pos = 11; +		else /* IEEE80211_TX_RC_{20_MHZ_WIDTH,FIXME:DUP_DATA} */ +			*pos = 0; +		pos++; + +		/* u8 mcs_nss[4] */ +		*pos = (ieee80211_rate_get_vht_mcs(&info->status.rates[0]) << 4) | +			ieee80211_rate_get_vht_nss(&info->status.rates[0]); +		pos += 4; + +		/* u8 coding */ +		pos++; +		/* u8 group_id */ +		pos++; +		/* u16 partial_aid */ +		pos += 2; +	} +} + +static void ieee80211_report_used_skb(struct ieee80211_local *local, +				      struct sk_buff *skb, bool dropped) +{ +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct ieee80211_hdr *hdr = (void *)skb->data; +	bool acked = info->flags & IEEE80211_TX_STAT_ACK; + +	if (dropped) +		acked = false; + +	if (info->flags & (IEEE80211_TX_INTFL_NL80211_FRAME_TX | +			   IEEE80211_TX_INTFL_MLME_CONN_TX)) { +		struct ieee80211_sub_if_data *sdata = NULL; +		struct ieee80211_sub_if_data *iter_sdata; +		u64 cookie = (unsigned long)skb; + +		rcu_read_lock(); + +		if (skb->dev) { +			list_for_each_entry_rcu(iter_sdata, &local->interfaces, +						list) { +				if (!iter_sdata->dev) +					continue; + +				if (skb->dev == iter_sdata->dev) { +					sdata = iter_sdata; +					break; +				} +			} +		} else { +			sdata = rcu_dereference(local->p2p_sdata); +		} + +		if (!sdata) { +			skb->dev = NULL; +		} else if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) { +			ieee80211_mgd_conn_tx_status(sdata, hdr->frame_control, +						     acked); +		} else if (ieee80211_is_nullfunc(hdr->frame_control) || +			   ieee80211_is_qos_nullfunc(hdr->frame_control)) { +			cfg80211_probe_status(sdata->dev, hdr->addr1, +					      cookie, acked, GFP_ATOMIC); +		} else { +			cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data, +						skb->len, acked, GFP_ATOMIC); +		} + +		rcu_read_unlock(); +	} + +	if (unlikely(info->ack_frame_id)) { +		struct sk_buff *ack_skb; +		unsigned long flags; + +		spin_lock_irqsave(&local->ack_status_lock, flags); +		ack_skb = idr_find(&local->ack_status_frames, +				   info->ack_frame_id); +		if (ack_skb) +			idr_remove(&local->ack_status_frames, +				   info->ack_frame_id); +		spin_unlock_irqrestore(&local->ack_status_lock, flags); + +		if (ack_skb) { +			if (!dropped) { +				/* consumes ack_skb */ +				skb_complete_wifi_ack(ack_skb, acked); +			} else { +				dev_kfree_skb_any(ack_skb); +			} +		} +	} +} + +/* + * Measure Tx frame completion and removal time for Tx latency statistics + * calculation. A single Tx frame latency should be measured from when it + * is entering the Kernel until we receive Tx complete confirmation indication + * and remove the skb. + */ +static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local, +					    struct sk_buff *skb, +					    struct sta_info *sta, +					    struct ieee80211_hdr *hdr) +{ +	ktime_t skb_dprt; +	struct timespec dprt_time; +	u32 msrmnt; +	u16 tid; +	u8 *qc; +	int i, bin_range_count; +	u32 *bin_ranges; +	__le16 fc; +	struct ieee80211_tx_latency_stat *tx_lat; +	struct ieee80211_tx_latency_bin_ranges *tx_latency; +	ktime_t skb_arv = skb->tstamp; + +	tx_latency = rcu_dereference(local->tx_latency); + +	/* assert Tx latency stats are enabled & frame arrived when enabled */ +	if (!tx_latency || !ktime_to_ns(skb_arv)) +		return; + +	fc = hdr->frame_control; + +	if (!ieee80211_is_data(fc)) /* make sure it is a data frame */ +		return; + +	/* get frame tid */ +	if (ieee80211_is_data_qos(hdr->frame_control)) { +		qc = ieee80211_get_qos_ctl(hdr); +		tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; +	} else { +		tid = 0;  	} -	if ((sdata->vif.type == NL80211_IFTYPE_STATION) && -	    (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) -		ieee80211_sta_tx_notify(sdata, (void *) skb->data); +	tx_lat = &sta->tx_lat[tid]; + +	ktime_get_ts(&dprt_time); /* time stamp completion time */ +	skb_dprt = ktime_set(dprt_time.tv_sec, dprt_time.tv_nsec); +	msrmnt = ktime_to_ms(ktime_sub(skb_dprt, skb_arv)); + +	if (tx_lat->max < msrmnt) /* update stats */ +		tx_lat->max = msrmnt; +	tx_lat->counter++; +	tx_lat->sum += msrmnt; + +	if (!tx_lat->bins) /* bins not activated */ +		return; + +	/* count how many Tx frames transmitted with the appropriate latency */ +	bin_range_count = tx_latency->n_ranges; +	bin_ranges = tx_latency->ranges; + +	for (i = 0; i < bin_range_count; i++) { +		if (msrmnt <= bin_ranges[i]) { +			tx_lat->bins[i]++; +			break; +		} +	} +	if (i == bin_range_count) /* msrmnt is bigger than the biggest range */ +		tx_lat->bins[i]++;  }  /* @@ -170,34 +541,62 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)   */  #define STA_LOST_PKT_THRESHOLD	50 +static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb) +{ +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + +	/* This packet was aggregated but doesn't carry status info */ +	if ((info->flags & IEEE80211_TX_CTL_AMPDU) && +	    !(info->flags & IEEE80211_TX_STAT_AMPDU)) +		return; + +	if (++sta->lost_packets < STA_LOST_PKT_THRESHOLD) +		return; + +	cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr, +				    sta->lost_packets, GFP_ATOMIC); +	sta->lost_packets = 0; +} +  void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)  {  	struct sk_buff *skb2;  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;  	struct ieee80211_local *local = hw_to_local(hw);  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); -	u16 frag, type;  	__le16 fc;  	struct ieee80211_supported_band *sband; -	struct ieee80211_tx_status_rtap_hdr *rthdr;  	struct ieee80211_sub_if_data *sdata;  	struct net_device *prev_dev = NULL;  	struct sta_info *sta, *tmp;  	int retry_count = -1, i;  	int rates_idx = -1;  	bool send_to_cooked; +	bool acked; +	struct ieee80211_bar *bar; +	int rtap_len; +	int shift = 0;  	for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { -		/* the HW cannot have attempted that rate */ -		if (i >= hw->max_report_rates) { +		if ((info->flags & IEEE80211_TX_CTL_AMPDU) && +		    !(info->flags & IEEE80211_TX_STAT_AMPDU)) { +			/* just the first aggr frame carry status info */  			info->status.rates[i].idx = -1;  			info->status.rates[i].count = 0; -		} else if (info->status.rates[i].idx >= 0) { -			rates_idx = i; +			break; +		} else if (info->status.rates[i].idx < 0) { +			break; +		} else if (i >= hw->max_report_rates) { +			/* the HW cannot have attempted that rate */ +			info->status.rates[i].idx = -1; +			info->status.rates[i].count = 0; +			break;  		}  		retry_count += info->status.rates[i].count;  	} +	rates_idx = i - 1; +  	if (retry_count < 0)  		retry_count = 0; @@ -208,11 +607,16 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)  	for_each_sta_info(local, hdr->addr1, sta, tmp) {  		/* skip wrong virtual interface */ -		if (memcmp(hdr->addr2, sta->sdata->vif.addr, ETH_ALEN)) +		if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))  			continue; -		if (!(info->flags & IEEE80211_TX_STAT_ACK) && -		    test_sta_flags(sta, WLAN_STA_PS_STA)) { +		shift = ieee80211_vif_get_shift(&sta->sdata->vif); + +		if (info->flags & IEEE80211_TX_STATUS_EOSP) +			clear_sta_flag(sta, WLAN_STA_SP); + +		acked = !!(info->flags & IEEE80211_TX_STAT_ACK); +		if (!acked && test_sta_flag(sta, WLAN_STA_PS_STA)) {  			/*  			 * The STA is in power save mode, so assume  			 * that this TX packet failed because of that. @@ -222,7 +626,15 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)  			return;  		} +		/* mesh Peer Service Period support */ +		if (ieee80211_vif_is_mesh(&sta->sdata->vif) && +		    ieee80211_is_data_qos(fc)) +			ieee80211_mpsp_trigger_process( +					ieee80211_get_qos_ctl(hdr), +					sta, true, acked); +  		if ((local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) && +		    (ieee80211_is_data(hdr->frame_control)) &&  		    (rates_idx != -1))  			sta->last_tx_rate = info->status.rates[rates_idx]; @@ -235,16 +647,37 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)  			tid = qc[0] & 0xf;  			ssn = ((le16_to_cpu(hdr->seq_ctrl) + 0x10)  						& IEEE80211_SCTL_SEQ); -			ieee80211_send_bar(sta->sdata, hdr->addr1, +			ieee80211_send_bar(&sta->sdata->vif, hdr->addr1,  					   tid, ssn);  		} +		if (!acked && ieee80211_is_back_req(fc)) { +			u16 tid, control; + +			/* +			 * BAR failed, store the last SSN and retry sending +			 * the BAR when the next unicast transmission on the +			 * same TID succeeds. +			 */ +			bar = (struct ieee80211_bar *) skb->data; +			control = le16_to_cpu(bar->control); +			if (!(control & IEEE80211_BAR_CTRL_MULTI_TID)) { +				u16 ssn = le16_to_cpu(bar->start_seq_num); + +				tid = (control & +				       IEEE80211_BAR_CTRL_TID_INFO_MASK) >> +				      IEEE80211_BAR_CTRL_TID_INFO_SHIFT; + +				ieee80211_set_bar_pending(sta, tid, ssn); +			} +		} +  		if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) {  			ieee80211_handle_filtered_frame(local, sta, skb);  			rcu_read_unlock();  			return;  		} else { -			if (!(info->flags & IEEE80211_TX_STAT_ACK)) +			if (!acked)  				sta->tx_retry_failed++;  			sta->tx_retry_count += retry_count;  		} @@ -253,38 +686,42 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)  		if (ieee80211_vif_is_mesh(&sta->sdata->vif))  			ieee80211s_update_metric(local, sta, skb); -		if (!(info->flags & IEEE80211_TX_CTL_INJECTED) && -		    (info->flags & IEEE80211_TX_STAT_ACK)) +		if (!(info->flags & IEEE80211_TX_CTL_INJECTED) && acked)  			ieee80211_frame_acked(sta, skb); +		if ((sta->sdata->vif.type == NL80211_IFTYPE_STATION) && +		    (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) +			ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data, acked); +  		if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {  			if (info->flags & IEEE80211_TX_STAT_ACK) {  				if (sta->lost_packets)  					sta->lost_packets = 0; -			} else if (++sta->lost_packets >= STA_LOST_PKT_THRESHOLD) { -				cfg80211_cqm_pktloss_notify(sta->sdata->dev, -							    sta->sta.addr, -							    sta->lost_packets, -							    GFP_ATOMIC); -				sta->lost_packets = 0; +			} else { +				ieee80211_lost_packet(sta, skb);  			}  		} + +		if (acked) +			sta->last_ack_signal = info->status.ack_signal; + +		/* +		 * Measure frame removal for tx latency +		 * statistics calculation +		 */ +		ieee80211_tx_latency_end_msrmnt(local, skb, sta, hdr);  	}  	rcu_read_unlock(); -	ieee80211_led_tx(local, 0); +	ieee80211_led_tx(local);  	/* SNMP counters  	 * Fragments are passed to low-level drivers as separate skbs, so these  	 * are actually fragments, not frames. Update frame counters only for  	 * the first fragment of the frame. */ - -	frag = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; -	type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE; -  	if (info->flags & IEEE80211_TX_STAT_ACK) { -		if (frag == 0) { +		if (ieee80211_is_first_frag(hdr->seq_ctrl)) {  			local->dot11TransmittedFrameCount++;  			if (is_multicast_ether_addr(hdr->addr1))  				local->dot11MulticastTransmittedFrameCount++; @@ -299,11 +736,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)  		 * with a multicast address in the address 1 field of type Data  		 * or Management. */  		if (!is_multicast_ether_addr(hdr->addr1) || -		    type == IEEE80211_FTYPE_DATA || -		    type == IEEE80211_FTYPE_MGMT) +		    ieee80211_is_data(fc) || +		    ieee80211_is_mgmt(fc))  			local->dot11TransmittedFragmentCount++;  	} else { -		if (frag == 0) +		if (ieee80211_is_first_frag(hdr->seq_ctrl))  			local->dot11FailedCount++;  	} @@ -314,24 +751,19 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)  		if (info->flags & IEEE80211_TX_STAT_ACK) {  			local->ps_sdata->u.mgd.flags |=  					IEEE80211_STA_NULLFUNC_ACKED; -			ieee80211_queue_work(&local->hw, -					&local->dynamic_ps_enable_work);  		} else  			mod_timer(&local->dynamic_ps_timer, jiffies +  					msecs_to_jiffies(10));  	} -	if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) -		cfg80211_mgmt_tx_status( -			skb->dev, (unsigned long) skb, skb->data, skb->len, -			!!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC); +	ieee80211_report_used_skb(local, skb, false);  	/* this was a transmitted frame, but now we want to reuse it */  	skb_orphan(skb);  	/* Need to make a copy before skb->cb gets cleared */  	send_to_cooked = !!(info->flags & IEEE80211_TX_CTL_INJECTED) || -			(type != IEEE80211_FTYPE_DATA); +			 !(ieee80211_is_data(fc));  	/*  	 * This is a bit racy but we can avoid a lot of work @@ -343,44 +775,14 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)  	}  	/* send frame to monitor interfaces now */ - -	if (skb_headroom(skb) < sizeof(*rthdr)) { -		printk(KERN_ERR "ieee80211_tx_status: headroom too small\n"); +	rtap_len = ieee80211_tx_radiotap_len(info); +	if (WARN_ON_ONCE(skb_headroom(skb) < rtap_len)) { +		pr_err("ieee80211_tx_status: headroom too small\n");  		dev_kfree_skb(skb);  		return;  	} - -	rthdr = (struct ieee80211_tx_status_rtap_hdr *) -				skb_push(skb, sizeof(*rthdr)); - -	memset(rthdr, 0, sizeof(*rthdr)); -	rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr)); -	rthdr->hdr.it_present = -		cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) | -			    (1 << IEEE80211_RADIOTAP_DATA_RETRIES) | -			    (1 << IEEE80211_RADIOTAP_RATE)); - -	if (!(info->flags & IEEE80211_TX_STAT_ACK) && -	    !is_multicast_ether_addr(hdr->addr1)) -		rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_FAIL); - -	/* -	 * XXX: Once radiotap gets the bitmap reset thing the vendor -	 *	extensions proposal contains, we can actually report -	 *	the whole set of tries we did. -	 */ -	if ((info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || -	    (info->status.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) -		rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_CTS); -	else if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) -		rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_RTS); -	if (info->status.rates[0].idx >= 0 && -	    !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) -		rthdr->rate = sband->bitrates[ -				info->status.rates[0].idx].bitrate / 5; - -	/* for now report the total retry_count */ -	rthdr->data_retries = retry_count; +	ieee80211_add_tx_radiotap_header(local, sband, skb, retry_count, +					 rtap_len, shift);  	/* XXX: is this sufficient for BPF? */  	skb_set_mac_header(skb, 0); @@ -419,3 +821,29 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)  	dev_kfree_skb(skb);  }  EXPORT_SYMBOL(ieee80211_tx_status); + +void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets) +{ +	struct sta_info *sta = container_of(pubsta, struct sta_info, sta); +	cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr, +				    num_packets, GFP_ATOMIC); +} +EXPORT_SYMBOL(ieee80211_report_low_ack); + +void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb) +{ +	struct ieee80211_local *local = hw_to_local(hw); + +	ieee80211_report_used_skb(local, skb, true); +	dev_kfree_skb_any(skb); +} +EXPORT_SYMBOL(ieee80211_free_txskb); + +void ieee80211_purge_tx_queue(struct ieee80211_hw *hw, +			      struct sk_buff_head *skbs) +{ +	struct sk_buff *skb; + +	while ((skb = __skb_dequeue(skbs))) +		ieee80211_free_txskb(hw, skb); +} diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c new file mode 100644 index 00000000000..652813b2d3d --- /dev/null +++ b/net/mac80211/tdls.c @@ -0,0 +1,325 @@ +/* + * mac80211 TDLS handling code + * + * Copyright 2006-2010	Johannes Berg <johannes@sipsolutions.net> + * Copyright 2014, Intel Corporation + * + * This file is GPLv2 as found in COPYING. + */ + +#include <linux/ieee80211.h> +#include "ieee80211_i.h" + +static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) +{ +	u8 *pos = (void *)skb_put(skb, 7); + +	*pos++ = WLAN_EID_EXT_CAPABILITY; +	*pos++ = 5; /* len */ +	*pos++ = 0x0; +	*pos++ = 0x0; +	*pos++ = 0x0; +	*pos++ = 0x0; +	*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; +} + +static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local; +	u16 capab; + +	capab = 0; +	if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ) +		return capab; + +	if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) +		capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; +	if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) +		capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; + +	return capab; +} + +static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, +				       const u8 *peer, const u8 *bssid) +{ +	struct ieee80211_tdls_lnkie *lnkid; + +	lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); + +	lnkid->ie_type = WLAN_EID_LINK_ID; +	lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; + +	memcpy(lnkid->bssid, bssid, ETH_ALEN); +	memcpy(lnkid->init_sta, src_addr, ETH_ALEN); +	memcpy(lnkid->resp_sta, peer, ETH_ALEN); +} + +static int +ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, +			       const u8 *peer, u8 action_code, u8 dialog_token, +			       u16 status_code, struct sk_buff *skb) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	enum ieee80211_band band = ieee80211_get_sdata_band(sdata); +	struct ieee80211_tdls_data *tf; + +	tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); + +	memcpy(tf->da, peer, ETH_ALEN); +	memcpy(tf->sa, sdata->vif.addr, ETH_ALEN); +	tf->ether_type = cpu_to_be16(ETH_P_TDLS); +	tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; + +	switch (action_code) { +	case WLAN_TDLS_SETUP_REQUEST: +		tf->category = WLAN_CATEGORY_TDLS; +		tf->action_code = WLAN_TDLS_SETUP_REQUEST; + +		skb_put(skb, sizeof(tf->u.setup_req)); +		tf->u.setup_req.dialog_token = dialog_token; +		tf->u.setup_req.capability = +			cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + +		ieee80211_add_srates_ie(sdata, skb, false, band); +		ieee80211_add_ext_srates_ie(sdata, skb, false, band); +		ieee80211_tdls_add_ext_capab(skb); +		break; +	case WLAN_TDLS_SETUP_RESPONSE: +		tf->category = WLAN_CATEGORY_TDLS; +		tf->action_code = WLAN_TDLS_SETUP_RESPONSE; + +		skb_put(skb, sizeof(tf->u.setup_resp)); +		tf->u.setup_resp.status_code = cpu_to_le16(status_code); +		tf->u.setup_resp.dialog_token = dialog_token; +		tf->u.setup_resp.capability = +			cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + +		ieee80211_add_srates_ie(sdata, skb, false, band); +		ieee80211_add_ext_srates_ie(sdata, skb, false, band); +		ieee80211_tdls_add_ext_capab(skb); +		break; +	case WLAN_TDLS_SETUP_CONFIRM: +		tf->category = WLAN_CATEGORY_TDLS; +		tf->action_code = WLAN_TDLS_SETUP_CONFIRM; + +		skb_put(skb, sizeof(tf->u.setup_cfm)); +		tf->u.setup_cfm.status_code = cpu_to_le16(status_code); +		tf->u.setup_cfm.dialog_token = dialog_token; +		break; +	case WLAN_TDLS_TEARDOWN: +		tf->category = WLAN_CATEGORY_TDLS; +		tf->action_code = WLAN_TDLS_TEARDOWN; + +		skb_put(skb, sizeof(tf->u.teardown)); +		tf->u.teardown.reason_code = cpu_to_le16(status_code); +		break; +	case WLAN_TDLS_DISCOVERY_REQUEST: +		tf->category = WLAN_CATEGORY_TDLS; +		tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; + +		skb_put(skb, sizeof(tf->u.discover_req)); +		tf->u.discover_req.dialog_token = dialog_token; +		break; +	default: +		return -EINVAL; +	} + +	return 0; +} + +static int +ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, +			   const u8 *peer, u8 action_code, u8 dialog_token, +			   u16 status_code, struct sk_buff *skb) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	enum ieee80211_band band = ieee80211_get_sdata_band(sdata); +	struct ieee80211_mgmt *mgmt; + +	mgmt = (void *)skb_put(skb, 24); +	memset(mgmt, 0, 24); +	memcpy(mgmt->da, peer, ETH_ALEN); +	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); +	memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); + +	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | +					  IEEE80211_STYPE_ACTION); + +	switch (action_code) { +	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: +		skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); +		mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; +		mgmt->u.action.u.tdls_discover_resp.action_code = +			WLAN_PUB_ACTION_TDLS_DISCOVER_RES; +		mgmt->u.action.u.tdls_discover_resp.dialog_token = +			dialog_token; +		mgmt->u.action.u.tdls_discover_resp.capability = +			cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + +		ieee80211_add_srates_ie(sdata, skb, false, band); +		ieee80211_add_ext_srates_ie(sdata, skb, false, band); +		ieee80211_tdls_add_ext_capab(skb); +		break; +	default: +		return -EINVAL; +	} + +	return 0; +} + +int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, +			const u8 *peer, u8 action_code, u8 dialog_token, +			u16 status_code, u32 peer_capability, +			const u8 *extra_ies, size_t extra_ies_len) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local; +	struct sk_buff *skb = NULL; +	bool send_direct; +	int ret; + +	if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) +		return -ENOTSUPP; + +	/* make sure we are in managed mode, and associated */ +	if (sdata->vif.type != NL80211_IFTYPE_STATION || +	    !sdata->u.mgd.associated) +		return -EINVAL; + +	tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n", +		 action_code, peer); + +	skb = dev_alloc_skb(local->hw.extra_tx_headroom + +			    max(sizeof(struct ieee80211_mgmt), +				sizeof(struct ieee80211_tdls_data)) + +			    50 + /* supported rates */ +			    7 + /* ext capab */ +			    extra_ies_len + +			    sizeof(struct ieee80211_tdls_lnkie)); +	if (!skb) +		return -ENOMEM; + +	skb_reserve(skb, local->hw.extra_tx_headroom); + +	switch (action_code) { +	case WLAN_TDLS_SETUP_REQUEST: +	case WLAN_TDLS_SETUP_RESPONSE: +	case WLAN_TDLS_SETUP_CONFIRM: +	case WLAN_TDLS_TEARDOWN: +	case WLAN_TDLS_DISCOVERY_REQUEST: +		ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer, +						     action_code, dialog_token, +						     status_code, skb); +		send_direct = false; +		break; +	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: +		ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code, +						 dialog_token, status_code, +						 skb); +		send_direct = true; +		break; +	default: +		ret = -ENOTSUPP; +		break; +	} + +	if (ret < 0) +		goto fail; + +	if (extra_ies_len) +		memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); + +	/* the TDLS link IE is always added last */ +	switch (action_code) { +	case WLAN_TDLS_SETUP_REQUEST: +	case WLAN_TDLS_SETUP_CONFIRM: +	case WLAN_TDLS_TEARDOWN: +	case WLAN_TDLS_DISCOVERY_REQUEST: +		/* we are the initiator */ +		ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer, +					   sdata->u.mgd.bssid); +		break; +	case WLAN_TDLS_SETUP_RESPONSE: +	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: +		/* we are the responder */ +		ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr, +					   sdata->u.mgd.bssid); +		break; +	default: +		ret = -ENOTSUPP; +		goto fail; +	} + +	if (send_direct) { +		ieee80211_tx_skb(sdata, skb); +		return 0; +	} + +	/* +	 * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise +	 * we should default to AC_VI. +	 */ +	switch (action_code) { +	case WLAN_TDLS_SETUP_REQUEST: +	case WLAN_TDLS_SETUP_RESPONSE: +		skb_set_queue_mapping(skb, IEEE80211_AC_BK); +		skb->priority = 2; +		break; +	default: +		skb_set_queue_mapping(skb, IEEE80211_AC_VI); +		skb->priority = 5; +		break; +	} + +	/* disable bottom halves when entering the Tx path */ +	local_bh_disable(); +	ret = ieee80211_subif_start_xmit(skb, dev); +	local_bh_enable(); + +	return ret; + +fail: +	dev_kfree_skb(skb); +	return ret; +} + +int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, +			const u8 *peer, enum nl80211_tdls_operation oper) +{ +	struct sta_info *sta; +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + +	if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) +		return -ENOTSUPP; + +	if (sdata->vif.type != NL80211_IFTYPE_STATION) +		return -EINVAL; + +	tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); + +	switch (oper) { +	case NL80211_TDLS_ENABLE_LINK: +		rcu_read_lock(); +		sta = sta_info_get(sdata, peer); +		if (!sta) { +			rcu_read_unlock(); +			return -ENOLINK; +		} + +		set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); +		rcu_read_unlock(); +		break; +	case NL80211_TDLS_DISABLE_LINK: +		return sta_info_destroy_addr(sdata, peer); +	case NL80211_TDLS_TEARDOWN: +	case NL80211_TDLS_SETUP: +	case NL80211_TDLS_DISCOVERY_REQ: +		/* We don't support in-driver setup/teardown/discovery */ +		return -ENOTSUPP; +	default: +		return -ENOTSUPP; +	} + +	return 0; +} diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c index e840c9cd46d..0ae207771a5 100644 --- a/net/mac80211/tkip.c +++ b/net/mac80211/tkip.c @@ -10,6 +10,7 @@  #include <linux/bitops.h>  #include <linux/types.h>  #include <linux/netdevice.h> +#include <linux/export.h>  #include <asm/unaligned.h>  #include <net/mac80211.h> @@ -101,6 +102,7 @@ static void tkip_mixing_phase1(const u8 *tk, struct tkip_ctx *ctx,  		p1k[4] += tkipS(p1k[3] ^ get_unaligned_le16(tk + 0 + j)) + i;  	}  	ctx->state = TKIP_STATE_PHASE1_DONE; +	ctx->p1k_iv32 = tsc_IV32;  }  static void tkip_mixing_phase2(const u8 *tk, struct tkip_ctx *ctx, @@ -140,60 +142,78 @@ static void tkip_mixing_phase2(const u8 *tk, struct tkip_ctx *ctx,  /* Add TKIP IV and Ext. IV at @pos. @iv0, @iv1, and @iv2 are the first octets   * of the IV. Returns pointer to the octet following IVs (i.e., beginning of   * the packet payload). */ -u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, u16 iv16) +u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key)  { -	pos = write_tkip_iv(pos, iv16); +	lockdep_assert_held(&key->u.tkip.txlock); + +	pos = write_tkip_iv(pos, key->u.tkip.tx.iv16);  	*pos++ = (key->conf.keyidx << 6) | (1 << 5) /* Ext IV */;  	put_unaligned_le32(key->u.tkip.tx.iv32, pos);  	return pos + 4;  } -void ieee80211_get_tkip_key(struct ieee80211_key_conf *keyconf, -			struct sk_buff *skb, enum ieee80211_tkip_key_type type, -			u8 *outkey) +static void ieee80211_compute_tkip_p1k(struct ieee80211_key *key, u32 iv32)  { -	struct ieee80211_key *key = (struct ieee80211_key *) -			container_of(keyconf, struct ieee80211_key, conf); -	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; -	u8 *data; -	const u8 *tk; -	struct tkip_ctx *ctx; -	u16 iv16; -	u32 iv32; - -	data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control); -	iv16 = data[2] | (data[0] << 8); -	iv32 = get_unaligned_le32(&data[4]); +	struct ieee80211_sub_if_data *sdata = key->sdata; +	struct tkip_ctx *ctx = &key->u.tkip.tx; +	const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; -	tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; -	ctx = &key->u.tkip.tx; +	lockdep_assert_held(&key->u.tkip.txlock); + +	/* +	 * Update the P1K when the IV32 is different from the value it +	 * had when we last computed it (or when not initialised yet). +	 * This might flip-flop back and forth if packets are processed +	 * out-of-order due to the different ACs, but then we have to +	 * just compute the P1K more often. +	 */ +	if (ctx->p1k_iv32 != iv32 || ctx->state == TKIP_STATE_NOT_INIT) +		tkip_mixing_phase1(tk, ctx, sdata->vif.addr, iv32); +} -#ifdef CONFIG_MAC80211_TKIP_DEBUG -	printk(KERN_DEBUG "TKIP encrypt: iv16 = 0x%04x, iv32 = 0x%08x\n", -			iv16, iv32); +void ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *keyconf, +			       u32 iv32, u16 *p1k) +{ +	struct ieee80211_key *key = (struct ieee80211_key *) +			container_of(keyconf, struct ieee80211_key, conf); +	struct tkip_ctx *ctx = &key->u.tkip.tx; -	if (iv32 != ctx->iv32) { -		printk(KERN_DEBUG "skb: iv32 = 0x%08x key: iv32 = 0x%08x\n", -			iv32, ctx->iv32); -		printk(KERN_DEBUG "Wrap around of iv16 in the middle of a " -			"fragmented packet\n"); -	} -#endif +	spin_lock_bh(&key->u.tkip.txlock); +	ieee80211_compute_tkip_p1k(key, iv32); +	memcpy(p1k, ctx->p1k, sizeof(ctx->p1k)); +	spin_unlock_bh(&key->u.tkip.txlock); +} +EXPORT_SYMBOL(ieee80211_get_tkip_p1k_iv); -	/* Update the p1k only when the iv16 in the packet wraps around, this -	 * might occur after the wrap around of iv16 in the key in case of -	 * fragmented packets. */ -	if (iv16 == 0 || ctx->state == TKIP_STATE_NOT_INIT) -		tkip_mixing_phase1(tk, ctx, hdr->addr2, iv32); +void ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf, +			       const u8 *ta, u32 iv32, u16 *p1k) +{ +	const u8 *tk = &keyconf->key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; +	struct tkip_ctx ctx; -	if (type == IEEE80211_TKIP_P1_KEY) { -		memcpy(outkey, ctx->p1k, sizeof(u16) * 5); -		return; -	} +	tkip_mixing_phase1(tk, &ctx, ta, iv32); +	memcpy(p1k, ctx.p1k, sizeof(ctx.p1k)); +} +EXPORT_SYMBOL(ieee80211_get_tkip_rx_p1k); -	tkip_mixing_phase2(tk, ctx, iv16, outkey); +void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf, +			    struct sk_buff *skb, u8 *p2k) +{ +	struct ieee80211_key *key = (struct ieee80211_key *) +			container_of(keyconf, struct ieee80211_key, conf); +	const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; +	struct tkip_ctx *ctx = &key->u.tkip.tx; +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; +	const u8 *data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control); +	u32 iv32 = get_unaligned_le32(&data[4]); +	u16 iv16 = data[2] | (data[0] << 8); + +	spin_lock(&key->u.tkip.txlock); +	ieee80211_compute_tkip_p1k(key, iv32); +	tkip_mixing_phase2(tk, ctx, iv16, p2k); +	spin_unlock(&key->u.tkip.txlock);  } -EXPORT_SYMBOL(ieee80211_get_tkip_key); +EXPORT_SYMBOL(ieee80211_get_tkip_p2k);  /*   * Encrypt packet payload with TKIP using @key. @pos is a pointer to the @@ -202,28 +222,24 @@ EXPORT_SYMBOL(ieee80211_get_tkip_key);   * @payload_len is the length of payload (_not_ including IV/ICV length).   * @ta is the transmitter addresses.   */ -int ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm, +int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm,  				struct ieee80211_key *key, -				u8 *pos, size_t payload_len, u8 *ta) +				struct sk_buff *skb, +				u8 *payload, size_t payload_len)  {  	u8 rc4key[16]; -	struct tkip_ctx *ctx = &key->u.tkip.tx; -	const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; - -	/* Calculate per-packet key */ -	if (ctx->iv16 == 0 || ctx->state == TKIP_STATE_NOT_INIT) -		tkip_mixing_phase1(tk, ctx, ta, ctx->iv32); -	tkip_mixing_phase2(tk, ctx, ctx->iv16, rc4key); +	ieee80211_get_tkip_p2k(&key->conf, skb, rc4key); -	return ieee80211_wep_encrypt_data(tfm, rc4key, 16, pos, payload_len); +	return ieee80211_wep_encrypt_data(tfm, rc4key, 16, +					  payload, payload_len);  }  /* Decrypt packet payload with TKIP using @key. @pos is a pointer to the   * beginning of the buffer containing IEEE 802.11 header payload, i.e.,   * including IV, Ext. IV, real data, Michael MIC, ICV. @payload_len is the   * length of payload, including IV, Ext. IV, MIC, ICV.  */ -int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, +int ieee80211_tkip_decrypt_data(struct crypto_cipher *tfm,  				struct ieee80211_key *key,  				u8 *payload, size_t payload_len, u8 *ta,  				u8 *ra, int only_iv, int queue, @@ -242,17 +258,6 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,  	keyid = pos[3];  	iv32 = get_unaligned_le32(pos + 4);  	pos += 8; -#ifdef CONFIG_MAC80211_TKIP_DEBUG -	{ -		int i; -		printk(KERN_DEBUG "TKIP decrypt: data(len=%zd)", payload_len); -		for (i = 0; i < payload_len; i++) -			printk(" %02x", payload[i]); -		printk("\n"); -		printk(KERN_DEBUG "TKIP decrypt: iv16=%04x iv32=%08x\n", -		       iv16, iv32); -	} -#endif  	if (!(keyid & (1 << 5)))  		return TKIP_DECRYPT_NO_EXT_IV; @@ -263,16 +268,8 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,  	if (key->u.tkip.rx[queue].state != TKIP_STATE_NOT_INIT &&  	    (iv32 < key->u.tkip.rx[queue].iv32 ||  	     (iv32 == key->u.tkip.rx[queue].iv32 && -	      iv16 <= key->u.tkip.rx[queue].iv16))) { -#ifdef CONFIG_MAC80211_TKIP_DEBUG -		printk(KERN_DEBUG "TKIP replay detected for RX frame from " -		       "%pM (RX IV (%04x,%02x) <= prev. IV (%04x,%02x)\n", -		       ta, -		       iv32, iv16, key->u.tkip.rx[queue].iv32, -		       key->u.tkip.rx[queue].iv16); -#endif +	      iv16 <= key->u.tkip.rx[queue].iv16)))  		return TKIP_DECRYPT_REPLAY; -	}  	if (only_iv) {  		res = TKIP_DECRYPT_OK; @@ -284,22 +281,6 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,  	    key->u.tkip.rx[queue].iv32 != iv32) {  		/* IV16 wrapped around - perform TKIP phase 1 */  		tkip_mixing_phase1(tk, &key->u.tkip.rx[queue], ta, iv32); -#ifdef CONFIG_MAC80211_TKIP_DEBUG -		{ -			int i; -			u8 key_offset = NL80211_TKIP_DATA_OFFSET_ENCR_KEY; -			printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=%pM" -			       " TK=", ta); -			for (i = 0; i < 16; i++) -				printk("%02x ", -				       key->conf.key[key_offset + i]); -			printk("\n"); -			printk(KERN_DEBUG "TKIP decrypt: P1K="); -			for (i = 0; i < 5; i++) -				printk("%04x ", key->u.tkip.rx[queue].p1k[i]); -			printk("\n"); -		} -#endif  	}  	if (key->local->ops->update_tkip_key &&  	    key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE && @@ -315,15 +296,6 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,  	}  	tkip_mixing_phase2(tk, &key->u.tkip.rx[queue], iv16, rc4key); -#ifdef CONFIG_MAC80211_TKIP_DEBUG -	{ -		int i; -		printk(KERN_DEBUG "TKIP decrypt: Phase2 rc4key="); -		for (i = 0; i < 16; i++) -			printk("%02x ", rc4key[i]); -		printk("\n"); -	} -#endif  	res = ieee80211_wep_decrypt_data(tfm, rc4key, 16, pos, payload_len - 12);   done: diff --git a/net/mac80211/tkip.h b/net/mac80211/tkip.h index 7e83dee976f..e3ecb659b90 100644 --- a/net/mac80211/tkip.h +++ b/net/mac80211/tkip.h @@ -13,18 +13,20 @@  #include <linux/crypto.h>  #include "key.h" -u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, u16 iv16); +u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key); + +int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm, +				struct ieee80211_key *key, +				struct sk_buff *skb, +				u8 *payload, size_t payload_len); -int ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm, -				 struct ieee80211_key *key, -				 u8 *pos, size_t payload_len, u8 *ta);  enum {  	TKIP_DECRYPT_OK = 0,  	TKIP_DECRYPT_NO_EXT_IV = -1,  	TKIP_DECRYPT_INVALID_KEYIDX = -2,  	TKIP_DECRYPT_REPLAY = -3,  }; -int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, +int ieee80211_tkip_decrypt_data(struct crypto_cipher *tfm,  				struct ieee80211_key *key,  				u8 *payload, size_t payload_len, u8 *ta,  				u8 *ra, int only_iv, int queue, diff --git a/net/mac80211/trace.c b/net/mac80211/trace.c new file mode 100644 index 00000000000..386e45d8a95 --- /dev/null +++ b/net/mac80211/trace.c @@ -0,0 +1,75 @@ +/* bug in tracepoint.h, it should include this */ +#include <linux/module.h> + +/* sparse isn't too happy with all macros... */ +#ifndef __CHECKER__ +#include <net/cfg80211.h> +#include "driver-ops.h" +#include "debug.h" +#define CREATE_TRACE_POINTS +#include "trace.h" + +#ifdef CONFIG_MAC80211_MESSAGE_TRACING +void __sdata_info(const char *fmt, ...) +{ +	struct va_format vaf = { +		.fmt = fmt, +	}; +	va_list args; + +	va_start(args, fmt); +	vaf.va = &args; + +	pr_info("%pV", &vaf); +	trace_mac80211_info(&vaf); +	va_end(args); +} + +void __sdata_dbg(bool print, const char *fmt, ...) +{ +	struct va_format vaf = { +		.fmt = fmt, +	}; +	va_list args; + +	va_start(args, fmt); +	vaf.va = &args; + +	if (print) +		pr_debug("%pV", &vaf); +	trace_mac80211_dbg(&vaf); +	va_end(args); +} + +void __sdata_err(const char *fmt, ...) +{ +	struct va_format vaf = { +		.fmt = fmt, +	}; +	va_list args; + +	va_start(args, fmt); +	vaf.va = &args; + +	pr_err("%pV", &vaf); +	trace_mac80211_err(&vaf); +	va_end(args); +} + +void __wiphy_dbg(struct wiphy *wiphy, bool print, const char *fmt, ...) +{ +	struct va_format vaf = { +		.fmt = fmt, +	}; +	va_list args; + +	va_start(args, fmt); +	vaf.va = &args; + +	if (print) +		wiphy_dbg(wiphy, "%pV", &vaf); +	trace_mac80211_dbg(&vaf); +	va_end(args); +} +#endif +#endif diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h new file mode 100644 index 00000000000..cfe1a0688b5 --- /dev/null +++ b/net/mac80211/trace.h @@ -0,0 +1,2147 @@ +#if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ) +#define __MAC80211_DRIVER_TRACE + +#include <linux/tracepoint.h> +#include <net/mac80211.h> +#include "ieee80211_i.h" + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mac80211 + +#define MAXNAME		32 +#define LOCAL_ENTRY	__array(char, wiphy_name, 32) +#define LOCAL_ASSIGN	strlcpy(__entry->wiphy_name, wiphy_name(local->hw.wiphy), MAXNAME) +#define LOCAL_PR_FMT	"%s" +#define LOCAL_PR_ARG	__entry->wiphy_name + +#define STA_ENTRY	__array(char, sta_addr, ETH_ALEN) +#define STA_ASSIGN	(sta ? memcpy(__entry->sta_addr, sta->addr, ETH_ALEN) : memset(__entry->sta_addr, 0, ETH_ALEN)) +#define STA_PR_FMT	" sta:%pM" +#define STA_PR_ARG	__entry->sta_addr + +#define VIF_ENTRY	__field(enum nl80211_iftype, vif_type) __field(void *, sdata)	\ +			__field(bool, p2p)						\ +			__string(vif_name, sdata->name) +#define VIF_ASSIGN	__entry->vif_type = sdata->vif.type; __entry->sdata = sdata;	\ +			__entry->p2p = sdata->vif.p2p;					\ +			__assign_str(vif_name, sdata->name) +#define VIF_PR_FMT	" vif:%s(%d%s)" +#define VIF_PR_ARG	__get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" + +#define CHANDEF_ENTRY	__field(u32, control_freq)					\ +			__field(u32, chan_width)					\ +			__field(u32, center_freq1)					\ +			__field(u32, center_freq2) +#define CHANDEF_ASSIGN(c)								\ +			__entry->control_freq = (c)->chan ? (c)->chan->center_freq : 0;	\ +			__entry->chan_width = (c)->width;				\ +			__entry->center_freq1 = (c)->center_freq1;			\ +			__entry->center_freq2 = (c)->center_freq2; +#define CHANDEF_PR_FMT	" control:%d MHz width:%d center: %d/%d MHz" +#define CHANDEF_PR_ARG	__entry->control_freq, __entry->chan_width,			\ +			__entry->center_freq1, __entry->center_freq2 + +#define MIN_CHANDEF_ENTRY								\ +			__field(u32, min_control_freq)					\ +			__field(u32, min_chan_width)					\ +			__field(u32, min_center_freq1)					\ +			__field(u32, min_center_freq2) + +#define MIN_CHANDEF_ASSIGN(c)								\ +			__entry->min_control_freq = (c)->chan ? (c)->chan->center_freq : 0;	\ +			__entry->min_chan_width = (c)->width;				\ +			__entry->min_center_freq1 = (c)->center_freq1;			\ +			__entry->min_center_freq2 = (c)->center_freq2; +#define MIN_CHANDEF_PR_FMT	" min_control:%d MHz min_width:%d min_center: %d/%d MHz" +#define MIN_CHANDEF_PR_ARG	__entry->min_control_freq, __entry->min_chan_width,	\ +			__entry->min_center_freq1, __entry->min_center_freq2 + +#define CHANCTX_ENTRY	CHANDEF_ENTRY							\ +			MIN_CHANDEF_ENTRY						\ +			__field(u8, rx_chains_static)					\ +			__field(u8, rx_chains_dynamic) +#define CHANCTX_ASSIGN	CHANDEF_ASSIGN(&ctx->conf.def)					\ +			MIN_CHANDEF_ASSIGN(&ctx->conf.min_def)				\ +			__entry->rx_chains_static = ctx->conf.rx_chains_static;		\ +			__entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic +#define CHANCTX_PR_FMT	CHANDEF_PR_FMT MIN_CHANDEF_PR_FMT " chains:%d/%d" +#define CHANCTX_PR_ARG	CHANDEF_PR_ARG,	MIN_CHANDEF_PR_ARG,				\ +			__entry->rx_chains_static, __entry->rx_chains_dynamic + + + +/* + * Tracing for driver callbacks. + */ + +DECLARE_EVENT_CLASS(local_only_evt, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local), +	TP_STRUCT__entry( +		LOCAL_ENTRY +	), +	TP_fast_assign( +		LOCAL_ASSIGN; +	), +	TP_printk(LOCAL_PR_FMT, LOCAL_PR_ARG) +); + +DECLARE_EVENT_CLASS(local_sdata_addr_evt, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), +	TP_ARGS(local, sdata), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		__array(char, addr, ETH_ALEN) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		memcpy(__entry->addr, sdata->vif.addr, ETH_ALEN); +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT " addr:%pM", +		LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr +	) +); + +DECLARE_EVENT_CLASS(local_u32_evt, +	TP_PROTO(struct ieee80211_local *local, u32 value), +	TP_ARGS(local, value), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(u32, value) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->value = value; +	), + +	TP_printk( +		LOCAL_PR_FMT " value:%d", +		LOCAL_PR_ARG, __entry->value +	) +); + +DECLARE_EVENT_CLASS(local_sdata_evt, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), +	TP_ARGS(local, sdata), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +	), + +	TP_printk( +		LOCAL_PR_FMT VIF_PR_FMT, +		LOCAL_PR_ARG, VIF_PR_ARG +	) +); + +DEFINE_EVENT(local_only_evt, drv_return_void, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +TRACE_EVENT(drv_return_int, +	TP_PROTO(struct ieee80211_local *local, int ret), +	TP_ARGS(local, ret), +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(int, ret) +	), +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->ret = ret; +	), +	TP_printk(LOCAL_PR_FMT " - %d", LOCAL_PR_ARG, __entry->ret) +); + +TRACE_EVENT(drv_return_bool, +	TP_PROTO(struct ieee80211_local *local, bool ret), +	TP_ARGS(local, ret), +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(bool, ret) +	), +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->ret = ret; +	), +	TP_printk(LOCAL_PR_FMT " - %s", LOCAL_PR_ARG, (__entry->ret) ? +		  "true" : "false") +); + +TRACE_EVENT(drv_return_u32, +	TP_PROTO(struct ieee80211_local *local, u32 ret), +	TP_ARGS(local, ret), +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(u32, ret) +	), +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->ret = ret; +	), +	TP_printk(LOCAL_PR_FMT " - %u", LOCAL_PR_ARG, __entry->ret) +); + +TRACE_EVENT(drv_return_u64, +	TP_PROTO(struct ieee80211_local *local, u64 ret), +	TP_ARGS(local, ret), +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(u64, ret) +	), +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->ret = ret; +	), +	TP_printk(LOCAL_PR_FMT " - %llu", LOCAL_PR_ARG, __entry->ret) +); + +DEFINE_EVENT(local_only_evt, drv_start, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +DEFINE_EVENT(local_u32_evt, drv_get_et_strings, +	     TP_PROTO(struct ieee80211_local *local, u32 sset), +	     TP_ARGS(local, sset) +); + +DEFINE_EVENT(local_u32_evt, drv_get_et_sset_count, +	     TP_PROTO(struct ieee80211_local *local, u32 sset), +	     TP_ARGS(local, sset) +); + +DEFINE_EVENT(local_only_evt, drv_get_et_stats, +	     TP_PROTO(struct ieee80211_local *local), +	     TP_ARGS(local) +); + +DEFINE_EVENT(local_only_evt, drv_suspend, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +DEFINE_EVENT(local_only_evt, drv_resume, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +TRACE_EVENT(drv_set_wakeup, +	TP_PROTO(struct ieee80211_local *local, bool enabled), +	TP_ARGS(local, enabled), +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(bool, enabled) +	), +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->enabled = enabled; +	), +	TP_printk(LOCAL_PR_FMT " enabled:%d", LOCAL_PR_ARG, __entry->enabled) +); + +DEFINE_EVENT(local_only_evt, drv_stop, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +DEFINE_EVENT(local_sdata_addr_evt, drv_add_interface, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), +	TP_ARGS(local, sdata) +); + +TRACE_EVENT(drv_change_interface, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 enum nl80211_iftype type, bool p2p), + +	TP_ARGS(local, sdata, type, p2p), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		__field(u32, new_type) +		__field(bool, new_p2p) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		__entry->new_type = type; +		__entry->new_p2p = p2p; +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT " new type:%d%s", +		LOCAL_PR_ARG, VIF_PR_ARG, __entry->new_type, +		__entry->new_p2p ? "/p2p" : "" +	) +); + +DEFINE_EVENT(local_sdata_addr_evt, drv_remove_interface, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), +	TP_ARGS(local, sdata) +); + +TRACE_EVENT(drv_config, +	TP_PROTO(struct ieee80211_local *local, +		 u32 changed), + +	TP_ARGS(local, changed), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(u32, changed) +		__field(u32, flags) +		__field(int, power_level) +		__field(int, dynamic_ps_timeout) +		__field(int, max_sleep_period) +		__field(u16, listen_interval) +		__field(u8, long_frame_max_tx_count) +		__field(u8, short_frame_max_tx_count) +		CHANDEF_ENTRY +		__field(int, smps) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->changed = changed; +		__entry->flags = local->hw.conf.flags; +		__entry->power_level = local->hw.conf.power_level; +		__entry->dynamic_ps_timeout = local->hw.conf.dynamic_ps_timeout; +		__entry->max_sleep_period = local->hw.conf.max_sleep_period; +		__entry->listen_interval = local->hw.conf.listen_interval; +		__entry->long_frame_max_tx_count = +			local->hw.conf.long_frame_max_tx_count; +		__entry->short_frame_max_tx_count = +			local->hw.conf.short_frame_max_tx_count; +		CHANDEF_ASSIGN(&local->hw.conf.chandef) +		__entry->smps = local->hw.conf.smps_mode; +	), + +	TP_printk( +		LOCAL_PR_FMT " ch:%#x" CHANDEF_PR_FMT, +		LOCAL_PR_ARG, __entry->changed, CHANDEF_PR_ARG +	) +); + +TRACE_EVENT(drv_bss_info_changed, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_bss_conf *info, +		 u32 changed), + +	TP_ARGS(local, sdata, info, changed), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		__field(u32, changed) +		__field(bool, assoc) +		__field(bool, ibss_joined) +		__field(bool, ibss_creator) +		__field(u16, aid) +		__field(bool, cts) +		__field(bool, shortpre) +		__field(bool, shortslot) +		__field(bool, enable_beacon) +		__field(u8, dtimper) +		__field(u16, bcnint) +		__field(u16, assoc_cap) +		__field(u64, sync_tsf) +		__field(u32, sync_device_ts) +		__field(u8, sync_dtim_count) +		__field(u32, basic_rates) +		__array(int, mcast_rate, IEEE80211_NUM_BANDS) +		__field(u16, ht_operation_mode) +		__field(s32, cqm_rssi_thold); +		__field(s32, cqm_rssi_hyst); +		__field(u32, channel_width); +		__field(u32, channel_cfreq1); +		__dynamic_array(u32, arp_addr_list, +				info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ? +					IEEE80211_BSS_ARP_ADDR_LIST_LEN : +					info->arp_addr_cnt); +		__field(int, arp_addr_cnt); +		__field(bool, qos); +		__field(bool, idle); +		__field(bool, ps); +		__dynamic_array(u8, ssid, info->ssid_len); +		__field(bool, hidden_ssid); +		__field(int, txpower) +		__field(u8, p2p_oppps_ctwindow) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		__entry->changed = changed; +		__entry->aid = info->aid; +		__entry->assoc = info->assoc; +		__entry->ibss_joined = info->ibss_joined; +		__entry->ibss_creator = info->ibss_creator; +		__entry->shortpre = info->use_short_preamble; +		__entry->cts = info->use_cts_prot; +		__entry->shortslot = info->use_short_slot; +		__entry->enable_beacon = info->enable_beacon; +		__entry->dtimper = info->dtim_period; +		__entry->bcnint = info->beacon_int; +		__entry->assoc_cap = info->assoc_capability; +		__entry->sync_tsf = info->sync_tsf; +		__entry->sync_device_ts = info->sync_device_ts; +		__entry->sync_dtim_count = info->sync_dtim_count; +		__entry->basic_rates = info->basic_rates; +		memcpy(__entry->mcast_rate, info->mcast_rate, +		       sizeof(__entry->mcast_rate)); +		__entry->ht_operation_mode = info->ht_operation_mode; +		__entry->cqm_rssi_thold = info->cqm_rssi_thold; +		__entry->cqm_rssi_hyst = info->cqm_rssi_hyst; +		__entry->channel_width = info->chandef.width; +		__entry->channel_cfreq1 = info->chandef.center_freq1; +		__entry->arp_addr_cnt = info->arp_addr_cnt; +		memcpy(__get_dynamic_array(arp_addr_list), info->arp_addr_list, +		       sizeof(u32) * (info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ? +					IEEE80211_BSS_ARP_ADDR_LIST_LEN : +					info->arp_addr_cnt)); +		__entry->qos = info->qos; +		__entry->idle = info->idle; +		__entry->ps = info->ps; +		memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len); +		__entry->hidden_ssid = info->hidden_ssid; +		__entry->txpower = info->txpower; +		__entry->p2p_oppps_ctwindow = info->p2p_noa_attr.oppps_ctwindow; +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT " changed:%#x", +		LOCAL_PR_ARG, VIF_PR_ARG, __entry->changed +	) +); + +TRACE_EVENT(drv_prepare_multicast, +	TP_PROTO(struct ieee80211_local *local, int mc_count), + +	TP_ARGS(local, mc_count), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(int, mc_count) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->mc_count = mc_count; +	), + +	TP_printk( +		LOCAL_PR_FMT " prepare mc (%d)", +		LOCAL_PR_ARG, __entry->mc_count +	) +); + +TRACE_EVENT(drv_configure_filter, +	TP_PROTO(struct ieee80211_local *local, +		 unsigned int changed_flags, +		 unsigned int *total_flags, +		 u64 multicast), + +	TP_ARGS(local, changed_flags, total_flags, multicast), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(unsigned int, changed) +		__field(unsigned int, total) +		__field(u64, multicast) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->changed = changed_flags; +		__entry->total = *total_flags; +		__entry->multicast = multicast; +	), + +	TP_printk( +		LOCAL_PR_FMT " changed:%#x total:%#x", +		LOCAL_PR_ARG, __entry->changed, __entry->total +	) +); + +TRACE_EVENT(drv_set_tim, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sta *sta, bool set), + +	TP_ARGS(local, sta, set), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		STA_ENTRY +		__field(bool, set) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		STA_ASSIGN; +		__entry->set = set; +	), + +	TP_printk( +		LOCAL_PR_FMT STA_PR_FMT " set:%d", +		LOCAL_PR_ARG, STA_PR_ARG, __entry->set +	) +); + +TRACE_EVENT(drv_set_key, +	TP_PROTO(struct ieee80211_local *local, +		 enum set_key_cmd cmd, struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_sta *sta, +		 struct ieee80211_key_conf *key), + +	TP_ARGS(local, cmd, sdata, sta, key), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		STA_ENTRY +		__field(u32, cipher) +		__field(u8, hw_key_idx) +		__field(u8, flags) +		__field(s8, keyidx) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		STA_ASSIGN; +		__entry->cipher = key->cipher; +		__entry->flags = key->flags; +		__entry->keyidx = key->keyidx; +		__entry->hw_key_idx = key->hw_key_idx; +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT, +		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG +	) +); + +TRACE_EVENT(drv_update_tkip_key, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_key_conf *conf, +		 struct ieee80211_sta *sta, u32 iv32), + +	TP_ARGS(local, sdata, conf, sta, iv32), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		STA_ENTRY +		__field(u32, iv32) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		STA_ASSIGN; +		__entry->iv32 = iv32; +	), + +	TP_printk( +		LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " iv32:%#x", +		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->iv32 +	) +); + +DEFINE_EVENT(local_sdata_evt, drv_hw_scan, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), +	TP_ARGS(local, sdata) +); + +DEFINE_EVENT(local_sdata_evt, drv_cancel_hw_scan, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), +	TP_ARGS(local, sdata) +); + +DEFINE_EVENT(local_sdata_evt, drv_sched_scan_start, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), +	TP_ARGS(local, sdata) +); + +DEFINE_EVENT(local_sdata_evt, drv_sched_scan_stop, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), +	TP_ARGS(local, sdata) +); + +DEFINE_EVENT(local_only_evt, drv_sw_scan_start, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +DEFINE_EVENT(local_only_evt, drv_sw_scan_complete, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +TRACE_EVENT(drv_get_stats, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_low_level_stats *stats, +		 int ret), + +	TP_ARGS(local, stats, ret), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(int, ret) +		__field(unsigned int, ackfail) +		__field(unsigned int, rtsfail) +		__field(unsigned int, fcserr) +		__field(unsigned int, rtssucc) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->ret = ret; +		__entry->ackfail = stats->dot11ACKFailureCount; +		__entry->rtsfail = stats->dot11RTSFailureCount; +		__entry->fcserr = stats->dot11FCSErrorCount; +		__entry->rtssucc = stats->dot11RTSSuccessCount; +	), + +	TP_printk( +		LOCAL_PR_FMT " ret:%d", +		LOCAL_PR_ARG, __entry->ret +	) +); + +TRACE_EVENT(drv_get_tkip_seq, +	TP_PROTO(struct ieee80211_local *local, +		 u8 hw_key_idx, u32 *iv32, u16 *iv16), + +	TP_ARGS(local, hw_key_idx, iv32, iv16), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(u8, hw_key_idx) +		__field(u32, iv32) +		__field(u16, iv16) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->hw_key_idx = hw_key_idx; +		__entry->iv32 = *iv32; +		__entry->iv16 = *iv16; +	), + +	TP_printk( +		LOCAL_PR_FMT, LOCAL_PR_ARG +	) +); + +DEFINE_EVENT(local_u32_evt, drv_set_frag_threshold, +	TP_PROTO(struct ieee80211_local *local, u32 value), +	TP_ARGS(local, value) +); + +DEFINE_EVENT(local_u32_evt, drv_set_rts_threshold, +	TP_PROTO(struct ieee80211_local *local, u32 value), +	TP_ARGS(local, value) +); + +TRACE_EVENT(drv_set_coverage_class, +	TP_PROTO(struct ieee80211_local *local, u8 value), + +	TP_ARGS(local, value), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(u8, value) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->value = value; +	), + +	TP_printk( +		LOCAL_PR_FMT " value:%d", +		LOCAL_PR_ARG, __entry->value +	) +); + +TRACE_EVENT(drv_sta_notify, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 enum sta_notify_cmd cmd, +		 struct ieee80211_sta *sta), + +	TP_ARGS(local, sdata, cmd, sta), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		STA_ENTRY +		__field(u32, cmd) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		STA_ASSIGN; +		__entry->cmd = cmd; +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT " cmd:%d", +		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->cmd +	) +); + +TRACE_EVENT(drv_sta_state, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_sta *sta, +		 enum ieee80211_sta_state old_state, +		 enum ieee80211_sta_state new_state), + +	TP_ARGS(local, sdata, sta, old_state, new_state), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		STA_ENTRY +		__field(u32, old_state) +		__field(u32, new_state) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		STA_ASSIGN; +		__entry->old_state = old_state; +		__entry->new_state = new_state; +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT " state: %d->%d", +		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, +		__entry->old_state, __entry->new_state +	) +); + +TRACE_EVENT(drv_sta_rc_update, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_sta *sta, +		 u32 changed), + +	TP_ARGS(local, sdata, sta, changed), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		STA_ENTRY +		__field(u32, changed) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		STA_ASSIGN; +		__entry->changed = changed; +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT " changed: 0x%x", +		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->changed +	) +); + +DECLARE_EVENT_CLASS(sta_event, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_sta *sta), + +	TP_ARGS(local, sdata, sta), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		STA_ENTRY +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		STA_ASSIGN; +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT, +		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG +	) +); + +DEFINE_EVENT(sta_event, drv_sta_add, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_sta *sta), +	TP_ARGS(local, sdata, sta) +); + +DEFINE_EVENT(sta_event, drv_sta_remove, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_sta *sta), +	TP_ARGS(local, sdata, sta) +); + +DEFINE_EVENT(sta_event, drv_sta_pre_rcu_remove, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_sta *sta), +	TP_ARGS(local, sdata, sta) +); + +TRACE_EVENT(drv_conf_tx, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 u16 ac, const struct ieee80211_tx_queue_params *params), + +	TP_ARGS(local, sdata, ac, params), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		__field(u16, ac) +		__field(u16, txop) +		__field(u16, cw_min) +		__field(u16, cw_max) +		__field(u8, aifs) +		__field(bool, uapsd) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		__entry->ac = ac; +		__entry->txop = params->txop; +		__entry->cw_max = params->cw_max; +		__entry->cw_min = params->cw_min; +		__entry->aifs = params->aifs; +		__entry->uapsd = params->uapsd; +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT  " AC:%d", +		LOCAL_PR_ARG, VIF_PR_ARG, __entry->ac +	) +); + +DEFINE_EVENT(local_sdata_evt, drv_get_tsf, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), +	TP_ARGS(local, sdata) +); + +TRACE_EVENT(drv_set_tsf, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 u64 tsf), + +	TP_ARGS(local, sdata, tsf), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		__field(u64, tsf) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		__entry->tsf = tsf; +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT  " tsf:%llu", +		LOCAL_PR_ARG, VIF_PR_ARG, (unsigned long long)__entry->tsf +	) +); + +DEFINE_EVENT(local_sdata_evt, drv_reset_tsf, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), +	TP_ARGS(local, sdata) +); + +DEFINE_EVENT(local_only_evt, drv_tx_last_beacon, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +TRACE_EVENT(drv_ampdu_action, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 enum ieee80211_ampdu_mlme_action action, +		 struct ieee80211_sta *sta, u16 tid, +		 u16 *ssn, u8 buf_size), + +	TP_ARGS(local, sdata, action, sta, tid, ssn, buf_size), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		STA_ENTRY +		__field(u32, action) +		__field(u16, tid) +		__field(u16, ssn) +		__field(u8, buf_size) +		VIF_ENTRY +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		STA_ASSIGN; +		__entry->action = action; +		__entry->tid = tid; +		__entry->ssn = ssn ? *ssn : 0; +		__entry->buf_size = buf_size; +	), + +	TP_printk( +		LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d buf:%d", +		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action, +		__entry->tid, __entry->buf_size +	) +); + +TRACE_EVENT(drv_get_survey, +	TP_PROTO(struct ieee80211_local *local, int idx, +		 struct survey_info *survey), + +	TP_ARGS(local, idx, survey), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(int, idx) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->idx = idx; +	), + +	TP_printk( +		LOCAL_PR_FMT " idx:%d", +		LOCAL_PR_ARG, __entry->idx +	) +); + +TRACE_EVENT(drv_flush, +	TP_PROTO(struct ieee80211_local *local, +		 u32 queues, bool drop), + +	TP_ARGS(local, queues, drop), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(bool, drop) +		__field(u32, queues) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->drop = drop; +		__entry->queues = queues; +	), + +	TP_printk( +		LOCAL_PR_FMT " queues:0x%x drop:%d", +		LOCAL_PR_ARG, __entry->queues, __entry->drop +	) +); + +TRACE_EVENT(drv_channel_switch, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_channel_switch *ch_switch), + +	TP_ARGS(local, ch_switch), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		CHANDEF_ENTRY +		__field(u64, timestamp) +		__field(bool, block_tx) +		__field(u8, count) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		CHANDEF_ASSIGN(&ch_switch->chandef) +		__entry->timestamp = ch_switch->timestamp; +		__entry->block_tx = ch_switch->block_tx; +		__entry->count = ch_switch->count; +	), + +	TP_printk( +		LOCAL_PR_FMT " new " CHANDEF_PR_FMT " count:%d", +		LOCAL_PR_ARG, CHANDEF_PR_ARG, __entry->count +	) +); + +TRACE_EVENT(drv_set_antenna, +	TP_PROTO(struct ieee80211_local *local, u32 tx_ant, u32 rx_ant, int ret), + +	TP_ARGS(local, tx_ant, rx_ant, ret), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(u32, tx_ant) +		__field(u32, rx_ant) +		__field(int, ret) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->tx_ant = tx_ant; +		__entry->rx_ant = rx_ant; +		__entry->ret = ret; +	), + +	TP_printk( +		LOCAL_PR_FMT " tx_ant:%d rx_ant:%d ret:%d", +		LOCAL_PR_ARG, __entry->tx_ant, __entry->rx_ant, __entry->ret +	) +); + +TRACE_EVENT(drv_get_antenna, +	TP_PROTO(struct ieee80211_local *local, u32 tx_ant, u32 rx_ant, int ret), + +	TP_ARGS(local, tx_ant, rx_ant, ret), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(u32, tx_ant) +		__field(u32, rx_ant) +		__field(int, ret) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->tx_ant = tx_ant; +		__entry->rx_ant = rx_ant; +		__entry->ret = ret; +	), + +	TP_printk( +		LOCAL_PR_FMT " tx_ant:%d rx_ant:%d ret:%d", +		LOCAL_PR_ARG, __entry->tx_ant, __entry->rx_ant, __entry->ret +	) +); + +TRACE_EVENT(drv_remain_on_channel, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_channel *chan, +		 unsigned int duration, +		 enum ieee80211_roc_type type), + +	TP_ARGS(local, sdata, chan, duration, type), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		__field(int, center_freq) +		__field(unsigned int, duration) +		__field(u32, type) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		__entry->center_freq = chan->center_freq; +		__entry->duration = duration; +		__entry->type = type; +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT " freq:%dMHz duration:%dms type=%d", +		LOCAL_PR_ARG, VIF_PR_ARG, +		__entry->center_freq, __entry->duration, __entry->type +	) +); + +DEFINE_EVENT(local_only_evt, drv_cancel_remain_on_channel, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +TRACE_EVENT(drv_set_ringparam, +	TP_PROTO(struct ieee80211_local *local, u32 tx, u32 rx), + +	TP_ARGS(local, tx, rx), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(u32, tx) +		__field(u32, rx) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->tx = tx; +		__entry->rx = rx; +	), + +	TP_printk( +		LOCAL_PR_FMT " tx:%d rx %d", +		LOCAL_PR_ARG, __entry->tx, __entry->rx +	) +); + +TRACE_EVENT(drv_get_ringparam, +	TP_PROTO(struct ieee80211_local *local, u32 *tx, u32 *tx_max, +		 u32 *rx, u32 *rx_max), + +	TP_ARGS(local, tx, tx_max, rx, rx_max), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(u32, tx) +		__field(u32, tx_max) +		__field(u32, rx) +		__field(u32, rx_max) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->tx = *tx; +		__entry->tx_max = *tx_max; +		__entry->rx = *rx; +		__entry->rx_max = *rx_max; +	), + +	TP_printk( +		LOCAL_PR_FMT " tx:%d tx_max %d rx %d rx_max %d", +		LOCAL_PR_ARG, +		__entry->tx, __entry->tx_max, __entry->rx, __entry->rx_max +	) +); + +DEFINE_EVENT(local_only_evt, drv_tx_frames_pending, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +DEFINE_EVENT(local_only_evt, drv_offchannel_tx_cancel_wait, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +TRACE_EVENT(drv_set_bitrate_mask, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 const struct cfg80211_bitrate_mask *mask), + +	TP_ARGS(local, sdata, mask), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		__field(u32, legacy_2g) +		__field(u32, legacy_5g) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		__entry->legacy_2g = mask->control[IEEE80211_BAND_2GHZ].legacy; +		__entry->legacy_5g = mask->control[IEEE80211_BAND_5GHZ].legacy; +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT " 2G Mask:0x%x 5G Mask:0x%x", +		LOCAL_PR_ARG, VIF_PR_ARG, __entry->legacy_2g, __entry->legacy_5g +	) +); + +TRACE_EVENT(drv_set_rekey_data, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct cfg80211_gtk_rekey_data *data), + +	TP_ARGS(local, sdata, data), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		__array(u8, kek, NL80211_KEK_LEN) +		__array(u8, kck, NL80211_KCK_LEN) +		__array(u8, replay_ctr, NL80211_REPLAY_CTR_LEN) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		memcpy(__entry->kek, data->kek, NL80211_KEK_LEN); +		memcpy(__entry->kck, data->kck, NL80211_KCK_LEN); +		memcpy(__entry->replay_ctr, data->replay_ctr, +		       NL80211_REPLAY_CTR_LEN); +	), + +	TP_printk(LOCAL_PR_FMT VIF_PR_FMT, +		  LOCAL_PR_ARG, VIF_PR_ARG) +); + +TRACE_EVENT(drv_rssi_callback, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 enum ieee80211_rssi_event rssi_event), + +	TP_ARGS(local, sdata, rssi_event), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		__field(u32, rssi_event) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		__entry->rssi_event = rssi_event; +	), + +	TP_printk( +		LOCAL_PR_FMT VIF_PR_FMT " rssi_event:%d", +		LOCAL_PR_ARG, VIF_PR_ARG, __entry->rssi_event +	) +); + +DECLARE_EVENT_CLASS(release_evt, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sta *sta, +		 u16 tids, int num_frames, +		 enum ieee80211_frame_release_type reason, +		 bool more_data), + +	TP_ARGS(local, sta, tids, num_frames, reason, more_data), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		STA_ENTRY +		__field(u16, tids) +		__field(int, num_frames) +		__field(int, reason) +		__field(bool, more_data) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		STA_ASSIGN; +		__entry->tids = tids; +		__entry->num_frames = num_frames; +		__entry->reason = reason; +		__entry->more_data = more_data; +	), + +	TP_printk( +		LOCAL_PR_FMT STA_PR_FMT +		" TIDs:0x%.4x frames:%d reason:%d more:%d", +		LOCAL_PR_ARG, STA_PR_ARG, __entry->tids, __entry->num_frames, +		__entry->reason, __entry->more_data +	) +); + +DEFINE_EVENT(release_evt, drv_release_buffered_frames, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sta *sta, +		 u16 tids, int num_frames, +		 enum ieee80211_frame_release_type reason, +		 bool more_data), + +	TP_ARGS(local, sta, tids, num_frames, reason, more_data) +); + +DEFINE_EVENT(release_evt, drv_allow_buffered_frames, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sta *sta, +		 u16 tids, int num_frames, +		 enum ieee80211_frame_release_type reason, +		 bool more_data), + +	TP_ARGS(local, sta, tids, num_frames, reason, more_data) +); + +TRACE_EVENT(drv_get_rssi, +	TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta, +		 s8 rssi, int ret), + +	TP_ARGS(local, sta, rssi, ret), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		STA_ENTRY +		__field(s8, rssi) +		__field(int, ret) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		STA_ASSIGN; +		__entry->rssi = rssi; +		__entry->ret = ret; +	), + +	TP_printk( +		LOCAL_PR_FMT STA_PR_FMT " rssi:%d ret:%d", +		LOCAL_PR_ARG, STA_PR_ARG, __entry->rssi, __entry->ret +	) +); + +DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), + +	TP_ARGS(local, sdata) +); + +DECLARE_EVENT_CLASS(local_chanctx, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_chanctx *ctx), + +	TP_ARGS(local, ctx), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		CHANCTX_ENTRY +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		CHANCTX_ASSIGN; +	), + +	TP_printk( +		LOCAL_PR_FMT CHANCTX_PR_FMT, +		LOCAL_PR_ARG, CHANCTX_PR_ARG +	) +); + +DEFINE_EVENT(local_chanctx, drv_add_chanctx, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_chanctx *ctx), +	TP_ARGS(local, ctx) +); + +DEFINE_EVENT(local_chanctx, drv_remove_chanctx, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_chanctx *ctx), +	TP_ARGS(local, ctx) +); + +TRACE_EVENT(drv_change_chanctx, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_chanctx *ctx, +		 u32 changed), + +	TP_ARGS(local, ctx, changed), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		CHANCTX_ENTRY +		__field(u32, changed) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		CHANCTX_ASSIGN; +		__entry->changed = changed; +	), + +	TP_printk( +		LOCAL_PR_FMT CHANCTX_PR_FMT " changed:%#x", +		LOCAL_PR_ARG, CHANCTX_PR_ARG, __entry->changed +	) +); + +#if !defined(__TRACE_VIF_ENTRY) +#define __TRACE_VIF_ENTRY +struct trace_vif_entry { +	enum nl80211_iftype vif_type; +	bool p2p; +	char vif_name[IFNAMSIZ]; +} __packed; + +struct trace_chandef_entry { +	u32 control_freq; +	u32 chan_width; +	u32 center_freq1; +	u32 center_freq2; +} __packed; + +struct trace_switch_entry { +	struct trace_vif_entry vif; +	struct trace_chandef_entry old_chandef; +	struct trace_chandef_entry new_chandef; +} __packed; + +#define SWITCH_ENTRY_ASSIGN(to, from) local_vifs[i].to = vifs[i].from +#endif + +TRACE_EVENT(drv_switch_vif_chanctx, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_vif_chanctx_switch *vifs, +		 int n_vifs, enum ieee80211_chanctx_switch_mode mode), +	    TP_ARGS(local, vifs, n_vifs, mode), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(int, n_vifs) +		__field(u32, mode) +		__dynamic_array(u8, vifs, +				sizeof(struct trace_switch_entry) * n_vifs) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->n_vifs = n_vifs; +		__entry->mode = mode; +		{ +			struct trace_switch_entry *local_vifs = +				__get_dynamic_array(vifs); +			int i; + +			for (i = 0; i < n_vifs; i++) { +				struct ieee80211_sub_if_data *sdata; + +				sdata = container_of(vifs[i].vif, +						struct ieee80211_sub_if_data, +						vif); + +				SWITCH_ENTRY_ASSIGN(vif.vif_type, vif->type); +				SWITCH_ENTRY_ASSIGN(vif.p2p, vif->p2p); +				strncpy(local_vifs[i].vif.vif_name, +					sdata->name, +					sizeof(local_vifs[i].vif.vif_name)); +				SWITCH_ENTRY_ASSIGN(old_chandef.control_freq, +						old_ctx->def.chan->center_freq); +				SWITCH_ENTRY_ASSIGN(old_chandef.chan_width, +						    old_ctx->def.width); +				SWITCH_ENTRY_ASSIGN(old_chandef.center_freq1, +						    old_ctx->def.center_freq1); +				SWITCH_ENTRY_ASSIGN(old_chandef.center_freq2, +						    old_ctx->def.center_freq2); +				SWITCH_ENTRY_ASSIGN(new_chandef.control_freq, +						new_ctx->def.chan->center_freq); +				SWITCH_ENTRY_ASSIGN(new_chandef.chan_width, +						    new_ctx->def.width); +				SWITCH_ENTRY_ASSIGN(new_chandef.center_freq1, +						    new_ctx->def.center_freq1); +				SWITCH_ENTRY_ASSIGN(new_chandef.center_freq2, +						    new_ctx->def.center_freq2); +			} +		} +	), + +	TP_printk( +		LOCAL_PR_FMT " n_vifs:%d mode:%d", +		LOCAL_PR_ARG, __entry->n_vifs, __entry->mode +	) +); + +DECLARE_EVENT_CLASS(local_sdata_chanctx, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_chanctx *ctx), + +	TP_ARGS(local, sdata, ctx), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		CHANCTX_ENTRY +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		CHANCTX_ASSIGN; +	), + +	TP_printk( +		LOCAL_PR_FMT VIF_PR_FMT CHANCTX_PR_FMT, +		LOCAL_PR_ARG, VIF_PR_ARG, CHANCTX_PR_ARG +	) +); + +DEFINE_EVENT(local_sdata_chanctx, drv_assign_vif_chanctx, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_chanctx *ctx), +	TP_ARGS(local, sdata, ctx) +); + +DEFINE_EVENT(local_sdata_chanctx, drv_unassign_vif_chanctx, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_chanctx *ctx), +	TP_ARGS(local, sdata, ctx) +); + +TRACE_EVENT(drv_start_ap, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_bss_conf *info), + +	TP_ARGS(local, sdata, info), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		__field(u8, dtimper) +		__field(u16, bcnint) +		__dynamic_array(u8, ssid, info->ssid_len); +		__field(bool, hidden_ssid); +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		__entry->dtimper = info->dtim_period; +		__entry->bcnint = info->beacon_int; +		memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len); +		__entry->hidden_ssid = info->hidden_ssid; +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT, +		LOCAL_PR_ARG, VIF_PR_ARG +	) +); + +DEFINE_EVENT(local_sdata_evt, drv_stop_ap, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), +	TP_ARGS(local, sdata) +); + +DEFINE_EVENT(local_only_evt, drv_restart_complete, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +#if IS_ENABLED(CONFIG_IPV6) +DEFINE_EVENT(local_sdata_evt, drv_ipv6_addr_change, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), +	TP_ARGS(local, sdata) +); +#endif + +TRACE_EVENT(drv_join_ibss, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct ieee80211_bss_conf *info), + +	TP_ARGS(local, sdata, info), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		__field(u8, dtimper) +		__field(u16, bcnint) +		__dynamic_array(u8, ssid, info->ssid_len); +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		__entry->dtimper = info->dtim_period; +		__entry->bcnint = info->beacon_int; +		memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len); +	), + +	TP_printk( +		LOCAL_PR_FMT  VIF_PR_FMT, +		LOCAL_PR_ARG, VIF_PR_ARG +	) +); + +DEFINE_EVENT(local_sdata_evt, drv_leave_ibss, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata), +	TP_ARGS(local, sdata) +); + +TRACE_EVENT(drv_get_expected_throughput, +	TP_PROTO(struct ieee80211_sta *sta), + +	TP_ARGS(sta), + +	TP_STRUCT__entry( +		STA_ENTRY +	), + +	TP_fast_assign( +		STA_ASSIGN; +	), + +	TP_printk( +		STA_PR_FMT, STA_PR_ARG +	) +); + +/* + * Tracing for API calls that drivers call. + */ + +TRACE_EVENT(api_start_tx_ba_session, +	TP_PROTO(struct ieee80211_sta *sta, u16 tid), + +	TP_ARGS(sta, tid), + +	TP_STRUCT__entry( +		STA_ENTRY +		__field(u16, tid) +	), + +	TP_fast_assign( +		STA_ASSIGN; +		__entry->tid = tid; +	), + +	TP_printk( +		STA_PR_FMT " tid:%d", +		STA_PR_ARG, __entry->tid +	) +); + +TRACE_EVENT(api_start_tx_ba_cb, +	TP_PROTO(struct ieee80211_sub_if_data *sdata, const u8 *ra, u16 tid), + +	TP_ARGS(sdata, ra, tid), + +	TP_STRUCT__entry( +		VIF_ENTRY +		__array(u8, ra, ETH_ALEN) +		__field(u16, tid) +	), + +	TP_fast_assign( +		VIF_ASSIGN; +		memcpy(__entry->ra, ra, ETH_ALEN); +		__entry->tid = tid; +	), + +	TP_printk( +		VIF_PR_FMT " ra:%pM tid:%d", +		VIF_PR_ARG, __entry->ra, __entry->tid +	) +); + +TRACE_EVENT(api_stop_tx_ba_session, +	TP_PROTO(struct ieee80211_sta *sta, u16 tid), + +	TP_ARGS(sta, tid), + +	TP_STRUCT__entry( +		STA_ENTRY +		__field(u16, tid) +	), + +	TP_fast_assign( +		STA_ASSIGN; +		__entry->tid = tid; +	), + +	TP_printk( +		STA_PR_FMT " tid:%d", +		STA_PR_ARG, __entry->tid +	) +); + +TRACE_EVENT(api_stop_tx_ba_cb, +	TP_PROTO(struct ieee80211_sub_if_data *sdata, const u8 *ra, u16 tid), + +	TP_ARGS(sdata, ra, tid), + +	TP_STRUCT__entry( +		VIF_ENTRY +		__array(u8, ra, ETH_ALEN) +		__field(u16, tid) +	), + +	TP_fast_assign( +		VIF_ASSIGN; +		memcpy(__entry->ra, ra, ETH_ALEN); +		__entry->tid = tid; +	), + +	TP_printk( +		VIF_PR_FMT " ra:%pM tid:%d", +		VIF_PR_ARG, __entry->ra, __entry->tid +	) +); + +DEFINE_EVENT(local_only_evt, api_restart_hw, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +TRACE_EVENT(api_beacon_loss, +	TP_PROTO(struct ieee80211_sub_if_data *sdata), + +	TP_ARGS(sdata), + +	TP_STRUCT__entry( +		VIF_ENTRY +	), + +	TP_fast_assign( +		VIF_ASSIGN; +	), + +	TP_printk( +		VIF_PR_FMT, +		VIF_PR_ARG +	) +); + +TRACE_EVENT(api_connection_loss, +	TP_PROTO(struct ieee80211_sub_if_data *sdata), + +	TP_ARGS(sdata), + +	TP_STRUCT__entry( +		VIF_ENTRY +	), + +	TP_fast_assign( +		VIF_ASSIGN; +	), + +	TP_printk( +		VIF_PR_FMT, +		VIF_PR_ARG +	) +); + +TRACE_EVENT(api_cqm_rssi_notify, +	TP_PROTO(struct ieee80211_sub_if_data *sdata, +		 enum nl80211_cqm_rssi_threshold_event rssi_event), + +	TP_ARGS(sdata, rssi_event), + +	TP_STRUCT__entry( +		VIF_ENTRY +		__field(u32, rssi_event) +	), + +	TP_fast_assign( +		VIF_ASSIGN; +		__entry->rssi_event = rssi_event; +	), + +	TP_printk( +		VIF_PR_FMT " event:%d", +		VIF_PR_ARG, __entry->rssi_event +	) +); + +TRACE_EVENT(api_scan_completed, +	TP_PROTO(struct ieee80211_local *local, bool aborted), + +	TP_ARGS(local, aborted), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(bool, aborted) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->aborted = aborted; +	), + +	TP_printk( +		LOCAL_PR_FMT " aborted:%d", +		LOCAL_PR_ARG, __entry->aborted +	) +); + +TRACE_EVENT(api_sched_scan_results, +	TP_PROTO(struct ieee80211_local *local), + +	TP_ARGS(local), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +	), + +	TP_printk( +		LOCAL_PR_FMT, LOCAL_PR_ARG +	) +); + +TRACE_EVENT(api_sched_scan_stopped, +	TP_PROTO(struct ieee80211_local *local), + +	TP_ARGS(local), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +	), + +	TP_printk( +		LOCAL_PR_FMT, LOCAL_PR_ARG +	) +); + +TRACE_EVENT(api_sta_block_awake, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sta *sta, bool block), + +	TP_ARGS(local, sta, block), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		STA_ENTRY +		__field(bool, block) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		STA_ASSIGN; +		__entry->block = block; +	), + +	TP_printk( +		LOCAL_PR_FMT STA_PR_FMT " block:%d", +		LOCAL_PR_ARG, STA_PR_ARG, __entry->block +	) +); + +TRACE_EVENT(api_chswitch_done, +	TP_PROTO(struct ieee80211_sub_if_data *sdata, bool success), + +	TP_ARGS(sdata, success), + +	TP_STRUCT__entry( +		VIF_ENTRY +		__field(bool, success) +	), + +	TP_fast_assign( +		VIF_ASSIGN; +		__entry->success = success; +	), + +	TP_printk( +		VIF_PR_FMT " success=%d", +		VIF_PR_ARG, __entry->success +	) +); + +DEFINE_EVENT(local_only_evt, api_ready_on_channel, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +DEFINE_EVENT(local_only_evt, api_remain_on_channel_expired, +	TP_PROTO(struct ieee80211_local *local), +	TP_ARGS(local) +); + +TRACE_EVENT(api_gtk_rekey_notify, +	TP_PROTO(struct ieee80211_sub_if_data *sdata, +		 const u8 *bssid, const u8 *replay_ctr), + +	TP_ARGS(sdata, bssid, replay_ctr), + +	TP_STRUCT__entry( +		VIF_ENTRY +		__array(u8, bssid, ETH_ALEN) +		__array(u8, replay_ctr, NL80211_REPLAY_CTR_LEN) +	), + +	TP_fast_assign( +		VIF_ASSIGN; +		memcpy(__entry->bssid, bssid, ETH_ALEN); +		memcpy(__entry->replay_ctr, replay_ctr, NL80211_REPLAY_CTR_LEN); +	), + +	TP_printk(VIF_PR_FMT, VIF_PR_ARG) +); + +TRACE_EVENT(api_enable_rssi_reports, +	TP_PROTO(struct ieee80211_sub_if_data *sdata, +		 int rssi_min_thold, int rssi_max_thold), + +	TP_ARGS(sdata, rssi_min_thold, rssi_max_thold), + +	TP_STRUCT__entry( +		VIF_ENTRY +		__field(int, rssi_min_thold) +		__field(int, rssi_max_thold) +	), + +	TP_fast_assign( +		VIF_ASSIGN; +		__entry->rssi_min_thold = rssi_min_thold; +		__entry->rssi_max_thold = rssi_max_thold; +	), + +	TP_printk( +		VIF_PR_FMT " rssi_min_thold =%d, rssi_max_thold = %d", +		VIF_PR_ARG, __entry->rssi_min_thold, __entry->rssi_max_thold +	) +); + +TRACE_EVENT(api_eosp, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sta *sta), + +	TP_ARGS(local, sta), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		STA_ENTRY +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		STA_ASSIGN; +	), + +	TP_printk( +		LOCAL_PR_FMT STA_PR_FMT, +		LOCAL_PR_ARG, STA_PR_ARG +	) +); + +TRACE_EVENT(api_sta_set_buffered, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sta *sta, +		 u8 tid, bool buffered), + +	TP_ARGS(local, sta, tid, buffered), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		STA_ENTRY +		__field(u8, tid) +		__field(bool, buffered) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		STA_ASSIGN; +		__entry->tid = tid; +		__entry->buffered = buffered; +	), + +	TP_printk( +		LOCAL_PR_FMT STA_PR_FMT " tid:%d buffered:%d", +		LOCAL_PR_ARG, STA_PR_ARG, __entry->tid, __entry->buffered +	) +); + +/* + * Tracing for internal functions + * (which may also be called in response to driver calls) + */ + +TRACE_EVENT(wake_queue, +	TP_PROTO(struct ieee80211_local *local, u16 queue, +		 enum queue_stop_reason reason), + +	TP_ARGS(local, queue, reason), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(u16, queue) +		__field(u32, reason) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->queue = queue; +		__entry->reason = reason; +	), + +	TP_printk( +		LOCAL_PR_FMT " queue:%d, reason:%d", +		LOCAL_PR_ARG, __entry->queue, __entry->reason +	) +); + +TRACE_EVENT(stop_queue, +	TP_PROTO(struct ieee80211_local *local, u16 queue, +		 enum queue_stop_reason reason), + +	TP_ARGS(local, queue, reason), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		__field(u16, queue) +		__field(u32, reason) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		__entry->queue = queue; +		__entry->reason = reason; +	), + +	TP_printk( +		LOCAL_PR_FMT " queue:%d, reason:%d", +		LOCAL_PR_ARG, __entry->queue, __entry->reason +	) +); + +TRACE_EVENT(drv_set_default_unicast_key, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 int key_idx), + +	TP_ARGS(local, sdata, key_idx), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		__field(int, key_idx) +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		__entry->key_idx = key_idx; +	), + +	TP_printk(LOCAL_PR_FMT VIF_PR_FMT " key_idx:%d", +		  LOCAL_PR_ARG, VIF_PR_ARG, __entry->key_idx) +); + +TRACE_EVENT(api_radar_detected, +	TP_PROTO(struct ieee80211_local *local), + +	TP_ARGS(local), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +	), + +	TP_printk( +		LOCAL_PR_FMT " radar detected", +		LOCAL_PR_ARG +	) +); + +TRACE_EVENT(drv_channel_switch_beacon, +	TP_PROTO(struct ieee80211_local *local, +		 struct ieee80211_sub_if_data *sdata, +		 struct cfg80211_chan_def *chandef), + +	TP_ARGS(local, sdata, chandef), + +	TP_STRUCT__entry( +		LOCAL_ENTRY +		VIF_ENTRY +		CHANDEF_ENTRY +	), + +	TP_fast_assign( +		LOCAL_ASSIGN; +		VIF_ASSIGN; +		CHANDEF_ASSIGN(chandef); +	), + +	TP_printk( +		LOCAL_PR_FMT VIF_PR_FMT " channel switch to " CHANDEF_PR_FMT, +		LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG +	) +); + + +#ifdef CONFIG_MAC80211_MESSAGE_TRACING +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mac80211_msg + +#define MAX_MSG_LEN	100 + +DECLARE_EVENT_CLASS(mac80211_msg_event, +	TP_PROTO(struct va_format *vaf), + +	TP_ARGS(vaf), + +	TP_STRUCT__entry( +		__dynamic_array(char, msg, MAX_MSG_LEN) +	), + +	TP_fast_assign( +		WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), +				       MAX_MSG_LEN, vaf->fmt, +				       *vaf->va) >= MAX_MSG_LEN); +	), + +	TP_printk("%s", __get_str(msg)) +); + +DEFINE_EVENT(mac80211_msg_event, mac80211_info, +	TP_PROTO(struct va_format *vaf), +	TP_ARGS(vaf) +); +DEFINE_EVENT(mac80211_msg_event, mac80211_dbg, +	TP_PROTO(struct va_format *vaf), +	TP_ARGS(vaf) +); +DEFINE_EVENT(mac80211_msg_event, mac80211_err, +	TP_PROTO(struct va_format *vaf), +	TP_ARGS(vaf) +); +#endif + +#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace +#include <trace/define_trace.h> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index e69483647f3..1a252c606ad 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -18,6 +18,8 @@  #include <linux/etherdevice.h>  #include <linux/bitmap.h>  #include <linux/rcupdate.h> +#include <linux/export.h> +#include <linux/time.h>  #include <net/net_namespace.h>  #include <net/ieee80211_radiotap.h>  #include <net/cfg80211.h> @@ -33,32 +35,39 @@  #include "wme.h"  #include "rate.h" -#define IEEE80211_TX_OK		0 -#define IEEE80211_TX_AGAIN	1 -#define IEEE80211_TX_PENDING	2 -  /* misc utils */ -static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, +static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, +				 struct sk_buff *skb, int group_addr,  				 int next_frag_len)  { -	int rate, mrate, erp, dur, i; +	int rate, mrate, erp, dur, i, shift = 0;  	struct ieee80211_rate *txrate;  	struct ieee80211_local *local = tx->local;  	struct ieee80211_supported_band *sband;  	struct ieee80211_hdr *hdr; -	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct ieee80211_chanctx_conf *chanctx_conf; +	u32 rate_flags = 0; + +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(tx->sdata->vif.chanctx_conf); +	if (chanctx_conf) { +		shift = ieee80211_chandef_get_shift(&chanctx_conf->def); +		rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def); +	} +	rcu_read_unlock();  	/* assume HW handles this */ -	if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) +	if (tx->rate.flags & IEEE80211_TX_RC_MCS)  		return 0;  	/* uh huh? */ -	if (WARN_ON_ONCE(info->control.rates[0].idx < 0)) +	if (WARN_ON_ONCE(tx->rate.idx < 0))  		return 0; -	sband = local->hw.wiphy->bands[tx->channel->band]; -	txrate = &sband->bitrates[info->control.rates[0].idx]; +	sband = local->hw.wiphy->bands[info->band]; +	txrate = &sband->bitrates[tx->rate.idx];  	erp = txrate->flags & IEEE80211_RATE_ERP_G; @@ -79,7 +88,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,  	 *   at the highest possible rate belonging to the PHY rates in the  	 *   BSSBasicRateSet  	 */ -	hdr = (struct ieee80211_hdr *)tx->skb->data; +	hdr = (struct ieee80211_hdr *)skb->data;  	if (ieee80211_is_ctl(hdr->frame_control)) {  		/* TODO: These control frames are not currently sent by  		 * mac80211, but should they be implemented, this function @@ -124,8 +133,11 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,  		if (r->bitrate > txrate->bitrate)  			break; +		if ((rate_flags & r->flags) != rate_flags) +			continue; +  		if (tx->sdata->vif.bss_conf.basic_rates & BIT(i)) -			rate = r->bitrate; +			rate = DIV_ROUND_UP(r->bitrate, 1 << shift);  		switch (sband->band) {  		case IEEE80211_BAND_2GHZ: { @@ -142,6 +154,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,  			if (r->flags & IEEE80211_RATE_MANDATORY_A)  				mrate = r->bitrate;  			break; +		case IEEE80211_BAND_60GHZ: +			/* TODO, for now fall through */  		case IEEE80211_NUM_BANDS:  			WARN_ON(1);  			break; @@ -150,35 +164,35 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,  	if (rate == -1) {  		/* No matching basic rate found; use highest suitable mandatory  		 * PHY rate */ -		rate = mrate; +		rate = DIV_ROUND_UP(mrate, 1 << shift);  	} -	/* Time needed to transmit ACK -	 * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up -	 * to closest integer */ - -	dur = ieee80211_frame_duration(local, 10, rate, erp, -				tx->sdata->vif.bss_conf.use_short_preamble); +	/* Don't calculate ACKs for QoS Frames with NoAck Policy set */ +	if (ieee80211_is_data_qos(hdr->frame_control) && +	    *(ieee80211_get_qos_ctl(hdr)) & IEEE80211_QOS_CTL_ACK_POLICY_NOACK) +		dur = 0; +	else +		/* Time needed to transmit ACK +		 * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up +		 * to closest integer */ +		dur = ieee80211_frame_duration(sband->band, 10, rate, erp, +				tx->sdata->vif.bss_conf.use_short_preamble, +				shift);  	if (next_frag_len) {  		/* Frame is fragmented: duration increases with time needed to  		 * transmit next fragment plus ACK and 2 x SIFS. */  		dur *= 2; /* ACK + SIFS */  		/* next fragment */ -		dur += ieee80211_frame_duration(local, next_frag_len, +		dur += ieee80211_frame_duration(sband->band, next_frag_len,  				txrate->bitrate, erp, -				tx->sdata->vif.bss_conf.use_short_preamble); +				tx->sdata->vif.bss_conf.use_short_preamble, +				shift);  	}  	return cpu_to_le16(dur);  } -static int inline is_ieee80211_device(struct ieee80211_local *local, -				      struct net_device *dev) -{ -	return local == wdev_priv(dev->ieee80211_ptr); -} -  /* tx handlers */  static ieee80211_tx_result debug_noinline  ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx) @@ -224,22 +238,28 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)  	 * have correct qos tag for some reason, due the network or the  	 * peer application.  	 * -	 * Note: local->uapsd_queues access is racy here. If the value is +	 * Note: ifmgd->uapsd_queues access is racy here. If the value is  	 * changed via debugfs, user needs to reassociate manually to have  	 * everything in sync.  	 */ -	if ((ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) -	    && (local->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) -	    && skb_get_queue_mapping(tx->skb) == 0) +	if ((ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) && +	    (ifmgd->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) && +	    skb_get_queue_mapping(tx->skb) == IEEE80211_AC_VO)  		return TX_CONTINUE;  	if (local->hw.conf.flags & IEEE80211_CONF_PS) {  		ieee80211_stop_queues_by_reason(&local->hw, +						IEEE80211_MAX_QUEUE_MAP,  						IEEE80211_QUEUE_STOP_REASON_PS); +		ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;  		ieee80211_queue_work(&local->hw,  				     &local->dynamic_ps_disable_work);  	} +	/* Don't restart the timer if we're not disassociated */ +	if (!ifmgd->associated) +		return TX_CONTINUE; +  	mod_timer(&local->dynamic_ps_timer, jiffies +  		  msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); @@ -252,12 +272,13 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); -	u32 sta_flags; +	bool assoc = false;  	if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED))  		return TX_CONTINUE; -	if (unlikely(test_bit(SCAN_OFF_CHANNEL, &tx->local->scanning)) && +	if (unlikely(test_bit(SCAN_SW_SCANNING, &tx->local->scanning)) && +	    test_bit(SDATA_STATE_OFFCHANNEL, &tx->sdata->state) &&  	    !ieee80211_is_probe_req(hdr->frame_control) &&  	    !ieee80211_is_nullfunc(hdr->frame_control))  		/* @@ -282,31 +303,28 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)  	if (tx->flags & IEEE80211_TX_PS_BUFFERED)  		return TX_CONTINUE; -	sta_flags = tx->sta ? get_sta_flags(tx->sta) : 0; +	if (tx->sta) +		assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC);  	if (likely(tx->flags & IEEE80211_TX_UNICAST)) { -		if (unlikely(!(sta_flags & WLAN_STA_ASSOC) && -			     tx->sdata->vif.type != NL80211_IFTYPE_ADHOC && +		if (unlikely(!assoc &&  			     ieee80211_is_data(hdr->frame_control))) {  #ifdef CONFIG_MAC80211_VERBOSE_DEBUG -			printk(KERN_DEBUG "%s: dropped data frame to not " -			       "associated station %pM\n", -			       tx->sdata->name, hdr->addr1); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ +			sdata_info(tx->sdata, +				   "dropped data frame to not associated station %pM\n", +				   hdr->addr1); +#endif  			I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc);  			return TX_DROP;  		} -	} else { -		if (unlikely(ieee80211_is_data(hdr->frame_control) && -			     tx->local->num_sta == 0 && -			     tx->sdata->vif.type != NL80211_IFTYPE_ADHOC)) { -			/* -			 * No associated STAs - no need to send multicast -			 * frames. -			 */ -			return TX_DROP; -		} -		return TX_CONTINUE; +	} else if (unlikely(tx->sdata->vif.type == NL80211_IFTYPE_AP && +			    ieee80211_is_data(hdr->frame_control) && +			    !atomic_read(&tx->sdata->u.ap.num_mcast_sta))) { +		/* +		 * No associated STAs - no need to send multicast +		 * frames. +		 */ +		return TX_DROP;  	}  	return TX_CONTINUE; @@ -323,40 +341,44 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)  	struct ieee80211_sub_if_data *sdata;  	struct sta_info *sta; -	/* -	 * virtual interfaces are protected by RCU -	 */ -	rcu_read_lock(); -  	list_for_each_entry_rcu(sdata, &local->interfaces, list) { -		struct ieee80211_if_ap *ap; -		if (sdata->vif.type != NL80211_IFTYPE_AP) +		struct ps_data *ps; + +		if (sdata->vif.type == NL80211_IFTYPE_AP) +			ps = &sdata->u.ap.ps; +		else if (ieee80211_vif_is_mesh(&sdata->vif)) +			ps = &sdata->u.mesh.ps; +		else  			continue; -		ap = &sdata->u.ap; -		skb = skb_dequeue(&ap->ps_bc_buf); + +		skb = skb_dequeue(&ps->bc_buf);  		if (skb) {  			purged++;  			dev_kfree_skb(skb);  		} -		total += skb_queue_len(&ap->ps_bc_buf); +		total += skb_queue_len(&ps->bc_buf);  	} +	/* +	 * Drop one frame from each station from the lowest-priority +	 * AC that has frames at all. +	 */  	list_for_each_entry_rcu(sta, &local->sta_list, list) { -		skb = skb_dequeue(&sta->ps_tx_buf); -		if (skb) { -			purged++; -			dev_kfree_skb(skb); +		int ac; + +		for (ac = IEEE80211_AC_BK; ac >= IEEE80211_AC_VO; ac--) { +			skb = skb_dequeue(&sta->ps_tx_buf[ac]); +			total += skb_queue_len(&sta->ps_tx_buf[ac]); +			if (skb) { +				purged++; +				ieee80211_free_txskb(&local->hw, skb); +				break; +			}  		} -		total += skb_queue_len(&sta->ps_tx_buf);  	} -	rcu_read_unlock(); -  	local->total_ps_buffered = total; -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG -	wiphy_debug(local->hw.wiphy, "PS buffers full - purged %d frames\n", -		    purged); -#endif +	ps_dbg_hw(&local->hw, "PS buffers full - purged %d frames\n", purged);  }  static ieee80211_tx_result @@ -364,25 +386,42 @@ ieee80211_tx_h_multicast_ps_buf(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; +	struct ps_data *ps;  	/*  	 * broadcast/multicast frame  	 * -	 * If any of the associated stations is in power save mode, +	 * If any of the associated/peer stations is in power save mode,  	 * the frame is buffered to be sent after DTIM beacon frame.  	 * This is done either by the hardware or us.  	 */ -	/* powersaving STAs only in AP/VLAN mode */ -	if (!tx->sdata->bss) +	/* powersaving STAs currently only in AP/VLAN/mesh mode */ +	if (tx->sdata->vif.type == NL80211_IFTYPE_AP || +	    tx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { +		if (!tx->sdata->bss) +			return TX_CONTINUE; + +		ps = &tx->sdata->bss->ps; +	} else if (ieee80211_vif_is_mesh(&tx->sdata->vif)) { +		ps = &tx->sdata->u.mesh.ps; +	} else {  		return TX_CONTINUE; +	} +  	/* no buffering for ordered frames */  	if (ieee80211_has_order(hdr->frame_control))  		return TX_CONTINUE; +	if (ieee80211_is_probe_req(hdr->frame_control)) +		return TX_CONTINUE; + +	if (tx->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) +		info->hw_queue = tx->sdata->vif.cab_queue; +  	/* no stations in PS mode */ -	if (!atomic_read(&tx->sdata->bss->num_sta_ps)) +	if (!atomic_read(&ps->num_sta_ps))  		return TX_CONTINUE;  	info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM; @@ -395,17 +434,14 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)  	if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)  		purge_old_ps_buffers(tx->local); -	if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= AP_MAX_BC_BUFFER) { -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG -		if (net_ratelimit()) -			printk(KERN_DEBUG "%s: BC TX buffer full - dropping the oldest frame\n", -			       tx->sdata->name); -#endif -		dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf)); +	if (skb_queue_len(&ps->bc_buf) >= AP_MAX_BC_BUFFER) { +		ps_dbg(tx->sdata, +		       "BC TX buffer full - dropping the oldest frame\n"); +		dev_kfree_skb(skb_dequeue(&ps->bc_buf));  	} else  		tx->local->total_ps_buffered++; -	skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb); +	skb_queue_tail(&ps->bc_buf, tx->skb);  	return TX_QUEUED;  } @@ -416,11 +452,10 @@ static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta,  	if (!ieee80211_is_mgmt(fc))  		return 0; -	if (sta == NULL || !test_sta_flags(sta, WLAN_STA_MFP)) +	if (sta == NULL || !test_sta_flag(sta, WLAN_STA_MFP))  		return 0; -	if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) -					    skb->data)) +	if (!ieee80211_is_robust_mgmt_frame(skb))  		return 0;  	return 1; @@ -433,68 +468,72 @@ ieee80211_tx_h_unicast_ps_buf(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;  	struct ieee80211_local *local = tx->local; -	u32 staflags; -	if (unlikely(!sta || -		     ieee80211_is_probe_resp(hdr->frame_control) || -		     ieee80211_is_auth(hdr->frame_control) || -		     ieee80211_is_assoc_resp(hdr->frame_control) || -		     ieee80211_is_reassoc_resp(hdr->frame_control))) +	if (unlikely(!sta))  		return TX_CONTINUE; -	staflags = get_sta_flags(sta); +	if (unlikely((test_sta_flag(sta, WLAN_STA_PS_STA) || +		      test_sta_flag(sta, WLAN_STA_PS_DRIVER)) && +		     !(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) { +		int ac = skb_get_queue_mapping(tx->skb); + +		if (ieee80211_is_mgmt(hdr->frame_control) && +		    !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) { +			info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER; +			return TX_CONTINUE; +		} -	if (unlikely((staflags & (WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) && -		     !(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE))) { -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG -		printk(KERN_DEBUG "STA %pM aid %d: PS buffer (entries " -		       "before %d)\n", -		       sta->sta.addr, sta->sta.aid, -		       skb_queue_len(&sta->ps_tx_buf)); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ +		ps_dbg(sta->sdata, "STA %pM aid %d: PS buffer for AC %d\n", +		       sta->sta.addr, sta->sta.aid, ac);  		if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)  			purge_old_ps_buffers(tx->local); -		if (skb_queue_len(&sta->ps_tx_buf) >= STA_MAX_TX_BUFFER) { -			struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf); -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG -			if (net_ratelimit()) { -				printk(KERN_DEBUG "%s: STA %pM TX " -				       "buffer full - dropping oldest frame\n", -				       tx->sdata->name, sta->sta.addr); -			} -#endif -			dev_kfree_skb(old); -		} else -			tx->local->total_ps_buffered++; +		/* sync with ieee80211_sta_ps_deliver_wakeup */ +		spin_lock(&sta->ps_lock);  		/* -		 * Queue frame to be sent after STA wakes up/polls, -		 * but don't set the TIM bit if the driver is blocking -		 * wakeup or poll response transmissions anyway. +		 * STA woke up the meantime and all the frames on ps_tx_buf have +		 * been queued to pending queue. No reordering can happen, go +		 * ahead and Tx the packet.  		 */ -		if (skb_queue_empty(&sta->ps_tx_buf) && -		    !(staflags & WLAN_STA_PS_DRIVER)) -			sta_info_set_tim_bit(sta); +		if (!test_sta_flag(sta, WLAN_STA_PS_STA) && +		    !test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { +			spin_unlock(&sta->ps_lock); +			return TX_CONTINUE; +		} + +		if (skb_queue_len(&sta->ps_tx_buf[ac]) >= STA_MAX_TX_BUFFER) { +			struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf[ac]); +			ps_dbg(tx->sdata, +			       "STA %pM TX buffer for AC %d full - dropping oldest frame\n", +			       sta->sta.addr, ac); +			ieee80211_free_txskb(&local->hw, old); +		} else +			tx->local->total_ps_buffered++;  		info->control.jiffies = jiffies;  		info->control.vif = &tx->sdata->vif;  		info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; -		skb_queue_tail(&sta->ps_tx_buf, tx->skb); +		info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS; +		skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb); +		spin_unlock(&sta->ps_lock);  		if (!timer_pending(&local->sta_cleanup))  			mod_timer(&local->sta_cleanup,  				  round_jiffies(jiffies +  						STA_INFO_CLEANUP_INTERVAL)); +		/* +		 * We queued up some frames, so the TIM bit might +		 * need to be set, recalculate it. +		 */ +		sta_info_recalc_tim(sta); +  		return TX_QUEUED; -	} -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG -	else if (unlikely(staflags & WLAN_STA_PS_STA)) { -		printk(KERN_DEBUG "%s: STA %pM in PS mode, but pspoll " -		       "set -> send frame\n", tx->sdata->name, +	} else if (unlikely(test_sta_flag(sta, WLAN_STA_PS_STA))) { +		ps_dbg(tx->sdata, +		       "STA %pM in PS mode, but polling/in SP -> send frame\n",  		       sta->sta.addr);  	} -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */  	return TX_CONTINUE;  } @@ -516,9 +555,11 @@ ieee80211_tx_h_check_control_port_protocol(struct ieee80211_tx_data *tx)  {  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); -	if (unlikely(tx->sdata->control_port_protocol == tx->skb->protocol && -		     tx->sdata->control_port_no_encrypt)) -		info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; +	if (unlikely(tx->sdata->control_port_protocol == tx->skb->protocol)) { +		if (tx->sdata->control_port_no_encrypt) +			info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; +		info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO; +	}  	return TX_CONTINUE;  } @@ -526,31 +567,43 @@ ieee80211_tx_h_check_control_port_protocol(struct ieee80211_tx_data *tx)  static ieee80211_tx_result debug_noinline  ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)  { -	struct ieee80211_key *key = NULL; +	struct ieee80211_key *key;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;  	if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT))  		tx->key = NULL; -	else if (tx->sta && (key = rcu_dereference(tx->sta->ptk))) +	else if (tx->sta && +		 (key = rcu_dereference(tx->sta->ptk[tx->sta->ptk_idx])))  		tx->key = key;  	else if (ieee80211_is_mgmt(hdr->frame_control) &&  		 is_multicast_ether_addr(hdr->addr1) && -		 ieee80211_is_robust_mgmt_frame(hdr) && +		 ieee80211_is_robust_mgmt_frame(tx->skb) &&  		 (key = rcu_dereference(tx->sdata->default_mgmt_key)))  		tx->key = key; -	else if ((key = rcu_dereference(tx->sdata->default_key))) +	else if (is_multicast_ether_addr(hdr->addr1) && +		 (key = rcu_dereference(tx->sdata->default_multicast_key))) +		tx->key = key; +	else if (!is_multicast_ether_addr(hdr->addr1) && +		 (key = rcu_dereference(tx->sdata->default_unicast_key)))  		tx->key = key; -	else if (tx->sdata->drop_unencrypted && -		 (tx->skb->protocol != tx->sdata->control_port_protocol) && -		 !(info->flags & IEEE80211_TX_CTL_INJECTED) && -		 (!ieee80211_is_robust_mgmt_frame(hdr) || -		  (ieee80211_is_action(hdr->frame_control) && -		   tx->sta && test_sta_flags(tx->sta, WLAN_STA_MFP)))) { +	else if (info->flags & IEEE80211_TX_CTL_INJECTED) +		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; -	} else -		tx->key = NULL; +	}  	if (tx->key) {  		bool skip_hw = false; @@ -561,8 +614,6 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)  		switch (tx->key->conf.cipher) {  		case WLAN_CIPHER_SUITE_WEP40:  		case WLAN_CIPHER_SUITE_WEP104: -			if (ieee80211_is_auth(hdr->frame_control)) -				break;  		case WLAN_CIPHER_SUITE_TKIP:  			if (!ieee80211_is_data_present(hdr->frame_control))  				tx->key = NULL; @@ -574,7 +625,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)  				tx->key = NULL;  			else  				skip_hw = (tx->key->conf.flags & -					   IEEE80211_KEY_FLAG_SW_MGMT) && +					   IEEE80211_KEY_FLAG_SW_MGMT_TX) &&  					ieee80211_is_mgmt(hdr->frame_control);  			break;  		case WLAN_CIPHER_SUITE_AES_CMAC: @@ -583,6 +634,10 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)  			break;  		} +		if (unlikely(tx->key && tx->key->flags & KEY_FLAG_TAINTED && +			     !ieee80211_is_deauth(hdr->frame_control))) +			return TX_DROP; +  		if (!skip_hw && tx->key &&  		    tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)  			info->control.hw_key = &tx->key->conf; @@ -597,39 +652,46 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);  	struct ieee80211_hdr *hdr = (void *)tx->skb->data;  	struct ieee80211_supported_band *sband; -	struct ieee80211_rate *rate; -	int i;  	u32 len; -	bool inval = false, rts = false, short_preamble = false;  	struct ieee80211_tx_rate_control txrc; -	u32 sta_flags; +	struct ieee80211_sta_rates *ratetbl = NULL; +	bool assoc = false;  	memset(&txrc, 0, sizeof(txrc)); -	sband = tx->local->hw.wiphy->bands[tx->channel->band]; +	sband = tx->local->hw.wiphy->bands[info->band];  	len = min_t(u32, tx->skb->len + FCS_LEN,  			 tx->local->hw.wiphy->frag_threshold);  	/* set up the tx rate control struct we give the RC algo */ -	txrc.hw = local_to_hw(tx->local); +	txrc.hw = &tx->local->hw;  	txrc.sband = sband;  	txrc.bss_conf = &tx->sdata->vif.bss_conf;  	txrc.skb = tx->skb;  	txrc.reported_rate.idx = -1; -	txrc.rate_idx_mask = tx->sdata->rc_rateidx_mask[tx->channel->band]; +	txrc.rate_idx_mask = tx->sdata->rc_rateidx_mask[info->band];  	if (txrc.rate_idx_mask == (1 << sband->n_bitrates) - 1)  		txrc.max_rate_idx = -1;  	else  		txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; + +	if (tx->sdata->rc_has_mcs_mask[info->band]) +		txrc.rate_idx_mcs_mask = +			tx->sdata->rc_rateidx_mcs_mask[info->band]; +  	txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP || +		    tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||  		    tx->sdata->vif.type == NL80211_IFTYPE_ADHOC);  	/* set up RTS protection if desired */  	if (len > tx->local->hw.wiphy->rts_threshold) { -		txrc.rts = rts = true; +		txrc.rts = true;  	} +	info->control.use_rts = txrc.rts; +	info->control.use_cts_prot = tx->sdata->vif.bss_conf.use_cts_prot; +  	/*  	 * Use short preamble if the BSS can handle it, but not for  	 * management frames unless we know the receiver can handle @@ -638,23 +700,25 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)  	 */  	if (tx->sdata->vif.bss_conf.use_short_preamble &&  	    (ieee80211_is_data(hdr->frame_control) || -	     (tx->sta && test_sta_flags(tx->sta, WLAN_STA_SHORT_PREAMBLE)))) -		txrc.short_preamble = short_preamble = true; +	     (tx->sta && test_sta_flag(tx->sta, WLAN_STA_SHORT_PREAMBLE)))) +		txrc.short_preamble = true; + +	info->control.short_preamble = txrc.short_preamble; -	sta_flags = tx->sta ? get_sta_flags(tx->sta) : 0; +	if (tx->sta) +		assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC);  	/*  	 * Lets not bother rate control if we're associated and cannot  	 * talk to the sta. This should not happen.  	 */ -	if (WARN(test_bit(SCAN_SW_SCANNING, &tx->local->scanning) && -		 (sta_flags & WLAN_STA_ASSOC) && +	if (WARN(test_bit(SCAN_SW_SCANNING, &tx->local->scanning) && assoc &&  		 !rate_usable_index_exists(sband, &tx->sta->sta),  		 "%s: Dropped data frame as no usable bitrate found while "  		 "scanning and associated. Target station: "  		 "%pM on %d GHz band\n",  		 tx->sdata->name, hdr->addr1, -		 tx->channel->band ? 5 : 2)) +		 info->band ? 5 : 2))  		return TX_DROP;  	/* @@ -663,15 +727,38 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)  	 */  	rate_control_get_rate(tx->sdata, tx->sta, &txrc); -	if (unlikely(info->control.rates[0].idx < 0)) -		return TX_DROP; +	if (tx->sta && !info->control.skip_table) +		ratetbl = rcu_dereference(tx->sta->sta.rates); -	if (txrc.reported_rate.idx < 0) -		txrc.reported_rate = info->control.rates[0]; +	if (unlikely(info->control.rates[0].idx < 0)) { +		if (ratetbl) { +			struct ieee80211_tx_rate rate = { +				.idx = ratetbl->rate[0].idx, +				.flags = ratetbl->rate[0].flags, +				.count = ratetbl->rate[0].count +			}; -	if (tx->sta) +			if (ratetbl->rate[0].idx < 0) +				return TX_DROP; + +			tx->rate = rate; +		} else { +			return TX_DROP; +		} +	} else { +		tx->rate = info->control.rates[0]; +	} + +	if (txrc.reported_rate.idx < 0) { +		txrc.reported_rate = tx->rate; +		if (tx->sta && ieee80211_is_data(hdr->frame_control)) +			tx->sta->last_tx_rate = txrc.reported_rate; +	} else if (tx->sta)  		tx->sta->last_tx_rate = txrc.reported_rate; +	if (ratetbl) +		return TX_CONTINUE; +  	if (unlikely(!info->control.rates[0].count))  		info->control.rates[0].count = 1; @@ -679,91 +766,6 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)  			 (info->flags & IEEE80211_TX_CTL_NO_ACK)))  		info->control.rates[0].count = 1; -	if (is_multicast_ether_addr(hdr->addr1)) { -		/* -		 * XXX: verify the rate is in the basic rateset -		 */ -		return TX_CONTINUE; -	} - -	/* -	 * set up the RTS/CTS rate as the fastest basic rate -	 * that is not faster than the data rate -	 * -	 * XXX: Should this check all retry rates? -	 */ -	if (!(info->control.rates[0].flags & IEEE80211_TX_RC_MCS)) { -		s8 baserate = 0; - -		rate = &sband->bitrates[info->control.rates[0].idx]; - -		for (i = 0; i < sband->n_bitrates; i++) { -			/* must be a basic rate */ -			if (!(tx->sdata->vif.bss_conf.basic_rates & BIT(i))) -				continue; -			/* must not be faster than the data rate */ -			if (sband->bitrates[i].bitrate > rate->bitrate) -				continue; -			/* maximum */ -			if (sband->bitrates[baserate].bitrate < -			     sband->bitrates[i].bitrate) -				baserate = i; -		} - -		info->control.rts_cts_rate_idx = baserate; -	} - -	for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { -		/* -		 * make sure there's no valid rate following -		 * an invalid one, just in case drivers don't -		 * take the API seriously to stop at -1. -		 */ -		if (inval) { -			info->control.rates[i].idx = -1; -			continue; -		} -		if (info->control.rates[i].idx < 0) { -			inval = true; -			continue; -		} - -		/* -		 * For now assume MCS is already set up correctly, this -		 * needs to be fixed. -		 */ -		if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS) { -			WARN_ON(info->control.rates[i].idx > 76); -			continue; -		} - -		/* set up RTS protection if desired */ -		if (rts) -			info->control.rates[i].flags |= -				IEEE80211_TX_RC_USE_RTS_CTS; - -		/* RC is busted */ -		if (WARN_ON_ONCE(info->control.rates[i].idx >= -				 sband->n_bitrates)) { -			info->control.rates[i].idx = -1; -			continue; -		} - -		rate = &sband->bitrates[info->control.rates[i].idx]; - -		/* set up short preamble */ -		if (short_preamble && -		    rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) -			info->control.rates[i].flags |= -				IEEE80211_TX_RC_USE_SHORT_PREAMBLE; - -		/* set up G protection */ -		if (!rts && tx->sdata->vif.bss_conf.use_cts_prot && -		    rate->flags & IEEE80211_RATE_ERP_G) -			info->control.rates[i].flags |= -				IEEE80211_TX_RC_USE_CTS_PROTECT; -	} -  	return TX_CONTINUE;  } @@ -790,12 +792,17 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)  	if (ieee80211_hdrlen(hdr->frame_control) < 24)  		return TX_CONTINUE; +	if (ieee80211_is_qos_nullfunc(hdr->frame_control)) +		return TX_CONTINUE; +  	/*  	 * Anything but QoS data that has a sequence number field  	 * (is long enough) gets a sequence number from the global -	 * counter. +	 * counter.  QoS data frames with a multicast destination +	 * also use the global counter (802.11-2012 9.3.2.10).  	 */ -	if (!ieee80211_is_data_qos(hdr->frame_control)) { +	if (!ieee80211_is_data_qos(hdr->frame_control) || +	    is_multicast_ether_addr(hdr->addr1)) {  		/* driver should assign sequence number */  		info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;  		/* for pure STA mode without beacons, we can do it */ @@ -826,11 +833,13 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)  	return TX_CONTINUE;  } -static int ieee80211_fragment(struct ieee80211_local *local, +static int ieee80211_fragment(struct ieee80211_tx_data *tx,  			      struct sk_buff *skb, int hdrlen,  			      int frag_threshold)  { -	struct sk_buff *tail = skb, *tmp; +	struct ieee80211_local *local = tx->local; +	struct ieee80211_tx_info *info; +	struct sk_buff *tmp;  	int per_fragm = frag_threshold - hdrlen - FCS_LEN;  	int pos = hdrlen + per_fragm;  	int rem = skb->len - hdrlen - per_fragm; @@ -838,6 +847,8 @@ static int ieee80211_fragment(struct ieee80211_local *local,  	if (WARN_ON(rem < 0))  		return -EINVAL; +	/* first fragment was already added to queue by caller */ +  	while (rem) {  		int fraglen = per_fragm; @@ -846,16 +857,26 @@ static int ieee80211_fragment(struct ieee80211_local *local,  		rem -= fraglen;  		tmp = dev_alloc_skb(local->tx_headroom +  				    frag_threshold + -				    IEEE80211_ENCRYPT_HEADROOM + +				    tx->sdata->encrypt_headroom +  				    IEEE80211_ENCRYPT_TAILROOM);  		if (!tmp)  			return -ENOMEM; -		tail->next = tmp; -		tail = tmp; -		skb_reserve(tmp, local->tx_headroom + -				 IEEE80211_ENCRYPT_HEADROOM); + +		__skb_queue_tail(&tx->skbs, tmp); + +		skb_reserve(tmp, +			    local->tx_headroom + tx->sdata->encrypt_headroom); +  		/* copy control information */  		memcpy(tmp->cb, skb->cb, sizeof(tmp->cb)); + +		info = IEEE80211_SKB_CB(tmp); +		info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | +				 IEEE80211_TX_CTL_FIRST_FRAGMENT); + +		if (rem) +			info->flags |= IEEE80211_TX_CTL_MORE_FRAMES; +  		skb_copy_queue_mapping(tmp, skb);  		tmp->priority = skb->priority;  		tmp->dev = skb->dev; @@ -867,7 +888,8 @@ static int ieee80211_fragment(struct ieee80211_local *local,  		pos += fraglen;  	} -	skb->len = hdrlen + per_fragm; +	/* adjust first fragment's length */ +	skb_trim(skb, hdrlen + per_fragm);  	return 0;  } @@ -881,7 +903,14 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)  	int hdrlen;  	int fragnum; -	if (!(tx->flags & IEEE80211_TX_FRAGMENTED)) +	/* no matter what happens, tx->skb moves to tx->skbs */ +	__skb_queue_tail(&tx->skbs, skb); +	tx->skb = NULL; + +	if (info->flags & IEEE80211_TX_CTL_DONTFRAG) +		return TX_CONTINUE; + +	if (tx->local->ops->set_frag_threshold)  		return TX_CONTINUE;  	/* @@ -894,7 +923,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)  	hdrlen = ieee80211_hdrlen(hdr->frame_control); -	/* internal error, why is TX_FRAGMENTED set? */ +	/* internal error, why isn't DONTFRAG set? */  	if (WARN_ON(skb->len + FCS_LEN <= frag_threshold))  		return TX_DROP; @@ -906,21 +935,20 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)  	 * of the fragments then we will simply pretend to accept the skb  	 * but store it away as pending.  	 */ -	if (ieee80211_fragment(tx->local, skb, hdrlen, frag_threshold)) +	if (ieee80211_fragment(tx, skb, hdrlen, frag_threshold))  		return TX_DROP;  	/* update duration/seq/flags of fragments */  	fragnum = 0; -	do { -		int next_len; + +	skb_queue_walk(&tx->skbs, skb) {  		const __le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);  		hdr = (void *)skb->data;  		info = IEEE80211_SKB_CB(skb); -		if (skb->next) { +		if (!skb_queue_is_last(&tx->skbs, skb)) {  			hdr->frame_control |= morefrags; -			next_len = skb->next->len;  			/*  			 * No multi-rate retries for fragmented frames, that  			 * would completely throw off the NAV at other STAs. @@ -928,17 +956,14 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)  			info->control.rates[1].idx = -1;  			info->control.rates[2].idx = -1;  			info->control.rates[3].idx = -1; -			info->control.rates[4].idx = -1; -			BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 5); +			BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 4);  			info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;  		} else {  			hdr->frame_control &= ~morefrags; -			next_len = 0;  		} -		hdr->duration_id = ieee80211_duration(tx, 0, next_len);  		hdr->seq_ctrl |= cpu_to_le16(fragnum & IEEE80211_SCTL_FRAG);  		fragnum++; -	} while ((skb = skb->next)); +	}  	return TX_CONTINUE;  } @@ -946,16 +971,19 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)  static ieee80211_tx_result debug_noinline  ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)  { -	struct sk_buff *skb = tx->skb; +	struct sk_buff *skb; +	int ac = -1;  	if (!tx->sta)  		return TX_CONTINUE; -	tx->sta->tx_packets++; -	do { +	skb_queue_walk(&tx->skbs, skb) { +		ac = skb_get_queue_mapping(skb);  		tx->sta->tx_fragments++; -		tx->sta->tx_bytes += skb->len; -	} while ((skb = skb->next)); +		tx->sta->tx_bytes[ac] += skb->len; +	} +	if (ac >= 0) +		tx->sta->tx_packets[ac]++;  	return TX_CONTINUE;  } @@ -963,8 +991,6 @@ ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)  static ieee80211_tx_result debug_noinline  ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)  { -	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); -  	if (!tx->key)  		return TX_CONTINUE; @@ -979,13 +1005,7 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)  	case WLAN_CIPHER_SUITE_AES_CMAC:  		return ieee80211_crypto_aes_cmac_encrypt(tx);  	default: -		/* handle hw-only algorithm */ -		if (info->control.hw_key) { -			ieee80211_tx_set_protected(tx); -			return TX_CONTINUE; -		} -		break; - +		return ieee80211_crypto_hw_encrypt(tx);  	}  	return TX_DROP; @@ -994,124 +1014,31 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)  static ieee80211_tx_result debug_noinline  ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx)  { -	struct sk_buff *skb = tx->skb; +	struct sk_buff *skb;  	struct ieee80211_hdr *hdr;  	int next_len;  	bool group_addr; -	do { +	skb_queue_walk(&tx->skbs, skb) {  		hdr = (void *) skb->data;  		if (unlikely(ieee80211_is_pspoll(hdr->frame_control)))  			break; /* must not overwrite AID */ -		next_len = skb->next ? skb->next->len : 0; +		if (!skb_queue_is_last(&tx->skbs, skb)) { +			struct sk_buff *next = skb_queue_next(&tx->skbs, skb); +			next_len = next->len; +		} else +			next_len = 0;  		group_addr = is_multicast_ether_addr(hdr->addr1);  		hdr->duration_id = -			ieee80211_duration(tx, group_addr, next_len); -	} while ((skb = skb->next)); +			ieee80211_duration(tx, skb, group_addr, next_len); +	}  	return TX_CONTINUE;  }  /* actual transmit path */ -/* - * deal with packet injection down monitor interface - * with Radiotap Header -- only called for monitor mode interface - */ -static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, -					  struct sk_buff *skb) -{ -	/* -	 * this is the moment to interpret and discard the radiotap header that -	 * must be at the start of the packet injected in Monitor mode -	 * -	 * Need to take some care with endian-ness since radiotap -	 * args are little-endian -	 */ - -	struct ieee80211_radiotap_iterator iterator; -	struct ieee80211_radiotap_header *rthdr = -		(struct ieee80211_radiotap_header *) skb->data; -	struct ieee80211_supported_band *sband; -	bool hw_frag; -	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); -	int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, -						   NULL); - -	sband = tx->local->hw.wiphy->bands[tx->channel->band]; - -	info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; -	tx->flags &= ~IEEE80211_TX_FRAGMENTED; - -	/* packet is fragmented in HW if we have a non-NULL driver callback */ -	hw_frag = (tx->local->ops->set_frag_threshold != NULL); - -	/* -	 * for every radiotap entry that is present -	 * (ieee80211_radiotap_iterator_next returns -ENOENT when no more -	 * entries present, or -EINVAL on error) -	 */ - -	while (!ret) { -		ret = ieee80211_radiotap_iterator_next(&iterator); - -		if (ret) -			continue; - -		/* see if this argument is something we can use */ -		switch (iterator.this_arg_index) { -		/* -		 * You must take care when dereferencing iterator.this_arg -		 * for multibyte types... the pointer is not aligned.  Use -		 * get_unaligned((type *)iterator.this_arg) to dereference -		 * iterator.this_arg for type "type" safely on all arches. -		*/ -		case IEEE80211_RADIOTAP_FLAGS: -			if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FCS) { -				/* -				 * this indicates that the skb we have been -				 * handed has the 32-bit FCS CRC at the end... -				 * we should react to that by snipping it off -				 * because it will be recomputed and added -				 * on transmission -				 */ -				if (skb->len < (iterator._max_length + FCS_LEN)) -					return false; - -				skb_trim(skb, skb->len - FCS_LEN); -			} -			if (*iterator.this_arg & IEEE80211_RADIOTAP_F_WEP) -				info->flags &= ~IEEE80211_TX_INTFL_DONT_ENCRYPT; -			if ((*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG) && -								!hw_frag) -				tx->flags |= IEEE80211_TX_FRAGMENTED; -			break; - -		/* -		 * Please update the file -		 * Documentation/networking/mac80211-injection.txt -		 * when parsing new fields here. -		 */ - -		default: -			break; -		} -	} - -	if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */ -		return false; - -	/* -	 * remove the radiotap header -	 * iterator->_max_length was sanity-checked against -	 * skb->len by iterator init -	 */ -	skb_pull(skb, iterator._max_length); - -	return true; -} -  static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,  				  struct sk_buff *skb,  				  struct ieee80211_tx_info *info, @@ -1119,9 +1046,12 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,  				  int tid)  {  	bool queued = false; +	bool reset_agg_timer = false; +	struct sk_buff *purge_skb = NULL;  	if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {  		info->flags |= IEEE80211_TX_CTL_AMPDU; +		reset_agg_timer = true;  	} else if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {  		/*  		 * nothing -- this aggregation session is being started @@ -1147,21 +1077,32 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,  		 *     packet pass through because splicing the frames  		 *     back is already done.  		 */ -		tid_tx = tx->sta->ampdu_mlme.tid_tx[tid]; +		tid_tx = rcu_dereference_protected_tid_tx(tx->sta, tid);  		if (!tid_tx) {  			/* do nothing, let packet pass through */  		} else if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {  			info->flags |= IEEE80211_TX_CTL_AMPDU; +			reset_agg_timer = true;  		} else {  			queued = true;  			info->control.vif = &tx->sdata->vif;  			info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; +			info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;  			__skb_queue_tail(&tid_tx->pending, skb); +			if (skb_queue_len(&tid_tx->pending) > STA_MAX_TX_BUFFER) +				purge_skb = __skb_dequeue(&tid_tx->pending);  		}  		spin_unlock(&tx->sta->lock); + +		if (purge_skb) +			ieee80211_free_txskb(&tx->local->hw, purge_skb);  	} +	/* reset session timer */ +	if (reset_agg_timer && tid_tx->timeout) +		tid_tx->last_tx = jiffies; +  	return queued;  } @@ -1176,34 +1117,14 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_hdr *hdr;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); -	int hdrlen, tid; +	int tid;  	u8 *qc;  	memset(tx, 0, sizeof(*tx));  	tx->skb = skb;  	tx->local = local;  	tx->sdata = sdata; -	tx->channel = local->hw.conf.channel; -	/* -	 * Set this flag (used below to indicate "automatic fragmentation"), -	 * it will be cleared/left by radiotap as desired. -	 * Only valid when fragmentation is done by the stack. -	 */ -	if (!local->ops->set_frag_threshold) -		tx->flags |= IEEE80211_TX_FRAGMENTED; - -	/* process and remove the injection radiotap header */ -	if (unlikely(info->flags & IEEE80211_TX_INTFL_HAS_RADIOTAP)) { -		if (!__ieee80211_parse_tx_radiotap(tx, skb)) -			return TX_DROP; - -		/* -		 * __ieee80211_parse_tx_radiotap has now removed -		 * the radiotap header that was present and pre-filled -		 * 'tx' with tx control information. -		 */ -		info->flags &= ~IEEE80211_TX_INTFL_HAS_RADIOTAP; -	} +	__skb_queue_head_init(&tx->skbs);  	/*  	 * If this flag is set to true anywhere, and we get here, @@ -1218,14 +1139,18 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,  		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) { +	} 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 (!tx->sta)  		tx->sta = sta_info_get(sdata, hdr->addr1);  	if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && -	    (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) { +	    !ieee80211_is_qos_nullfunc(hdr->frame_control) && +	    (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) && +	    !(local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)) {  		struct tid_ampdu_tx *tid_tx;  		qc = ieee80211_get_qos_ctl(hdr); @@ -1246,110 +1171,163 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,  	if (is_multicast_ether_addr(hdr->addr1)) {  		tx->flags &= ~IEEE80211_TX_UNICAST;  		info->flags |= IEEE80211_TX_CTL_NO_ACK; -	} else { +	} else  		tx->flags |= IEEE80211_TX_UNICAST; -		if (unlikely(local->wifi_wme_noack_test)) -			info->flags |= IEEE80211_TX_CTL_NO_ACK; -		else -			info->flags &= ~IEEE80211_TX_CTL_NO_ACK; -	} -	if (tx->flags & IEEE80211_TX_FRAGMENTED) { -		if ((tx->flags & IEEE80211_TX_UNICAST) && -		    skb->len + FCS_LEN > local->hw.wiphy->frag_threshold && -		    !(info->flags & IEEE80211_TX_CTL_AMPDU)) -			tx->flags |= IEEE80211_TX_FRAGMENTED; -		else -			tx->flags &= ~IEEE80211_TX_FRAGMENTED; +	if (!(info->flags & IEEE80211_TX_CTL_DONTFRAG)) { +		if (!(tx->flags & IEEE80211_TX_UNICAST) || +		    skb->len + FCS_LEN <= local->hw.wiphy->frag_threshold || +		    info->flags & IEEE80211_TX_CTL_AMPDU) +			info->flags |= IEEE80211_TX_CTL_DONTFRAG;  	}  	if (!tx->sta)  		info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; -	else if (test_and_clear_sta_flags(tx->sta, WLAN_STA_CLEAR_PS_FILT)) +	else if (test_and_clear_sta_flag(tx->sta, WLAN_STA_CLEAR_PS_FILT))  		info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; -	hdrlen = ieee80211_hdrlen(hdr->frame_control); -	if (skb->len > hdrlen + sizeof(rfc1042_header) + 2) { -		u8 *pos = &skb->data[hdrlen + sizeof(rfc1042_header)]; -		tx->ethertype = (pos[0] << 8) | pos[1]; -	}  	info->flags |= IEEE80211_TX_CTL_FIRST_FRAGMENT;  	return TX_CONTINUE;  } -static int __ieee80211_tx(struct ieee80211_local *local, -			  struct sk_buff **skbp, -			  struct sta_info *sta, -			  bool txpending) +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 sk_buff *skb = *skbp, *next; -	struct ieee80211_tx_info *info; -	struct ieee80211_sub_if_data *sdata; +	struct ieee80211_tx_control control; +	struct sk_buff *skb, *tmp;  	unsigned long flags; -	int ret, len; -	bool fragm = false; -	while (skb) { -		int q = skb_get_queue_mapping(skb); +	skb_queue_walk_safe(skbs, skb, tmp) { +		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +		int q = info->hw_queue; + +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG +		if (WARN_ON_ONCE(q >= local->hw.queues)) { +			__skb_unlink(skb, skbs); +			ieee80211_free_txskb(&local->hw, skb); +			continue; +		} +#endif  		spin_lock_irqsave(&local->queue_stop_reason_lock, flags); -		ret = IEEE80211_TX_OK;  		if (local->queue_stop_reasons[q] || -		    (!txpending && !skb_queue_empty(&local->pending[q]))) -			ret = IEEE80211_TX_PENDING; +		    (!txpending && !skb_queue_empty(&local->pending[q]))) { +			if (unlikely(info->flags & +				     IEEE80211_TX_INTFL_OFFCHAN_TX_OK)) { +				if (local->queue_stop_reasons[q] & +				    ~BIT(IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL)) { +					/* +					 * Drop off-channel frames if queues +					 * are stopped for any reason other +					 * than off-channel operation. Never +					 * queue them. +					 */ +					spin_unlock_irqrestore( +						&local->queue_stop_reason_lock, +						flags); +					ieee80211_purge_tx_queue(&local->hw, +								 skbs); +					return true; +				} +			} else { + +				/* +				 * Since queue is stopped, queue up frames for +				 * later transmission from the tx-pending +				 * tasklet when the queue is woken again. +				 */ +				if (txpending) +					skb_queue_splice_init(skbs, +							      &local->pending[q]); +				else +					skb_queue_splice_tail_init(skbs, +								   &local->pending[q]); + +				spin_unlock_irqrestore(&local->queue_stop_reason_lock, +						       flags); +				return false; +			} +		}  		spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); -		if (ret != IEEE80211_TX_OK) -			return ret; -		info = IEEE80211_SKB_CB(skb); +		info->control.vif = vif; +		control.sta = sta; -		if (fragm) -			info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | -					 IEEE80211_TX_CTL_FIRST_FRAGMENT); +		__skb_unlink(skb, skbs); +		drv_tx(local, &control, skb); +	} -		next = skb->next; -		len = skb->len; +	return true; +} -		if (next) -			info->flags |= IEEE80211_TX_CTL_MORE_FRAMES; +/* + * Returns false if the frame couldn't be transmitted but was queued instead. + */ +static bool __ieee80211_tx(struct ieee80211_local *local, +			   struct sk_buff_head *skbs, int led_len, +			   struct sta_info *sta, bool txpending) +{ +	struct ieee80211_tx_info *info; +	struct ieee80211_sub_if_data *sdata; +	struct ieee80211_vif *vif; +	struct ieee80211_sta *pubsta; +	struct sk_buff *skb; +	bool result = true; +	__le16 fc; -		sdata = vif_to_sdata(info->control.vif); +	if (WARN_ON(skb_queue_empty(skbs))) +		return true; -		switch (sdata->vif.type) { -		case NL80211_IFTYPE_MONITOR: -			info->control.vif = NULL; -			break; -		case NL80211_IFTYPE_AP_VLAN: -			info->control.vif = &container_of(sdata->bss, -				struct ieee80211_sub_if_data, u.ap)->vif; -			break; -		default: -			/* keep */ +	skb = skb_peek(skbs); +	fc = ((struct ieee80211_hdr *)skb->data)->frame_control; +	info = IEEE80211_SKB_CB(skb); +	sdata = vif_to_sdata(info->control.vif); +	if (sta && !sta->uploaded) +		sta = NULL; + +	if (sta) +		pubsta = &sta->sta; +	else +		pubsta = NULL; + +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_MONITOR: +		if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) { +			vif = &sdata->vif;  			break;  		} +		sdata = rcu_dereference(local->monitor_sdata); +		if (sdata) { +			vif = &sdata->vif; +			info->hw_queue = +				vif->hw_queue[skb_get_queue_mapping(skb)]; +		} else if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) { +			dev_kfree_skb(skb); +			return true; +		} else +			vif = NULL; +		break; +	case NL80211_IFTYPE_AP_VLAN: +		sdata = container_of(sdata->bss, +				     struct ieee80211_sub_if_data, u.ap); +		/* fall through */ +	default: +		vif = &sdata->vif; +		break; +	} -		if (sta && sta->uploaded) -			info->control.sta = &sta->sta; -		else -			info->control.sta = NULL; +	result = ieee80211_tx_frags(local, vif, pubsta, skbs, +				    txpending); -		ret = drv_tx(local, skb); -		if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) { -			dev_kfree_skb(skb); -			ret = NETDEV_TX_OK; -		} -		if (ret != NETDEV_TX_OK) { -			info->control.vif = &sdata->vif; -			return IEEE80211_TX_AGAIN; -		} +	ieee80211_tpt_led_trig_tx(local, fc, led_len); -		*skbp = skb = next; -		ieee80211_led_tx(local, 1); -		fragm = true; -	} +	WARN_ON_ONCE(!skb_queue_empty(skbs)); -	return IEEE80211_TX_OK; +	return result;  }  /* @@ -1358,8 +1336,7 @@ static int __ieee80211_tx(struct ieee80211_local *local,   */  static int invoke_tx_handlers(struct ieee80211_tx_data *tx)  { -	struct sk_buff *skb = tx->skb; -	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);  	ieee80211_tx_result res = TX_DROP;  #define CALL_TXH(txh) \ @@ -1377,8 +1354,11 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)  	if (!(tx->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL))  		CALL_TXH(ieee80211_tx_h_rate_ctrl); -	if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) +	if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) { +		__skb_queue_tail(&tx->skbs, tx->skb); +		tx->skb = NULL;  		goto txh_done; +	}  	CALL_TXH(ieee80211_tx_h_michael_mic_add);  	CALL_TXH(ieee80211_tx_h_sequence); @@ -1386,19 +1366,17 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)  	/* handlers after fragment must be aware of tx info fragmentation! */  	CALL_TXH(ieee80211_tx_h_stats);  	CALL_TXH(ieee80211_tx_h_encrypt); -	CALL_TXH(ieee80211_tx_h_calculate_duration); +	if (!(tx->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)) +		CALL_TXH(ieee80211_tx_h_calculate_duration);  #undef CALL_TXH   txh_done:  	if (unlikely(res == TX_DROP)) {  		I802_DEBUG_INC(tx->local->tx_handlers_drop); -		while (skb) { -			struct sk_buff *next; - -			next = skb->next; -			dev_kfree_skb(skb); -			skb = next; -		} +		if (tx->skb) +			ieee80211_free_txskb(&tx->local->hw, tx->skb); +		else +			ieee80211_purge_tx_queue(&tx->local->hw, &tx->skbs);  		return -1;  	} else if (unlikely(res == TX_QUEUED)) {  		I802_DEBUG_INC(tx->local->tx_handlers_queued); @@ -1408,141 +1386,101 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)  	return 0;  } -static void ieee80211_tx(struct ieee80211_sub_if_data *sdata, -			 struct sk_buff *skb, bool txpending) +bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, +			      struct ieee80211_vif *vif, struct sk_buff *skb, +			      int band, struct ieee80211_sta **sta) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct ieee80211_tx_data tx; + +	if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP) +		return false; + +	info->band = band; +	info->control.vif = vif; +	info->hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)]; + +	if (invoke_tx_handlers(&tx)) +		return false; + +	if (sta) { +		if (tx.sta) +			*sta = &tx.sta->sta; +		else +			*sta = NULL; +	} + +	return true; +} +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, +			 enum ieee80211_band band)  {  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_tx_data tx;  	ieee80211_tx_result res_prepare;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); -	struct sk_buff *next; -	unsigned long flags; -	int ret, retries; -	u16 queue; - -	queue = skb_get_queue_mapping(skb); +	bool result = true; +	int led_len;  	if (unlikely(skb->len < 10)) {  		dev_kfree_skb(skb); -		return; +		return true;  	} -	rcu_read_lock(); -  	/* initialises tx */ +	led_len = skb->len;  	res_prepare = ieee80211_tx_prepare(sdata, &tx, skb);  	if (unlikely(res_prepare == TX_DROP)) { -		dev_kfree_skb(skb); -		rcu_read_unlock(); -		return; +		ieee80211_free_txskb(&local->hw, skb); +		return true;  	} else if (unlikely(res_prepare == TX_QUEUED)) { -		rcu_read_unlock(); -		return; +		return true;  	} -	tx.channel = local->hw.conf.channel; -	info->band = tx.channel->band; - -	if (invoke_tx_handlers(&tx)) -		goto out; - -	retries = 0; - retry: -	ret = __ieee80211_tx(local, &tx.skb, tx.sta, txpending); -	switch (ret) { -	case IEEE80211_TX_OK: -		break; -	case IEEE80211_TX_AGAIN: -		/* -		 * Since there are no fragmented frames on A-MPDU -		 * queues, there's no reason for a driver to reject -		 * a frame there, warn and drop it. -		 */ -		if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU)) -			goto drop; -		/* fall through */ -	case IEEE80211_TX_PENDING: -		skb = tx.skb; - -		spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - -		if (local->queue_stop_reasons[queue] || -		    !skb_queue_empty(&local->pending[queue])) { -			/* -			 * if queue is stopped, queue up frames for later -			 * transmission from the tasklet -			 */ -			do { -				next = skb->next; -				skb->next = NULL; -				if (unlikely(txpending)) -					__skb_queue_head(&local->pending[queue], -							 skb); -				else -					__skb_queue_tail(&local->pending[queue], -							 skb); -			} while ((skb = next)); - -			spin_unlock_irqrestore(&local->queue_stop_reason_lock, -					       flags); -		} else { -			/* -			 * otherwise retry, but this is a race condition or -			 * a driver bug (which we warn about if it persists) -			 */ -			spin_unlock_irqrestore(&local->queue_stop_reason_lock, -					       flags); +	info->band = band; -			retries++; -			if (WARN(retries > 10, "tx refused but queue active\n")) -				goto drop; -			goto retry; -		} -	} - out: -	rcu_read_unlock(); -	return; +	/* set up hw_queue value early */ +	if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) || +	    !(local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)) +		info->hw_queue = +			sdata->vif.hw_queue[skb_get_queue_mapping(skb)]; - drop: -	rcu_read_unlock(); +	if (!invoke_tx_handlers(&tx)) +		result = __ieee80211_tx(local, &tx.skbs, led_len, +					tx.sta, txpending); -	skb = tx.skb; -	while (skb) { -		next = skb->next; -		dev_kfree_skb(skb); -		skb = next; -	} +	return result;  }  /* device xmit handlers */ -static int ieee80211_skb_resize(struct ieee80211_local *local, +static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,  				struct sk_buff *skb,  				int head_need, bool may_encrypt)  { +	struct ieee80211_local *local = sdata->local;  	int tail_need = 0; -	/* -	 * This could be optimised, devices that do full hardware -	 * crypto (including TKIP MMIC) need no tailroom... But we -	 * have no drivers for such devices currently. -	 */ -	if (may_encrypt) { +	if (may_encrypt && sdata->crypto_tx_tailroom_needed_cnt) {  		tail_need = IEEE80211_ENCRYPT_TAILROOM;  		tail_need -= skb_tailroom(skb);  		tail_need = max_t(int, tail_need, 0);  	} -	if (head_need || tail_need) { -		/* Sorry. Can't account for this any more */ -		skb_orphan(skb); -	} - -	if (skb_header_cloned(skb)) +	if (skb_cloned(skb))  		I802_DEBUG_INC(local->tx_expand_skb_head_cloned); -	else +	else if (head_need || tail_need)  		I802_DEBUG_INC(local->tx_expand_skb_head); +	else +		return 0;  	if (pskb_expand_head(skb, head_need, tail_need, GFP_ATOMIC)) {  		wiphy_debug(local->hw.wiphy, @@ -1550,122 +1488,144 @@ static int ieee80211_skb_resize(struct ieee80211_local *local,  		return -ENOMEM;  	} -	/* update truesize too */ -	skb->truesize += head_need + tail_need; -  	return 0;  } -static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, -			   struct sk_buff *skb) +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, +		    enum ieee80211_band band)  {  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; -	struct ieee80211_sub_if_data *tmp_sdata;  	int headroom;  	bool may_encrypt; -	rcu_read_lock(); - -	if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) { -		int hdrlen; -		u16 len_rthdr; - -		info->flags |= IEEE80211_TX_CTL_INJECTED | -			       IEEE80211_TX_INTFL_HAS_RADIOTAP; - -		len_rthdr = ieee80211_get_radiotap_len(skb->data); -		hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr); -		hdrlen = ieee80211_hdrlen(hdr->frame_control); - -		/* check the header is complete in the frame */ -		if (likely(skb->len >= len_rthdr + hdrlen)) { -			/* -			 * We process outgoing injected frames that have a -			 * local address we handle as though they are our -			 * own frames. -			 * This code here isn't entirely correct, the local -			 * MAC address is not necessarily enough to find -			 * the interface to use; for that proper VLAN/WDS -			 * support we will need a different mechanism. -			 */ - -			list_for_each_entry_rcu(tmp_sdata, &local->interfaces, -						list) { -				if (!ieee80211_sdata_running(tmp_sdata)) -					continue; -				if (tmp_sdata->vif.type != NL80211_IFTYPE_AP) -					continue; -				if (compare_ether_addr(tmp_sdata->vif.addr, -						       hdr->addr2) == 0) { -					sdata = tmp_sdata; -					break; -				} -			} -		} -	} -  	may_encrypt = !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT);  	headroom = local->tx_headroom;  	if (may_encrypt) -		headroom += IEEE80211_ENCRYPT_HEADROOM; +		headroom += sdata->encrypt_headroom;  	headroom -= skb_headroom(skb);  	headroom = max_t(int, 0, headroom); -	if (ieee80211_skb_resize(local, skb, headroom, may_encrypt)) { -		dev_kfree_skb(skb); -		rcu_read_unlock(); +	if (ieee80211_skb_resize(sdata, skb, headroom, may_encrypt)) { +		ieee80211_free_txskb(&local->hw, skb);  		return;  	}  	hdr = (struct ieee80211_hdr *) skb->data;  	info->control.vif = &sdata->vif; -	if (ieee80211_vif_is_mesh(&sdata->vif) && -	    ieee80211_is_data(hdr->frame_control) && -		!is_multicast_ether_addr(hdr->addr1)) -			if (mesh_nexthop_lookup(skb, sdata)) { -				/* skb queued: don't free */ -				rcu_read_unlock(); -				return; +	if (ieee80211_vif_is_mesh(&sdata->vif)) { +		if (ieee80211_is_data(hdr->frame_control) && +		    is_unicast_ether_addr(hdr->addr1)) { +			if (mesh_nexthop_resolve(sdata, skb)) +				return; /* skb queued: don't free */ +		} else { +			ieee80211_mps_set_frame_flags(sdata, NULL, hdr); +		} +	} + +	ieee80211_set_qos_hdr(sdata, skb); +	ieee80211_tx(sdata, skb, false, band); +} + +static bool ieee80211_parse_tx_radiotap(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); +	int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, +						   NULL); +	u16 txflags; + +	info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | +		       IEEE80211_TX_CTL_DONTFRAG; + +	/* +	 * for every radiotap entry that is present +	 * (ieee80211_radiotap_iterator_next returns -ENOENT when no more +	 * entries present, or -EINVAL on error) +	 */ + +	while (!ret) { +		ret = ieee80211_radiotap_iterator_next(&iterator); + +		if (ret) +			continue; + +		/* see if this argument is something we can use */ +		switch (iterator.this_arg_index) { +		/* +		 * You must take care when dereferencing iterator.this_arg +		 * for multibyte types... the pointer is not aligned.  Use +		 * get_unaligned((type *)iterator.this_arg) to dereference +		 * iterator.this_arg for type "type" safely on all arches. +		*/ +		case IEEE80211_RADIOTAP_FLAGS: +			if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FCS) { +				/* +				 * this indicates that the skb we have been +				 * handed has the 32-bit FCS CRC at the end... +				 * we should react to that by snipping it off +				 * because it will be recomputed and added +				 * on transmission +				 */ +				if (skb->len < (iterator._max_length + FCS_LEN)) +					return false; + +				skb_trim(skb, skb->len - FCS_LEN);  			} +			if (*iterator.this_arg & IEEE80211_RADIOTAP_F_WEP) +				info->flags &= ~IEEE80211_TX_INTFL_DONT_ENCRYPT; +			if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG) +				info->flags &= ~IEEE80211_TX_CTL_DONTFRAG; +			break; -	ieee80211_set_qos_hdr(local, skb); -	ieee80211_tx(sdata, skb, false); -	rcu_read_unlock(); +		case IEEE80211_RADIOTAP_TX_FLAGS: +			txflags = get_unaligned_le16(iterator.this_arg); +			if (txflags & IEEE80211_RADIOTAP_F_TX_NOACK) +				info->flags |= IEEE80211_TX_CTL_NO_ACK; +			break; + +		/* +		 * Please update the file +		 * Documentation/networking/mac80211-injection.txt +		 * when parsing new fields here. +		 */ + +		default: +			break; +		} +	} + +	if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */ +		return false; + +	/* +	 * remove the radiotap header +	 * iterator->_max_length was sanity-checked against +	 * skb->len by iterator init +	 */ +	skb_pull(skb, iterator._max_length); + +	return true;  }  netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,  					 struct net_device *dev)  {  	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); -	struct ieee80211_channel *chan = local->hw.conf.channel; +	struct ieee80211_chanctx_conf *chanctx_conf; +	struct ieee80211_channel *chan;  	struct ieee80211_radiotap_header *prthdr =  		(struct ieee80211_radiotap_header *)skb->data;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct ieee80211_hdr *hdr; +	struct ieee80211_sub_if_data *tmp_sdata, *sdata;  	u16 len_rthdr; - -	/* -	 * Frame injection is not allowed if beaconing is not allowed -	 * or if we need radar detection. Beaconing is usually not allowed when -	 * the mode or operation (Adhoc, AP, Mesh) does not support DFS. -	 * Passive scan is also used in world regulatory domains where -	 * your country is not known and as such it should be treated as -	 * NO TX unless the channel is explicitly allowed in which case -	 * your current regulatory domain would not have the passive scan -	 * flag. -	 * -	 * Since AP mode uses monitor interfaces to inject/TX management -	 * frames we can make AP mode the exception to this rule once it -	 * supports radar detection as its implementation can deal with -	 * radar detection by itself. We can do that later by adding a -	 * monitor flag interfaces used for AP support. -	 */ -	if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR | -	     IEEE80211_CHAN_PASSIVE_SCAN))) -		goto fail; +	int hdrlen;  	/* check for not even having the fixed radiotap header part */  	if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) @@ -1696,19 +1656,128 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,  	skb_set_network_header(skb, len_rthdr);  	skb_set_transport_header(skb, len_rthdr); +	if (skb->len < len_rthdr + 2) +		goto fail; + +	hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr); +	hdrlen = ieee80211_hdrlen(hdr->frame_control); + +	if (skb->len < len_rthdr + hdrlen) +		goto fail; + +	/* +	 * Initialize skb->protocol if the injected frame is a data frame +	 * carrying a rfc1042 header +	 */ +	if (ieee80211_is_data(hdr->frame_control) && +	    skb->len >= len_rthdr + hdrlen + sizeof(rfc1042_header) + 2) { +		u8 *payload = (u8 *)hdr + hdrlen; + +		if (ether_addr_equal(payload, rfc1042_header)) +			skb->protocol = cpu_to_be16((payload[6] << 8) | +						    payload[7]); +	} +  	memset(info, 0, sizeof(*info)); -	info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; +	info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS | +		      IEEE80211_TX_CTL_INJECTED; + +	/* process and remove the injection radiotap header */ +	if (!ieee80211_parse_tx_radiotap(skb)) +		goto fail; + +	rcu_read_lock(); + +	/* +	 * We process outgoing injected frames that have a local address +	 * we handle as though they are non-injected frames. +	 * This code here isn't entirely correct, the local MAC address +	 * isn't always enough to find the interface to use; for proper +	 * VLAN/WDS support we will need a different mechanism (which +	 * likely isn't going to be monitor interfaces). +	 */ +	sdata = IEEE80211_DEV_TO_SUB_IF(dev); + +	list_for_each_entry_rcu(tmp_sdata, &local->interfaces, list) { +		if (!ieee80211_sdata_running(tmp_sdata)) +			continue; +		if (tmp_sdata->vif.type == NL80211_IFTYPE_MONITOR || +		    tmp_sdata->vif.type == NL80211_IFTYPE_AP_VLAN || +		    tmp_sdata->vif.type == NL80211_IFTYPE_WDS) +			continue; +		if (ether_addr_equal(tmp_sdata->vif.addr, hdr->addr2)) { +			sdata = tmp_sdata; +			break; +		} +	} + +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	if (!chanctx_conf) { +		tmp_sdata = rcu_dereference(local->monitor_sdata); +		if (tmp_sdata) +			chanctx_conf = +				rcu_dereference(tmp_sdata->vif.chanctx_conf); +	} + +	if (chanctx_conf) +		chan = chanctx_conf->def.chan; +	else if (!local->use_chanctx) +		chan = local->_oper_chandef.chan; +	else +		goto fail_rcu; + +	/* +	 * Frame injection is not allowed if beaconing is not allowed +	 * or if we need radar detection. Beaconing is usually not allowed when +	 * the mode or operation (Adhoc, AP, Mesh) does not support DFS. +	 * Passive scan is also used in world regulatory domains where +	 * your country is not known and as such it should be treated as +	 * NO TX unless the channel is explicitly allowed in which case +	 * your current regulatory domain would not have the passive scan +	 * flag. +	 * +	 * Since AP mode uses monitor interfaces to inject/TX management +	 * frames we can make AP mode the exception to this rule once it +	 * supports radar detection as its implementation can deal with +	 * radar detection by itself. We can do that later by adding a +	 * monitor flag interfaces used for AP support. +	 */ +	if ((chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR))) +		goto fail_rcu; + +	ieee80211_xmit(sdata, skb, chan->band); +	rcu_read_unlock(); -	/* pass the radiotap header up to xmit */ -	ieee80211_xmit(IEEE80211_DEV_TO_SUB_IF(dev), skb);  	return NETDEV_TX_OK; +fail_rcu: +	rcu_read_unlock();  fail:  	dev_kfree_skb(skb);  	return NETDEV_TX_OK; /* meaning, we dealt with the skb */  } +/* + * Measure Tx frame arrival time for Tx latency statistics calculation + * A single Tx frame latency should be measured from when it is entering the + * Kernel until we receive Tx complete confirmation indication and the skb is + * freed. + */ +static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local, +					      struct sk_buff *skb) +{ +	struct timespec skb_arv; +	struct ieee80211_tx_latency_bin_ranges *tx_latency; + +	tx_latency = rcu_dereference(local->tx_latency); +	if (!tx_latency) +		return; + +	ktime_get_ts(&skb_arv); +	skb->tstamp = ktime_set(skb_arv.tv_sec, skb_arv.tv_nsec); +} +  /**   * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type   * subinterfaces (wlan#, WDS, and VLAN interfaces) @@ -1729,34 +1798,41 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = sdata->local; -	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); -	int ret = NETDEV_TX_BUSY, head_need; +	struct ieee80211_tx_info *info; +	int head_need;  	u16 ethertype, hdrlen,  meshhdrlen = 0;  	__le16 fc;  	struct ieee80211_hdr hdr;  	struct ieee80211s_hdr mesh_hdr __maybe_unused; +	struct mesh_path __maybe_unused *mppath = NULL, *mpath = NULL;  	const u8 *encaps_data;  	int encaps_len, skip_header_bytes;  	int nh_pos, h_pos;  	struct sta_info *sta = NULL; -	u32 sta_flags = 0; - -	if (unlikely(skb->len < ETH_HLEN)) { -		ret = NETDEV_TX_OK; +	bool wme_sta = false, authorized = false, tdls_auth = false; +	bool tdls_direct = false; +	bool multicast; +	u32 info_flags = 0; +	u16 info_id = 0; +	struct ieee80211_chanctx_conf *chanctx_conf; +	struct ieee80211_sub_if_data *ap_sdata; +	enum ieee80211_band band; + +	if (unlikely(skb->len < ETH_HLEN))  		goto fail; -	} - -	nh_pos = skb_network_header(skb) - skb->data; -	h_pos = skb_transport_header(skb) - skb->data;  	/* convert Ethernet header to proper 802.11 header (based on  	 * operation mode) */  	ethertype = (skb->data[12] << 8) | skb->data[13];  	fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); +	rcu_read_lock(); + +	/* Measure frame arrival for Tx latency statistics calculation */ +	ieee80211_tx_latency_start_msrmnt(local, skb); +  	switch (sdata->vif.type) {  	case NL80211_IFTYPE_AP_VLAN: -		rcu_read_lock();  		sta = rcu_dereference(sdata->u.vlan.sta);  		if (sta) {  			fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); @@ -1766,19 +1842,30 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  			memcpy(hdr.addr3, skb->data, ETH_ALEN);  			memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);  			hdrlen = 30; -			sta_flags = get_sta_flags(sta); +			authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); +			wme_sta = test_sta_flag(sta, WLAN_STA_WME);  		} -		rcu_read_unlock(); +		ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, +					u.ap); +		chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf); +		if (!chanctx_conf) +			goto fail_rcu; +		band = chanctx_conf->def.chan->band;  		if (sta)  			break;  		/* fall through */  	case NL80211_IFTYPE_AP: +		if (sdata->vif.type == NL80211_IFTYPE_AP) +			chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +		if (!chanctx_conf) +			goto fail_rcu;  		fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);  		/* DA BSSID SA */  		memcpy(hdr.addr1, skb->data, ETH_ALEN);  		memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);  		memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);  		hdrlen = 24; +		band = chanctx_conf->def.chan->band;  		break;  	case NL80211_IFTYPE_WDS:  		fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); @@ -1788,73 +1875,122 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  		memcpy(hdr.addr3, skb->data, ETH_ALEN);  		memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);  		hdrlen = 30; +		/* +		 * This is the exception! WDS style interfaces are prohibited +		 * when channel contexts are in used so this must be valid +		 */ +		band = local->hw.conf.chandef.chan->band;  		break;  #ifdef CONFIG_MAC80211_MESH  	case NL80211_IFTYPE_MESH_POINT: -		if (!sdata->u.mesh.mshcfg.dot11MeshTTL) { -			/* Do not send frames with mesh_ttl == 0 */ -			sdata->u.mesh.mshstats.dropped_frames_ttl++; -			ret = NETDEV_TX_OK; -			goto fail; +		if (!is_multicast_ether_addr(skb->data)) { +			struct sta_info *next_hop; +			bool mpp_lookup = true; + +			mpath = mesh_path_lookup(sdata, skb->data); +			if (mpath) { +				mpp_lookup = false; +				next_hop = rcu_dereference(mpath->next_hop); +				if (!next_hop || +				    !(mpath->flags & (MESH_PATH_ACTIVE | +						      MESH_PATH_RESOLVING))) +					mpp_lookup = true; +			} + +			if (mpp_lookup) +				mppath = mpp_path_lookup(sdata, skb->data); + +			if (mppath && mpath) +				mesh_path_del(mpath->sdata, mpath->dst);  		} -		if (compare_ether_addr(sdata->vif.addr, -				       skb->data + ETH_ALEN) == 0) { +		/* +		 * Use address extension if it is a packet from +		 * another interface or if we know the destination +		 * is being proxied by a portal (i.e. portal address +		 * differs from proxied address) +		 */ +		if (ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN) && +		    !(mppath && !ether_addr_equal(mppath->mpp, skb->data))) {  			hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,  					skb->data, skb->data + ETH_ALEN); -			meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, -					sdata, NULL, NULL, NULL); +			meshhdrlen = ieee80211_new_mesh_header(sdata, &mesh_hdr, +							       NULL, NULL);  		} else { -			/* packet from other interface */ -			struct mesh_path *mppath; -			int is_mesh_mcast = 1; -			const u8 *mesh_da; +			/* DS -> MBSS (802.11-2012 13.11.3.3). +			 * For unicast with unknown forwarding information, +			 * destination might be in the MBSS or if that fails +			 * forwarded to another mesh gate. In either case +			 * resolution will be handled in ieee80211_xmit(), so +			 * leave the original DA. This also works for mcast */ +			const u8 *mesh_da = skb->data; + +			if (mppath) +				mesh_da = mppath->mpp; +			else if (mpath) +				mesh_da = mpath->dst; -			rcu_read_lock(); -			if (is_multicast_ether_addr(skb->data)) -				/* DA TA mSA AE:SA */ -				mesh_da = skb->data; -			else { -				static const u8 bcast[ETH_ALEN] = -					{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - -				mppath = mpp_path_lookup(skb->data, sdata); -				if (mppath) { -					/* RA TA mDA mSA AE:DA SA */ -					mesh_da = mppath->mpp; -					is_mesh_mcast = 0; -				} else { -					/* DA TA mSA AE:SA */ -					mesh_da = bcast; -				} -			}  			hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,  					mesh_da, sdata->vif.addr); -			rcu_read_unlock(); -			if (is_mesh_mcast) -				meshhdrlen = -					ieee80211_new_mesh_header(&mesh_hdr, -							sdata, -							skb->data + ETH_ALEN, -							NULL, -							NULL); +			if (is_multicast_ether_addr(mesh_da)) +				/* DA TA mSA AE:SA */ +				meshhdrlen = ieee80211_new_mesh_header( +						sdata, &mesh_hdr, +						skb->data + ETH_ALEN, NULL);  			else -				meshhdrlen = -					ieee80211_new_mesh_header(&mesh_hdr, -							sdata, -							NULL, -							skb->data, -							skb->data + ETH_ALEN); +				/* RA TA mDA mSA AE:DA SA */ +				meshhdrlen = ieee80211_new_mesh_header( +						sdata, &mesh_hdr, skb->data, +						skb->data + ETH_ALEN);  		} +		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +		if (!chanctx_conf) +			goto fail_rcu; +		band = chanctx_conf->def.chan->band;  		break;  #endif  	case NL80211_IFTYPE_STATION: -		memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN); -		if (sdata->u.mgd.use_4addr && -		    cpu_to_be16(ethertype) != sdata->control_port_protocol) { -			fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); +		if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) { +			bool tdls_peer = false; + +			sta = sta_info_get(sdata, skb->data); +			if (sta) { +				authorized = test_sta_flag(sta, +							WLAN_STA_AUTHORIZED); +				wme_sta = test_sta_flag(sta, WLAN_STA_WME); +				tdls_peer = test_sta_flag(sta, +							 WLAN_STA_TDLS_PEER); +				tdls_auth = test_sta_flag(sta, +						WLAN_STA_TDLS_PEER_AUTH); +			} + +			/* +			 * If the TDLS link is enabled, send everything +			 * directly. Otherwise, allow TDLS setup frames +			 * to be transmitted indirectly. +			 */ +			tdls_direct = tdls_peer && (tdls_auth || +				 !(ethertype == ETH_P_TDLS && skb->len > 14 && +				   skb->data[14] == WLAN_TDLS_SNAP_RFTYPE)); +		} + +		if (tdls_direct) { +			/* link during setup - throw out frames to peer */ +			if (!tdls_auth) +				goto fail_rcu; + +			/* DA SA BSSID */ +			memcpy(hdr.addr1, skb->data, ETH_ALEN); +			memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); +			memcpy(hdr.addr3, sdata->u.mgd.bssid, ETH_ALEN); +			hdrlen = 24; +		}  else if (sdata->u.mgd.use_4addr && +			    cpu_to_be16(ethertype) != sdata->control_port_protocol) { +			fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | +					  IEEE80211_FCTL_TODS);  			/* RA TA DA SA */ +			memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN);  			memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);  			memcpy(hdr.addr3, skb->data, ETH_ALEN);  			memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); @@ -1862,10 +1998,15 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  		} else {  			fc |= cpu_to_le16(IEEE80211_FCTL_TODS);  			/* BSSID SA DA */ +			memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN);  			memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);  			memcpy(hdr.addr3, skb->data, ETH_ALEN);  			hdrlen = 24;  		} +		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +		if (!chanctx_conf) +			goto fail_rcu; +		band = chanctx_conf->def.chan->band;  		break;  	case NL80211_IFTYPE_ADHOC:  		/* DA SA BSSID */ @@ -1873,10 +2014,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  		memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);  		memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN);  		hdrlen = 24; +		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +		if (!chanctx_conf) +			goto fail_rcu; +		band = chanctx_conf->def.chan->band;  		break;  	default: -		ret = NETDEV_TX_OK; -		goto fail; +		goto fail_rcu;  	}  	/* @@ -1884,16 +2028,21 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  	 * if it is a multicast address (which can only happen  	 * in AP mode)  	 */ -	if (!is_multicast_ether_addr(hdr.addr1)) { -		rcu_read_lock(); +	multicast = is_multicast_ether_addr(hdr.addr1); +	if (!multicast) {  		sta = sta_info_get(sdata, hdr.addr1); -		if (sta) -			sta_flags = get_sta_flags(sta); -		rcu_read_unlock(); +		if (sta) { +			authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); +			wme_sta = test_sta_flag(sta, WLAN_STA_WME); +		}  	} +	/* For mesh, the use of the QoS header is mandatory */ +	if (ieee80211_vif_is_mesh(&sdata->vif)) +		wme_sta = true; +  	/* receiver and we are QoS enabled, use a QoS type frame */ -	if ((sta_flags & WLAN_STA_WME) && local->hw.queues >= 4) { +	if (wme_sta && local->hw.queues >= IEEE80211_NUM_ACS) {  		fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);  		hdrlen += 2;  	} @@ -1902,23 +2051,63 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  	 * Drop unicast frames to unauthorised stations unless they are  	 * EAPOL frames from the local station.  	 */ -	if (!ieee80211_vif_is_mesh(&sdata->vif) && -		unlikely(!is_multicast_ether_addr(hdr.addr1) && -		      !(sta_flags & WLAN_STA_AUTHORIZED) && -		      !(cpu_to_be16(ethertype) == sdata->control_port_protocol && -		       compare_ether_addr(sdata->vif.addr, -					  skb->data + ETH_ALEN) == 0))) { +	if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) && +		     !multicast && !authorized && +		     (cpu_to_be16(ethertype) != sdata->control_port_protocol || +		      !ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {  #ifdef CONFIG_MAC80211_VERBOSE_DEBUG -		if (net_ratelimit()) -			printk(KERN_DEBUG "%s: dropped frame to %pM" -			       " (unauthorized port)\n", dev->name, -			       hdr.addr1); +		net_info_ratelimited("%s: dropped frame to %pM (unauthorized port)\n", +				    dev->name, hdr.addr1);  #endif  		I802_DEBUG_INC(local->tx_handlers_drop_unauth_port); -		ret = NETDEV_TX_OK; -		goto fail; +		goto fail_rcu; +	} + +	if (unlikely(!multicast && skb->sk && +		     skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) { +		struct sk_buff *orig_skb = skb; + +		skb = skb_clone(skb, GFP_ATOMIC); +		if (skb) { +			unsigned long flags; +			int id; + +			spin_lock_irqsave(&local->ack_status_lock, flags); +			id = idr_alloc(&local->ack_status_frames, orig_skb, +				       1, 0x10000, GFP_ATOMIC); +			spin_unlock_irqrestore(&local->ack_status_lock, flags); + +			if (id >= 0) { +				info_id = id; +				info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; +			} else if (skb_shared(skb)) { +				kfree_skb(orig_skb); +			} else { +				kfree_skb(skb); +				skb = orig_skb; +			} +		} else { +			/* couldn't clone -- lose tx status ... */ +			skb = orig_skb; +		} +	} + +	/* +	 * If the skb is shared we need to obtain our own copy. +	 */ +	if (skb_shared(skb)) { +		struct sk_buff *tmp_skb = skb; + +		/* can't happen -- skb is a clone if info_id != 0 */ +		WARN_ON(info_id); + +		skb = skb_clone(skb, GFP_ATOMIC); +		kfree_skb(tmp_skb); + +		if (!skb) +			goto fail_rcu;  	}  	hdr.frame_control = fc; @@ -1930,7 +2119,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  		encaps_data = bridge_tunnel_header;  		encaps_len = sizeof(bridge_tunnel_header);  		skip_header_bytes -= 2; -	} else if (ethertype >= 0x600) { +	} else if (ethertype >= ETH_P_802_3_MIN) {  		encaps_data = rfc1042_header;  		encaps_len = sizeof(rfc1042_header);  		skip_header_bytes -= 2; @@ -1939,6 +2128,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  		encaps_len = 0;  	} +	nh_pos = skb_network_header(skb) - skb->data; +	h_pos = skb_transport_header(skb) - skb->data; +  	skb_pull(skb, skip_header_bytes);  	nh_pos -= skip_header_bytes;  	h_pos -= skip_header_bytes; @@ -1958,11 +2150,14 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  	 */  	if (head_need > 0 || skb_cloned(skb)) { -		head_need += IEEE80211_ENCRYPT_HEADROOM; +		head_need += sdata->encrypt_headroom;  		head_need += local->tx_headroom;  		head_need = max_t(int, 0, head_need); -		if (ieee80211_skb_resize(local, skb, head_need, true)) -			goto fail; +		if (ieee80211_skb_resize(sdata, skb, head_need, true)) { +			ieee80211_free_txskb(&local->hw, skb); +			skb = NULL; +			goto fail_rcu; +		}  	}  	if (encaps_data) { @@ -1982,7 +2177,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  	if (ieee80211_is_data_qos(fc)) {  		__le16 *qos_control; -		qos_control = (__le16*) skb_push(skb, 2); +		qos_control = (__le16 *) skb_push(skb, 2);  		memcpy(skb_push(skb, hdrlen - 2), &hdr, hdrlen - 2);  		/*  		 * Maybe we could actually set some fields here, for now just @@ -2005,18 +2200,24 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  	skb_set_network_header(skb, nh_pos);  	skb_set_transport_header(skb, h_pos); +	info = IEEE80211_SKB_CB(skb);  	memset(info, 0, sizeof(*info));  	dev->trans_start = jiffies; -	ieee80211_xmit(sdata, skb); + +	info->flags = info_flags; +	info->ack_frame_id = info_id; + +	ieee80211_xmit(sdata, skb, band); +	rcu_read_unlock();  	return NETDEV_TX_OK; + fail_rcu: +	rcu_read_unlock();   fail: -	if (ret == NETDEV_TX_OK) -		dev_kfree_skb(skb); - -	return ret; +	dev_kfree_skb(skb); +	return NETDEV_TX_OK;  } @@ -2026,12 +2227,20 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,   */  void ieee80211_clear_tx_pending(struct ieee80211_local *local)  { +	struct sk_buff *skb;  	int i; -	for (i = 0; i < local->hw.queues; i++) -		skb_queue_purge(&local->pending[i]); +	for (i = 0; i < local->hw.queues; i++) { +		while ((skb = skb_dequeue(&local->pending[i])) != NULL) +			ieee80211_free_txskb(&local->hw, skb); +	}  } +/* + * Returns false if the frame couldn't be transmitted but was queued instead, + * which in this case means re-queued -- take as an indication to stop sending + * more pending frames. + */  static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,  				     struct sk_buff *skb)  { @@ -2039,20 +2248,29 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,  	struct ieee80211_sub_if_data *sdata;  	struct sta_info *sta;  	struct ieee80211_hdr *hdr; -	int ret; -	bool result = true; +	bool result; +	struct ieee80211_chanctx_conf *chanctx_conf;  	sdata = vif_to_sdata(info->control.vif);  	if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) { -		ieee80211_tx(sdata, skb, true); +		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +		if (unlikely(!chanctx_conf)) { +			dev_kfree_skb(skb); +			return true; +		} +		result = ieee80211_tx(sdata, skb, true, +				      chanctx_conf->def.chan->band);  	} else { +		struct sk_buff_head skbs; + +		__skb_queue_head_init(&skbs); +		__skb_queue_tail(&skbs, skb); +  		hdr = (struct ieee80211_hdr *)skb->data;  		sta = sta_info_get(sdata, hdr->addr1); -		ret = __ieee80211_tx(local, &skb, sta, true); -		if (ret != IEEE80211_TX_OK) -			result = false; +		result = __ieee80211_tx(local, &skbs, skb->len, sta, true);  	}  	return result; @@ -2064,7 +2282,6 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,  void ieee80211_tx_pending(unsigned long data)  {  	struct ieee80211_local *local = (struct ieee80211_local *)data; -	struct ieee80211_sub_if_data *sdata;  	unsigned long flags;  	int i;  	bool txok; @@ -2086,7 +2303,7 @@ void ieee80211_tx_pending(unsigned long data)  			struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);  			if (WARN_ON(!info->control.vif)) { -				kfree_skb(skb); +				ieee80211_free_txskb(&local->hw, skb);  				continue;  			} @@ -2094,8 +2311,6 @@ void ieee80211_tx_pending(unsigned long data)  						flags);  			txok = ieee80211_tx_pending_skb(local, skb); -			if (!txok) -				__skb_queue_head(&local->pending[i], skb);  			spin_lock_irqsave(&local->queue_stop_reason_lock,  					  flags);  			if (!txok) @@ -2103,8 +2318,7 @@ void ieee80211_tx_pending(unsigned long data)  		}  		if (skb_queue_empty(&local->pending[i])) -			list_for_each_entry_rcu(sdata, &local->interfaces, list) -				netif_wake_subqueue(sdata->dev, i); +			ieee80211_propagate_queue_wake(local, i);  	}  	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); @@ -2113,9 +2327,9 @@ void ieee80211_tx_pending(unsigned long data)  /* functions for drivers to get certain frames */ -static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss, -				     struct sk_buff *skb, -				     struct beacon_data *beacon) +static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, +				       struct ps_data *ps, struct sk_buff *skb, +				       bool is_template)  {  	u8 *pos, *tim;  	int aid0 = 0; @@ -2123,40 +2337,43 @@ static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss,  	/* Generate bitmap for TIM only if there are any STAs in power save  	 * mode. */ -	if (atomic_read(&bss->num_sta_ps) > 0) +	if (atomic_read(&ps->num_sta_ps) > 0)  		/* in the hope that this is faster than  		 * checking byte-for-byte */ -		have_bits = !bitmap_empty((unsigned long*)bss->tim, +		have_bits = !bitmap_empty((unsigned long *)ps->tim,  					  IEEE80211_MAX_AID+1); - -	if (bss->dtim_count == 0) -		bss->dtim_count = beacon->dtim_period - 1; -	else -		bss->dtim_count--; +	if (!is_template) { +		if (ps->dtim_count == 0) +			ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1; +		else +			ps->dtim_count--; +	}  	tim = pos = (u8 *) skb_put(skb, 6);  	*pos++ = WLAN_EID_TIM;  	*pos++ = 4; -	*pos++ = bss->dtim_count; -	*pos++ = beacon->dtim_period; +	*pos++ = ps->dtim_count; +	*pos++ = sdata->vif.bss_conf.dtim_period; -	if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf)) +	if (ps->dtim_count == 0 && !skb_queue_empty(&ps->bc_buf))  		aid0 = 1; +	ps->dtim_bc_mc = aid0 == 1; +  	if (have_bits) {  		/* Find largest even number N1 so that bits numbered 1 through  		 * (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits  		 * (N2 + 1) x 8 through 2007 are 0. */  		n1 = 0;  		for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) { -			if (bss->tim[i]) { +			if (ps->tim[i]) {  				n1 = i & 0xfe;  				break;  			}  		}  		n2 = n1;  		for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) { -			if (bss->tim[i]) { +			if (ps->tim[i]) {  				n2 = i;  				break;  			} @@ -2165,52 +2382,211 @@ static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss,  		/* Bitmap control */  		*pos++ = n1 | aid0;  		/* Part Virt Bitmap */ -		memcpy(pos, bss->tim + n1, n2 - n1 + 1); +		skb_put(skb, n2 - n1); +		memcpy(pos, ps->tim + n1, n2 - n1 + 1);  		tim[1] = n2 - n1 + 4; -		skb_put(skb, n2 - n1);  	} else {  		*pos++ = aid0; /* Bitmap control */  		*pos++ = 0; /* Part Virt Bitmap */  	}  } -struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, -					 struct ieee80211_vif *vif, -					 u16 *tim_offset, u16 *tim_length) +static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, +				    struct ps_data *ps, struct sk_buff *skb, +				    bool is_template) +{ +	struct ieee80211_local *local = sdata->local; + +	/* +	 * Not very nice, but we want to allow the driver to call +	 * ieee80211_beacon_get() as a response to the set_tim() +	 * callback. That, however, is already invoked under the +	 * sta_lock to guarantee consistent and race-free update +	 * of the tim bitmap in mac80211 and the driver. +	 */ +	if (local->tim_in_locked_section) { +		__ieee80211_beacon_add_tim(sdata, ps, skb, is_template); +	} else { +		spin_lock_bh(&local->tim_lock); +		__ieee80211_beacon_add_tim(sdata, ps, skb, is_template); +		spin_unlock_bh(&local->tim_lock); +	} + +	return 0; +} + +static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata, +			      struct beacon_data *beacon) +{ +	struct probe_resp *resp; +	u8 *beacon_data; +	size_t beacon_data_len; +	int i; +	u8 count = sdata->csa_current_counter; + +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_AP: +		beacon_data = beacon->tail; +		beacon_data_len = beacon->tail_len; +		break; +	case NL80211_IFTYPE_ADHOC: +		beacon_data = beacon->head; +		beacon_data_len = beacon->head_len; +		break; +	case NL80211_IFTYPE_MESH_POINT: +		beacon_data = beacon->head; +		beacon_data_len = beacon->head_len; +		break; +	default: +		return; +	} + +	for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; ++i) { +		u16 counter_offset_beacon = +			sdata->csa_counter_offset_beacon[i]; +		u16 counter_offset_presp = sdata->csa_counter_offset_presp[i]; + +		if (counter_offset_beacon) { +			if (WARN_ON(counter_offset_beacon >= beacon_data_len)) +				return; + +			beacon_data[counter_offset_beacon] = count; +		} + +		if (sdata->vif.type == NL80211_IFTYPE_AP && +		    counter_offset_presp) { +			rcu_read_lock(); +			resp = rcu_dereference(sdata->u.ap.probe_resp); + +			/* If nl80211 accepted the offset, this should +			 * not happen. +			 */ +			if (WARN_ON(!resp)) { +				rcu_read_unlock(); +				return; +			} +			resp->data[counter_offset_presp] = count; +			rcu_read_unlock(); +		} +	} +} + +u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + +	sdata->csa_current_counter--; + +	/* the counter should never reach 0 */ +	WARN_ON(!sdata->csa_current_counter); + +	return sdata->csa_current_counter; +} +EXPORT_SYMBOL(ieee80211_csa_update_counter); + +bool ieee80211_csa_is_complete(struct ieee80211_vif *vif) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); +	struct beacon_data *beacon = NULL; +	u8 *beacon_data; +	size_t beacon_data_len; +	int counter_beacon = sdata->csa_counter_offset_beacon[0]; +	int ret = false; + +	if (!ieee80211_sdata_running(sdata)) +		return false; + +	rcu_read_lock(); +	if (vif->type == NL80211_IFTYPE_AP) { +		struct ieee80211_if_ap *ap = &sdata->u.ap; + +		beacon = rcu_dereference(ap->beacon); +		if (WARN_ON(!beacon || !beacon->tail)) +			goto out; +		beacon_data = beacon->tail; +		beacon_data_len = beacon->tail_len; +	} else if (vif->type == NL80211_IFTYPE_ADHOC) { +		struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + +		beacon = rcu_dereference(ifibss->presp); +		if (!beacon) +			goto out; + +		beacon_data = beacon->head; +		beacon_data_len = beacon->head_len; +	} else if (vif->type == NL80211_IFTYPE_MESH_POINT) { +		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + +		beacon = rcu_dereference(ifmsh->beacon); +		if (!beacon) +			goto out; + +		beacon_data = beacon->head; +		beacon_data_len = beacon->head_len; +	} else { +		WARN_ON(1); +		goto out; +	} + +	if (WARN_ON(counter_beacon > beacon_data_len)) +		goto out; + +	if (beacon_data[counter_beacon] == 1) +		ret = true; + out: +	rcu_read_unlock(); + +	return ret; +} +EXPORT_SYMBOL(ieee80211_csa_is_complete); + +static struct sk_buff * +__ieee80211_beacon_get(struct ieee80211_hw *hw, +		       struct ieee80211_vif *vif, +		       struct ieee80211_mutable_offsets *offs, +		       bool is_template)  {  	struct ieee80211_local *local = hw_to_local(hw);  	struct sk_buff *skb = NULL;  	struct ieee80211_tx_info *info;  	struct ieee80211_sub_if_data *sdata = NULL; -	struct ieee80211_if_ap *ap = NULL; -	struct beacon_data *beacon; -	struct ieee80211_supported_band *sband; -	enum ieee80211_band band = local->hw.conf.channel->band; +	enum ieee80211_band band;  	struct ieee80211_tx_rate_control txrc; - -	sband = local->hw.wiphy->bands[band]; +	struct ieee80211_chanctx_conf *chanctx_conf; +	int csa_off_base = 0;  	rcu_read_lock();  	sdata = vif_to_sdata(vif); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); -	if (tim_offset) -		*tim_offset = 0; -	if (tim_length) -		*tim_length = 0; +	if (!ieee80211_sdata_running(sdata) || !chanctx_conf) +		goto out; + +	if (offs) +		memset(offs, 0, sizeof(*offs));  	if (sdata->vif.type == NL80211_IFTYPE_AP) { -		ap = &sdata->u.ap; -		beacon = rcu_dereference(ap->beacon); -		if (ap && beacon) { +		struct ieee80211_if_ap *ap = &sdata->u.ap; +		struct beacon_data *beacon = rcu_dereference(ap->beacon); + +		if (beacon) { +			if (sdata->vif.csa_active) { +				if (!is_template) +					ieee80211_csa_update_counter(vif); + +				ieee80211_set_csa(sdata, beacon); +			} +  			/*  			 * headroom, head length,  			 * tail length and maximum TIM length  			 */  			skb = dev_alloc_skb(local->tx_headroom +  					    beacon->head_len + -					    beacon->tail_len + 256); +					    beacon->tail_len + 256 + +					    local->hw.extra_beacon_tailroom);  			if (!skb)  				goto out; @@ -2218,27 +2594,16 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  			memcpy(skb_put(skb, beacon->head_len), beacon->head,  			       beacon->head_len); -			/* -			 * Not very nice, but we want to allow the driver to call -			 * ieee80211_beacon_get() as a response to the set_tim() -			 * callback. That, however, is already invoked under the -			 * sta_lock to guarantee consistent and race-free update -			 * of the tim bitmap in mac80211 and the driver. -			 */ -			if (local->tim_in_locked_section) { -				ieee80211_beacon_add_tim(ap, skb, beacon); -			} else { -				unsigned long flags; +			ieee80211_beacon_add_tim(sdata, &ap->ps, skb, +						 is_template); -				spin_lock_irqsave(&local->sta_lock, flags); -				ieee80211_beacon_add_tim(ap, skb, beacon); -				spin_unlock_irqrestore(&local->sta_lock, flags); -			} +			if (offs) { +				offs->tim_offset = beacon->head_len; +				offs->tim_length = skb->len - beacon->head_len; -			if (tim_offset) -				*tim_offset = beacon->head_len; -			if (tim_length) -				*tim_length = skb->len - beacon->head_len; +				/* for AP the csa offsets are from tail */ +				csa_off_base = skb->len; +			}  			if (beacon->tail)  				memcpy(skb_put(skb, beacon->tail_len), @@ -2248,50 +2613,89 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  	} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {  		struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;  		struct ieee80211_hdr *hdr; -		struct sk_buff *presp = rcu_dereference(ifibss->presp); +		struct beacon_data *presp = rcu_dereference(ifibss->presp);  		if (!presp)  			goto out; -		skb = skb_copy(presp, GFP_ATOMIC); +		if (sdata->vif.csa_active) { +			if (!is_template) +				ieee80211_csa_update_counter(vif); + +			ieee80211_set_csa(sdata, presp); +		} + +		skb = dev_alloc_skb(local->tx_headroom + presp->head_len + +				    local->hw.extra_beacon_tailroom);  		if (!skb)  			goto out; +		skb_reserve(skb, local->tx_headroom); +		memcpy(skb_put(skb, presp->head_len), presp->head, +		       presp->head_len);  		hdr = (struct ieee80211_hdr *) skb->data;  		hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |  						 IEEE80211_STYPE_BEACON);  	} else if (ieee80211_vif_is_mesh(&sdata->vif)) { -		struct ieee80211_mgmt *mgmt; -		u8 *pos; +		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +		struct beacon_data *bcn = rcu_dereference(ifmsh->beacon); + +		if (!bcn) +			goto out; + +		if (sdata->vif.csa_active) { +			if (!is_template) +				/* TODO: For mesh csa_counter is in TU, so +				 * decrementing it by one isn't correct, but +				 * for now we leave it consistent with overall +				 * mac80211's behavior. +				 */ +				ieee80211_csa_update_counter(vif); -		/* headroom, head length, tail length and maximum TIM length */ -		skb = dev_alloc_skb(local->tx_headroom + 400); +			ieee80211_set_csa(sdata, bcn); +		} + +		if (ifmsh->sync_ops) +			ifmsh->sync_ops->adjust_tbtt(sdata, bcn); + +		skb = dev_alloc_skb(local->tx_headroom + +				    bcn->head_len + +				    256 + /* TIM IE */ +				    bcn->tail_len + +				    local->hw.extra_beacon_tailroom);  		if (!skb)  			goto out; +		skb_reserve(skb, local->tx_headroom); +		memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len); +		ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb, is_template); -		skb_reserve(skb, local->hw.extra_tx_headroom); -		mgmt = (struct ieee80211_mgmt *) -			skb_put(skb, 24 + sizeof(mgmt->u.beacon)); -		memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); -		mgmt->frame_control = -		    cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); -		memset(mgmt->da, 0xff, ETH_ALEN); -		memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); -		memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); -		mgmt->u.beacon.beacon_int = -			cpu_to_le16(sdata->vif.bss_conf.beacon_int); -		mgmt->u.beacon.capab_info = 0x0; /* 0x0 for MPs */ - -		pos = skb_put(skb, 2); -		*pos++ = WLAN_EID_SSID; -		*pos++ = 0x0; - -		mesh_mgmt_ies_add(skb, sdata); +		if (offs) { +			offs->tim_offset = bcn->head_len; +			offs->tim_length = skb->len - bcn->head_len; +		} + +		memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len);  	} else {  		WARN_ON(1);  		goto out;  	} +	/* CSA offsets */ +	if (offs) { +		int i; + +		for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; i++) { +			u16 csa_off = sdata->csa_counter_offset_beacon[i]; + +			if (!csa_off) +				continue; + +			offs->csa_counter_offs[i] = csa_off_base + csa_off; +		} +	} + +	band = chanctx_conf->def.chan->band; +  	info = IEEE80211_SKB_CB(skb);  	info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; @@ -2300,12 +2704,12 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,  	memset(&txrc, 0, sizeof(txrc));  	txrc.hw = hw; -	txrc.sband = sband; +	txrc.sband = local->hw.wiphy->bands[band];  	txrc.bss_conf = &sdata->vif.bss_conf;  	txrc.skb = skb;  	txrc.reported_rate.idx = -1;  	txrc.rate_idx_mask = sdata->rc_rateidx_mask[band]; -	if (txrc.rate_idx_mask == (1 << sband->n_bitrates) - 1) +	if (txrc.rate_idx_mask == (1 << txrc.sband->n_bitrates) - 1)  		txrc.max_rate_idx = -1;  	else  		txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; @@ -2320,9 +2724,69 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,   out:  	rcu_read_unlock();  	return skb; + +} + +struct sk_buff * +ieee80211_beacon_get_template(struct ieee80211_hw *hw, +			      struct ieee80211_vif *vif, +			      struct ieee80211_mutable_offsets *offs) +{ +	return __ieee80211_beacon_get(hw, vif, offs, true); +} +EXPORT_SYMBOL(ieee80211_beacon_get_template); + +struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, +					 struct ieee80211_vif *vif, +					 u16 *tim_offset, u16 *tim_length) +{ +	struct ieee80211_mutable_offsets offs = {}; +	struct sk_buff *bcn = __ieee80211_beacon_get(hw, vif, &offs, false); + +	if (tim_offset) +		*tim_offset = offs.tim_offset; + +	if (tim_length) +		*tim_length = offs.tim_length; + +	return bcn;  }  EXPORT_SYMBOL(ieee80211_beacon_get_tim); +struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw, +					struct ieee80211_vif *vif) +{ +	struct ieee80211_if_ap *ap = NULL; +	struct sk_buff *skb = NULL; +	struct probe_resp *presp = NULL; +	struct ieee80211_hdr *hdr; +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + +	if (sdata->vif.type != NL80211_IFTYPE_AP) +		return NULL; + +	rcu_read_lock(); + +	ap = &sdata->u.ap; +	presp = rcu_dereference(ap->probe_resp); +	if (!presp) +		goto out; + +	skb = dev_alloc_skb(presp->len); +	if (!skb) +		goto out; + +	memcpy(skb_put(skb, presp->len), presp->data, presp->len); + +	hdr = (struct ieee80211_hdr *) skb->data; +	memset(hdr->addr1, 0, sizeof(hdr->addr1)); + +out: +	rcu_read_unlock(); +	return skb; +} +EXPORT_SYMBOL(ieee80211_proberesp_get); +  struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw,  				     struct ieee80211_vif *vif)  { @@ -2340,11 +2804,9 @@ struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw,  	local = sdata->local;  	skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*pspoll)); -	if (!skb) { -		printk(KERN_DEBUG "%s: failed to allocate buffer for " -		       "pspoll template\n", sdata->name); +	if (!skb)  		return NULL; -	} +  	skb_reserve(skb, local->hw.extra_tx_headroom);  	pspoll = (struct ieee80211_pspoll *) skb_put(skb, sizeof(*pspoll)); @@ -2380,11 +2842,9 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,  	local = sdata->local;  	skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*nullfunc)); -	if (!skb) { -		printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc " -		       "template\n", sdata->name); +	if (!skb)  		return NULL; -	} +  	skb_reserve(skb, local->hw.extra_tx_headroom);  	nullfunc = (struct ieee80211_hdr_3addr *) skb_put(skb, @@ -2404,7 +2864,7 @@ EXPORT_SYMBOL(ieee80211_nullfunc_get);  struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,  				       struct ieee80211_vif *vif,  				       const u8 *ssid, size_t ssid_len, -				       const u8 *ie, size_t ie_len) +				       size_t tailroom)  {  	struct ieee80211_sub_if_data *sdata;  	struct ieee80211_local *local; @@ -2418,12 +2878,9 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,  	ie_ssid_len = 2 + ssid_len;  	skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) + -			    ie_ssid_len + ie_len); -	if (!skb) { -		printk(KERN_DEBUG "%s: failed to allocate buffer for probe " -		       "request template\n", sdata->name); +			    ie_ssid_len + tailroom); +	if (!skb)  		return NULL; -	}  	skb_reserve(skb, local->hw.extra_tx_headroom); @@ -2431,22 +2888,17 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,  	memset(hdr, 0, sizeof(*hdr));  	hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |  					 IEEE80211_STYPE_PROBE_REQ); -	memset(hdr->addr1, 0xff, ETH_ALEN); +	eth_broadcast_addr(hdr->addr1);  	memcpy(hdr->addr2, vif->addr, ETH_ALEN); -	memset(hdr->addr3, 0xff, ETH_ALEN); +	eth_broadcast_addr(hdr->addr3);  	pos = skb_put(skb, ie_ssid_len);  	*pos++ = WLAN_EID_SSID;  	*pos++ = ssid_len; -	if (ssid) +	if (ssid_len)  		memcpy(pos, ssid, ssid_len);  	pos += ssid_len; -	if (ie) { -		pos = skb_put(skb, ie_len); -		memcpy(pos, ie, ie_len); -	} -  	return skb;  }  EXPORT_SYMBOL(ieee80211_probereq_get); @@ -2488,32 +2940,44 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,  {  	struct ieee80211_local *local = hw_to_local(hw);  	struct sk_buff *skb = NULL; -	struct sta_info *sta;  	struct ieee80211_tx_data tx;  	struct ieee80211_sub_if_data *sdata; -	struct ieee80211_if_ap *bss = NULL; -	struct beacon_data *beacon; +	struct ps_data *ps;  	struct ieee80211_tx_info *info; +	struct ieee80211_chanctx_conf *chanctx_conf;  	sdata = vif_to_sdata(vif); -	bss = &sdata->u.ap;  	rcu_read_lock(); -	beacon = rcu_dereference(bss->beacon); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + +	if (!chanctx_conf) +		goto out; + +	if (sdata->vif.type == NL80211_IFTYPE_AP) { +		struct beacon_data *beacon = +				rcu_dereference(sdata->u.ap.beacon); + +		if (!beacon || !beacon->head) +			goto out; -	if (sdata->vif.type != NL80211_IFTYPE_AP || !beacon || !beacon->head) +		ps = &sdata->u.ap.ps; +	} else if (ieee80211_vif_is_mesh(&sdata->vif)) { +		ps = &sdata->u.mesh.ps; +	} else {  		goto out; +	} -	if (bss->dtim_count != 0) +	if (ps->dtim_count != 0 || !ps->dtim_bc_mc)  		goto out; /* send buffered bc/mc only after DTIM beacon */  	while (1) { -		skb = skb_dequeue(&bss->ps_bc_buf); +		skb = skb_dequeue(&ps->bc_buf);  		if (!skb)  			goto out;  		local->total_ps_buffered--; -		if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) { +		if (!skb_queue_empty(&ps->bc_buf) && skb->len >= 2) {  			struct ieee80211_hdr *hdr =  				(struct ieee80211_hdr *) skb->data;  			/* more buffered multicast/broadcast frames ==> set @@ -2523,6 +2987,8 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,  				cpu_to_le16(IEEE80211_FCTL_MOREDATA);  		} +		if (sdata->vif.type == NL80211_IFTYPE_AP) +			sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);  		if (!ieee80211_tx_prepare(sdata, &tx, skb))  			break;  		dev_kfree_skb_any(skb); @@ -2530,10 +2996,8 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,  	info = IEEE80211_SKB_CB(skb); -	sta = tx.sta;  	tx.flags |= IEEE80211_TX_PS_BUFFERED; -	tx.channel = local->hw.conf.channel; -	info->band = tx.channel->band; +	info->band = chanctx_conf->def.chan->band;  	if (invoke_tx_handlers(&tx))  		skb = NULL; @@ -2544,14 +3008,20 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,  }  EXPORT_SYMBOL(ieee80211_get_buffered_bc); -void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) +void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, +				 struct sk_buff *skb, int tid, +				 enum ieee80211_band band)  { +	int ac = ieee802_1d_to_ac[tid & 7]; +  	skb_set_mac_header(skb, 0);  	skb_set_network_header(skb, 0);  	skb_set_transport_header(skb, 0); -	/* send all internal mgmt frames on VO */ -	skb_set_queue_mapping(skb, 0); +	skb_set_queue_mapping(skb, ac); +	skb->priority = tid; + +	skb->dev = sdata->dev;  	/*  	 * The other path calling ieee80211_xmit is from the tasklet, @@ -2559,6 +3029,6 @@ void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)  	 * requirements are that we do not come into tx with bhs on.  	 */  	local_bh_disable(); -	ieee80211_xmit(sdata, skb); +	ieee80211_xmit(sdata, skb, band);  	local_bh_enable();  } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e497476174c..a6cda52ed92 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -13,6 +13,7 @@  #include <net/mac80211.h>  #include <linux/netdevice.h> +#include <linux/export.h>  #include <linux/types.h>  #include <linux/slab.h>  #include <linux/skbuff.h> @@ -33,7 +34,7 @@  #include "wep.h"  /* privid for wiphys to determine whether they belong to us or not */ -void *mac80211_wiphy_privid = &mac80211_wiphy_privid; +const void *const mac80211_wiphy_privid = &mac80211_wiphy_privid;  struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)  { @@ -75,7 +76,7 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,  	}  	if (ieee80211_is_ctl(fc)) { -		if(ieee80211_is_pspoll(fc)) +		if (ieee80211_is_pspoll(fc))  			return hdr->addr1;  		if (ieee80211_is_back_req(fc)) { @@ -96,17 +97,18 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,  void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)  { -	struct sk_buff *skb = tx->skb; +	struct sk_buff *skb;  	struct ieee80211_hdr *hdr; -	do { +	skb_queue_walk(&tx->skbs, skb) {  		hdr = (struct ieee80211_hdr *) skb->data;  		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); -	} while ((skb = skb->next)); +	}  } -int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, -			     int rate, int erp, int short_preamble) +int ieee80211_frame_duration(enum ieee80211_band band, size_t len, +			     int rate, int erp, int short_preamble, +			     int shift)  {  	int dur; @@ -117,9 +119,12 @@ int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,  	 *  	 * rate is in 100 kbps, so divident is multiplied by 10 in the  	 * DIV_ROUND_UP() operations. +	 * +	 * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and +	 * is assumed to be 0 otherwise.  	 */ -	if (local->hw.conf.channel->band == IEEE80211_BAND_5GHZ || erp) { +	if (band == IEEE80211_BAND_5GHZ || erp) {  		/*  		 * OFDM:  		 * @@ -129,13 +134,23 @@ int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,  		 * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext  		 *  		 * T_SYM = 4 usec -		 * 802.11a - 17.5.2: aSIFSTime = 16 usec +		 * 802.11a - 18.5.2: aSIFSTime = 16 usec  		 * 802.11g - 19.8.4: aSIFSTime = 10 usec +  		 *	signal ext = 6 usec  		 */  		dur = 16; /* SIFS + signal ext */ -		dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */ -		dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */ +		dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */ +		dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */ + +		/* IEEE 802.11-2012 18.3.2.4: all values above are: +		 *  * times 4 for 5 MHz +		 *  * times 2 for 10 MHz +		 */ +		dur *= 1 << shift; + +		/* rates should already consider the channel bandwidth, +		 * don't apply divisor again. +		 */  		dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,  					4 * rate); /* T_SYM x N_SYM */  	} else { @@ -161,13 +176,13 @@ int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,  /* Exported duration function for driver use */  __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,  					struct ieee80211_vif *vif, +					enum ieee80211_band band,  					size_t frame_len,  					struct ieee80211_rate *rate)  { -	struct ieee80211_local *local = hw_to_local(hw);  	struct ieee80211_sub_if_data *sdata;  	u16 dur; -	int erp; +	int erp, shift = 0;  	bool short_preamble = false;  	erp = 0; @@ -176,10 +191,11 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,  		short_preamble = sdata->vif.bss_conf.use_short_preamble;  		if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)  			erp = rate->flags & IEEE80211_RATE_ERP_G; +		shift = ieee80211_vif_get_shift(vif);  	} -	dur = ieee80211_frame_duration(local, frame_len, rate->bitrate, erp, -				       short_preamble); +	dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp, +				       short_preamble, shift);  	return cpu_to_le16(dur);  } @@ -193,11 +209,11 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,  	struct ieee80211_rate *rate;  	struct ieee80211_sub_if_data *sdata;  	bool short_preamble; -	int erp; +	int erp, shift = 0, bitrate;  	u16 dur;  	struct ieee80211_supported_band *sband; -	sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; +	sband = local->hw.wiphy->bands[frame_txctl->band];  	short_preamble = false; @@ -209,17 +225,20 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,  		short_preamble = sdata->vif.bss_conf.use_short_preamble;  		if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)  			erp = rate->flags & IEEE80211_RATE_ERP_G; +		shift = ieee80211_vif_get_shift(vif);  	} +	bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift); +  	/* CTS duration */ -	dur = ieee80211_frame_duration(local, 10, rate->bitrate, -				       erp, short_preamble); +	dur = ieee80211_frame_duration(sband->band, 10, bitrate, +				       erp, short_preamble, shift);  	/* Data frame duration */ -	dur += ieee80211_frame_duration(local, frame_len, rate->bitrate, -					erp, short_preamble); +	dur += ieee80211_frame_duration(sband->band, frame_len, bitrate, +					erp, short_preamble, shift);  	/* ACK duration */ -	dur += ieee80211_frame_duration(local, 10, rate->bitrate, -					erp, short_preamble); +	dur += ieee80211_frame_duration(sband->band, 10, bitrate, +					erp, short_preamble, shift);  	return cpu_to_le16(dur);  } @@ -234,11 +253,11 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,  	struct ieee80211_rate *rate;  	struct ieee80211_sub_if_data *sdata;  	bool short_preamble; -	int erp; +	int erp, shift = 0, bitrate;  	u16 dur;  	struct ieee80211_supported_band *sband; -	sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; +	sband = local->hw.wiphy->bands[frame_txctl->band];  	short_preamble = false; @@ -249,32 +268,67 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,  		short_preamble = sdata->vif.bss_conf.use_short_preamble;  		if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)  			erp = rate->flags & IEEE80211_RATE_ERP_G; +		shift = ieee80211_vif_get_shift(vif);  	} +	bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift); +  	/* Data frame duration */ -	dur = ieee80211_frame_duration(local, frame_len, rate->bitrate, -				       erp, short_preamble); +	dur = ieee80211_frame_duration(sband->band, frame_len, bitrate, +				       erp, short_preamble, shift);  	if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) {  		/* ACK duration */ -		dur += ieee80211_frame_duration(local, 10, rate->bitrate, -						erp, short_preamble); +		dur += ieee80211_frame_duration(sband->band, 10, bitrate, +						erp, short_preamble, shift);  	}  	return cpu_to_le16(dur);  }  EXPORT_SYMBOL(ieee80211_ctstoself_duration); +void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) +{ +	struct ieee80211_sub_if_data *sdata; +	int n_acs = IEEE80211_NUM_ACS; + +	if (local->hw.queues < IEEE80211_NUM_ACS) +		n_acs = 1; + +	list_for_each_entry_rcu(sdata, &local->interfaces, list) { +		int ac; + +		if (!sdata->dev) +			continue; + +		if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE && +		    local->queue_stop_reasons[sdata->vif.cab_queue] != 0) +			continue; + +		for (ac = 0; ac < n_acs; ac++) { +			int ac_queue = sdata->vif.hw_queue[ac]; + +			if (ac_queue == queue || +			    (sdata->vif.cab_queue == queue && +			     local->queue_stop_reasons[ac_queue] == 0 && +			     skb_queue_empty(&local->pending[ac_queue]))) +				netif_wake_subqueue(sdata->dev, ac); +		} +	} +} +  static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,  				   enum queue_stop_reason reason)  {  	struct ieee80211_local *local = hw_to_local(hw); -	struct ieee80211_sub_if_data *sdata;  	trace_wake_queue(local, queue, reason);  	if (WARN_ON(queue >= hw->queues))  		return; +	if (!test_bit(reason, &local->queue_stop_reasons[queue])) +		return; +  	__clear_bit(reason, &local->queue_stop_reasons[queue]);  	if (local->queue_stop_reasons[queue] != 0) @@ -283,11 +337,7 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,  	if (skb_queue_empty(&local->pending[queue])) {  		rcu_read_lock(); -		list_for_each_entry_rcu(sdata, &local->interfaces, list) { -			if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) -				continue; -			netif_wake_subqueue(sdata->dev, queue); -		} +		ieee80211_propagate_queue_wake(local, queue);  		rcu_read_unlock();  	} else  		tasklet_schedule(&local->tx_pending_tasklet); @@ -316,17 +366,34 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,  {  	struct ieee80211_local *local = hw_to_local(hw);  	struct ieee80211_sub_if_data *sdata; +	int n_acs = IEEE80211_NUM_ACS;  	trace_stop_queue(local, queue, reason);  	if (WARN_ON(queue >= hw->queues))  		return; +	if (test_bit(reason, &local->queue_stop_reasons[queue])) +		return; +  	__set_bit(reason, &local->queue_stop_reasons[queue]); +	if (local->hw.queues < IEEE80211_NUM_ACS) +		n_acs = 1; +  	rcu_read_lock(); -	list_for_each_entry_rcu(sdata, &local->interfaces, list) -		netif_stop_subqueue(sdata->dev, queue); +	list_for_each_entry_rcu(sdata, &local->interfaces, list) { +		int ac; + +		if (!sdata->dev) +			continue; + +		for (ac = 0; ac < n_acs; ac++) { +			if (sdata->vif.hw_queue[ac] == queue || +			    sdata->vif.cab_queue == queue) +				netif_stop_subqueue(sdata->dev, ac); +		} +	}  	rcu_read_unlock();  } @@ -353,11 +420,11 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local,  {  	struct ieee80211_hw *hw = &local->hw;  	unsigned long flags; -	int queue = skb_get_queue_mapping(skb);  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	int queue = info->hw_queue;  	if (WARN_ON(!info->control.vif)) { -		kfree_skb(skb); +		ieee80211_free_txskb(&local->hw, skb);  		return;  	} @@ -368,52 +435,40 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local,  	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);  } -int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, -				  struct sk_buff_head *skbs, -				  void (*fn)(void *data), void *data) +void ieee80211_add_pending_skbs(struct ieee80211_local *local, +				struct sk_buff_head *skbs)  {  	struct ieee80211_hw *hw = &local->hw;  	struct sk_buff *skb;  	unsigned long flags; -	int queue, ret = 0, i; +	int queue, i;  	spin_lock_irqsave(&local->queue_stop_reason_lock, flags); -	for (i = 0; i < hw->queues; i++) -		__ieee80211_stop_queue(hw, i, -			IEEE80211_QUEUE_STOP_REASON_SKB_ADD); -  	while ((skb = skb_dequeue(skbs))) {  		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);  		if (WARN_ON(!info->control.vif)) { -			kfree_skb(skb); +			ieee80211_free_txskb(&local->hw, skb);  			continue;  		} -		ret++; -		queue = skb_get_queue_mapping(skb); +		queue = info->hw_queue; + +		__ieee80211_stop_queue(hw, queue, +				IEEE80211_QUEUE_STOP_REASON_SKB_ADD); +  		__skb_queue_tail(&local->pending[queue], skb);  	} -	if (fn) -		fn(data); -  	for (i = 0; i < hw->queues; i++)  		__ieee80211_wake_queue(hw, i,  			IEEE80211_QUEUE_STOP_REASON_SKB_ADD);  	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - -	return ret; -} - -int ieee80211_add_pending_skbs(struct ieee80211_local *local, -			       struct sk_buff_head *skbs) -{ -	return ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL);  }  void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, -				    enum queue_stop_reason reason) +				     unsigned long queues, +				     enum queue_stop_reason reason)  {  	struct ieee80211_local *local = hw_to_local(hw);  	unsigned long flags; @@ -421,7 +476,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,  	spin_lock_irqsave(&local->queue_stop_reason_lock, flags); -	for (i = 0; i < hw->queues; i++) +	for_each_set_bit(i, &queues, hw->queues)  		__ieee80211_stop_queue(hw, i, reason);  	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); @@ -429,7 +484,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,  void ieee80211_stop_queues(struct ieee80211_hw *hw)  { -	ieee80211_stop_queues_by_reason(hw, +	ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_DRIVER);  }  EXPORT_SYMBOL(ieee80211_stop_queues); @@ -444,13 +499,15 @@ int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)  		return true;  	spin_lock_irqsave(&local->queue_stop_reason_lock, flags); -	ret = !!local->queue_stop_reasons[queue]; +	ret = test_bit(IEEE80211_QUEUE_STOP_REASON_DRIVER, +		       &local->queue_stop_reasons[queue]);  	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);  	return ret;  }  EXPORT_SYMBOL(ieee80211_queue_stopped);  void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, +				     unsigned long queues,  				     enum queue_stop_reason reason)  {  	struct ieee80211_local *local = hw_to_local(hw); @@ -459,7 +516,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,  	spin_lock_irqsave(&local->queue_stop_reason_lock, flags); -	for (i = 0; i < hw->queues; i++) +	for_each_set_bit(i, &queues, hw->queues)  		__ieee80211_wake_queue(hw, i, reason);  	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); @@ -467,66 +524,131 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,  void ieee80211_wake_queues(struct ieee80211_hw *hw)  { -	ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_DRIVER); +	ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_DRIVER);  }  EXPORT_SYMBOL(ieee80211_wake_queues); -void ieee80211_iterate_active_interfaces( -	struct ieee80211_hw *hw, -	void (*iterator)(void *data, u8 *mac, -			 struct ieee80211_vif *vif), -	void *data) +void ieee80211_flush_queues(struct ieee80211_local *local, +			    struct ieee80211_sub_if_data *sdata)  { -	struct ieee80211_local *local = hw_to_local(hw); -	struct ieee80211_sub_if_data *sdata; +	u32 queues; -	mutex_lock(&local->iflist_mtx); +	if (!local->ops->flush) +		return; -	list_for_each_entry(sdata, &local->interfaces, list) { +	if (sdata && local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) { +		int ac; + +		queues = 0; + +		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) +			queues |= BIT(sdata->vif.hw_queue[ac]); +		if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE) +			queues |= BIT(sdata->vif.cab_queue); +	} else { +		/* all queues */ +		queues = BIT(local->hw.queues) - 1; +	} + +	ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_FLUSH); + +	drv_flush(local, sdata, queues, false); + +	ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_FLUSH); +} + +static void __iterate_active_interfaces(struct ieee80211_local *local, +					u32 iter_flags, +					void (*iterator)(void *data, u8 *mac, +						struct ieee80211_vif *vif), +					void *data) +{ +	struct ieee80211_sub_if_data *sdata; + +	list_for_each_entry_rcu(sdata, &local->interfaces, list) {  		switch (sdata->vif.type) {  		case NL80211_IFTYPE_MONITOR: +			if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) +				continue; +			break;  		case NL80211_IFTYPE_AP_VLAN:  			continue;  		default:  			break;  		} +		if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && +		    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) +			continue;  		if (ieee80211_sdata_running(sdata))  			iterator(data, sdata->vif.addr,  				 &sdata->vif);  	} +	sdata = rcu_dereference_check(local->monitor_sdata, +				      lockdep_is_held(&local->iflist_mtx) || +				      lockdep_rtnl_is_held()); +	if (sdata && +	    (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || +	     sdata->flags & IEEE80211_SDATA_IN_DRIVER)) +		iterator(data, sdata->vif.addr, &sdata->vif); +} + +void ieee80211_iterate_active_interfaces( +	struct ieee80211_hw *hw, u32 iter_flags, +	void (*iterator)(void *data, u8 *mac, +			 struct ieee80211_vif *vif), +	void *data) +{ +	struct ieee80211_local *local = hw_to_local(hw); + +	mutex_lock(&local->iflist_mtx); +	__iterate_active_interfaces(local, iter_flags, iterator, data);  	mutex_unlock(&local->iflist_mtx);  }  EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);  void ieee80211_iterate_active_interfaces_atomic( -	struct ieee80211_hw *hw, +	struct ieee80211_hw *hw, u32 iter_flags,  	void (*iterator)(void *data, u8 *mac,  			 struct ieee80211_vif *vif),  	void *data)  {  	struct ieee80211_local *local = hw_to_local(hw); -	struct ieee80211_sub_if_data *sdata;  	rcu_read_lock(); - -	list_for_each_entry_rcu(sdata, &local->interfaces, list) { -		switch (sdata->vif.type) { -		case NL80211_IFTYPE_MONITOR: -		case NL80211_IFTYPE_AP_VLAN: -			continue; -		default: -			break; -		} -		if (ieee80211_sdata_running(sdata)) -			iterator(data, sdata->vif.addr, -				 &sdata->vif); -	} - +	__iterate_active_interfaces(local, iter_flags, iterator, data);  	rcu_read_unlock();  }  EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); +void ieee80211_iterate_active_interfaces_rtnl( +	struct ieee80211_hw *hw, u32 iter_flags, +	void (*iterator)(void *data, u8 *mac, +			 struct ieee80211_vif *vif), +	void *data) +{ +	struct ieee80211_local *local = hw_to_local(hw); + +	ASSERT_RTNL(); + +	__iterate_active_interfaces(local, iter_flags, iterator, data); +} +EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); + +struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + +	if (!ieee80211_sdata_running(sdata) || +	    !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) +		return NULL; +	return &sdata->vif; +} +EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif); +  /*   * Nothing should have been stuffed into the workqueue during   * the suspend->resume cycle. If this WARN is seen then there @@ -567,37 +689,83 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,  }  EXPORT_SYMBOL(ieee80211_queue_delayed_work); -void ieee802_11_parse_elems(u8 *start, size_t len, -			    struct ieee802_11_elems *elems) -{ -	ieee802_11_parse_elems_crc(start, len, elems, 0, 0); -} - -u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, +u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,  			       struct ieee802_11_elems *elems,  			       u64 filter, u32 crc)  {  	size_t left = len; -	u8 *pos = start; +	const u8 *pos = start;  	bool calc_crc = filter != 0; +	DECLARE_BITMAP(seen_elems, 256); +	const u8 *ie; +	bitmap_zero(seen_elems, 256);  	memset(elems, 0, sizeof(*elems));  	elems->ie_start = start;  	elems->total_len = len;  	while (left >= 2) {  		u8 id, elen; +		bool elem_parse_failed;  		id = *pos++;  		elen = *pos++;  		left -= 2; -		if (elen > left) +		if (elen > left) { +			elems->parse_error = true;  			break; +		} + +		switch (id) { +		case WLAN_EID_SSID: +		case WLAN_EID_SUPP_RATES: +		case WLAN_EID_FH_PARAMS: +		case WLAN_EID_DS_PARAMS: +		case WLAN_EID_CF_PARAMS: +		case WLAN_EID_TIM: +		case WLAN_EID_IBSS_PARAMS: +		case WLAN_EID_CHALLENGE: +		case WLAN_EID_RSN: +		case WLAN_EID_ERP_INFO: +		case WLAN_EID_EXT_SUPP_RATES: +		case WLAN_EID_HT_CAPABILITY: +		case WLAN_EID_HT_OPERATION: +		case WLAN_EID_VHT_CAPABILITY: +		case WLAN_EID_VHT_OPERATION: +		case WLAN_EID_MESH_ID: +		case WLAN_EID_MESH_CONFIG: +		case WLAN_EID_PEER_MGMT: +		case WLAN_EID_PREQ: +		case WLAN_EID_PREP: +		case WLAN_EID_PERR: +		case WLAN_EID_RANN: +		case WLAN_EID_CHANNEL_SWITCH: +		case WLAN_EID_EXT_CHANSWITCH_ANN: +		case WLAN_EID_COUNTRY: +		case WLAN_EID_PWR_CONSTRAINT: +		case WLAN_EID_TIMEOUT_INTERVAL: +		case WLAN_EID_SECONDARY_CHANNEL_OFFSET: +		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: +		case WLAN_EID_CHAN_SWITCH_PARAM: +		/* +		 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible +		 * that if the content gets bigger it might be needed more than once +		 */ +			if (test_bit(id, seen_elems)) { +				elems->parse_error = true; +				left -= elen; +				pos += elen; +				continue; +			} +			break; +		}  		if (calc_crc && id < 64 && (filter & (1ULL << id)))  			crc = crc32_be(crc, pos - 2, elen + 2); +		elem_parse_failed = false; +  		switch (id) {  		case WLAN_EID_SSID:  			elems->ssid = pos; @@ -607,27 +775,18 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  			elems->supp_rates = pos;  			elems->supp_rates_len = elen;  			break; -		case WLAN_EID_FH_PARAMS: -			elems->fh_params = pos; -			elems->fh_params_len = elen; -			break;  		case WLAN_EID_DS_PARAMS: -			elems->ds_params = pos; -			elems->ds_params_len = elen; -			break; -		case WLAN_EID_CF_PARAMS: -			elems->cf_params = pos; -			elems->cf_params_len = elen; +			if (elen >= 1) +				elems->ds_params = pos; +			else +				elem_parse_failed = true;  			break;  		case WLAN_EID_TIM:  			if (elen >= sizeof(struct ieee80211_tim_ie)) {  				elems->tim = (void *)pos;  				elems->tim_len = elen; -			} -			break; -		case WLAN_EID_IBSS_PARAMS: -			elems->ibss_params = pos; -			elems->ibss_params_len = elen; +			} else +				elem_parse_failed = true;  			break;  		case WLAN_EID_CHALLENGE:  			elems->challenge = pos; @@ -641,11 +800,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  				if (calc_crc)  					crc = crc32_be(crc, pos - 2, elen + 2); -				if (pos[3] == 1) { -					/* OUI Type 1 - WPA IE */ -					elems->wpa = pos; -					elems->wpa_len = elen; -				} else if (elen >= 5 && pos[3] == 2) { +				if (elen >= 5 && pos[3] == 2) {  					/* OUI Type 2 - WMM IE */  					if (pos[4] == 0) {  						elems->wmm_info = pos; @@ -662,8 +817,10 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  			elems->rsn_len = elen;  			break;  		case WLAN_EID_ERP_INFO: -			elems->erp_info = pos; -			elems->erp_info_len = elen; +			if (elen >= 1) +				elems->erp_info = pos; +			else +				elem_parse_failed = true;  			break;  		case WLAN_EID_EXT_SUPP_RATES:  			elems->ext_supp_rates = pos; @@ -672,10 +829,32 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  		case WLAN_EID_HT_CAPABILITY:  			if (elen >= sizeof(struct ieee80211_ht_cap))  				elems->ht_cap_elem = (void *)pos; +			else +				elem_parse_failed = true; +			break; +		case WLAN_EID_HT_OPERATION: +			if (elen >= sizeof(struct ieee80211_ht_operation)) +				elems->ht_operation = (void *)pos; +			else +				elem_parse_failed = true; +			break; +		case WLAN_EID_VHT_CAPABILITY: +			if (elen >= sizeof(struct ieee80211_vht_cap)) +				elems->vht_cap_elem = (void *)pos; +			else +				elem_parse_failed = true;  			break; -		case WLAN_EID_HT_INFORMATION: -			if (elen >= sizeof(struct ieee80211_ht_info)) -				elems->ht_info_elem = (void *)pos; +		case WLAN_EID_VHT_OPERATION: +			if (elen >= sizeof(struct ieee80211_vht_operation)) +				elems->vht_operation = (void *)pos; +			else +				elem_parse_failed = true; +			break; +		case WLAN_EID_OPMODE_NOTIF: +			if (elen > 0) +				elems->opmode_notif = pos; +			else +				elem_parse_failed = true;  			break;  		case WLAN_EID_MESH_ID:  			elems->mesh_id = pos; @@ -684,10 +863,16 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  		case WLAN_EID_MESH_CONFIG:  			if (elen >= sizeof(struct ieee80211_meshconf_ie))  				elems->mesh_config = (void *)pos; +			else +				elem_parse_failed = true; +			break; +		case WLAN_EID_PEER_MGMT: +			elems->peering = pos; +			elems->peering_len = elen;  			break; -		case WLAN_EID_PEER_LINK: -			elems->peer_link = pos; -			elems->peer_link_len = elen; +		case WLAN_EID_MESH_AWAKE_WINDOW: +			if (elen >= 2) +				elems->awake_window = (void *)pos;  			break;  		case WLAN_EID_PREQ:  			elems->preq = pos; @@ -704,192 +889,230 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,  		case WLAN_EID_RANN:  			if (elen >= sizeof(struct ieee80211_rann_ie))  				elems->rann = (void *)pos; +			else +				elem_parse_failed = true;  			break;  		case WLAN_EID_CHANNEL_SWITCH: -			elems->ch_switch_elem = pos; -			elems->ch_switch_elem_len = elen; +			if (elen != sizeof(struct ieee80211_channel_sw_ie)) { +				elem_parse_failed = true; +				break; +			} +			elems->ch_switch_ie = (void *)pos; +			break; +		case WLAN_EID_EXT_CHANSWITCH_ANN: +			if (elen != sizeof(struct ieee80211_ext_chansw_ie)) { +				elem_parse_failed = true; +				break; +			} +			elems->ext_chansw_ie = (void *)pos; +			break; +		case WLAN_EID_SECONDARY_CHANNEL_OFFSET: +			if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) { +				elem_parse_failed = true; +				break; +			} +			elems->sec_chan_offs = (void *)pos; +			break; +		case WLAN_EID_CHAN_SWITCH_PARAM: +			if (elen != +			    sizeof(*elems->mesh_chansw_params_ie)) { +				elem_parse_failed = true; +				break; +			} +			elems->mesh_chansw_params_ie = (void *)pos;  			break; -		case WLAN_EID_QUIET: -			if (!elems->quiet_elem) { -				elems->quiet_elem = pos; -				elems->quiet_elem_len = elen; +		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: +			if (!action || +			    elen != sizeof(*elems->wide_bw_chansw_ie)) { +				elem_parse_failed = true; +				break; +			} +			elems->wide_bw_chansw_ie = (void *)pos; +			break; +		case WLAN_EID_CHANNEL_SWITCH_WRAPPER: +			if (action) { +				elem_parse_failed = true; +				break; +			} +			/* +			 * This is a bit tricky, but as we only care about +			 * the wide bandwidth channel switch element, so +			 * just parse it out manually. +			 */ +			ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, +					      pos, elen); +			if (ie) { +				if (ie[1] == sizeof(*elems->wide_bw_chansw_ie)) +					elems->wide_bw_chansw_ie = +						(void *)(ie + 2); +				else +					elem_parse_failed = true;  			} -			elems->num_of_quiet_elem++;  			break;  		case WLAN_EID_COUNTRY:  			elems->country_elem = pos;  			elems->country_elem_len = elen;  			break;  		case WLAN_EID_PWR_CONSTRAINT: +			if (elen != 1) { +				elem_parse_failed = true; +				break; +			}  			elems->pwr_constr_elem = pos; -			elems->pwr_constr_elem_len = elen;  			break;  		case WLAN_EID_TIMEOUT_INTERVAL: -			elems->timeout_int = pos; -			elems->timeout_int_len = elen; +			if (elen >= sizeof(struct ieee80211_timeout_interval_ie)) +				elems->timeout_int = (void *)pos; +			else +				elem_parse_failed = true;  			break;  		default:  			break;  		} +		if (elem_parse_failed) +			elems->parse_error = true; +		else +			__set_bit(id, seen_elems); +  		left -= elen;  		pos += elen;  	} +	if (left != 0) +		elems->parse_error = true; +  	return crc;  } -void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) +void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, +			       bool bss_notify)  {  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_tx_queue_params qparam; -	int queue; -	bool use_11b; +	struct ieee80211_chanctx_conf *chanctx_conf; +	int ac; +	bool use_11b, enable_qos;  	int aCWmin, aCWmax;  	if (!local->ops->conf_tx)  		return; +	if (local->hw.queues < IEEE80211_NUM_ACS) +		return; +  	memset(&qparam, 0, sizeof(qparam)); -	use_11b = (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) && +	rcu_read_lock(); +	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); +	use_11b = (chanctx_conf && +		   chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ) &&  		 !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE); +	rcu_read_unlock(); -	for (queue = 0; queue < local_to_hw(local)->queues; queue++) { -		/* Set defaults according to 802.11-2007 Table 7-37 */ -		aCWmax = 1023; -		if (use_11b) -			aCWmin = 31; -		else -			aCWmin = 15; - -		switch (queue) { -		case 3: /* AC_BK */ -			qparam.cw_max = aCWmax; -			qparam.cw_min = aCWmin; -			qparam.txop = 0; -			qparam.aifs = 7; -			break; -		default: /* never happens but let's not leave undefined */ -		case 2: /* AC_BE */ -			qparam.cw_max = aCWmax; -			qparam.cw_min = aCWmin; -			qparam.txop = 0; -			qparam.aifs = 3; -			break; -		case 1: /* AC_VI */ -			qparam.cw_max = aCWmin; -			qparam.cw_min = (aCWmin + 1) / 2 - 1; -			if (use_11b) -				qparam.txop = 6016/32; -			else -				qparam.txop = 3008/32; -			qparam.aifs = 2; -			break; -		case 0: /* AC_VO */ -			qparam.cw_max = (aCWmin + 1) / 2 - 1; -			qparam.cw_min = (aCWmin + 1) / 4 - 1; -			if (use_11b) -				qparam.txop = 3264/32; -			else -				qparam.txop = 1504/32; -			qparam.aifs = 2; -			break; +	/* +	 * By default disable QoS in STA mode for old access points, which do +	 * not support 802.11e. New APs will provide proper queue parameters, +	 * that we will configure later. +	 */ +	enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION); + +	/* Set defaults according to 802.11-2007 Table 7-37 */ +	aCWmax = 1023; +	if (use_11b) +		aCWmin = 31; +	else +		aCWmin = 15; + +	/* Confiure old 802.11b/g medium access rules. */ +	qparam.cw_max = aCWmax; +	qparam.cw_min = aCWmin; +	qparam.txop = 0; +	qparam.aifs = 2; + +	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { +		/* Update if QoS is enabled. */ +		if (enable_qos) { +			switch (ac) { +			case IEEE80211_AC_BK: +				qparam.cw_max = aCWmax; +				qparam.cw_min = aCWmin; +				qparam.txop = 0; +				qparam.aifs = 7; +				break; +			/* never happens but let's not leave undefined */ +			default: +			case IEEE80211_AC_BE: +				qparam.cw_max = aCWmax; +				qparam.cw_min = aCWmin; +				qparam.txop = 0; +				qparam.aifs = 3; +				break; +			case IEEE80211_AC_VI: +				qparam.cw_max = aCWmin; +				qparam.cw_min = (aCWmin + 1) / 2 - 1; +				if (use_11b) +					qparam.txop = 6016/32; +				else +					qparam.txop = 3008/32; +				qparam.aifs = 2; +				break; +			case IEEE80211_AC_VO: +				qparam.cw_max = (aCWmin + 1) / 2 - 1; +				qparam.cw_min = (aCWmin + 1) / 4 - 1; +				if (use_11b) +					qparam.txop = 3264/32; +				else +					qparam.txop = 1504/32; +				qparam.aifs = 2; +				break; +			}  		}  		qparam.uapsd = false; -		drv_conf_tx(local, queue, &qparam); +		sdata->tx_conf[ac] = qparam; +		drv_conf_tx(local, sdata, ac, &qparam);  	} -	/* after reinitialize QoS TX queues setting to default, -	 * disable QoS at all */ - -	if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { -		sdata->vif.bss_conf.qos = -			sdata->vif.type != NL80211_IFTYPE_STATION; -		ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS); +	if (sdata->vif.type != NL80211_IFTYPE_MONITOR && +	    sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) { +		sdata->vif.bss_conf.qos = enable_qos; +		if (bss_notify) +			ieee80211_bss_info_change_notify(sdata, +							 BSS_CHANGED_QOS);  	}  } -void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, -				  const size_t supp_rates_len, -				  const u8 *supp_rates) -{ -	struct ieee80211_local *local = sdata->local; -	int i, have_higher_than_11mbit = 0; - -	/* cf. IEEE 802.11 9.2.12 */ -	for (i = 0; i < supp_rates_len; i++) -		if ((supp_rates[i] & 0x7f) * 5 > 110) -			have_higher_than_11mbit = 1; - -	if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ && -	    have_higher_than_11mbit) -		sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; -	else -		sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; - -	ieee80211_set_wmm_default(sdata); -} - -u32 ieee80211_mandatory_rates(struct ieee80211_local *local, -			      enum ieee80211_band band) -{ -	struct ieee80211_supported_band *sband; -	struct ieee80211_rate *bitrates; -	u32 mandatory_rates; -	enum ieee80211_rate_flags mandatory_flag; -	int i; - -	sband = local->hw.wiphy->bands[band]; -	if (!sband) { -		WARN_ON(1); -		sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; -	} - -	if (band == IEEE80211_BAND_2GHZ) -		mandatory_flag = IEEE80211_RATE_MANDATORY_B; -	else -		mandatory_flag = IEEE80211_RATE_MANDATORY_A; - -	bitrates = sband->bitrates; -	mandatory_rates = 0; -	for (i = 0; i < sband->n_bitrates; i++) -		if (bitrates[i].flags & mandatory_flag) -			mandatory_rates |= BIT(i); -	return mandatory_rates; -} -  void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, -			 u16 transaction, u16 auth_alg, -			 u8 *extra, size_t extra_len, const u8 *bssid, -			 const u8 *key, u8 key_len, u8 key_idx) +			 u16 transaction, u16 auth_alg, u16 status, +			 const u8 *extra, size_t extra_len, const u8 *da, +			 const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx, +			 u32 tx_flags)  {  	struct ieee80211_local *local = sdata->local;  	struct sk_buff *skb;  	struct ieee80211_mgmt *mgmt;  	int err; -	skb = dev_alloc_skb(local->hw.extra_tx_headroom + -			    sizeof(*mgmt) + 6 + extra_len); -	if (!skb) { -		printk(KERN_DEBUG "%s: failed to allocate buffer for auth " -		       "frame\n", sdata->name); +	/* 24 + 6 = header + auth_algo + auth_transaction + status_code */ +	skb = dev_alloc_skb(local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN + +			    24 + 6 + extra_len + IEEE80211_WEP_ICV_LEN); +	if (!skb)  		return; -	} -	skb_reserve(skb, local->hw.extra_tx_headroom); + +	skb_reserve(skb, local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN);  	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6);  	memset(mgmt, 0, 24 + 6);  	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |  					  IEEE80211_STYPE_AUTH); -	memcpy(mgmt->da, bssid, ETH_ALEN); +	memcpy(mgmt->da, da, ETH_ALEN);  	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);  	memcpy(mgmt->bssid, bssid, ETH_ALEN);  	mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);  	mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); -	mgmt->u.auth.status_code = cpu_to_le16(0); +	mgmt->u.auth.status_code = cpu_to_le16(status);  	if (extra)  		memcpy(skb_put(skb, extra_len), extra, extra_len); @@ -899,36 +1122,88 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,  		WARN_ON(err);  	} -	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; +	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | +					tx_flags;  	ieee80211_tx_skb(sdata, skb);  } +void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, +				    const u8 *bssid, u16 stype, u16 reason, +				    bool send_frame, u8 *frame_buf) +{ +	struct ieee80211_local *local = sdata->local; +	struct sk_buff *skb; +	struct ieee80211_mgmt *mgmt = (void *)frame_buf; + +	/* build frame */ +	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); +	mgmt->duration = 0; /* initialize only */ +	mgmt->seq_ctrl = 0; /* initialize only */ +	memcpy(mgmt->da, bssid, ETH_ALEN); +	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); +	memcpy(mgmt->bssid, bssid, ETH_ALEN); +	/* u.deauth.reason_code == u.disassoc.reason_code */ +	mgmt->u.deauth.reason_code = cpu_to_le16(reason); + +	if (send_frame) { +		skb = dev_alloc_skb(local->hw.extra_tx_headroom + +				    IEEE80211_DEAUTH_FRAME_LEN); +		if (!skb) +			return; + +		skb_reserve(skb, local->hw.extra_tx_headroom); + +		/* copy in frame */ +		memcpy(skb_put(skb, IEEE80211_DEAUTH_FRAME_LEN), +		       mgmt, IEEE80211_DEAUTH_FRAME_LEN); + +		if (sdata->vif.type != NL80211_IFTYPE_STATION || +		    !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED)) +			IEEE80211_SKB_CB(skb)->flags |= +				IEEE80211_TX_INTFL_DONT_ENCRYPT; + +		ieee80211_tx_skb(sdata, skb); +	} +} +  int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, -			     const u8 *ie, size_t ie_len, +			     size_t buffer_len, const u8 *ie, size_t ie_len,  			     enum ieee80211_band band, u32 rate_mask, -			     u8 channel) +			     struct cfg80211_chan_def *chandef)  {  	struct ieee80211_supported_band *sband; -	u8 *pos; +	u8 *pos = buffer, *end = buffer + buffer_len;  	size_t offset = 0, noffset;  	int supp_rates_len, i;  	u8 rates[32];  	int num_rates;  	int ext_rates_len; +	int shift; +	u32 rate_flags;  	sband = local->hw.wiphy->bands[band]; +	if (WARN_ON_ONCE(!sband)) +		return 0; -	pos = buffer; +	rate_flags = ieee80211_chandef_rate_flags(chandef); +	shift = ieee80211_chandef_get_shift(chandef);  	num_rates = 0;  	for (i = 0; i < sband->n_bitrates; i++) {  		if ((BIT(i) & rate_mask) == 0)  			continue; /* skip rate */ -		rates[num_rates++] = (u8) (sband->bitrates[i].bitrate / 5); +		if ((rate_flags & sband->bitrates[i].flags) != rate_flags) +			continue; + +		rates[num_rates++] = +			(u8) DIV_ROUND_UP(sband->bitrates[i].bitrate, +					  (1 << shift) * 5);  	}  	supp_rates_len = min_t(int, num_rates, 8); +	if (end - pos < 2 + supp_rates_len) +		goto out_err;  	*pos++ = WLAN_EID_SUPP_RATES;  	*pos++ = supp_rates_len;  	memcpy(pos, rates, supp_rates_len); @@ -945,6 +1220,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,  					     before_extrates,  					     ARRAY_SIZE(before_extrates),  					     offset); +		if (end - pos < noffset - offset) +			goto out_err;  		memcpy(pos, ie + offset, noffset - offset);  		pos += noffset - offset;  		offset = noffset; @@ -952,16 +1229,21 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,  	ext_rates_len = num_rates - supp_rates_len;  	if (ext_rates_len > 0) { +		if (end - pos < 2 + ext_rates_len) +			goto out_err;  		*pos++ = WLAN_EID_EXT_SUPP_RATES;  		*pos++ = ext_rates_len;  		memcpy(pos, rates + supp_rates_len, ext_rates_len);  		pos += ext_rates_len;  	} -	if (channel && sband->band == IEEE80211_BAND_2GHZ) { +	if (chandef->chan && sband->band == IEEE80211_BAND_2GHZ) { +		if (end - pos < 3) +			goto out_err;  		*pos++ = WLAN_EID_DS_PARAMS;  		*pos++ = 1; -		*pos++ = channel; +		*pos++ = ieee80211_frequency_to_channel( +				chandef->chan->center_freq);  	}  	/* insert custom IEs that go before HT */ @@ -977,33 +1259,18 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,  		noffset = ieee80211_ie_split(ie, ie_len,  					     before_ht, ARRAY_SIZE(before_ht),  					     offset); +		if (end - pos < noffset - offset) +			goto out_err;  		memcpy(pos, ie + offset, noffset - offset);  		pos += noffset - offset;  		offset = noffset;  	}  	if (sband->ht_cap.ht_supported) { -		u16 cap = sband->ht_cap.cap; -		__le16 tmp; - -		if (ieee80211_disable_40mhz_24ghz && -		    sband->band == IEEE80211_BAND_2GHZ) { -			cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; -			cap &= ~IEEE80211_HT_CAP_SGI_40; -		} - -		*pos++ = WLAN_EID_HT_CAPABILITY; -		*pos++ = sizeof(struct ieee80211_ht_cap); -		memset(pos, 0, sizeof(struct ieee80211_ht_cap)); -		tmp = cpu_to_le16(cap); -		memcpy(pos, &tmp, sizeof(u16)); -		pos += sizeof(u16); -		*pos++ = sband->ht_cap.ampdu_factor | -			 (sband->ht_cap.ampdu_density << -				IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); -		memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); -		pos += sizeof(sband->ht_cap.mcs); -		pos += 2 + 4 + 1; /* ext info, BF cap, antsel */ +		if (end - pos < 2 + sizeof(struct ieee80211_ht_cap)) +			goto out_err; +		pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, +						sband->ht_cap.cap);  	}  	/* @@ -1011,48 +1278,90 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,  	 * that calculates local->scan_ies_len.  	 */ +	/* insert custom IEs that go before VHT */ +	if (ie && ie_len) { +		static const u8 before_vht[] = { +			WLAN_EID_SSID, +			WLAN_EID_SUPP_RATES, +			WLAN_EID_REQUEST, +			WLAN_EID_EXT_SUPP_RATES, +			WLAN_EID_DS_PARAMS, +			WLAN_EID_SUPPORTED_REGULATORY_CLASSES, +			WLAN_EID_HT_CAPABILITY, +			WLAN_EID_BSS_COEX_2040, +			WLAN_EID_EXT_CAPABILITY, +			WLAN_EID_SSID_LIST, +			WLAN_EID_CHANNEL_USAGE, +			WLAN_EID_INTERWORKING, +			/* mesh ID can't happen here */ +			/* 60 GHz can't happen here right now */ +		}; +		noffset = ieee80211_ie_split(ie, ie_len, +					     before_vht, ARRAY_SIZE(before_vht), +					     offset); +		if (end - pos < noffset - offset) +			goto out_err; +		memcpy(pos, ie + offset, noffset - offset); +		pos += noffset - offset; +		offset = noffset; +	} + +	if (sband->vht_cap.vht_supported) { +		if (end - pos < 2 + sizeof(struct ieee80211_vht_cap)) +			goto out_err; +		pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, +						 sband->vht_cap.cap); +	} +  	/* add any remaining custom IEs */  	if (ie && ie_len) {  		noffset = ie_len; +		if (end - pos < noffset - offset) +			goto out_err;  		memcpy(pos, ie + offset, noffset - offset);  		pos += noffset - offset;  	}  	return pos - buffer; + out_err: +	WARN_ONCE(1, "not enough space for preq IEs\n"); +	return pos - buffer;  }  struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, -					  u8 *dst, +					  u8 *dst, u32 ratemask, +					  struct ieee80211_channel *chan,  					  const u8 *ssid, size_t ssid_len, -					  const u8 *ie, size_t ie_len) +					  const u8 *ie, size_t ie_len, +					  bool directed)  {  	struct ieee80211_local *local = sdata->local; +	struct cfg80211_chan_def chandef;  	struct sk_buff *skb;  	struct ieee80211_mgmt *mgmt; -	size_t buf_len; -	u8 *buf; -	u8 chan; - -	/* FIXME: come up with a proper value */ -	buf = kmalloc(200 + ie_len, GFP_KERNEL); -	if (!buf) { -		printk(KERN_DEBUG "%s: failed to allocate temporary IE " -		       "buffer\n", sdata->name); -		return NULL; -	} - -	chan = ieee80211_frequency_to_channel( -		local->hw.conf.channel->center_freq); +	int ies_len; -	buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, -					   local->hw.conf.channel->band, -					   sdata->rc_rateidx_mask -					   [local->hw.conf.channel->band], -					   chan); +	/* +	 * Do not send DS Channel parameter for directed probe requests +	 * in order to maximize the chance that we get a response.  Some +	 * badly-behaved APs don't respond when this parameter is included. +	 */ +	chandef.width = sdata->vif.bss_conf.chandef.width; +	if (directed) +		chandef.chan = NULL; +	else +		chandef.chan = chan;  	skb = ieee80211_probereq_get(&local->hw, &sdata->vif, -				     ssid, ssid_len, -				     buf, buf_len); +				     ssid, ssid_len, 100 + ie_len); +	if (!skb) +		return NULL; + +	ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb), +					   skb_tailroom(skb), +					   ie, ie_len, chan->band, +					   ratemask, &chandef); +	skb_put(skb, ies_len);  	if (dst) {  		mgmt = (struct ieee80211_mgmt *) skb->data; @@ -1061,54 +1370,79 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,  	}  	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; -	kfree(buf);  	return skb;  }  void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,  			      const u8 *ssid, size_t ssid_len, -			      const u8 *ie, size_t ie_len) +			      const u8 *ie, size_t ie_len, +			      u32 ratemask, bool directed, u32 tx_flags, +			      struct ieee80211_channel *channel, bool scan)  {  	struct sk_buff *skb; -	skb = ieee80211_build_probe_req(sdata, dst, ssid, ssid_len, ie, ie_len); -	if (skb) -		ieee80211_tx_skb(sdata, skb); +	skb = ieee80211_build_probe_req(sdata, dst, ratemask, channel, +					ssid, ssid_len, +					ie, ie_len, directed); +	if (skb) { +		IEEE80211_SKB_CB(skb)->flags |= tx_flags; +		if (scan) +			ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band); +		else +			ieee80211_tx_skb(sdata, skb); +	}  } -u32 ieee80211_sta_get_rates(struct ieee80211_local *local, +u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,  			    struct ieee802_11_elems *elems, -			    enum ieee80211_band band) +			    enum ieee80211_band band, u32 *basic_rates)  {  	struct ieee80211_supported_band *sband; -	struct ieee80211_rate *bitrates;  	size_t num_rates; -	u32 supp_rates; -	int i, j; -	sband = local->hw.wiphy->bands[band]; +	u32 supp_rates, rate_flags; +	int i, j, shift; +	sband = sdata->local->hw.wiphy->bands[band]; -	if (!sband) { -		WARN_ON(1); -		sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; -	} +	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); +	shift = ieee80211_vif_get_shift(&sdata->vif); + +	if (WARN_ON(!sband)) +		return 1; -	bitrates = sband->bitrates;  	num_rates = sband->n_bitrates;  	supp_rates = 0;  	for (i = 0; i < elems->supp_rates_len +  		     elems->ext_supp_rates_len; i++) {  		u8 rate = 0;  		int own_rate; +		bool is_basic;  		if (i < elems->supp_rates_len)  			rate = elems->supp_rates[i];  		else if (elems->ext_supp_rates)  			rate = elems->ext_supp_rates  				[i - elems->supp_rates_len];  		own_rate = 5 * (rate & 0x7f); -		for (j = 0; j < num_rates; j++) -			if (bitrates[j].bitrate == own_rate) +		is_basic = !!(rate & 0x80); + +		if (is_basic && (rate & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY) +			continue; + +		for (j = 0; j < num_rates; j++) { +			int brate; +			if ((rate_flags & sband->bitrates[j].flags) +			    != rate_flags) +				continue; + +			brate = DIV_ROUND_UP(sband->bitrates[j].bitrate, +					     1 << shift); + +			if (brate == own_rate) {  				supp_rates |= BIT(j); +				if (basic_rates && is_basic) +					*basic_rates |= BIT(j); +			} +		}  	}  	return supp_rates;  } @@ -1116,6 +1450,7 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,  void ieee80211_stop_device(struct ieee80211_local *local)  {  	ieee80211_led_radio(local, false); +	ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO);  	cancel_work_sync(&local->reconfig_filter); @@ -1123,63 +1458,198 @@ void ieee80211_stop_device(struct ieee80211_local *local)  	drv_stop(local);  } +static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local) +{ +	struct ieee80211_sub_if_data *sdata; +	struct ieee80211_chanctx *ctx; + +	/* +	 * We get here if during resume the device can't be restarted properly. +	 * We might also get here if this happens during HW reset, which is a +	 * slightly different situation and we need to drop all connections in +	 * the latter case. +	 * +	 * Ask cfg80211 to turn off all interfaces, this will result in more +	 * warnings but at least we'll then get into a clean stopped state. +	 */ + +	local->resuming = false; +	local->suspended = false; +	local->started = false; + +	/* scheduled scan clearly can't be running any more, but tell +	 * cfg80211 and clear local state +	 */ +	ieee80211_sched_scan_end(local); + +	list_for_each_entry(sdata, &local->interfaces, list) +		sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; + +	/* Mark channel contexts as not being in the driver any more to avoid +	 * removing them from the driver during the shutdown process... +	 */ +	mutex_lock(&local->chanctx_mtx); +	list_for_each_entry(ctx, &local->chanctx_list, list) +		ctx->driver_present = false; +	mutex_unlock(&local->chanctx_mtx); + +	cfg80211_shutdown_all_interfaces(local->hw.wiphy); +} + +static void ieee80211_assign_chanctx(struct ieee80211_local *local, +				     struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_chanctx_conf *conf; +	struct ieee80211_chanctx *ctx; + +	if (!local->use_chanctx) +		return; + +	mutex_lock(&local->chanctx_mtx); +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	if (conf) { +		ctx = container_of(conf, struct ieee80211_chanctx, conf); +		drv_assign_vif_chanctx(local, sdata, ctx); +	} +	mutex_unlock(&local->chanctx_mtx); +} +  int ieee80211_reconfig(struct ieee80211_local *local)  {  	struct ieee80211_hw *hw = &local->hw;  	struct ieee80211_sub_if_data *sdata; +	struct ieee80211_chanctx *ctx;  	struct sta_info *sta; -	int res; +	int res, i; +	bool reconfig_due_to_wowlan = false; +	struct ieee80211_sub_if_data *sched_scan_sdata; +	bool sched_scan_stopped = false; +#ifdef CONFIG_PM  	if (local->suspended)  		local->resuming = true; -	/* restart hardware */ -	if (local->open_count) { -		/* -		 * Upon resume hardware can sometimes be goofy due to -		 * various platform / driver / bus issues, so restarting -		 * the device may at times not work immediately. Propagate -		 * the error. -		 */ -		res = drv_start(local); -		if (res) { -			WARN(local->suspended, "Hardware became unavailable " -			     "upon resume. This could be a software issue " -			     "prior to suspend or a hardware issue.\n"); +	if (local->wowlan) { +		res = drv_resume(local); +		local->wowlan = false; +		if (res < 0) { +			local->resuming = false;  			return res;  		} +		if (res == 0) +			goto wake_up; +		WARN_ON(res > 1); +		/* +		 * res is 1, which means the driver requested +		 * to go through a regular reset on wakeup. +		 */ +		reconfig_due_to_wowlan = true; +	} +#endif +	/* everything else happens only if HW was up & running */ +	if (!local->open_count) +		goto wake_up; -		ieee80211_led_radio(local, true); +	/* +	 * Upon resume hardware can sometimes be goofy due to +	 * various platform / driver / bus issues, so restarting +	 * the device may at times not work immediately. Propagate +	 * the error. +	 */ +	res = drv_start(local); +	if (res) { +		if (local->suspended) +			WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n"); +		else +			WARN(1, "Hardware became unavailable during restart.\n"); +		ieee80211_handle_reconfig_failure(local); +		return res;  	} +	/* setup fragmentation threshold */ +	drv_set_frag_threshold(local, hw->wiphy->frag_threshold); + +	/* setup RTS threshold */ +	drv_set_rts_threshold(local, hw->wiphy->rts_threshold); + +	/* reset coverage class */ +	drv_set_coverage_class(local, hw->wiphy->coverage_class); + +	ieee80211_led_radio(local, true); +	ieee80211_mod_tpt_led_trig(local, +				   IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); +  	/* add interfaces */ +	sdata = rtnl_dereference(local->monitor_sdata); +	if (sdata) { +		/* in HW restart it exists already */ +		WARN_ON(local->resuming); +		res = drv_add_interface(local, sdata); +		if (WARN_ON(res)) { +			RCU_INIT_POINTER(local->monitor_sdata, NULL); +			synchronize_net(); +			kfree(sdata); +		} +	} +  	list_for_each_entry(sdata, &local->interfaces, list) {  		if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&  		    sdata->vif.type != NL80211_IFTYPE_MONITOR &&  		    ieee80211_sdata_running(sdata)) -			res = drv_add_interface(local, &sdata->vif); +			res = drv_add_interface(local, sdata); +	} + +	/* add channel contexts */ +	if (local->use_chanctx) { +		mutex_lock(&local->chanctx_mtx); +		list_for_each_entry(ctx, &local->chanctx_list, list) +			WARN_ON(drv_add_chanctx(local, ctx)); +		mutex_unlock(&local->chanctx_mtx); + +		list_for_each_entry(sdata, &local->interfaces, list) { +			if (!ieee80211_sdata_running(sdata)) +				continue; +			ieee80211_assign_chanctx(local, sdata); +		} + +		sdata = rtnl_dereference(local->monitor_sdata); +		if (sdata && ieee80211_sdata_running(sdata)) +			ieee80211_assign_chanctx(local, sdata);  	}  	/* add STAs back */  	mutex_lock(&local->sta_mtx);  	list_for_each_entry(sta, &local->sta_list, list) { -		if (sta->uploaded) { -			sdata = sta->sdata; -			if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) -				sdata = container_of(sdata->bss, -					     struct ieee80211_sub_if_data, -					     u.ap); - -			WARN_ON(drv_sta_add(local, sdata, &sta->sta)); -		} +		enum ieee80211_sta_state state; + +		if (!sta->uploaded) +			continue; + +		/* AP-mode stations will be added later */ +		if (sta->sdata->vif.type == NL80211_IFTYPE_AP) +			continue; + +		for (state = IEEE80211_STA_NOTEXIST; +		     state < sta->sta_state; state++) +			WARN_ON(drv_sta_state(local, sta->sdata, sta, state, +					      state + 1));  	}  	mutex_unlock(&local->sta_mtx); -	/* setup fragmentation threshold */ -	drv_set_frag_threshold(local, hw->wiphy->frag_threshold); +	/* reconfigure tx conf */ +	if (hw->queues >= IEEE80211_NUM_ACS) { +		list_for_each_entry(sdata, &local->interfaces, list) { +			if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN || +			    sdata->vif.type == NL80211_IFTYPE_MONITOR || +			    !ieee80211_sdata_running(sdata)) +				continue; -	/* setup RTS threshold */ -	drv_set_rts_threshold(local, hw->wiphy->rts_threshold); +			for (i = 0; i < IEEE80211_NUM_ACS; i++) +				drv_conf_tx(local, sdata, i, +					    &sdata->tx_conf[i]); +		} +	}  	/* reconfigure hardware */  	ieee80211_hw_config(local, ~0); @@ -1202,27 +1672,50 @@ int ieee80211_reconfig(struct ieee80211_local *local)  			  BSS_CHANGED_BEACON_INT |  			  BSS_CHANGED_BSSID |  			  BSS_CHANGED_CQM | -			  BSS_CHANGED_QOS; +			  BSS_CHANGED_QOS | +			  BSS_CHANGED_IDLE | +			  BSS_CHANGED_TXPOWER;  		switch (sdata->vif.type) {  		case NL80211_IFTYPE_STATION: -			changed |= BSS_CHANGED_ASSOC; +			changed |= BSS_CHANGED_ASSOC | +				   BSS_CHANGED_ARP_FILTER | +				   BSS_CHANGED_PS; + +			/* Re-send beacon info report to the driver */ +			if (sdata->u.mgd.have_beacon) +				changed |= BSS_CHANGED_BEACON_INFO; + +			sdata_lock(sdata);  			ieee80211_bss_info_change_notify(sdata, changed); +			sdata_unlock(sdata);  			break;  		case NL80211_IFTYPE_ADHOC:  			changed |= BSS_CHANGED_IBSS;  			/* fall through */  		case NL80211_IFTYPE_AP: +			changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS; + +			if (sdata->vif.type == NL80211_IFTYPE_AP) { +				changed |= BSS_CHANGED_AP_PROBE_RESP; + +				if (rcu_access_pointer(sdata->u.ap.beacon)) +					drv_start_ap(local, sdata); +			} + +			/* fall through */  		case NL80211_IFTYPE_MESH_POINT: -			changed |= BSS_CHANGED_BEACON | -				   BSS_CHANGED_BEACON_ENABLED; -			ieee80211_bss_info_change_notify(sdata, changed); +			if (sdata->vif.bss_conf.enable_beacon) { +				changed |= BSS_CHANGED_BEACON | +					   BSS_CHANGED_BEACON_ENABLED; +				ieee80211_bss_info_change_notify(sdata, changed); +			}  			break;  		case NL80211_IFTYPE_WDS: -			break;  		case NL80211_IFTYPE_AP_VLAN:  		case NL80211_IFTYPE_MONITOR: -			/* ignore virtual */ +		case NL80211_IFTYPE_P2P_DEVICE: +			/* nothing to do */  			break;  		case NL80211_IFTYPE_UNSPECIFIED:  		case NUM_NL80211_IFTYPES: @@ -1233,6 +1726,55 @@ int ieee80211_reconfig(struct ieee80211_local *local)  		}  	} +	ieee80211_recalc_ps(local, -1); + +	/* +	 * The sta might be in psm against the ap (e.g. because +	 * this was the state before a hw restart), so we +	 * explicitly send a null packet in order to make sure +	 * it'll sync against the ap (and get out of psm). +	 */ +	if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) { +		list_for_each_entry(sdata, &local->interfaces, list) { +			if (sdata->vif.type != NL80211_IFTYPE_STATION) +				continue; +			if (!sdata->u.mgd.associated) +				continue; + +			ieee80211_send_nullfunc(local, sdata, 0); +		} +	} + +	/* APs are now beaconing, add back stations */ +	mutex_lock(&local->sta_mtx); +	list_for_each_entry(sta, &local->sta_list, list) { +		enum ieee80211_sta_state state; + +		if (!sta->uploaded) +			continue; + +		if (sta->sdata->vif.type != NL80211_IFTYPE_AP) +			continue; + +		for (state = IEEE80211_STA_NOTEXIST; +		     state < sta->sta_state; state++) +			WARN_ON(drv_sta_state(local, sta->sdata, sta, state, +					      state + 1)); +	} +	mutex_unlock(&local->sta_mtx); + +	/* add back keys */ +	list_for_each_entry(sdata, &local->interfaces, list) +		if (ieee80211_sdata_running(sdata)) +			ieee80211_enable_keys(sdata); + + wake_up: +	local->in_reconfig = false; +	barrier(); + +	if (local->monitors == local->open_count && local->monitors > 0) +		ieee80211_add_virtual_monitor(local); +  	/*  	 * Clear the WLAN_STA_BLOCK_BA flag so new aggregation  	 * sessions can be established after a resume. @@ -1247,25 +1789,44 @@ int ieee80211_reconfig(struct ieee80211_local *local)  		mutex_lock(&local->sta_mtx);  		list_for_each_entry(sta, &local->sta_list, list) { -			ieee80211_sta_tear_down_BA_sessions(sta, true); -			clear_sta_flags(sta, WLAN_STA_BLOCK_BA); +			ieee80211_sta_tear_down_BA_sessions( +					sta, AGG_STOP_LOCAL_REQUEST); +			clear_sta_flag(sta, WLAN_STA_BLOCK_BA);  		}  		mutex_unlock(&local->sta_mtx);  	} -	/* add back keys */ -	list_for_each_entry(sdata, &local->interfaces, list) -		if (ieee80211_sdata_running(sdata)) -			ieee80211_enable_keys(sdata); +	ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_SUSPEND); -	ieee80211_wake_queues_by_reason(hw, -			IEEE80211_QUEUE_STOP_REASON_SUSPEND); +	/* +	 * Reconfigure sched scan if it was interrupted by FW restart or +	 * suspend. +	 */ +	mutex_lock(&local->mtx); +	sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, +						lockdep_is_held(&local->mtx)); +	if (sched_scan_sdata && local->sched_scan_req) +		/* +		 * Sched scan stopped, but we don't want to report it. Instead, +		 * we're trying to reschedule. +		 */ +		if (__ieee80211_request_sched_scan_start(sched_scan_sdata, +							 local->sched_scan_req)) +			sched_scan_stopped = true; +	mutex_unlock(&local->mtx); + +	if (sched_scan_stopped) +		cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy);  	/*  	 * If this is for hw restart things are still running.  	 * We may want to change that later, however.  	 */ +	if (!local->suspended || reconfig_due_to_wowlan) +		drv_restart_complete(local); +  	if (!local->suspended)  		return 0; @@ -1276,92 +1837,85 @@ int ieee80211_reconfig(struct ieee80211_local *local)  	local->resuming = false;  	list_for_each_entry(sdata, &local->interfaces, list) { -		switch(sdata->vif.type) { -		case NL80211_IFTYPE_STATION: +		if (!ieee80211_sdata_running(sdata)) +			continue; +		if (sdata->vif.type == NL80211_IFTYPE_STATION)  			ieee80211_sta_restart(sdata); -			break; -		case NL80211_IFTYPE_ADHOC: -			ieee80211_ibss_restart(sdata); -			break; -		case NL80211_IFTYPE_MESH_POINT: -			ieee80211_mesh_restart(sdata); -			break; -		default: -			break; -		}  	} -	add_timer(&local->sta_cleanup); - -	mutex_lock(&local->sta_mtx); -	list_for_each_entry(sta, &local->sta_list, list) -		mesh_plink_restart(sta); -	mutex_unlock(&local->sta_mtx); +	mod_timer(&local->sta_cleanup, jiffies + 1);  #else  	WARN_ON(1);  #endif +  	return 0;  } -static int check_mgd_smps(struct ieee80211_if_managed *ifmgd, -			  enum ieee80211_smps_mode *smps_mode) +void ieee80211_resume_disconnect(struct ieee80211_vif *vif)  { -	if (ifmgd->associated) { -		*smps_mode = ifmgd->ap_smps; +	struct ieee80211_sub_if_data *sdata; +	struct ieee80211_local *local; +	struct ieee80211_key *key; -		if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) { -			if (ifmgd->powersave) -				*smps_mode = IEEE80211_SMPS_DYNAMIC; -			else -				*smps_mode = IEEE80211_SMPS_OFF; -		} +	if (WARN_ON(!vif)) +		return; -		return 1; -	} +	sdata = vif_to_sdata(vif); +	local = sdata->local; -	return 0; +	if (WARN_ON(!local->resuming)) +		return; + +	if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) +		return; + +	sdata->flags |= IEEE80211_SDATA_DISCONNECT_RESUME; + +	mutex_lock(&local->key_mtx); +	list_for_each_entry(key, &sdata->key_list, list) +		key->flags |= KEY_FLAG_TAINTED; +	mutex_unlock(&local->key_mtx);  } +EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); -/* must hold iflist_mtx */ -void ieee80211_recalc_smps(struct ieee80211_local *local) +void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata)  { -	struct ieee80211_sub_if_data *sdata; -	enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF; -	int count = 0; +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx_conf *chanctx_conf; +	struct ieee80211_chanctx *chanctx; -	lockdep_assert_held(&local->iflist_mtx); +	mutex_lock(&local->chanctx_mtx); -	/* -	 * This function could be improved to handle multiple -	 * interfaces better, but right now it makes any -	 * non-station interfaces force SM PS to be turned -	 * off. If there are multiple station interfaces it -	 * could also use the best possible mode, e.g. if -	 * one is in static and the other in dynamic then -	 * dynamic is ok. -	 */ +	chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					lockdep_is_held(&local->chanctx_mtx)); -	list_for_each_entry(sdata, &local->interfaces, list) { -		if (!ieee80211_sdata_running(sdata)) -			continue; -		if (sdata->vif.type != NL80211_IFTYPE_STATION) -			goto set; +	if (WARN_ON_ONCE(!chanctx_conf)) +		goto unlock; -		count += check_mgd_smps(&sdata->u.mgd, &smps_mode); +	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); +	ieee80211_recalc_smps_chanctx(local, chanctx); + unlock: +	mutex_unlock(&local->chanctx_mtx); +} -		if (count > 1) { -			smps_mode = IEEE80211_SMPS_OFF; -			break; -		} -	} +void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_chanctx_conf *chanctx_conf; +	struct ieee80211_chanctx *chanctx; -	if (smps_mode == local->smps_mode) -		return; +	mutex_lock(&local->chanctx_mtx); + +	chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					lockdep_is_held(&local->chanctx_mtx)); + +	if (WARN_ON_ONCE(!chanctx_conf)) +		goto unlock; - set: -	local->smps_mode = smps_mode; -	/* changed flag is auto-detected for this */ -	ieee80211_hw_config(local, 0); +	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); +	ieee80211_recalc_chanctx_min_def(local, chanctx); + unlock: +	mutex_unlock(&local->chanctx_mtx);  }  static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) @@ -1419,3 +1973,983 @@ size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)  	return pos;  } + +static void _ieee80211_enable_rssi_reports(struct ieee80211_sub_if_data *sdata, +					    int rssi_min_thold, +					    int rssi_max_thold) +{ +	trace_api_enable_rssi_reports(sdata, rssi_min_thold, rssi_max_thold); + +	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) +		return; + +	/* +	 * Scale up threshold values before storing it, as the RSSI averaging +	 * algorithm uses a scaled up value as well. Change this scaling +	 * factor if the RSSI averaging algorithm changes. +	 */ +	sdata->u.mgd.rssi_min_thold = rssi_min_thold*16; +	sdata->u.mgd.rssi_max_thold = rssi_max_thold*16; +} + +void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, +				    int rssi_min_thold, +				    int rssi_max_thold) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + +	WARN_ON(rssi_min_thold == rssi_max_thold || +		rssi_min_thold > rssi_max_thold); + +	_ieee80211_enable_rssi_reports(sdata, rssi_min_thold, +				       rssi_max_thold); +} +EXPORT_SYMBOL(ieee80211_enable_rssi_reports); + +void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + +	_ieee80211_enable_rssi_reports(sdata, 0, 0); +} +EXPORT_SYMBOL(ieee80211_disable_rssi_reports); + +u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, +			      u16 cap) +{ +	__le16 tmp; + +	*pos++ = WLAN_EID_HT_CAPABILITY; +	*pos++ = sizeof(struct ieee80211_ht_cap); +	memset(pos, 0, sizeof(struct ieee80211_ht_cap)); + +	/* capability flags */ +	tmp = cpu_to_le16(cap); +	memcpy(pos, &tmp, sizeof(u16)); +	pos += sizeof(u16); + +	/* AMPDU parameters */ +	*pos++ = ht_cap->ampdu_factor | +		 (ht_cap->ampdu_density << +			IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); + +	/* MCS set */ +	memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs)); +	pos += sizeof(ht_cap->mcs); + +	/* extended capabilities */ +	pos += sizeof(__le16); + +	/* BF capabilities */ +	pos += sizeof(__le32); + +	/* antenna selection */ +	pos += sizeof(u8); + +	return pos; +} + +u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, +			       u32 cap) +{ +	__le32 tmp; + +	*pos++ = WLAN_EID_VHT_CAPABILITY; +	*pos++ = sizeof(struct ieee80211_vht_cap); +	memset(pos, 0, sizeof(struct ieee80211_vht_cap)); + +	/* capability flags */ +	tmp = cpu_to_le32(cap); +	memcpy(pos, &tmp, sizeof(u32)); +	pos += sizeof(u32); + +	/* VHT MCS set */ +	memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs)); +	pos += sizeof(vht_cap->vht_mcs); + +	return pos; +} + +u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, +			       const struct cfg80211_chan_def *chandef, +			       u16 prot_mode) +{ +	struct ieee80211_ht_operation *ht_oper; +	/* Build HT Information */ +	*pos++ = WLAN_EID_HT_OPERATION; +	*pos++ = sizeof(struct ieee80211_ht_operation); +	ht_oper = (struct ieee80211_ht_operation *)pos; +	ht_oper->primary_chan = ieee80211_frequency_to_channel( +					chandef->chan->center_freq); +	switch (chandef->width) { +	case NL80211_CHAN_WIDTH_160: +	case NL80211_CHAN_WIDTH_80P80: +	case NL80211_CHAN_WIDTH_80: +	case NL80211_CHAN_WIDTH_40: +		if (chandef->center_freq1 > chandef->chan->center_freq) +			ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; +		else +			ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; +		break; +	default: +		ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; +		break; +	} +	if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && +	    chandef->width != NL80211_CHAN_WIDTH_20_NOHT && +	    chandef->width != NL80211_CHAN_WIDTH_20) +		ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; + +	ht_oper->operation_mode = cpu_to_le16(prot_mode); +	ht_oper->stbc_param = 0x0000; + +	/* It seems that Basic MCS set and Supported MCS set +	   are identical for the first 10 bytes */ +	memset(&ht_oper->basic_set, 0, 16); +	memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10); + +	return pos + sizeof(struct ieee80211_ht_operation); +} + +void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, +				  const struct ieee80211_ht_operation *ht_oper, +				  struct cfg80211_chan_def *chandef) +{ +	enum nl80211_channel_type channel_type; + +	if (!ht_oper) { +		cfg80211_chandef_create(chandef, control_chan, +					NL80211_CHAN_NO_HT); +		return; +	} + +	switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { +	case IEEE80211_HT_PARAM_CHA_SEC_NONE: +		channel_type = NL80211_CHAN_HT20; +		break; +	case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: +		channel_type = NL80211_CHAN_HT40PLUS; +		break; +	case IEEE80211_HT_PARAM_CHA_SEC_BELOW: +		channel_type = NL80211_CHAN_HT40MINUS; +		break; +	default: +		channel_type = NL80211_CHAN_NO_HT; +	} + +	cfg80211_chandef_create(chandef, control_chan, channel_type); +} + +int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef, +			     const struct ieee80211_supported_band *sband, +			     const u8 *srates, int srates_len, u32 *rates) +{ +	u32 rate_flags = ieee80211_chandef_rate_flags(chandef); +	int shift = ieee80211_chandef_get_shift(chandef); +	struct ieee80211_rate *br; +	int brate, rate, i, j, count = 0; + +	*rates = 0; + +	for (i = 0; i < srates_len; i++) { +		rate = srates[i] & 0x7f; + +		for (j = 0; j < sband->n_bitrates; j++) { +			br = &sband->bitrates[j]; +			if ((rate_flags & br->flags) != rate_flags) +				continue; + +			brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5); +			if (brate == rate) { +				*rates |= BIT(j); +				count++; +				break; +			} +		} +	} +	return count; +} + +int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, +			    struct sk_buff *skb, bool need_basic, +			    enum ieee80211_band band) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_supported_band *sband; +	int rate, shift; +	u8 i, rates, *pos; +	u32 basic_rates = sdata->vif.bss_conf.basic_rates; +	u32 rate_flags; + +	shift = ieee80211_vif_get_shift(&sdata->vif); +	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); +	sband = local->hw.wiphy->bands[band]; +	rates = 0; +	for (i = 0; i < sband->n_bitrates; i++) { +		if ((rate_flags & sband->bitrates[i].flags) != rate_flags) +			continue; +		rates++; +	} +	if (rates > 8) +		rates = 8; + +	if (skb_tailroom(skb) < rates + 2) +		return -ENOMEM; + +	pos = skb_put(skb, rates + 2); +	*pos++ = WLAN_EID_SUPP_RATES; +	*pos++ = rates; +	for (i = 0; i < rates; i++) { +		u8 basic = 0; +		if ((rate_flags & sband->bitrates[i].flags) != rate_flags) +			continue; + +		if (need_basic && basic_rates & BIT(i)) +			basic = 0x80; +		rate = sband->bitrates[i].bitrate; +		rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, +				    5 * (1 << shift)); +		*pos++ = basic | (u8) rate; +	} + +	return 0; +} + +int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, +				struct sk_buff *skb, bool need_basic, +				enum ieee80211_band band) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_supported_band *sband; +	int rate, shift; +	u8 i, exrates, *pos; +	u32 basic_rates = sdata->vif.bss_conf.basic_rates; +	u32 rate_flags; + +	rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); +	shift = ieee80211_vif_get_shift(&sdata->vif); + +	sband = local->hw.wiphy->bands[band]; +	exrates = 0; +	for (i = 0; i < sband->n_bitrates; i++) { +		if ((rate_flags & sband->bitrates[i].flags) != rate_flags) +			continue; +		exrates++; +	} + +	if (exrates > 8) +		exrates -= 8; +	else +		exrates = 0; + +	if (skb_tailroom(skb) < exrates + 2) +		return -ENOMEM; + +	if (exrates) { +		pos = skb_put(skb, exrates + 2); +		*pos++ = WLAN_EID_EXT_SUPP_RATES; +		*pos++ = exrates; +		for (i = 8; i < sband->n_bitrates; i++) { +			u8 basic = 0; +			if ((rate_flags & sband->bitrates[i].flags) +			    != rate_flags) +				continue; +			if (need_basic && basic_rates & BIT(i)) +				basic = 0x80; +			rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, +					    5 * (1 << shift)); +			*pos++ = basic | (u8) rate; +		} +	} +	return 0; +} + +int ieee80211_ave_rssi(struct ieee80211_vif *vif) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); +	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + +	if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) { +		/* non-managed type inferfaces */ +		return 0; +	} +	return ifmgd->ave_beacon_signal / 16; +} +EXPORT_SYMBOL_GPL(ieee80211_ave_rssi); + +u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs) +{ +	if (!mcs) +		return 1; + +	/* TODO: consider rx_highest */ + +	if (mcs->rx_mask[3]) +		return 4; +	if (mcs->rx_mask[2]) +		return 3; +	if (mcs->rx_mask[1]) +		return 2; +	return 1; +} + +/** + * ieee80211_calculate_rx_timestamp - calculate timestamp in frame + * @local: mac80211 hw info struct + * @status: RX status + * @mpdu_len: total MPDU length (including FCS) + * @mpdu_offset: offset into MPDU to calculate timestamp at + * + * This function calculates the RX timestamp at the given MPDU offset, taking + * into account what the RX timestamp was. An offset of 0 will just normalize + * the timestamp to TSF at beginning of MPDU reception. + */ +u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, +				     struct ieee80211_rx_status *status, +				     unsigned int mpdu_len, +				     unsigned int mpdu_offset) +{ +	u64 ts = status->mactime; +	struct rate_info ri; +	u16 rate; + +	if (WARN_ON(!ieee80211_have_rx_timestamp(status))) +		return 0; + +	memset(&ri, 0, sizeof(ri)); + +	/* Fill cfg80211 rate info */ +	if (status->flag & RX_FLAG_HT) { +		ri.mcs = status->rate_idx; +		ri.flags |= RATE_INFO_FLAGS_MCS; +		if (status->flag & RX_FLAG_40MHZ) +			ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; +		if (status->flag & RX_FLAG_SHORT_GI) +			ri.flags |= RATE_INFO_FLAGS_SHORT_GI; +	} else if (status->flag & RX_FLAG_VHT) { +		ri.flags |= RATE_INFO_FLAGS_VHT_MCS; +		ri.mcs = status->rate_idx; +		ri.nss = status->vht_nss; +		if (status->flag & RX_FLAG_40MHZ) +			ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; +		if (status->vht_flag & RX_VHT_FLAG_80MHZ) +			ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; +		if (status->vht_flag & RX_VHT_FLAG_80P80MHZ) +			ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; +		if (status->vht_flag & RX_VHT_FLAG_160MHZ) +			ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; +		if (status->flag & RX_FLAG_SHORT_GI) +			ri.flags |= RATE_INFO_FLAGS_SHORT_GI; +	} else { +		struct ieee80211_supported_band *sband; +		int shift = 0; +		int bitrate; + +		if (status->flag & RX_FLAG_10MHZ) +			shift = 1; +		if (status->flag & RX_FLAG_5MHZ) +			shift = 2; + +		sband = local->hw.wiphy->bands[status->band]; +		bitrate = sband->bitrates[status->rate_idx].bitrate; +		ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift)); +	} + +	rate = cfg80211_calculate_bitrate(&ri); +	if (WARN_ONCE(!rate, +		      "Invalid bitrate: flags=0x%x, idx=%d, vht_nss=%d\n", +		      status->flag, status->rate_idx, status->vht_nss)) +		return 0; + +	/* rewind from end of MPDU */ +	if (status->flag & RX_FLAG_MACTIME_END) +		ts -= mpdu_len * 8 * 10 / rate; + +	ts += mpdu_offset * 8 * 10 / rate; + +	return ts; +} + +void ieee80211_dfs_cac_cancel(struct ieee80211_local *local) +{ +	struct ieee80211_sub_if_data *sdata; +	struct cfg80211_chan_def chandef; + +	mutex_lock(&local->mtx); +	mutex_lock(&local->iflist_mtx); +	list_for_each_entry(sdata, &local->interfaces, list) { +		/* it might be waiting for the local->mtx, but then +		 * by the time it gets it, sdata->wdev.cac_started +		 * will no longer be true +		 */ +		cancel_delayed_work(&sdata->dfs_cac_timer_work); + +		if (sdata->wdev.cac_started) { +			chandef = sdata->vif.bss_conf.chandef; +			ieee80211_vif_release_channel(sdata); +			cfg80211_cac_event(sdata->dev, +					   &chandef, +					   NL80211_RADAR_CAC_ABORTED, +					   GFP_KERNEL); +		} +	} +	mutex_unlock(&local->iflist_mtx); +	mutex_unlock(&local->mtx); +} + +void ieee80211_dfs_radar_detected_work(struct work_struct *work) +{ +	struct ieee80211_local *local = +		container_of(work, struct ieee80211_local, radar_detected_work); +	struct cfg80211_chan_def chandef = local->hw.conf.chandef; + +	ieee80211_dfs_cac_cancel(local); + +	if (local->use_chanctx) +		/* currently not handled */ +		WARN_ON(1); +	else +		cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL); +} + +void ieee80211_radar_detected(struct ieee80211_hw *hw) +{ +	struct ieee80211_local *local = hw_to_local(hw); + +	trace_api_radar_detected(local); + +	ieee80211_queue_work(hw, &local->radar_detected_work); +} +EXPORT_SYMBOL(ieee80211_radar_detected); + +u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) +{ +	u32 ret; +	int tmp; + +	switch (c->width) { +	case NL80211_CHAN_WIDTH_20: +		c->width = NL80211_CHAN_WIDTH_20_NOHT; +		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; +		break; +	case NL80211_CHAN_WIDTH_40: +		c->width = NL80211_CHAN_WIDTH_20; +		c->center_freq1 = c->chan->center_freq; +		ret = IEEE80211_STA_DISABLE_40MHZ | +		      IEEE80211_STA_DISABLE_VHT; +		break; +	case NL80211_CHAN_WIDTH_80: +		tmp = (30 + c->chan->center_freq - c->center_freq1)/20; +		/* n_P40 */ +		tmp /= 2; +		/* freq_P40 */ +		c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; +		c->width = NL80211_CHAN_WIDTH_40; +		ret = IEEE80211_STA_DISABLE_VHT; +		break; +	case NL80211_CHAN_WIDTH_80P80: +		c->center_freq2 = 0; +		c->width = NL80211_CHAN_WIDTH_80; +		ret = IEEE80211_STA_DISABLE_80P80MHZ | +		      IEEE80211_STA_DISABLE_160MHZ; +		break; +	case NL80211_CHAN_WIDTH_160: +		/* n_P20 */ +		tmp = (70 + c->chan->center_freq - c->center_freq1)/20; +		/* n_P80 */ +		tmp /= 4; +		c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; +		c->width = NL80211_CHAN_WIDTH_80; +		ret = IEEE80211_STA_DISABLE_80P80MHZ | +		      IEEE80211_STA_DISABLE_160MHZ; +		break; +	default: +	case NL80211_CHAN_WIDTH_20_NOHT: +		WARN_ON_ONCE(1); +		c->width = NL80211_CHAN_WIDTH_20_NOHT; +		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; +		break; +	case NL80211_CHAN_WIDTH_5: +	case NL80211_CHAN_WIDTH_10: +		WARN_ON_ONCE(1); +		/* keep c->width */ +		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; +		break; +	} + +	WARN_ON_ONCE(!cfg80211_chandef_valid(c)); + +	return ret; +} + +/* + * Returns true if smps_mode_new is strictly more restrictive than + * smps_mode_old. + */ +bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, +				   enum ieee80211_smps_mode smps_mode_new) +{ +	if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC || +			 smps_mode_new == IEEE80211_SMPS_AUTOMATIC)) +		return false; + +	switch (smps_mode_old) { +	case IEEE80211_SMPS_STATIC: +		return false; +	case IEEE80211_SMPS_DYNAMIC: +		return smps_mode_new == IEEE80211_SMPS_STATIC; +	case IEEE80211_SMPS_OFF: +		return smps_mode_new != IEEE80211_SMPS_OFF; +	default: +		WARN_ON(1); +	} + +	return false; +} + +int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, +			      struct cfg80211_csa_settings *csa_settings) +{ +	struct sk_buff *skb; +	struct ieee80211_mgmt *mgmt; +	struct ieee80211_local *local = sdata->local; +	int freq; +	int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) + +			       sizeof(mgmt->u.action.u.chan_switch); +	u8 *pos; + +	if (sdata->vif.type != NL80211_IFTYPE_ADHOC && +	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT) +		return -EOPNOTSUPP; + +	skb = dev_alloc_skb(local->tx_headroom + hdr_len + +			    5 + /* channel switch announcement element */ +			    3 + /* secondary channel offset element */ +			    8); /* mesh channel switch parameters element */ +	if (!skb) +		return -ENOMEM; + +	skb_reserve(skb, local->tx_headroom); +	mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len); +	memset(mgmt, 0, hdr_len); +	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | +					  IEEE80211_STYPE_ACTION); + +	eth_broadcast_addr(mgmt->da); +	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); +	if (ieee80211_vif_is_mesh(&sdata->vif)) { +		memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); +	} else { +		struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; +		memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); +	} +	mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; +	mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH; +	pos = skb_put(skb, 5); +	*pos++ = WLAN_EID_CHANNEL_SWITCH;			/* EID */ +	*pos++ = 3;						/* IE length */ +	*pos++ = csa_settings->block_tx ? 1 : 0;		/* CSA mode */ +	freq = csa_settings->chandef.chan->center_freq; +	*pos++ = ieee80211_frequency_to_channel(freq);		/* channel */ +	*pos++ = csa_settings->count;				/* count */ + +	if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) { +		enum nl80211_channel_type ch_type; + +		skb_put(skb, 3); +		*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;	/* EID */ +		*pos++ = 1;					/* IE length */ +		ch_type = cfg80211_get_chandef_type(&csa_settings->chandef); +		if (ch_type == NL80211_CHAN_HT40PLUS) +			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; +		else +			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW; +	} + +	if (ieee80211_vif_is_mesh(&sdata->vif)) { +		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + +		skb_put(skb, 8); +		*pos++ = WLAN_EID_CHAN_SWITCH_PARAM;		/* EID */ +		*pos++ = 6;					/* IE length */ +		*pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL;	/* Mesh TTL */ +		*pos = 0x00;	/* Mesh Flag: Tx Restrict, Initiator, Reason */ +		*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; +		*pos++ |= csa_settings->block_tx ? +			  WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; +		put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */ +		pos += 2; +		put_unaligned_le16(ifmsh->pre_value, pos);/* Precedence Value */ +		pos += 2; +	} + +	ieee80211_tx_skb(sdata, skb); +	return 0; +} + +bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs) +{ +	return !(cs == NULL || cs->cipher == 0 || +		 cs->hdr_len < cs->pn_len + cs->pn_off || +		 cs->hdr_len <= cs->key_idx_off || +		 cs->key_idx_shift > 7 || +		 cs->key_idx_mask == 0); +} + +bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n) +{ +	int i; + +	/* Ensure we have enough iftype bitmap space for all iftype values */ +	WARN_ON((NUM_NL80211_IFTYPES / 8 + 1) > sizeof(cs[0].iftype)); + +	for (i = 0; i < n; i++) +		if (!ieee80211_cs_valid(&cs[i])) +			return false; + +	return true; +} + +const struct ieee80211_cipher_scheme * +ieee80211_cs_get(struct ieee80211_local *local, u32 cipher, +		 enum nl80211_iftype iftype) +{ +	const struct ieee80211_cipher_scheme *l = local->hw.cipher_schemes; +	int n = local->hw.n_cipher_schemes; +	int i; +	const struct ieee80211_cipher_scheme *cs = NULL; + +	for (i = 0; i < n; i++) { +		if (l[i].cipher == cipher) { +			cs = &l[i]; +			break; +		} +	} + +	if (!cs || !(cs->iftype & BIT(iftype))) +		return NULL; + +	return cs; +} + +int ieee80211_cs_headroom(struct ieee80211_local *local, +			  struct cfg80211_crypto_settings *crypto, +			  enum nl80211_iftype iftype) +{ +	const struct ieee80211_cipher_scheme *cs; +	int headroom = IEEE80211_ENCRYPT_HEADROOM; +	int i; + +	for (i = 0; i < crypto->n_ciphers_pairwise; i++) { +		cs = ieee80211_cs_get(local, crypto->ciphers_pairwise[i], +				      iftype); + +		if (cs && headroom < cs->hdr_len) +			headroom = cs->hdr_len; +	} + +	cs = ieee80211_cs_get(local, crypto->cipher_group, iftype); +	if (cs && headroom < cs->hdr_len) +		headroom = cs->hdr_len; + +	return headroom; +} + +static bool +ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i) +{ +	s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1); +	int skip; + +	if (end > 0) +		return false; + +	/* End time is in the past, check for repetitions */ +	skip = DIV_ROUND_UP(-end, data->desc[i].interval); +	if (data->count[i] < 255) { +		if (data->count[i] <= skip) { +			data->count[i] = 0; +			return false; +		} + +		data->count[i] -= skip; +	} + +	data->desc[i].start += skip * data->desc[i].interval; + +	return true; +} + +static bool +ieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf, +			     s32 *offset) +{ +	bool ret = false; +	int i; + +	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { +		s32 cur; + +		if (!data->count[i]) +			continue; + +		if (ieee80211_extend_noa_desc(data, tsf + *offset, i)) +			ret = true; + +		cur = data->desc[i].start - tsf; +		if (cur > *offset) +			continue; + +		cur = data->desc[i].start + data->desc[i].duration - tsf; +		if (cur > *offset) +			*offset = cur; +	} + +	return ret; +} + +static u32 +ieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf) +{ +	s32 offset = 0; +	int tries = 0; +	/* +	 * arbitrary limit, used to avoid infinite loops when combined NoA +	 * descriptors cover the full time period. +	 */ +	int max_tries = 5; + +	ieee80211_extend_absent_time(data, tsf, &offset); +	do { +		if (!ieee80211_extend_absent_time(data, tsf, &offset)) +			break; + +		tries++; +	} while (tries < max_tries); + +	return offset; +} + +void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf) +{ +	u32 next_offset = BIT(31) - 1; +	int i; + +	data->absent = 0; +	data->has_next_tsf = false; +	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { +		s32 start; + +		if (!data->count[i]) +			continue; + +		ieee80211_extend_noa_desc(data, tsf, i); +		start = data->desc[i].start - tsf; +		if (start <= 0) +			data->absent |= BIT(i); + +		if (next_offset > start) +			next_offset = start; + +		data->has_next_tsf = true; +	} + +	if (data->absent) +		next_offset = ieee80211_get_noa_absent_time(data, tsf); + +	data->next_tsf = tsf + next_offset; +} +EXPORT_SYMBOL(ieee80211_update_p2p_noa); + +int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr, +			    struct ieee80211_noa_data *data, u32 tsf) +{ +	int ret = 0; +	int i; + +	memset(data, 0, sizeof(*data)); + +	for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { +		const struct ieee80211_p2p_noa_desc *desc = &attr->desc[i]; + +		if (!desc->count || !desc->duration) +			continue; + +		data->count[i] = desc->count; +		data->desc[i].start = le32_to_cpu(desc->start_time); +		data->desc[i].duration = le32_to_cpu(desc->duration); +		data->desc[i].interval = le32_to_cpu(desc->interval); + +		if (data->count[i] > 1 && +		    data->desc[i].interval < data->desc[i].duration) +			continue; + +		ieee80211_extend_noa_desc(data, tsf, i); +		ret++; +	} + +	if (ret) +		ieee80211_update_p2p_noa(data, tsf); + +	return ret; +} +EXPORT_SYMBOL(ieee80211_parse_p2p_noa); + +void ieee80211_recalc_dtim(struct ieee80211_local *local, +			   struct ieee80211_sub_if_data *sdata) +{ +	u64 tsf = drv_get_tsf(local, sdata); +	u64 dtim_count = 0; +	u16 beacon_int = sdata->vif.bss_conf.beacon_int * 1024; +	u8 dtim_period = sdata->vif.bss_conf.dtim_period; +	struct ps_data *ps; +	u8 bcns_from_dtim; + +	if (tsf == -1ULL || !beacon_int || !dtim_period) +		return; + +	if (sdata->vif.type == NL80211_IFTYPE_AP || +	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { +		if (!sdata->bss) +			return; + +		ps = &sdata->bss->ps; +	} else if (ieee80211_vif_is_mesh(&sdata->vif)) { +		ps = &sdata->u.mesh.ps; +	} else { +		return; +	} + +	/* +	 * actually finds last dtim_count, mac80211 will update in +	 * __beacon_add_tim(). +	 * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period +	 */ +	do_div(tsf, beacon_int); +	bcns_from_dtim = do_div(tsf, dtim_period); +	/* just had a DTIM */ +	if (!bcns_from_dtim) +		dtim_count = 0; +	else +		dtim_count = dtim_period - bcns_from_dtim; + +	ps->dtim_count = dtim_count; +} + +int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, +				 const struct cfg80211_chan_def *chandef, +				 enum ieee80211_chanctx_mode chanmode, +				 u8 radar_detect) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_sub_if_data *sdata_iter; +	enum nl80211_iftype iftype = sdata->wdev.iftype; +	int num[NUM_NL80211_IFTYPES]; +	struct ieee80211_chanctx *ctx; +	int num_different_channels = 0; +	int total = 1; + +	lockdep_assert_held(&local->chanctx_mtx); + +	if (WARN_ON(hweight32(radar_detect) > 1)) +		return -EINVAL; + +	if (WARN_ON(chandef && chanmode == IEEE80211_CHANCTX_SHARED && +		    !chandef->chan)) +		return -EINVAL; + +	if (chandef) +		num_different_channels = 1; + +	if (WARN_ON(iftype >= NUM_NL80211_IFTYPES)) +		return -EINVAL; + +	/* Always allow software iftypes */ +	if (local->hw.wiphy->software_iftypes & BIT(iftype)) { +		if (radar_detect) +			return -EINVAL; +		return 0; +	} + +	memset(num, 0, sizeof(num)); + +	if (iftype != NL80211_IFTYPE_UNSPECIFIED) +		num[iftype] = 1; + +	list_for_each_entry(ctx, &local->chanctx_list, list) { +		if (ctx->conf.radar_enabled) +			radar_detect |= BIT(ctx->conf.def.width); +		if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) { +			num_different_channels++; +			continue; +		} +		if (chandef && chanmode == IEEE80211_CHANCTX_SHARED && +		    cfg80211_chandef_compatible(chandef, +						&ctx->conf.def)) +			continue; +		num_different_channels++; +	} + +	list_for_each_entry_rcu(sdata_iter, &local->interfaces, list) { +		struct wireless_dev *wdev_iter; + +		wdev_iter = &sdata_iter->wdev; + +		if (sdata_iter == sdata || +		    rcu_access_pointer(sdata_iter->vif.chanctx_conf) == NULL || +		    local->hw.wiphy->software_iftypes & BIT(wdev_iter->iftype)) +			continue; + +		num[wdev_iter->iftype]++; +		total++; +	} + +	if (total == 1 && !radar_detect) +		return 0; + +	return cfg80211_check_combinations(local->hw.wiphy, +					   num_different_channels, +					   radar_detect, num); +} + +static void +ieee80211_iter_max_chans(const struct ieee80211_iface_combination *c, +			 void *data) +{ +	u32 *max_num_different_channels = data; + +	*max_num_different_channels = max(*max_num_different_channels, +					  c->num_different_channels); +} + +int ieee80211_max_num_channels(struct ieee80211_local *local) +{ +	struct ieee80211_sub_if_data *sdata; +	int num[NUM_NL80211_IFTYPES] = {}; +	struct ieee80211_chanctx *ctx; +	int num_different_channels = 0; +	u8 radar_detect = 0; +	u32 max_num_different_channels = 1; +	int err; + +	lockdep_assert_held(&local->chanctx_mtx); + +	list_for_each_entry(ctx, &local->chanctx_list, list) { +		num_different_channels++; + +		if (ctx->conf.radar_enabled) +			radar_detect |= BIT(ctx->conf.def.width); +	} + +	list_for_each_entry_rcu(sdata, &local->interfaces, list) +		num[sdata->wdev.iftype]++; + +	err = cfg80211_iter_combinations(local->hw.wiphy, +					 num_different_channels, radar_detect, +					 num, ieee80211_iter_max_chans, +					 &max_num_different_channels); +	if (err < 0) +		return err; + +	return max_num_different_channels; +} diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c new file mode 100644 index 00000000000..9265adfdabf --- /dev/null +++ b/net/mac80211/vht.c @@ -0,0 +1,419 @@ +/* + * VHT handling + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ieee80211.h> +#include <linux/export.h> +#include <net/mac80211.h> +#include "ieee80211_i.h" +#include "rate.h" + + +static void __check_vhtcap_disable(struct ieee80211_sub_if_data *sdata, +				   struct ieee80211_sta_vht_cap *vht_cap, +				   u32 flag) +{ +	__le32 le_flag = cpu_to_le32(flag); + +	if (sdata->u.mgd.vht_capa_mask.vht_cap_info & le_flag && +	    !(sdata->u.mgd.vht_capa.vht_cap_info & le_flag)) +		vht_cap->cap &= ~flag; +} + +void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata, +				      struct ieee80211_sta_vht_cap *vht_cap) +{ +	int i; +	u16 rxmcs_mask, rxmcs_cap, rxmcs_n, txmcs_mask, txmcs_cap, txmcs_n; + +	if (!vht_cap->vht_supported) +		return; + +	if (sdata->vif.type != NL80211_IFTYPE_STATION) +		return; + +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_RXLDPC); +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_SHORT_GI_80); +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_SHORT_GI_160); +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_TXSTBC); +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN); +	__check_vhtcap_disable(sdata, vht_cap, +			       IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN); + +	/* Allow user to decrease AMPDU length exponent */ +	if (sdata->u.mgd.vht_capa_mask.vht_cap_info & +	    cpu_to_le32(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK)) { +		u32 cap, n; + +		n = le32_to_cpu(sdata->u.mgd.vht_capa.vht_cap_info) & +			IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; +		n >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; +		cap = vht_cap->cap & IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; +		cap >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; + +		if (n < cap) { +			vht_cap->cap &= +				~IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; +			vht_cap->cap |= +				n << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; +		} +	} + +	/* Allow the user to decrease MCSes */ +	rxmcs_mask = +		le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.rx_mcs_map); +	rxmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.rx_mcs_map); +	rxmcs_n &= rxmcs_mask; +	rxmcs_cap = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); + +	txmcs_mask = +		le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.tx_mcs_map); +	txmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.tx_mcs_map); +	txmcs_n &= txmcs_mask; +	txmcs_cap = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); +	for (i = 0; i < 8; i++) { +		u8 m, n, c; + +		m = (rxmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; +		n = (rxmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; +		c = (rxmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + +		if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) || +			  n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) { +			rxmcs_cap &= ~(3 << 2*i); +			rxmcs_cap |= (rxmcs_n & (3 << 2*i)); +		} + +		m = (txmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; +		n = (txmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; +		c = (txmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + +		if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) || +			  n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) { +			txmcs_cap &= ~(3 << 2*i); +			txmcs_cap |= (txmcs_n & (3 << 2*i)); +		} +	} +	vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(rxmcs_cap); +	vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(txmcs_cap); +} + +void +ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, +				    struct ieee80211_supported_band *sband, +				    const struct ieee80211_vht_cap *vht_cap_ie, +				    struct sta_info *sta) +{ +	struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; +	struct ieee80211_sta_vht_cap own_cap; +	u32 cap_info, i; + +	memset(vht_cap, 0, sizeof(*vht_cap)); + +	if (!sta->sta.ht_cap.ht_supported) +		return; + +	if (!vht_cap_ie || !sband->vht_cap.vht_supported) +		return; + +	/* +	 * A VHT STA must support 40 MHz, but if we verify that here +	 * then we break a few things - some APs (e.g. Netgear R6300v2 +	 * and others based on the BCM4360 chipset) will unset this +	 * capability bit when operating in 20 MHz. +	 */ + +	vht_cap->vht_supported = true; + +	own_cap = sband->vht_cap; +	/* +	 * If user has specified capability overrides, take care +	 * of that if the station we're setting up is the AP that +	 * we advertised a restricted capability set to. Override +	 * our own capabilities and then use those below. +	 */ +	if (sdata->vif.type == NL80211_IFTYPE_STATION && +	    !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) +		ieee80211_apply_vhtcap_overrides(sdata, &own_cap); + +	/* take some capabilities as-is */ +	cap_info = le32_to_cpu(vht_cap_ie->vht_cap_info); +	vht_cap->cap = cap_info; +	vht_cap->cap &= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 | +			IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 | +			IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | +			IEEE80211_VHT_CAP_RXLDPC | +			IEEE80211_VHT_CAP_VHT_TXOP_PS | +			IEEE80211_VHT_CAP_HTC_VHT | +			IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | +			IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB | +			IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB | +			IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | +			IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; + +	/* and some based on our own capabilities */ +	switch (own_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { +	case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: +		vht_cap->cap |= cap_info & +				IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; +		break; +	case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: +		vht_cap->cap |= cap_info & +				IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; +		break; +	default: +		/* nothing */ +		break; +	} + +	/* symmetric capabilities */ +	vht_cap->cap |= cap_info & own_cap.cap & +			(IEEE80211_VHT_CAP_SHORT_GI_80 | +			 IEEE80211_VHT_CAP_SHORT_GI_160); + +	/* remaining ones */ +	if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) +		vht_cap->cap |= cap_info & +				(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | +				 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK); + +	if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) +		vht_cap->cap |= cap_info & +				(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | +				 IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK); + +	if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) +		vht_cap->cap |= cap_info & +				IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; + +	if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) +		vht_cap->cap |= cap_info & +				IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; + +	if (own_cap.cap & IEEE80211_VHT_CAP_TXSTBC) +		vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_RXSTBC_MASK; + +	if (own_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK) +		vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_TXSTBC; + +	/* Copy peer MCS info, the driver might need them. */ +	memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, +	       sizeof(struct ieee80211_vht_mcs_info)); + +	/* but also restrict MCSes */ +	for (i = 0; i < 8; i++) { +		u16 own_rx, own_tx, peer_rx, peer_tx; + +		own_rx = le16_to_cpu(own_cap.vht_mcs.rx_mcs_map); +		own_rx = (own_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + +		own_tx = le16_to_cpu(own_cap.vht_mcs.tx_mcs_map); +		own_tx = (own_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + +		peer_rx = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); +		peer_rx = (peer_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + +		peer_tx = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); +		peer_tx = (peer_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + +		if (peer_tx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { +			if (own_rx == IEEE80211_VHT_MCS_NOT_SUPPORTED) +				peer_tx = IEEE80211_VHT_MCS_NOT_SUPPORTED; +			else if (own_rx < peer_tx) +				peer_tx = own_rx; +		} + +		if (peer_rx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { +			if (own_tx == IEEE80211_VHT_MCS_NOT_SUPPORTED) +				peer_rx = IEEE80211_VHT_MCS_NOT_SUPPORTED; +			else if (own_tx < peer_rx) +				peer_rx = own_tx; +		} + +		vht_cap->vht_mcs.rx_mcs_map &= +			~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); +		vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(peer_rx << i * 2); + +		vht_cap->vht_mcs.tx_mcs_map &= +			~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); +		vht_cap->vht_mcs.tx_mcs_map |= cpu_to_le16(peer_tx << i * 2); +	} + +	/* finally set up the bandwidth */ +	switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { +	case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: +	case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: +		sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; +		break; +	default: +		sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; +	} + +	sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); +} + +enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) +{ +	struct ieee80211_sub_if_data *sdata = sta->sdata; +	u32 cap = sta->sta.vht_cap.cap; +	enum ieee80211_sta_rx_bandwidth bw; + +	if (!sta->sta.vht_cap.vht_supported) { +		bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? +				IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; +		goto check_max; +	} + +	switch (sdata->vif.bss_conf.chandef.width) { +	default: +		WARN_ON_ONCE(1); +		/* fall through */ +	case NL80211_CHAN_WIDTH_20_NOHT: +	case NL80211_CHAN_WIDTH_20: +	case NL80211_CHAN_WIDTH_40: +		bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? +				IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; +		break; +	case NL80211_CHAN_WIDTH_160: +		if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) == +				IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) { +			bw = IEEE80211_STA_RX_BW_160; +			break; +		} +		/* fall through */ +	case NL80211_CHAN_WIDTH_80P80: +		if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) == +				IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) { +			bw = IEEE80211_STA_RX_BW_160; +			break; +		} +		/* fall through */ +	case NL80211_CHAN_WIDTH_80: +		bw = IEEE80211_STA_RX_BW_80; +	} + + check_max: +	if (bw > sta->cur_max_bandwidth) +		bw = sta->cur_max_bandwidth; +	return bw; +} + +void ieee80211_sta_set_rx_nss(struct sta_info *sta) +{ +	u8 ht_rx_nss = 0, vht_rx_nss = 0; + +	/* if we received a notification already don't overwrite it */ +	if (sta->sta.rx_nss) +		return; + +	if (sta->sta.ht_cap.ht_supported) { +		if (sta->sta.ht_cap.mcs.rx_mask[0]) +			ht_rx_nss++; +		if (sta->sta.ht_cap.mcs.rx_mask[1]) +			ht_rx_nss++; +		if (sta->sta.ht_cap.mcs.rx_mask[2]) +			ht_rx_nss++; +		if (sta->sta.ht_cap.mcs.rx_mask[3]) +			ht_rx_nss++; +		/* FIXME: consider rx_highest? */ +	} + +	if (sta->sta.vht_cap.vht_supported) { +		int i; +		u16 rx_mcs_map; + +		rx_mcs_map = le16_to_cpu(sta->sta.vht_cap.vht_mcs.rx_mcs_map); + +		for (i = 7; i >= 0; i--) { +			u8 mcs = (rx_mcs_map >> (2 * i)) & 3; + +			if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) { +				vht_rx_nss = i + 1; +				break; +			} +		} +		/* FIXME: consider rx_highest? */ +	} + +	ht_rx_nss = max(ht_rx_nss, vht_rx_nss); +	sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss); +} + +u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, +				  struct sta_info *sta, u8 opmode, +				  enum ieee80211_band band, bool nss_only) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_supported_band *sband; +	enum ieee80211_sta_rx_bandwidth new_bw; +	u32 changed = 0; +	u8 nss; + +	sband = local->hw.wiphy->bands[band]; + +	/* ignore - no support for BF yet */ +	if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF) +		return 0; + +	nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK; +	nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; +	nss += 1; + +	if (sta->sta.rx_nss != nss) { +		sta->sta.rx_nss = nss; +		changed |= IEEE80211_RC_NSS_CHANGED; +	} + +	if (nss_only) +		return changed; + +	switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) { +	case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ: +		sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20; +		break; +	case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ: +		sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40; +		break; +	case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ: +		sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; +		break; +	case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ: +		sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; +		break; +	} + +	new_bw = ieee80211_sta_cur_vht_bw(sta); +	if (new_bw != sta->sta.bandwidth) { +		sta->sta.bandwidth = new_bw; +		changed |= IEEE80211_RC_BW_CHANGED; +	} + +	return changed; +} + +void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, +				 struct sta_info *sta, u8 opmode, +				 enum ieee80211_band band, bool nss_only) +{ +	struct ieee80211_local *local = sdata->local; +	struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; + +	u32 changed = __ieee80211_vht_handle_opmode(sdata, sta, opmode, +						    band, nss_only); + +	if (changed > 0) +		rate_control_rate_update(local, sband, sta, changed); +} diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index 2ff6d1e3ed2..6ee2b586357 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -28,19 +28,17 @@  int ieee80211_wep_init(struct ieee80211_local *local)  {  	/* start WEP IV from a random value */ -	get_random_bytes(&local->wep_iv, WEP_IV_LEN); +	get_random_bytes(&local->wep_iv, IEEE80211_WEP_IV_LEN); -	local->wep_tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, -						CRYPTO_ALG_ASYNC); +	local->wep_tx_tfm = crypto_alloc_cipher("arc4", 0, CRYPTO_ALG_ASYNC);  	if (IS_ERR(local->wep_tx_tfm)) {  		local->wep_rx_tfm = ERR_PTR(-EINVAL);  		return PTR_ERR(local->wep_tx_tfm);  	} -	local->wep_rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, -						CRYPTO_ALG_ASYNC); +	local->wep_rx_tfm = crypto_alloc_cipher("arc4", 0, CRYPTO_ALG_ASYNC);  	if (IS_ERR(local->wep_rx_tfm)) { -		crypto_free_blkcipher(local->wep_tx_tfm); +		crypto_free_cipher(local->wep_tx_tfm);  		local->wep_tx_tfm = ERR_PTR(-EINVAL);  		return PTR_ERR(local->wep_rx_tfm);  	} @@ -51,9 +49,9 @@ int ieee80211_wep_init(struct ieee80211_local *local)  void ieee80211_wep_free(struct ieee80211_local *local)  {  	if (!IS_ERR(local->wep_tx_tfm)) -		crypto_free_blkcipher(local->wep_tx_tfm); +		crypto_free_cipher(local->wep_tx_tfm);  	if (!IS_ERR(local->wep_rx_tfm)) -		crypto_free_blkcipher(local->wep_rx_tfm); +		crypto_free_cipher(local->wep_rx_tfm);  }  static inline bool ieee80211_wep_weak_iv(u32 iv, int keylen) @@ -94,18 +92,27 @@ static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local,  				int keylen, int keyidx)  {  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);  	unsigned int hdrlen;  	u8 *newhdr;  	hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); -	if (WARN_ON(skb_tailroom(skb) < WEP_ICV_LEN || -		    skb_headroom(skb) < WEP_IV_LEN)) +	if (WARN_ON(skb_tailroom(skb) < IEEE80211_WEP_ICV_LEN || +		    skb_headroom(skb) < IEEE80211_WEP_IV_LEN))  		return NULL;  	hdrlen = ieee80211_hdrlen(hdr->frame_control); -	newhdr = skb_push(skb, WEP_IV_LEN); -	memmove(newhdr, newhdr + WEP_IV_LEN, hdrlen); +	newhdr = skb_push(skb, IEEE80211_WEP_IV_LEN); +	memmove(newhdr, newhdr + IEEE80211_WEP_IV_LEN, hdrlen); + +	/* the HW only needs room for the IV, but not the actual IV */ +	if (info->control.hw_key && +	    (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) +		return newhdr + hdrlen; + +	skb_set_network_header(skb, skb_network_offset(skb) + +				    IEEE80211_WEP_IV_LEN);  	ieee80211_wep_get_iv(local, keylen, keyidx, newhdr + hdrlen);  	return newhdr + hdrlen;  } @@ -119,20 +126,19 @@ static void ieee80211_wep_remove_iv(struct ieee80211_local *local,  	unsigned int hdrlen;  	hdrlen = ieee80211_hdrlen(hdr->frame_control); -	memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen); -	skb_pull(skb, WEP_IV_LEN); +	memmove(skb->data + IEEE80211_WEP_IV_LEN, skb->data, hdrlen); +	skb_pull(skb, IEEE80211_WEP_IV_LEN);  }  /* Perform WEP encryption using given key. data buffer must have tailroom   * for 4-byte ICV. data_len must not include this ICV. Note: this function   * does _not_ add IV. data = RC4(data | CRC32(data)) */ -int ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, +int ieee80211_wep_encrypt_data(struct crypto_cipher *tfm, u8 *rc4key,  			       size_t klen, u8 *data, size_t data_len)  { -	struct blkcipher_desc desc = { .tfm = tfm }; -	struct scatterlist sg;  	__le32 icv; +	int i;  	if (IS_ERR(tfm))  		return -1; @@ -140,9 +146,9 @@ int ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key,  	icv = cpu_to_le32(~crc32_le(~0, data, data_len));  	put_unaligned(icv, (__le32 *)(data + data_len)); -	crypto_blkcipher_setkey(tfm, rc4key, klen); -	sg_init_one(&sg, data, data_len + WEP_ICV_LEN); -	crypto_blkcipher_encrypt(&desc, &sg, &sg, sg.length); +	crypto_cipher_setkey(tfm, rc4key, klen); +	for (i = 0; i < data_len + IEEE80211_WEP_ICV_LEN; i++) +		crypto_cipher_encrypt_one(tfm, data + i, data + i);  	return 0;  } @@ -167,7 +173,7 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local,  	if (!iv)  		return -1; -	len = skb->len - (iv + WEP_IV_LEN - skb->data); +	len = skb->len - (iv + IEEE80211_WEP_IV_LEN - skb->data);  	/* Prepend 24-bit IV to RC4 key */  	memcpy(rc4key, iv, 3); @@ -176,32 +182,31 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local,  	memcpy(rc4key + 3, key, keylen);  	/* Add room for ICV */ -	skb_put(skb, WEP_ICV_LEN); +	skb_put(skb, IEEE80211_WEP_ICV_LEN);  	return ieee80211_wep_encrypt_data(local->wep_tx_tfm, rc4key, keylen + 3, -					  iv + WEP_IV_LEN, len); +					  iv + IEEE80211_WEP_IV_LEN, len);  }  /* Perform WEP decryption using given key. data buffer includes encrypted   * payload, including 4-byte ICV, but _not_ IV. data_len must not include ICV.   * Return 0 on success and -1 on ICV mismatch. */ -int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, +int ieee80211_wep_decrypt_data(struct crypto_cipher *tfm, u8 *rc4key,  			       size_t klen, u8 *data, size_t data_len)  { -	struct blkcipher_desc desc = { .tfm = tfm }; -	struct scatterlist sg;  	__le32 crc; +	int i;  	if (IS_ERR(tfm))  		return -1; -	crypto_blkcipher_setkey(tfm, rc4key, klen); -	sg_init_one(&sg, data, data_len + WEP_ICV_LEN); -	crypto_blkcipher_decrypt(&desc, &sg, &sg, sg.length); +	crypto_cipher_setkey(tfm, rc4key, klen); +	for (i = 0; i < data_len + IEEE80211_WEP_ICV_LEN; i++) +		crypto_cipher_decrypt_one(tfm, data + i, data + i);  	crc = cpu_to_le32(~crc32_le(~0, data, data_len)); -	if (memcmp(&crc, data + data_len, WEP_ICV_LEN) != 0) +	if (memcmp(&crc, data + data_len, IEEE80211_WEP_ICV_LEN) != 0)  		/* ICV mismatch */  		return -1; @@ -233,10 +238,10 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local,  		return -1;  	hdrlen = ieee80211_hdrlen(hdr->frame_control); -	if (skb->len < hdrlen + WEP_IV_LEN + WEP_ICV_LEN) +	if (skb->len < hdrlen + IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN)  		return -1; -	len = skb->len - hdrlen - WEP_IV_LEN - WEP_ICV_LEN; +	len = skb->len - hdrlen - IEEE80211_WEP_IV_LEN - IEEE80211_WEP_ICV_LEN;  	keyidx = skb->data[hdrlen + 3] >> 6; @@ -252,31 +257,29 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local,  	memcpy(rc4key + 3, key->conf.key, key->conf.keylen);  	if (ieee80211_wep_decrypt_data(local->wep_rx_tfm, rc4key, klen, -				       skb->data + hdrlen + WEP_IV_LEN, -				       len)) +				       skb->data + hdrlen + +				       IEEE80211_WEP_IV_LEN, len))  		ret = -1;  	/* Trim ICV */ -	skb_trim(skb, skb->len - WEP_ICV_LEN); +	skb_trim(skb, skb->len - IEEE80211_WEP_ICV_LEN);  	/* Remove IV */ -	memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen); -	skb_pull(skb, WEP_IV_LEN); +	memmove(skb->data + IEEE80211_WEP_IV_LEN, skb->data, hdrlen); +	skb_pull(skb, IEEE80211_WEP_IV_LEN);  	return ret;  } -bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key) +static bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, +				     struct ieee80211_key *key)  {  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;  	unsigned int hdrlen;  	u8 *ivpos;  	u32 iv; -	if (!ieee80211_has_protected(hdr->frame_control)) -		return false; -  	hdrlen = ieee80211_hdrlen(hdr->frame_control);  	ivpos = skb->data + hdrlen;  	iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2]; @@ -290,18 +293,28 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)  	struct sk_buff *skb = rx->skb;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; +	__le16 fc = hdr->frame_control; -	if (!ieee80211_is_data(hdr->frame_control) && -	    !ieee80211_is_auth(hdr->frame_control)) +	if (!ieee80211_is_data(fc) && !ieee80211_is_auth(fc))  		return RX_CONTINUE;  	if (!(status->flag & RX_FLAG_DECRYPTED)) { +		if (skb_linearize(rx->skb)) +			return RX_DROP_UNUSABLE; +		if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key)) +			rx->sta->wep_weak_iv_count++;  		if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key))  			return RX_DROP_UNUSABLE;  	} else if (!(status->flag & RX_FLAG_IV_STRIPPED)) { +		if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) + +					    IEEE80211_WEP_IV_LEN)) +			return RX_DROP_UNUSABLE; +		if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key)) +			rx->sta->wep_weak_iv_count++;  		ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);  		/* remove ICV */ -		skb_trim(rx->skb, rx->skb->len - WEP_ICV_LEN); +		if (pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN)) +			return RX_DROP_UNUSABLE;  	}  	return RX_CONTINUE; @@ -310,14 +323,15 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)  static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  {  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct ieee80211_key_conf *hw_key = info->control.hw_key; -	if (!info->control.hw_key) { +	if (!hw_key) {  		if (ieee80211_wep_encrypt(tx->local, skb, tx->key->conf.key,  					  tx->key->conf.keylen,  					  tx->key->conf.keyidx))  			return -1; -	} else if (info->control.hw_key->flags & -			IEEE80211_KEY_FLAG_GENERATE_IV) { +	} else if ((hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) || +		   (hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {  		if (!ieee80211_wep_add_iv(tx->local, skb,  					  tx->key->conf.keylen,  					  tx->key->conf.keyidx)) @@ -334,13 +348,12 @@ ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)  	ieee80211_tx_set_protected(tx); -	skb = tx->skb; -	do { +	skb_queue_walk(&tx->skbs, skb) {  		if (wep_encrypt_skb(tx, skb) < 0) {  			I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);  			return TX_DROP;  		} -	} while ((skb = skb->next)); +	}  	return TX_CONTINUE;  } diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h index 58654ee3351..9615749d1f6 100644 --- a/net/mac80211/wep.h +++ b/net/mac80211/wep.h @@ -18,14 +18,13 @@  int ieee80211_wep_init(struct ieee80211_local *local);  void ieee80211_wep_free(struct ieee80211_local *local); -int ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, +int ieee80211_wep_encrypt_data(struct crypto_cipher *tfm, u8 *rc4key,  				size_t klen, u8 *data, size_t data_len);  int ieee80211_wep_encrypt(struct ieee80211_local *local,  			  struct sk_buff *skb,  			  const u8 *key, int keylen, int keyidx); -int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, +int ieee80211_wep_decrypt_data(struct crypto_cipher *tfm, u8 *rc4key,  			       size_t klen, u8 *data, size_t data_len); -bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);  ieee80211_rx_result  ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx); diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 58e75bbc1f9..d51422c778d 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -52,6 +52,51 @@ static int wme_downgrade_ac(struct sk_buff *skb)  	}  } +static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata, +				     struct sk_buff *skb) +{ +	/* in case we are a client verify acm is not set for this ac */ +	while (unlikely(sdata->wmm_acm & BIT(skb->priority))) { +		if (wme_downgrade_ac(skb)) { +			/* +			 * This should not really happen. The AP has marked all +			 * lower ACs to require admission control which is not +			 * a reasonable configuration. Allow the frame to be +			 * transmitted using AC_BK as a workaround. +			 */ +			break; +		} +	} + +	/* look up which queue to use for frames with this 1d tag */ +	return ieee802_1d_to_ac[skb->priority]; +} + +/* Indicate which queue to use for this fully formed 802.11 frame */ +u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata, +				 struct sk_buff *skb, +				 struct ieee80211_hdr *hdr) +{ +	struct ieee80211_local *local = sdata->local; +	u8 *p; + +	if (local->hw.queues < IEEE80211_NUM_ACS) +		return 0; + +	if (!ieee80211_is_data(hdr->frame_control)) { +		skb->priority = 7; +		return ieee802_1d_to_ac[skb->priority]; +	} +	if (!ieee80211_is_data_qos(hdr->frame_control)) { +		skb->priority = 0; +		return ieee802_1d_to_ac[skb->priority]; +	} + +	p = ieee80211_get_qos_ctl(hdr); +	skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK; + +	return ieee80211_downgrade_queue(sdata, skb); +}  /* Indicate which queue to use. */  u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, @@ -59,26 +104,23 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,  {  	struct ieee80211_local *local = sdata->local;  	struct sta_info *sta = NULL; -	u32 sta_flags = 0;  	const u8 *ra = NULL;  	bool qos = false; +	struct mac80211_qos_map *qos_map; -	if (local->hw.queues < 4 || skb->len < 6) { +	if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) {  		skb->priority = 0; /* required for correct WPA/11i MIC */ -		return min_t(u16, local->hw.queues - 1, -			     ieee802_1d_to_ac[skb->priority]); +		return 0;  	}  	rcu_read_lock();  	switch (sdata->vif.type) {  	case NL80211_IFTYPE_AP_VLAN: -		rcu_read_lock();  		sta = rcu_dereference(sdata->u.vlan.sta); -		if (sta) -			sta_flags = get_sta_flags(sta); -		rcu_read_unlock(); -		if (sta) +		if (sta) { +			qos = test_sta_flag(sta, WLAN_STA_WME);  			break; +		}  	case NL80211_IFTYPE_AP:  		ra = skb->data;  		break; @@ -87,11 +129,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,  		break;  #ifdef CONFIG_MAC80211_MESH  	case NL80211_IFTYPE_MESH_POINT: -		/* -		 * XXX: This is clearly broken ... but already was before, -		 * because ieee80211_fill_mesh_addresses() would clear A1 -		 * except for multicast addresses. -		 */ +		qos = true;  		break;  #endif  	case NL80211_IFTYPE_STATION: @@ -107,62 +145,71 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,  	if (!sta && ra && !is_multicast_ether_addr(ra)) {  		sta = sta_info_get(sdata, ra);  		if (sta) -			sta_flags = get_sta_flags(sta); +			qos = test_sta_flag(sta, WLAN_STA_WME);  	} - -	if (sta_flags & WLAN_STA_WME) -		qos = true; -  	rcu_read_unlock();  	if (!qos) {  		skb->priority = 0; /* required for correct WPA/11i MIC */ -		return ieee802_1d_to_ac[skb->priority]; +		return IEEE80211_AC_BE; +	} + +	if (skb->protocol == sdata->control_port_protocol) { +		skb->priority = 7; +		return ieee80211_downgrade_queue(sdata, skb);  	}  	/* use the data classifier to determine what 802.1d tag the  	 * data frame has */ -	skb->priority = cfg80211_classify8021d(skb); +	rcu_read_lock(); +	qos_map = rcu_dereference(sdata->qos_map); +	skb->priority = cfg80211_classify8021d(skb, qos_map ? +					       &qos_map->qos_map : NULL); +	rcu_read_unlock(); -	return ieee80211_downgrade_queue(local, skb); +	return ieee80211_downgrade_queue(sdata, skb);  } -u16 ieee80211_downgrade_queue(struct ieee80211_local *local, -			      struct sk_buff *skb) +/** + * ieee80211_set_qos_hdr - Fill in the QoS header if there is one. + * + * @sdata: local subif + * @skb: packet to be updated + */ +void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, +			   struct sk_buff *skb)  { -	/* in case we are a client verify acm is not set for this ac */ -	while (unlikely(local->wmm_acm & BIT(skb->priority))) { -		if (wme_downgrade_ac(skb)) { -			/* -			 * This should not really happen. The AP has marked all -			 * lower ACs to require admission control which is not -			 * a reasonable configuration. Allow the frame to be -			 * transmitted using AC_BK as a workaround. -			 */ -			break; -		} -	} +	struct ieee80211_hdr *hdr = (void *)skb->data; +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	u8 *p; +	u8 ack_policy, tid; -	/* look up which queue to use for frames with this 1d tag */ -	return ieee802_1d_to_ac[skb->priority]; -} +	if (!ieee80211_is_data_qos(hdr->frame_control)) +		return; -void ieee80211_set_qos_hdr(struct ieee80211_local *local, struct sk_buff *skb) -{ -	struct ieee80211_hdr *hdr = (void *)skb->data; +	p = ieee80211_get_qos_ctl(hdr); +	tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; -	/* Fill in the QoS header if there is one. */ -	if (ieee80211_is_data_qos(hdr->frame_control)) { -		u8 *p = ieee80211_get_qos_ctl(hdr); -		u8 ack_policy = 0, tid; +	/* preserve EOSP bit */ +	ack_policy = *p & IEEE80211_QOS_CTL_EOSP; -		tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; +	if (is_multicast_ether_addr(hdr->addr1) || +	    sdata->noack_map & BIT(tid)) { +		ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK; +		info->flags |= IEEE80211_TX_CTL_NO_ACK; +	} -		if (unlikely(local->wifi_wme_noack_test)) -			ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK << -					QOS_CONTROL_ACK_POLICY_SHIFT; -		/* qos header is 2 bytes, second reserved */ -		*p++ = ack_policy | tid; +	/* qos header is 2 bytes */ +	*p++ = ack_policy | tid; +	if (ieee80211_vif_is_mesh(&sdata->vif)) { +		/* preserve RSPI and Mesh PS Level bit */ +		*p &= ((IEEE80211_QOS_CTL_RSPI | +			IEEE80211_QOS_CTL_MESH_PS_LEVEL) >> 8); + +		/* Nulls don't have a mesh header (frame body) */ +		if (!ieee80211_is_qos_nullfunc(hdr->frame_control)) +			*p |= (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8); +	} else {  		*p = 0;  	}  } diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h index 6053b1c9fee..7fea4bb8acb 100644 --- a/net/mac80211/wme.h +++ b/net/mac80211/wme.h @@ -13,18 +13,14 @@  #include <linux/netdevice.h>  #include "ieee80211_i.h" -#define QOS_CONTROL_ACK_POLICY_NORMAL 0 -#define QOS_CONTROL_ACK_POLICY_NOACK 1 - -#define QOS_CONTROL_ACK_POLICY_SHIFT 5 -  extern const int ieee802_1d_to_ac[8]; +u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata, +				 struct sk_buff *skb, +				 struct ieee80211_hdr *hdr);  u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,  			   struct sk_buff *skb); -void ieee80211_set_qos_hdr(struct ieee80211_local *local, struct sk_buff *skb); -u16 ieee80211_downgrade_queue(struct ieee80211_local *local, -                              struct sk_buff *skb); - +void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, +			   struct sk_buff *skb);  #endif /* _WME_H */ diff --git a/net/mac80211/work.c b/net/mac80211/work.c deleted file mode 100644 index ae344d1ba05..00000000000 --- a/net/mac80211/work.c +++ /dev/null @@ -1,1177 +0,0 @@ -/* - * mac80211 work implementation - * - * Copyright 2003-2008, Jouni Malinen <j@w1.fi> - * Copyright 2004, Instant802 Networks, Inc. - * Copyright 2005, Devicescape Software, Inc. - * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz> - * Copyright 2007, Michael Wu <flamingice@sourmilk.net> - * Copyright 2009, Johannes Berg <johannes@sipsolutions.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/delay.h> -#include <linux/if_ether.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/etherdevice.h> -#include <linux/crc32.h> -#include <linux/slab.h> -#include <net/mac80211.h> -#include <asm/unaligned.h> - -#include "ieee80211_i.h" -#include "rate.h" - -#define IEEE80211_AUTH_TIMEOUT (HZ / 5) -#define IEEE80211_AUTH_MAX_TRIES 3 -#define IEEE80211_ASSOC_TIMEOUT (HZ / 5) -#define IEEE80211_ASSOC_MAX_TRIES 3 -#define IEEE80211_MAX_PROBE_TRIES 5 - -enum work_action { -	WORK_ACT_MISMATCH, -	WORK_ACT_NONE, -	WORK_ACT_TIMEOUT, -	WORK_ACT_DONE, -}; - - -/* utils */ -static inline void ASSERT_WORK_MTX(struct ieee80211_local *local) -{ -	lockdep_assert_held(&local->mtx); -} - -/* - * We can have multiple work items (and connection probing) - * scheduling this timer, but we need to take care to only - * reschedule it when it should fire _earlier_ than it was - * asked for before, or if it's not pending right now. This - * function ensures that. Note that it then is required to - * run this function for all timeouts after the first one - * has happened -- the work that runs from this timer will - * do that. - */ -static void run_again(struct ieee80211_local *local, -		      unsigned long timeout) -{ -	ASSERT_WORK_MTX(local); - -	if (!timer_pending(&local->work_timer) || -	    time_before(timeout, local->work_timer.expires)) -		mod_timer(&local->work_timer, timeout); -} - -static void work_free_rcu(struct rcu_head *head) -{ -	struct ieee80211_work *wk = -		container_of(head, struct ieee80211_work, rcu_head); - -	kfree(wk); -} - -void free_work(struct ieee80211_work *wk) -{ -	call_rcu(&wk->rcu_head, work_free_rcu); -} - -static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len, -				      struct ieee80211_supported_band *sband, -				      u32 *rates) -{ -	int i, j, count; -	*rates = 0; -	count = 0; -	for (i = 0; i < supp_rates_len; i++) { -		int rate = (supp_rates[i] & 0x7F) * 5; - -		for (j = 0; j < sband->n_bitrates; j++) -			if (sband->bitrates[j].bitrate == rate) { -				*rates |= BIT(j); -				count++; -				break; -			} -	} - -	return count; -} - -/* frame sending functions */ - -static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie, -				struct ieee80211_supported_band *sband, -				struct ieee80211_channel *channel, -				enum ieee80211_smps_mode smps) -{ -	struct ieee80211_ht_info *ht_info; -	u8 *pos; -	u32 flags = channel->flags; -	u16 cap = sband->ht_cap.cap; -	__le16 tmp; - -	if (!sband->ht_cap.ht_supported) -		return; - -	if (!ht_info_ie) -		return; - -	if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info)) -		return; - -	ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2); - -	/* determine capability flags */ - -	if (ieee80211_disable_40mhz_24ghz && -	    sband->band == IEEE80211_BAND_2GHZ) { -		cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; -		cap &= ~IEEE80211_HT_CAP_SGI_40; -	} - -	switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { -	case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: -		if (flags & IEEE80211_CHAN_NO_HT40PLUS) { -			cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; -			cap &= ~IEEE80211_HT_CAP_SGI_40; -		} -		break; -	case IEEE80211_HT_PARAM_CHA_SEC_BELOW: -		if (flags & IEEE80211_CHAN_NO_HT40MINUS) { -			cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; -			cap &= ~IEEE80211_HT_CAP_SGI_40; -		} -		break; -	} - -	/* set SM PS mode properly */ -	cap &= ~IEEE80211_HT_CAP_SM_PS; -	switch (smps) { -	case IEEE80211_SMPS_AUTOMATIC: -	case IEEE80211_SMPS_NUM_MODES: -		WARN_ON(1); -	case IEEE80211_SMPS_OFF: -		cap |= WLAN_HT_CAP_SM_PS_DISABLED << -			IEEE80211_HT_CAP_SM_PS_SHIFT; -		break; -	case IEEE80211_SMPS_STATIC: -		cap |= WLAN_HT_CAP_SM_PS_STATIC << -			IEEE80211_HT_CAP_SM_PS_SHIFT; -		break; -	case IEEE80211_SMPS_DYNAMIC: -		cap |= WLAN_HT_CAP_SM_PS_DYNAMIC << -			IEEE80211_HT_CAP_SM_PS_SHIFT; -		break; -	} - -	/* reserve and fill IE */ - -	pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); -	*pos++ = WLAN_EID_HT_CAPABILITY; -	*pos++ = sizeof(struct ieee80211_ht_cap); -	memset(pos, 0, sizeof(struct ieee80211_ht_cap)); - -	/* capability flags */ -	tmp = cpu_to_le16(cap); -	memcpy(pos, &tmp, sizeof(u16)); -	pos += sizeof(u16); - -	/* AMPDU parameters */ -	*pos++ = sband->ht_cap.ampdu_factor | -		 (sband->ht_cap.ampdu_density << -			IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); - -	/* MCS set */ -	memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); -	pos += sizeof(sband->ht_cap.mcs); - -	/* extended capabilities */ -	pos += sizeof(__le16); - -	/* BF capabilities */ -	pos += sizeof(__le32); - -	/* antenna selection */ -	pos += sizeof(u8); -} - -static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, -				 struct ieee80211_work *wk) -{ -	struct ieee80211_local *local = sdata->local; -	struct sk_buff *skb; -	struct ieee80211_mgmt *mgmt; -	u8 *pos, qos_info; -	const u8 *ies; -	size_t offset = 0, noffset; -	int i, len, count, rates_len, supp_rates_len; -	u16 capab; -	struct ieee80211_supported_band *sband; -	u32 rates = 0; - -	sband = local->hw.wiphy->bands[wk->chan->band]; - -	if (wk->assoc.supp_rates_len) { -		/* -		 * Get all rates supported by the device and the AP as -		 * some APs don't like getting a superset of their rates -		 * in the association request (e.g. D-Link DAP 1353 in -		 * b-only mode)... -		 */ -		rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates, -						       wk->assoc.supp_rates_len, -						       sband, &rates); -	} else { -		/* -		 * In case AP not provide any supported rates information -		 * before association, we send information element(s) with -		 * all rates that we support. -		 */ -		rates = ~0; -		rates_len = sband->n_bitrates; -	} - -	skb = alloc_skb(local->hw.extra_tx_headroom + -			sizeof(*mgmt) + /* bit too much but doesn't matter */ -			2 + wk->assoc.ssid_len + /* SSID */ -			4 + rates_len + /* (extended) rates */ -			4 + /* power capability */ -			2 + 2 * sband->n_channels + /* supported channels */ -			2 + sizeof(struct ieee80211_ht_cap) + /* HT */ -			wk->ie_len + /* extra IEs */ -			9, /* WMM */ -			GFP_KERNEL); -	if (!skb) { -		printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " -		       "frame\n", sdata->name); -		return; -	} -	skb_reserve(skb, local->hw.extra_tx_headroom); - -	capab = WLAN_CAPABILITY_ESS; - -	if (sband->band == IEEE80211_BAND_2GHZ) { -		if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) -			capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; -		if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) -			capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; -	} - -	if (wk->assoc.capability & WLAN_CAPABILITY_PRIVACY) -		capab |= WLAN_CAPABILITY_PRIVACY; - -	if ((wk->assoc.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && -	    (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) -		capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; - -	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); -	memset(mgmt, 0, 24); -	memcpy(mgmt->da, wk->filter_ta, ETH_ALEN); -	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); -	memcpy(mgmt->bssid, wk->filter_ta, ETH_ALEN); - -	if (!is_zero_ether_addr(wk->assoc.prev_bssid)) { -		skb_put(skb, 10); -		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | -						  IEEE80211_STYPE_REASSOC_REQ); -		mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab); -		mgmt->u.reassoc_req.listen_interval = -				cpu_to_le16(local->hw.conf.listen_interval); -		memcpy(mgmt->u.reassoc_req.current_ap, wk->assoc.prev_bssid, -		       ETH_ALEN); -	} else { -		skb_put(skb, 4); -		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | -						  IEEE80211_STYPE_ASSOC_REQ); -		mgmt->u.assoc_req.capab_info = cpu_to_le16(capab); -		mgmt->u.assoc_req.listen_interval = -				cpu_to_le16(local->hw.conf.listen_interval); -	} - -	/* SSID */ -	ies = pos = skb_put(skb, 2 + wk->assoc.ssid_len); -	*pos++ = WLAN_EID_SSID; -	*pos++ = wk->assoc.ssid_len; -	memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len); - -	/* add all rates which were marked to be used above */ -	supp_rates_len = rates_len; -	if (supp_rates_len > 8) -		supp_rates_len = 8; - -	len = sband->n_bitrates; -	pos = skb_put(skb, supp_rates_len + 2); -	*pos++ = WLAN_EID_SUPP_RATES; -	*pos++ = supp_rates_len; - -	count = 0; -	for (i = 0; i < sband->n_bitrates; i++) { -		if (BIT(i) & rates) { -			int rate = sband->bitrates[i].bitrate; -			*pos++ = (u8) (rate / 5); -			if (++count == 8) -				break; -		} -	} - -	if (rates_len > count) { -		pos = skb_put(skb, rates_len - count + 2); -		*pos++ = WLAN_EID_EXT_SUPP_RATES; -		*pos++ = rates_len - count; - -		for (i++; i < sband->n_bitrates; i++) { -			if (BIT(i) & rates) { -				int rate = sband->bitrates[i].bitrate; -				*pos++ = (u8) (rate / 5); -			} -		} -	} - -	if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) { -		/* 1. power capabilities */ -		pos = skb_put(skb, 4); -		*pos++ = WLAN_EID_PWR_CAPABILITY; -		*pos++ = 2; -		*pos++ = 0; /* min tx power */ -		*pos++ = wk->chan->max_power; /* max tx power */ - -		/* 2. supported channels */ -		/* TODO: get this in reg domain format */ -		pos = skb_put(skb, 2 * sband->n_channels + 2); -		*pos++ = WLAN_EID_SUPPORTED_CHANNELS; -		*pos++ = 2 * sband->n_channels; -		for (i = 0; i < sband->n_channels; i++) { -			*pos++ = ieee80211_frequency_to_channel( -					sband->channels[i].center_freq); -			*pos++ = 1; /* one channel in the subband*/ -		} -	} - -	/* if present, add any custom IEs that go before HT */ -	if (wk->ie_len && wk->ie) { -		static const u8 before_ht[] = { -			WLAN_EID_SSID, -			WLAN_EID_SUPP_RATES, -			WLAN_EID_EXT_SUPP_RATES, -			WLAN_EID_PWR_CAPABILITY, -			WLAN_EID_SUPPORTED_CHANNELS, -			WLAN_EID_RSN, -			WLAN_EID_QOS_CAPA, -			WLAN_EID_RRM_ENABLED_CAPABILITIES, -			WLAN_EID_MOBILITY_DOMAIN, -			WLAN_EID_SUPPORTED_REGULATORY_CLASSES, -		}; -		noffset = ieee80211_ie_split(wk->ie, wk->ie_len, -					     before_ht, ARRAY_SIZE(before_ht), -					     offset); -		pos = skb_put(skb, noffset - offset); -		memcpy(pos, wk->ie + offset, noffset - offset); -		offset = noffset; -	} - -	if (wk->assoc.use_11n && wk->assoc.wmm_used && -	    local->hw.queues >= 4) -		ieee80211_add_ht_ie(skb, wk->assoc.ht_information_ie, -				    sband, wk->chan, wk->assoc.smps); - -	/* if present, add any custom non-vendor IEs that go after HT */ -	if (wk->ie_len && wk->ie) { -		noffset = ieee80211_ie_split_vendor(wk->ie, wk->ie_len, -						    offset); -		pos = skb_put(skb, noffset - offset); -		memcpy(pos, wk->ie + offset, noffset - offset); -		offset = noffset; -	} - -	if (wk->assoc.wmm_used && local->hw.queues >= 4) { -		if (wk->assoc.uapsd_used) { -			qos_info = local->uapsd_queues; -			qos_info |= (local->uapsd_max_sp_len << -				     IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT); -		} else { -			qos_info = 0; -		} - -		pos = skb_put(skb, 9); -		*pos++ = WLAN_EID_VENDOR_SPECIFIC; -		*pos++ = 7; /* len */ -		*pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ -		*pos++ = 0x50; -		*pos++ = 0xf2; -		*pos++ = 2; /* WME */ -		*pos++ = 0; /* WME info */ -		*pos++ = 1; /* WME ver */ -		*pos++ = qos_info; -	} - -	/* add any remaining custom (i.e. vendor specific here) IEs */ -	if (wk->ie_len && wk->ie) { -		noffset = wk->ie_len; -		pos = skb_put(skb, noffset - offset); -		memcpy(pos, wk->ie + offset, noffset - offset); -	} - -	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; -	ieee80211_tx_skb(sdata, skb); -} - -static void ieee80211_remove_auth_bss(struct ieee80211_local *local, -				      struct ieee80211_work *wk) -{ -	struct cfg80211_bss *cbss; -	u16 capa_val = WLAN_CAPABILITY_ESS; - -	if (wk->probe_auth.privacy) -		capa_val |= WLAN_CAPABILITY_PRIVACY; - -	cbss = cfg80211_get_bss(local->hw.wiphy, wk->chan, wk->filter_ta, -				wk->probe_auth.ssid, wk->probe_auth.ssid_len, -				WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, -				capa_val); -	if (!cbss) -		return; - -	cfg80211_unlink_bss(local->hw.wiphy, cbss); -	cfg80211_put_bss(cbss); -} - -static enum work_action __must_check -ieee80211_direct_probe(struct ieee80211_work *wk) -{ -	struct ieee80211_sub_if_data *sdata = wk->sdata; -	struct ieee80211_local *local = sdata->local; - -	wk->probe_auth.tries++; -	if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) { -		printk(KERN_DEBUG "%s: direct probe to %pM timed out\n", -		       sdata->name, wk->filter_ta); - -		/* -		 * Most likely AP is not in the range so remove the -		 * bss struct for that AP. -		 */ -		ieee80211_remove_auth_bss(local, wk); - -		return WORK_ACT_TIMEOUT; -	} - -	printk(KERN_DEBUG "%s: direct probe to %pM (try %d)\n", -			sdata->name, wk->filter_ta, wk->probe_auth.tries); - -	/* -	 * Direct probe is sent to broadcast address as some APs -	 * will not answer to direct packet in unassociated state. -	 */ -	ieee80211_send_probe_req(sdata, NULL, wk->probe_auth.ssid, -				 wk->probe_auth.ssid_len, NULL, 0); - -	wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; -	run_again(local, wk->timeout); - -	return WORK_ACT_NONE; -} - - -static enum work_action __must_check -ieee80211_authenticate(struct ieee80211_work *wk) -{ -	struct ieee80211_sub_if_data *sdata = wk->sdata; -	struct ieee80211_local *local = sdata->local; - -	wk->probe_auth.tries++; -	if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) { -		printk(KERN_DEBUG "%s: authentication with %pM" -		       " timed out\n", sdata->name, wk->filter_ta); - -		/* -		 * Most likely AP is not in the range so remove the -		 * bss struct for that AP. -		 */ -		ieee80211_remove_auth_bss(local, wk); - -		return WORK_ACT_TIMEOUT; -	} - -	printk(KERN_DEBUG "%s: authenticate with %pM (try %d)\n", -	       sdata->name, wk->filter_ta, wk->probe_auth.tries); - -	ieee80211_send_auth(sdata, 1, wk->probe_auth.algorithm, wk->ie, -			    wk->ie_len, wk->filter_ta, NULL, 0, 0); -	wk->probe_auth.transaction = 2; - -	wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; -	run_again(local, wk->timeout); - -	return WORK_ACT_NONE; -} - -static enum work_action __must_check -ieee80211_associate(struct ieee80211_work *wk) -{ -	struct ieee80211_sub_if_data *sdata = wk->sdata; -	struct ieee80211_local *local = sdata->local; - -	wk->assoc.tries++; -	if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) { -		printk(KERN_DEBUG "%s: association with %pM" -		       " timed out\n", -		       sdata->name, wk->filter_ta); - -		/* -		 * Most likely AP is not in the range so remove the -		 * bss struct for that AP. -		 */ -		if (wk->assoc.bss) -			cfg80211_unlink_bss(local->hw.wiphy, wk->assoc.bss); - -		return WORK_ACT_TIMEOUT; -	} - -	printk(KERN_DEBUG "%s: associate with %pM (try %d)\n", -	       sdata->name, wk->filter_ta, wk->assoc.tries); -	ieee80211_send_assoc(sdata, wk); - -	wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; -	run_again(local, wk->timeout); - -	return WORK_ACT_NONE; -} - -static enum work_action __must_check -ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk) -{ -	/* -	 * First time we run, do nothing -- the generic code will -	 * have switched to the right channel etc. -	 */ -	if (!wk->started) { -		wk->timeout = jiffies + msecs_to_jiffies(wk->remain.duration); - -		cfg80211_ready_on_channel(wk->sdata->dev, (unsigned long) wk, -					  wk->chan, wk->chan_type, -					  wk->remain.duration, GFP_KERNEL); - -		return WORK_ACT_NONE; -	} - -	return WORK_ACT_TIMEOUT; -} - -static enum work_action __must_check -ieee80211_assoc_beacon_wait(struct ieee80211_work *wk) -{ -	if (wk->started) -		return WORK_ACT_TIMEOUT; - -	/* -	 * Wait up to one beacon interval ... -	 * should this be more if we miss one? -	 */ -	printk(KERN_DEBUG "%s: waiting for beacon from %pM\n", -	       wk->sdata->name, wk->filter_ta); -	wk->timeout = TU_TO_EXP_TIME(wk->assoc.bss->beacon_interval); -	return WORK_ACT_NONE; -} - -static void ieee80211_auth_challenge(struct ieee80211_work *wk, -				     struct ieee80211_mgmt *mgmt, -				     size_t len) -{ -	struct ieee80211_sub_if_data *sdata = wk->sdata; -	u8 *pos; -	struct ieee802_11_elems elems; - -	pos = mgmt->u.auth.variable; -	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); -	if (!elems.challenge) -		return; -	ieee80211_send_auth(sdata, 3, wk->probe_auth.algorithm, -			    elems.challenge - 2, elems.challenge_len + 2, -			    wk->filter_ta, wk->probe_auth.key, -			    wk->probe_auth.key_len, wk->probe_auth.key_idx); -	wk->probe_auth.transaction = 4; -} - -static enum work_action __must_check -ieee80211_rx_mgmt_auth(struct ieee80211_work *wk, -		       struct ieee80211_mgmt *mgmt, size_t len) -{ -	u16 auth_alg, auth_transaction, status_code; - -	if (wk->type != IEEE80211_WORK_AUTH) -		return WORK_ACT_MISMATCH; - -	if (len < 24 + 6) -		return WORK_ACT_NONE; - -	auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); -	auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); -	status_code = le16_to_cpu(mgmt->u.auth.status_code); - -	if (auth_alg != wk->probe_auth.algorithm || -	    auth_transaction != wk->probe_auth.transaction) -		return WORK_ACT_NONE; - -	if (status_code != WLAN_STATUS_SUCCESS) { -		printk(KERN_DEBUG "%s: %pM denied authentication (status %d)\n", -		       wk->sdata->name, mgmt->sa, status_code); -		return WORK_ACT_DONE; -	} - -	switch (wk->probe_auth.algorithm) { -	case WLAN_AUTH_OPEN: -	case WLAN_AUTH_LEAP: -	case WLAN_AUTH_FT: -		break; -	case WLAN_AUTH_SHARED_KEY: -		if (wk->probe_auth.transaction != 4) { -			ieee80211_auth_challenge(wk, mgmt, len); -			/* need another frame */ -			return WORK_ACT_NONE; -		} -		break; -	default: -		WARN_ON(1); -		return WORK_ACT_NONE; -	} - -	printk(KERN_DEBUG "%s: authenticated\n", wk->sdata->name); -	return WORK_ACT_DONE; -} - -static enum work_action __must_check -ieee80211_rx_mgmt_assoc_resp(struct ieee80211_work *wk, -			     struct ieee80211_mgmt *mgmt, size_t len, -			     bool reassoc) -{ -	struct ieee80211_sub_if_data *sdata = wk->sdata; -	struct ieee80211_local *local = sdata->local; -	u16 capab_info, status_code, aid; -	struct ieee802_11_elems elems; -	u8 *pos; - -	if (wk->type != IEEE80211_WORK_ASSOC) -		return WORK_ACT_MISMATCH; - -	/* -	 * AssocResp and ReassocResp have identical structure, so process both -	 * of them in this function. -	 */ - -	if (len < 24 + 6) -		return WORK_ACT_NONE; - -	capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); -	status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); -	aid = le16_to_cpu(mgmt->u.assoc_resp.aid); - -	printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x " -	       "status=%d aid=%d)\n", -	       sdata->name, reassoc ? "Rea" : "A", mgmt->sa, -	       capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); - -	pos = mgmt->u.assoc_resp.variable; -	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); - -	if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && -	    elems.timeout_int && elems.timeout_int_len == 5 && -	    elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) { -		u32 tu, ms; -		tu = get_unaligned_le32(elems.timeout_int + 1); -		ms = tu * 1024 / 1000; -		printk(KERN_DEBUG "%s: %pM rejected association temporarily; " -		       "comeback duration %u TU (%u ms)\n", -		       sdata->name, mgmt->sa, tu, ms); -		wk->timeout = jiffies + msecs_to_jiffies(ms); -		if (ms > IEEE80211_ASSOC_TIMEOUT) -			run_again(local, wk->timeout); -		return WORK_ACT_NONE; -	} - -	if (status_code != WLAN_STATUS_SUCCESS) -		printk(KERN_DEBUG "%s: %pM denied association (code=%d)\n", -		       sdata->name, mgmt->sa, status_code); -	else -		printk(KERN_DEBUG "%s: associated\n", sdata->name); - -	return WORK_ACT_DONE; -} - -static enum work_action __must_check -ieee80211_rx_mgmt_probe_resp(struct ieee80211_work *wk, -			     struct ieee80211_mgmt *mgmt, size_t len, -			     struct ieee80211_rx_status *rx_status) -{ -	struct ieee80211_sub_if_data *sdata = wk->sdata; -	struct ieee80211_local *local = sdata->local; -	size_t baselen; - -	ASSERT_WORK_MTX(local); - -	if (wk->type != IEEE80211_WORK_DIRECT_PROBE) -		return WORK_ACT_MISMATCH; - -	if (len < 24 + 12) -		return WORK_ACT_NONE; - -	baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; -	if (baselen > len) -		return WORK_ACT_NONE; - -	printk(KERN_DEBUG "%s: direct probe responded\n", sdata->name); -	return WORK_ACT_DONE; -} - -static enum work_action __must_check -ieee80211_rx_mgmt_beacon(struct ieee80211_work *wk, -			 struct ieee80211_mgmt *mgmt, size_t len) -{ -	struct ieee80211_sub_if_data *sdata = wk->sdata; -	struct ieee80211_local *local = sdata->local; - -	ASSERT_WORK_MTX(local); - -	if (wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT) -		return WORK_ACT_MISMATCH; - -	if (len < 24 + 12) -		return WORK_ACT_NONE; - -	printk(KERN_DEBUG "%s: beacon received\n", sdata->name); -	return WORK_ACT_DONE; -} - -static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local, -					  struct sk_buff *skb) -{ -	struct ieee80211_rx_status *rx_status; -	struct ieee80211_mgmt *mgmt; -	struct ieee80211_work *wk; -	enum work_action rma = WORK_ACT_NONE; -	u16 fc; - -	rx_status = (struct ieee80211_rx_status *) skb->cb; -	mgmt = (struct ieee80211_mgmt *) skb->data; -	fc = le16_to_cpu(mgmt->frame_control); - -	mutex_lock(&local->mtx); - -	list_for_each_entry(wk, &local->work_list, list) { -		const u8 *bssid = NULL; - -		switch (wk->type) { -		case IEEE80211_WORK_DIRECT_PROBE: -		case IEEE80211_WORK_AUTH: -		case IEEE80211_WORK_ASSOC: -		case IEEE80211_WORK_ASSOC_BEACON_WAIT: -			bssid = wk->filter_ta; -			break; -		default: -			continue; -		} - -		/* -		 * Before queuing, we already verified mgmt->sa, -		 * so this is needed just for matching. -		 */ -		if (compare_ether_addr(bssid, mgmt->bssid)) -			continue; - -		switch (fc & IEEE80211_FCTL_STYPE) { -		case IEEE80211_STYPE_BEACON: -			rma = ieee80211_rx_mgmt_beacon(wk, mgmt, skb->len); -			break; -		case IEEE80211_STYPE_PROBE_RESP: -			rma = ieee80211_rx_mgmt_probe_resp(wk, mgmt, skb->len, -							   rx_status); -			break; -		case IEEE80211_STYPE_AUTH: -			rma = ieee80211_rx_mgmt_auth(wk, mgmt, skb->len); -			break; -		case IEEE80211_STYPE_ASSOC_RESP: -			rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt, -							   skb->len, false); -			break; -		case IEEE80211_STYPE_REASSOC_RESP: -			rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt, -							   skb->len, true); -			break; -		default: -			WARN_ON(1); -			rma = WORK_ACT_NONE; -		} - -		/* -		 * We've either received an unexpected frame, or we have -		 * multiple work items and need to match the frame to the -		 * right one. -		 */ -		if (rma == WORK_ACT_MISMATCH) -			continue; - -		/* -		 * We've processed this frame for that work, so it can't -		 * belong to another work struct. -		 * NB: this is also required for correctness for 'rma'! -		 */ -		break; -	} - -	switch (rma) { -	case WORK_ACT_MISMATCH: -		/* ignore this unmatched frame */ -		break; -	case WORK_ACT_NONE: -		break; -	case WORK_ACT_DONE: -		list_del_rcu(&wk->list); -		break; -	default: -		WARN(1, "unexpected: %d", rma); -	} - -	mutex_unlock(&local->mtx); - -	if (rma != WORK_ACT_DONE) -		goto out; - -	switch (wk->done(wk, skb)) { -	case WORK_DONE_DESTROY: -		free_work(wk); -		break; -	case WORK_DONE_REQUEUE: -		synchronize_rcu(); -		wk->started = false; /* restart */ -		mutex_lock(&local->mtx); -		list_add_tail(&wk->list, &local->work_list); -		mutex_unlock(&local->mtx); -	} - - out: -	kfree_skb(skb); -} - -static void ieee80211_work_timer(unsigned long data) -{ -	struct ieee80211_local *local = (void *) data; - -	if (local->quiescing) -		return; - -	ieee80211_queue_work(&local->hw, &local->work_work); -} - -static void ieee80211_work_work(struct work_struct *work) -{ -	struct ieee80211_local *local = -		container_of(work, struct ieee80211_local, work_work); -	struct sk_buff *skb; -	struct ieee80211_work *wk, *tmp; -	LIST_HEAD(free_work); -	enum work_action rma; -	bool remain_off_channel = false; - -	if (local->scanning) -		return; - -	/* -	 * ieee80211_queue_work() should have picked up most cases, -	 * here we'll pick the rest. -	 */ -	if (WARN(local->suspended, "work scheduled while going to suspend\n")) -		return; - -	/* first process frames to avoid timing out while a frame is pending */ -	while ((skb = skb_dequeue(&local->work_skb_queue))) -		ieee80211_work_rx_queued_mgmt(local, skb); - -	mutex_lock(&local->mtx); - -	ieee80211_recalc_idle(local); - -	list_for_each_entry_safe(wk, tmp, &local->work_list, list) { -		bool started = wk->started; - -		/* mark work as started if it's on the current off-channel */ -		if (!started && local->tmp_channel && -		    wk->chan == local->tmp_channel && -		    wk->chan_type == local->tmp_channel_type) { -			started = true; -			wk->timeout = jiffies; -		} - -		if (!started && !local->tmp_channel) { -			/* -			 * TODO: could optimize this by leaving the -			 *	 station vifs in awake mode if they -			 *	 happen to be on the same channel as -			 *	 the requested channel -			 */ -			ieee80211_offchannel_stop_beaconing(local); -			ieee80211_offchannel_stop_station(local); - -			local->tmp_channel = wk->chan; -			local->tmp_channel_type = wk->chan_type; -			ieee80211_hw_config(local, 0); -			started = true; -			wk->timeout = jiffies; -		} - -		/* don't try to work with items that aren't started */ -		if (!started) -			continue; - -		if (time_is_after_jiffies(wk->timeout)) { -			/* -			 * This work item isn't supposed to be worked on -			 * right now, but take care to adjust the timer -			 * properly. -			 */ -			run_again(local, wk->timeout); -			continue; -		} - -		switch (wk->type) { -		default: -			WARN_ON(1); -			/* nothing */ -			rma = WORK_ACT_NONE; -			break; -		case IEEE80211_WORK_ABORT: -			rma = WORK_ACT_TIMEOUT; -			break; -		case IEEE80211_WORK_DIRECT_PROBE: -			rma = ieee80211_direct_probe(wk); -			break; -		case IEEE80211_WORK_AUTH: -			rma = ieee80211_authenticate(wk); -			break; -		case IEEE80211_WORK_ASSOC: -			rma = ieee80211_associate(wk); -			break; -		case IEEE80211_WORK_REMAIN_ON_CHANNEL: -			rma = ieee80211_remain_on_channel_timeout(wk); -			break; -		case IEEE80211_WORK_ASSOC_BEACON_WAIT: -			rma = ieee80211_assoc_beacon_wait(wk); -			break; -		} - -		wk->started = started; - -		switch (rma) { -		case WORK_ACT_NONE: -			/* might have changed the timeout */ -			run_again(local, wk->timeout); -			break; -		case WORK_ACT_TIMEOUT: -			list_del_rcu(&wk->list); -			synchronize_rcu(); -			list_add(&wk->list, &free_work); -			break; -		default: -			WARN(1, "unexpected: %d", rma); -		} -	} - -	list_for_each_entry(wk, &local->work_list, list) { -		if (!wk->started) -			continue; -		if (wk->chan != local->tmp_channel) -			continue; -		if (wk->chan_type != local->tmp_channel_type) -			continue; -		remain_off_channel = true; -	} - -	if (!remain_off_channel && local->tmp_channel) { -		local->tmp_channel = NULL; -		ieee80211_hw_config(local, 0); -		ieee80211_offchannel_return(local, true); -		/* give connection some time to breathe */ -		run_again(local, jiffies + HZ/2); -	} - -	if (list_empty(&local->work_list) && local->scan_req && -	    !local->scanning) -		ieee80211_queue_delayed_work(&local->hw, -					     &local->scan_work, -					     round_jiffies_relative(0)); - -	ieee80211_recalc_idle(local); - -	mutex_unlock(&local->mtx); - -	list_for_each_entry_safe(wk, tmp, &free_work, list) { -		wk->done(wk, NULL); -		list_del(&wk->list); -		kfree(wk); -	} -} - -void ieee80211_add_work(struct ieee80211_work *wk) -{ -	struct ieee80211_local *local; - -	if (WARN_ON(!wk->chan)) -		return; - -	if (WARN_ON(!wk->sdata)) -		return; - -	if (WARN_ON(!wk->done)) -		return; - -	if (WARN_ON(!ieee80211_sdata_running(wk->sdata))) -		return; - -	wk->started = false; - -	local = wk->sdata->local; -	mutex_lock(&local->mtx); -	list_add_tail(&wk->list, &local->work_list); -	mutex_unlock(&local->mtx); - -	ieee80211_queue_work(&local->hw, &local->work_work); -} - -void ieee80211_work_init(struct ieee80211_local *local) -{ -	INIT_LIST_HEAD(&local->work_list); -	setup_timer(&local->work_timer, ieee80211_work_timer, -		    (unsigned long)local); -	INIT_WORK(&local->work_work, ieee80211_work_work); -	skb_queue_head_init(&local->work_skb_queue); -} - -void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_local *local = sdata->local; -	struct ieee80211_work *wk; - -	mutex_lock(&local->mtx); -	list_for_each_entry(wk, &local->work_list, list) { -		if (wk->sdata != sdata) -			continue; -		wk->type = IEEE80211_WORK_ABORT; -		wk->started = true; -		wk->timeout = jiffies; -	} -	mutex_unlock(&local->mtx); - -	/* run cleanups etc. */ -	ieee80211_work_work(&local->work_work); - -	mutex_lock(&local->mtx); -	list_for_each_entry(wk, &local->work_list, list) { -		if (wk->sdata != sdata) -			continue; -		WARN_ON(1); -		break; -	} -	mutex_unlock(&local->mtx); -} - -ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata, -					   struct sk_buff *skb) -{ -	struct ieee80211_local *local = sdata->local; -	struct ieee80211_mgmt *mgmt; -	struct ieee80211_work *wk; -	u16 fc; - -	if (skb->len < 24) -		return RX_DROP_MONITOR; - -	mgmt = (struct ieee80211_mgmt *) skb->data; -	fc = le16_to_cpu(mgmt->frame_control); - -	list_for_each_entry_rcu(wk, &local->work_list, list) { -		if (sdata != wk->sdata) -			continue; -		if (compare_ether_addr(wk->filter_ta, mgmt->sa)) -			continue; -		if (compare_ether_addr(wk->filter_ta, mgmt->bssid)) -			continue; - -		switch (fc & IEEE80211_FCTL_STYPE) { -		case IEEE80211_STYPE_AUTH: -		case IEEE80211_STYPE_PROBE_RESP: -		case IEEE80211_STYPE_ASSOC_RESP: -		case IEEE80211_STYPE_REASSOC_RESP: -		case IEEE80211_STYPE_BEACON: -			skb_queue_tail(&local->work_skb_queue, skb); -			ieee80211_queue_work(&local->hw, &local->work_work); -			return RX_QUEUED; -		} -	} - -	return RX_CONTINUE; -} - -static enum work_done_result ieee80211_remain_done(struct ieee80211_work *wk, -						   struct sk_buff *skb) -{ -	/* -	 * We are done serving the remain-on-channel command. -	 */ -	cfg80211_remain_on_channel_expired(wk->sdata->dev, (unsigned long) wk, -					   wk->chan, wk->chan_type, -					   GFP_KERNEL); - -	return WORK_DONE_DESTROY; -} - -int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, -				   struct ieee80211_channel *chan, -				   enum nl80211_channel_type channel_type, -				   unsigned int duration, u64 *cookie) -{ -	struct ieee80211_work *wk; - -	wk = kzalloc(sizeof(*wk), GFP_KERNEL); -	if (!wk) -		return -ENOMEM; - -	wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL; -	wk->chan = chan; -	wk->chan_type = channel_type; -	wk->sdata = sdata; -	wk->done = ieee80211_remain_done; - -	wk->remain.duration = duration; - -	*cookie = (unsigned long) wk; - -	ieee80211_add_work(wk); - -	return 0; -} - -int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata, -					  u64 cookie) -{ -	struct ieee80211_local *local = sdata->local; -	struct ieee80211_work *wk, *tmp; -	bool found = false; - -	mutex_lock(&local->mtx); -	list_for_each_entry_safe(wk, tmp, &local->work_list, list) { -		if ((unsigned long) wk == cookie) { -			wk->timeout = jiffies; -			found = true; -			break; -		} -	} -	mutex_unlock(&local->mtx); - -	if (!found) -		return -ENOENT; - -	ieee80211_queue_work(&local->hw, &local->work_work); - -	return 0; -} diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index bee230d8fd1..9b3dcc20114 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -15,6 +15,7 @@  #include <linux/gfp.h>  #include <asm/unaligned.h>  #include <net/mac80211.h> +#include <crypto/aes.h>  #include "ieee80211_i.h"  #include "michael.h" @@ -26,13 +27,12 @@  ieee80211_tx_result  ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)  { -	u8 *data, *key, *mic, key_offset; +	u8 *data, *key, *mic;  	size_t data_len;  	unsigned int hdrlen;  	struct ieee80211_hdr *hdr;  	struct sk_buff *skb = tx->skb;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); -	int authenticator;  	int tail;  	hdr = (struct ieee80211_hdr *)skb->data; @@ -47,8 +47,14 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)  	data = skb->data + hdrlen;  	data_len = skb->len - hdrlen; +	if (unlikely(info->flags & IEEE80211_TX_INTFL_TKIP_MIC_FAILURE)) { +		/* Need to use software crypto for the test */ +		info->control.hw_key = NULL; +	} +  	if (info->control.hw_key && -	    !(tx->flags & IEEE80211_TX_FRAGMENTED) && +	    (info->flags & IEEE80211_TX_CTL_DONTFRAG || +	     tx->local->ops->set_frag_threshold) &&  	    !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) {  		/* hwaccel - with no need for SW-generated MMIC */  		return TX_CONTINUE; @@ -56,23 +62,17 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)  	tail = MICHAEL_MIC_LEN;  	if (!info->control.hw_key) -		tail += TKIP_ICV_LEN; +		tail += IEEE80211_TKIP_ICV_LEN;  	if (WARN_ON(skb_tailroom(skb) < tail || -		    skb_headroom(skb) < TKIP_IV_LEN)) +		    skb_headroom(skb) < IEEE80211_TKIP_IV_LEN))  		return TX_DROP; -#if 0 -	authenticator = fc & IEEE80211_FCTL_FROMDS; /* FIX */ -#else -	authenticator = 1; -#endif -	key_offset = authenticator ? -		NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY : -		NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; -	key = &tx->key->conf.key[key_offset]; +	key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY];  	mic = skb_put(skb, MICHAEL_MIC_LEN);  	michael_mic(key, hdr, data, data_len, mic); +	if (unlikely(info->flags & IEEE80211_TX_INTFL_TKIP_MIC_FAILURE)) +		mic[0]++;  	return TX_CONTINUE;  } @@ -81,59 +81,98 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)  ieee80211_rx_result  ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)  { -	u8 *data, *key = NULL, key_offset; +	u8 *data, *key = NULL;  	size_t data_len;  	unsigned int hdrlen;  	u8 mic[MICHAEL_MIC_LEN];  	struct sk_buff *skb = rx->skb;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; -	int authenticator = 1, wpa_test = 0; -	/* No way to verify the MIC if the hardware stripped it */ -	if (status->flag & RX_FLAG_MMIC_STRIPPED) +	/* +	 * it makes no sense to check for MIC errors on anything other +	 * than data frames. +	 */ +	if (!ieee80211_is_data_present(hdr->frame_control))  		return RX_CONTINUE; +	/* +	 * No way to verify the MIC if the hardware stripped it or +	 * the IV with the key index. In this case we have solely rely +	 * on the driver to set RX_FLAG_MMIC_ERROR in the event of a +	 * MIC failure report. +	 */ +	if (status->flag & (RX_FLAG_MMIC_STRIPPED | RX_FLAG_IV_STRIPPED)) { +		if (status->flag & RX_FLAG_MMIC_ERROR) +			goto mic_fail_no_key; + +		if (!(status->flag & RX_FLAG_IV_STRIPPED) && rx->key && +		    rx->key->conf.cipher == WLAN_CIPHER_SUITE_TKIP) +			goto update_iv; + +		return RX_CONTINUE; +	} + +	/* +	 * Some hardware seems to generate Michael MIC failure reports; even +	 * though, the frame was not encrypted with TKIP and therefore has no +	 * MIC. Ignore the flag them to avoid triggering countermeasures. +	 */  	if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP || -	    !ieee80211_has_protected(hdr->frame_control) || -	    !ieee80211_is_data_present(hdr->frame_control)) +	    !(status->flag & RX_FLAG_DECRYPTED))  		return RX_CONTINUE; +	if (rx->sdata->vif.type == NL80211_IFTYPE_AP && rx->key->conf.keyidx) { +		/* +		 * APs with pairwise keys should never receive Michael MIC +		 * errors for non-zero keyidx because these are reserved for +		 * group keys and only the AP is sending real multicast +		 * frames in the BSS. +		 */ +		return RX_DROP_UNUSABLE; +	} + +	if (status->flag & RX_FLAG_MMIC_ERROR) +		goto mic_fail; +  	hdrlen = ieee80211_hdrlen(hdr->frame_control);  	if (skb->len < hdrlen + MICHAEL_MIC_LEN)  		return RX_DROP_UNUSABLE; +	if (skb_linearize(rx->skb)) +		return RX_DROP_UNUSABLE; +	hdr = (void *)skb->data; +  	data = skb->data + hdrlen;  	data_len = skb->len - hdrlen - MICHAEL_MIC_LEN; - -#if 0 -	authenticator = fc & IEEE80211_FCTL_TODS; /* FIX */ -#else -	authenticator = 1; -#endif -	key_offset = authenticator ? -		NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY : -		NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; -	key = &rx->key->conf.key[key_offset]; +	key = &rx->key->conf.key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY];  	michael_mic(key, hdr, data, data_len, mic); -	if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0 || wpa_test) { -		if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) -			return RX_DROP_UNUSABLE; - -		mac80211_ev_michael_mic_failure(rx->sdata, rx->key->conf.keyidx, -						(void *) skb->data, NULL, -						GFP_ATOMIC); -		return RX_DROP_UNUSABLE; -	} +	if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0) +		goto mic_fail;  	/* remove Michael MIC from payload */  	skb_trim(skb, skb->len - MICHAEL_MIC_LEN); +update_iv:  	/* update IV in key information to be able to detect replays */ -	rx->key->u.tkip.rx[rx->queue].iv32 = rx->tkip_iv32; -	rx->key->u.tkip.rx[rx->queue].iv16 = rx->tkip_iv16; +	rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip_iv32; +	rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip_iv16;  	return RX_CONTINUE; + +mic_fail: +	rx->key->u.tkip.mic_failures++; + +mic_fail_no_key: +	/* +	 * In some cases the key can be unset - e.g. a multicast packet, in +	 * a driver that supports HW encryption. Send up the key idx only if +	 * the key is set. +	 */ +	mac80211_ev_michael_mic_failure(rx->sdata, +					rx->key ? rx->key->conf.keyidx : -1, +					(void *) skb->data, NULL, GFP_ATOMIC); +	return RX_DROP_UNUSABLE;  } @@ -147,7 +186,8 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  	u8 *pos;  	if (info->control.hw_key && -	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) { +	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && +	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {  		/* hwaccel - with no need for software-generated IV */  		return 0;  	} @@ -158,47 +198,54 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  	if (info->control.hw_key)  		tail = 0;  	else -		tail = TKIP_ICV_LEN; +		tail = IEEE80211_TKIP_ICV_LEN;  	if (WARN_ON(skb_tailroom(skb) < tail || -		    skb_headroom(skb) < TKIP_IV_LEN)) +		    skb_headroom(skb) < IEEE80211_TKIP_IV_LEN))  		return -1; -	pos = skb_push(skb, TKIP_IV_LEN); -	memmove(pos, pos + TKIP_IV_LEN, hdrlen); +	pos = skb_push(skb, IEEE80211_TKIP_IV_LEN); +	memmove(pos, pos + IEEE80211_TKIP_IV_LEN, hdrlen); +	skb_set_network_header(skb, skb_network_offset(skb) + +				    IEEE80211_TKIP_IV_LEN);  	pos += hdrlen; +	/* the HW only needs room for the IV, but not the actual IV */ +	if (info->control.hw_key && +	    (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) +		return 0; +  	/* Increase IV for the frame */ +	spin_lock(&key->u.tkip.txlock);  	key->u.tkip.tx.iv16++;  	if (key->u.tkip.tx.iv16 == 0)  		key->u.tkip.tx.iv32++; - -	pos = ieee80211_tkip_add_iv(pos, key, key->u.tkip.tx.iv16); +	pos = ieee80211_tkip_add_iv(pos, key); +	spin_unlock(&key->u.tkip.txlock);  	/* hwaccel - with software IV */  	if (info->control.hw_key)  		return 0;  	/* Add room for ICV */ -	skb_put(skb, TKIP_ICV_LEN); +	skb_put(skb, IEEE80211_TKIP_ICV_LEN); -	hdr = (struct ieee80211_hdr *) skb->data;  	return ieee80211_tkip_encrypt_data(tx->local->wep_tx_tfm, -					   key, pos, len, hdr->addr2); +					   key, skb, pos, len);  }  ieee80211_tx_result  ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)  { -	struct sk_buff *skb = tx->skb; +	struct sk_buff *skb;  	ieee80211_tx_set_protected(tx); -	do { +	skb_queue_walk(&tx->skbs, skb) {  		if (tkip_encrypt_skb(tx, skb) < 0)  			return TX_DROP; -	} while ((skb = skb->next)); +	}  	return TX_CONTINUE;  } @@ -208,7 +255,7 @@ ieee80211_rx_result  ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)  {  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; -	int hdrlen, res, hwaccel = 0, wpa_test = 0; +	int hdrlen, res, hwaccel = 0;  	struct ieee80211_key *key = rx->key;  	struct sk_buff *skb = rx->skb;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); @@ -221,6 +268,11 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)  	if (!rx->sta || skb->len - hdrlen < 12)  		return RX_DROP_UNUSABLE; +	/* it may be possible to optimize this a bit more */ +	if (skb_linearize(rx->skb)) +		return RX_DROP_UNUSABLE; +	hdr = (void *)skb->data; +  	/*  	 * Let TKIP code verify IV, but skip decryption.  	 * In the case where hardware checks the IV as well, @@ -232,37 +284,32 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)  	res = ieee80211_tkip_decrypt_data(rx->local->wep_rx_tfm,  					  key, skb->data + hdrlen,  					  skb->len - hdrlen, rx->sta->sta.addr, -					  hdr->addr1, hwaccel, rx->queue, +					  hdr->addr1, hwaccel, rx->security_idx,  					  &rx->tkip_iv32,  					  &rx->tkip_iv16); -	if (res != TKIP_DECRYPT_OK || wpa_test) +	if (res != TKIP_DECRYPT_OK)  		return RX_DROP_UNUSABLE;  	/* Trim ICV */ -	skb_trim(skb, skb->len - TKIP_ICV_LEN); +	skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);  	/* Remove IV */ -	memmove(skb->data + TKIP_IV_LEN, skb->data, hdrlen); -	skb_pull(skb, TKIP_IV_LEN); +	memmove(skb->data + IEEE80211_TKIP_IV_LEN, skb->data, hdrlen); +	skb_pull(skb, IEEE80211_TKIP_IV_LEN);  	return RX_CONTINUE;  } -static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, -				int encrypted) +static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad)  {  	__le16 mask_fc;  	int a4_included, mgmt;  	u8 qos_tid; -	u8 *b_0, *aad; -	u16 data_len, len_a; +	u16 len_a;  	unsigned int hdrlen;  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; -	b_0 = scratch + 3 * AES_BLOCK_LEN; -	aad = scratch + 4 * AES_BLOCK_LEN; -  	/*  	 * Mask FC: zero subtype b4 b5 b6 (if not mgmt)  	 * Retry, PwrMgt, MoreData; set Protected @@ -284,20 +331,21 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,  	else  		qos_tid = 0; -	data_len = skb->len - hdrlen - CCMP_HDR_LEN; -	if (encrypted) -		data_len -= CCMP_MIC_LEN; +	/* 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)  	 */  	b_0[1] = qos_tid | (mgmt << 4);  	memcpy(&b_0[2], hdr->addr2, ETH_ALEN); -	memcpy(&b_0[8], pn, CCMP_PN_LEN); -	/* l(m) */ -	put_unaligned_be16(data_len, &b_0[14]); +	memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);  	/* AAD (extra authenticate-only data) / masked 802.11 header  	 * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ @@ -350,11 +398,18 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  	struct ieee80211_key *key = tx->key;  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);  	int hdrlen, len, tail; -	u8 *pos, *pn; -	int i; +	u8 *pos; +	u8 pn[6]; +	u64 pn64; +	u8 aad[2 * AES_BLOCK_SIZE]; +	u8 b_0[AES_BLOCK_SIZE];  	if (info->control.hw_key && -	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) { +	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && +	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) && +	    !((info->control.hw_key->flags & +	       IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) && +	      ieee80211_is_mgmt(hdr->frame_control))) {  		/*  		 * hwaccel has no need for preallocated room for CCMP  		 * header or MIC fields @@ -368,25 +423,33 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  	if (info->control.hw_key)  		tail = 0;  	else -		tail = CCMP_MIC_LEN; +		tail = IEEE80211_CCMP_MIC_LEN;  	if (WARN_ON(skb_tailroom(skb) < tail || -		    skb_headroom(skb) < CCMP_HDR_LEN)) +		    skb_headroom(skb) < IEEE80211_CCMP_HDR_LEN))  		return -1; -	pos = skb_push(skb, CCMP_HDR_LEN); -	memmove(pos, pos + CCMP_HDR_LEN, hdrlen); +	pos = skb_push(skb, IEEE80211_CCMP_HDR_LEN); +	memmove(pos, pos + IEEE80211_CCMP_HDR_LEN, hdrlen); +	skb_set_network_header(skb, skb_network_offset(skb) + +				    IEEE80211_CCMP_HDR_LEN); + +	/* the HW only needs room for the IV, but not the actual IV */ +	if (info->control.hw_key && +	    (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) +		return 0; +  	hdr = (struct ieee80211_hdr *) pos;  	pos += hdrlen; -	/* PN = PN + 1 */ -	pn = key->u.ccmp.tx_pn; +	pn64 = atomic64_inc_return(&key->u.ccmp.tx_pn); -	for (i = CCMP_PN_LEN - 1; i >= 0; i--) { -		pn[i]++; -		if (pn[i]) -			break; -	} +	pn[5] = pn64; +	pn[4] = pn64 >> 8; +	pn[3] = pn64 >> 16; +	pn[2] = pn64 >> 24; +	pn[1] = pn64 >> 32; +	pn[0] = pn64 >> 40;  	ccmp_pn2hdr(pos, pn, key->conf.keyidx); @@ -394,10 +457,10 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  	if (info->control.hw_key)  		return 0; -	pos += CCMP_HDR_LEN; -	ccmp_special_blocks(skb, pn, key->u.ccmp.tx_crypto_buf, 0); -	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, key->u.ccmp.tx_crypto_buf, pos, len, -				  pos, skb_put(skb, CCMP_MIC_LEN)); +	pos += IEEE80211_CCMP_HDR_LEN; +	ccmp_special_blocks(skb, pn, b_0, aad); +	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, +				  skb_put(skb, IEEE80211_CCMP_MIC_LEN));  	return 0;  } @@ -406,14 +469,14 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)  ieee80211_tx_result  ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)  { -	struct sk_buff *skb = tx->skb; +	struct sk_buff *skb;  	ieee80211_tx_set_protected(tx); -	do { +	skb_queue_walk(&tx->skbs, skb) {  		if (ccmp_encrypt_skb(tx, skb) < 0)  			return TX_DROP; -	} while ((skb = skb->next)); +	}  	return TX_CONTINUE;  } @@ -427,66 +490,192 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)  	struct ieee80211_key *key = rx->key;  	struct sk_buff *skb = rx->skb;  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); -	u8 pn[CCMP_PN_LEN]; +	u8 pn[IEEE80211_CCMP_PN_LEN];  	int data_len;  	int queue;  	hdrlen = ieee80211_hdrlen(hdr->frame_control);  	if (!ieee80211_is_data(hdr->frame_control) && -	    !ieee80211_is_robust_mgmt_frame(hdr)) +	    !ieee80211_is_robust_mgmt_frame(skb))  		return RX_CONTINUE; -	data_len = skb->len - hdrlen - CCMP_HDR_LEN - CCMP_MIC_LEN; +	data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - +		   IEEE80211_CCMP_MIC_LEN;  	if (!rx->sta || data_len < 0)  		return RX_DROP_UNUSABLE; +	if (status->flag & RX_FLAG_DECRYPTED) { +		if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_CCMP_HDR_LEN)) +			return RX_DROP_UNUSABLE; +	} else { +		if (skb_linearize(rx->skb)) +			return RX_DROP_UNUSABLE; +	} +  	ccmp_hdr2pn(pn, skb->data + hdrlen); -	queue = ieee80211_is_mgmt(hdr->frame_control) ? -		NUM_RX_DATA_QUEUES : rx->queue; +	queue = rx->security_idx; -	if (memcmp(pn, key->u.ccmp.rx_pn[queue], CCMP_PN_LEN) <= 0) { +	if (memcmp(pn, key->u.ccmp.rx_pn[queue], IEEE80211_CCMP_PN_LEN) <= 0) {  		key->u.ccmp.replays++;  		return RX_DROP_UNUSABLE;  	}  	if (!(status->flag & RX_FLAG_DECRYPTED)) { +		u8 aad[2 * AES_BLOCK_SIZE]; +		u8 b_0[AES_BLOCK_SIZE];  		/* hardware didn't decrypt/verify MIC */ -		ccmp_special_blocks(skb, pn, key->u.ccmp.rx_crypto_buf, 1); +		ccmp_special_blocks(skb, pn, b_0, aad);  		if (ieee80211_aes_ccm_decrypt( -			    key->u.ccmp.tfm, key->u.ccmp.rx_crypto_buf, -			    skb->data + hdrlen + CCMP_HDR_LEN, data_len, -			    skb->data + skb->len - CCMP_MIC_LEN, -			    skb->data + hdrlen + CCMP_HDR_LEN)) +			    key->u.ccmp.tfm, b_0, aad, +			    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN, +			    data_len, +			    skb->data + skb->len - IEEE80211_CCMP_MIC_LEN))  			return RX_DROP_UNUSABLE;  	} -	memcpy(key->u.ccmp.rx_pn[queue], pn, CCMP_PN_LEN); +	memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);  	/* Remove CCMP header and MIC */ -	skb_trim(skb, skb->len - CCMP_MIC_LEN); -	memmove(skb->data + CCMP_HDR_LEN, skb->data, hdrlen); -	skb_pull(skb, CCMP_HDR_LEN); +	if (pskb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN)) +		return RX_DROP_UNUSABLE; +	memmove(skb->data + IEEE80211_CCMP_HDR_LEN, skb->data, hdrlen); +	skb_pull(skb, IEEE80211_CCMP_HDR_LEN);  	return RX_CONTINUE;  } +static ieee80211_tx_result +ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx, +			    struct sk_buff *skb) +{ +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; +	struct ieee80211_key *key = tx->key; +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	const struct ieee80211_cipher_scheme *cs = key->sta->cipher_scheme; +	int hdrlen; +	u8 *pos; + +	if (info->control.hw_key && +	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { +		/* hwaccel has no need for preallocated head room */ +		return TX_CONTINUE; +	} + +	if (unlikely(skb_headroom(skb) < cs->hdr_len && +		     pskb_expand_head(skb, cs->hdr_len, 0, GFP_ATOMIC))) +		return TX_DROP; + +	hdrlen = ieee80211_hdrlen(hdr->frame_control); + +	pos = skb_push(skb, cs->hdr_len); +	memmove(pos, pos + cs->hdr_len, hdrlen); +	skb_set_network_header(skb, skb_network_offset(skb) + cs->hdr_len); + +	return TX_CONTINUE; +} + +static inline int ieee80211_crypto_cs_pn_compare(u8 *pn1, u8 *pn2, int len) +{ +	int i; + +	/* pn is little endian */ +	for (i = len - 1; i >= 0; i--) { +		if (pn1[i] < pn2[i]) +			return -1; +		else if (pn1[i] > pn2[i]) +			return 1; +	} + +	return 0; +} + +static ieee80211_rx_result +ieee80211_crypto_cs_decrypt(struct ieee80211_rx_data *rx) +{ +	struct ieee80211_key *key = rx->key; +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; +	const struct ieee80211_cipher_scheme *cs = NULL; +	int hdrlen = ieee80211_hdrlen(hdr->frame_control); +	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); +	int data_len; +	u8 *rx_pn; +	u8 *skb_pn; +	u8 qos_tid; + +	if (!rx->sta || !rx->sta->cipher_scheme || +	    !(status->flag & RX_FLAG_DECRYPTED)) +		return RX_DROP_UNUSABLE; + +	if (!ieee80211_is_data(hdr->frame_control)) +		return RX_CONTINUE; + +	cs = rx->sta->cipher_scheme; + +	data_len = rx->skb->len - hdrlen - cs->hdr_len; + +	if (data_len < 0) +		return RX_DROP_UNUSABLE; + +	if (ieee80211_is_data_qos(hdr->frame_control)) +		qos_tid = *ieee80211_get_qos_ctl(hdr) & +				IEEE80211_QOS_CTL_TID_MASK; +	else +		qos_tid = 0; + +	if (skb_linearize(rx->skb)) +		return RX_DROP_UNUSABLE; + +	hdr = (struct ieee80211_hdr *)rx->skb->data; + +	rx_pn = key->u.gen.rx_pn[qos_tid]; +	skb_pn = rx->skb->data + hdrlen + cs->pn_off; + +	if (ieee80211_crypto_cs_pn_compare(skb_pn, rx_pn, cs->pn_len) <= 0) +		return RX_DROP_UNUSABLE; + +	memcpy(rx_pn, skb_pn, cs->pn_len); + +	/* remove security header and MIC */ +	if (pskb_trim(rx->skb, rx->skb->len - cs->mic_len)) +		return RX_DROP_UNUSABLE; + +	memmove(rx->skb->data + cs->hdr_len, rx->skb->data, hdrlen); +	skb_pull(rx->skb, cs->hdr_len); + +	return RX_CONTINUE; +}  static void bip_aad(struct sk_buff *skb, u8 *aad)  { +	__le16 mask_fc; +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; +  	/* BIP AAD: FC(masked) || A1 || A2 || A3 */  	/* FC type/subtype */ -	aad[0] = skb->data[0];  	/* Mask FC Retry, PwrMgt, MoreData flags to zero */ -	aad[1] = skb->data[1] & ~(BIT(4) | BIT(5) | BIT(6)); +	mask_fc = hdr->frame_control; +	mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY | IEEE80211_FCTL_PM | +				IEEE80211_FCTL_MOREDATA); +	put_unaligned(mask_fc, (__le16 *) &aad[0]);  	/* A1 || A2 || A3 */ -	memcpy(aad + 2, skb->data + 4, 3 * ETH_ALEN); +	memcpy(aad + 2, &hdr->addr1, 3 * ETH_ALEN);  } +static inline void bip_ipn_set64(u8 *d, u64 pn) +{ +	*d++ = pn; +	*d++ = pn >> 8; +	*d++ = pn >> 16; +	*d++ = pn >> 24; +	*d++ = pn >> 32; +	*d = pn >> 40; +} +  static inline void bip_ipn_swap(u8 *d, const u8 *s)  {  	*d++ = s[5]; @@ -501,15 +690,22 @@ static inline void bip_ipn_swap(u8 *d, const u8 *s)  ieee80211_tx_result  ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx)  { -	struct sk_buff *skb = tx->skb; -	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); +	struct sk_buff *skb; +	struct ieee80211_tx_info *info;  	struct ieee80211_key *key = tx->key;  	struct ieee80211_mmie *mmie; -	u8 *pn, aad[20]; -	int i; +	u8 aad[20]; +	u64 pn64; + +	if (WARN_ON(skb_queue_len(&tx->skbs) != 1)) +		return TX_DROP; + +	skb = skb_peek(&tx->skbs); + +	info = IEEE80211_SKB_CB(skb);  	if (info->control.hw_key) -		return 0; +		return TX_CONTINUE;  	if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))  		return TX_DROP; @@ -520,22 +716,17 @@ ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx)  	mmie->key_id = cpu_to_le16(key->conf.keyidx);  	/* PN = PN + 1 */ -	pn = key->u.aes_cmac.tx_pn; +	pn64 = atomic64_inc_return(&key->u.aes_cmac.tx_pn); -	for (i = sizeof(key->u.aes_cmac.tx_pn) - 1; i >= 0; i--) { -		pn[i]++; -		if (pn[i]) -			break; -	} -	bip_ipn_swap(mmie->sequence_number, pn); +	bip_ipn_set64(mmie->sequence_number, pn64);  	bip_aad(skb, aad);  	/*  	 * MIC = AES-128-CMAC(IGTK, AAD || Management Frame Body || MMIE, 64)  	 */ -	ieee80211_aes_cmac(key->u.aes_cmac.tfm, key->u.aes_cmac.tx_crypto_buf, -			   aad, skb->data + 24, skb->len - 24, mmie->mic); +	ieee80211_aes_cmac(key->u.aes_cmac.tfm, aad, +			   skb->data + 24, skb->len - 24, mmie->mic);  	return TX_CONTINUE;  } @@ -554,6 +745,8 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)  	if (!ieee80211_is_mgmt(hdr->frame_control))  		return RX_CONTINUE; +	/* management frames are already linear */ +  	if (skb->len < 24 + sizeof(*mmie))  		return RX_DROP_UNUSABLE; @@ -573,8 +766,7 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)  	if (!(status->flag & RX_FLAG_DECRYPTED)) {  		/* hardware didn't decrypt/verify MIC */  		bip_aad(skb, aad); -		ieee80211_aes_cmac(key->u.aes_cmac.tfm, -				   key->u.aes_cmac.rx_crypto_buf, aad, +		ieee80211_aes_cmac(key->u.aes_cmac.tfm, aad,  				   skb->data + 24, skb->len - 24, mic);  		if (memcmp(mic, mmie->mic, sizeof(mmie->mic)) != 0) {  			key->u.aes_cmac.icverrors++; @@ -589,3 +781,38 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)  	return RX_CONTINUE;  } + +ieee80211_tx_result +ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx) +{ +	struct sk_buff *skb; +	struct ieee80211_tx_info *info = NULL; +	ieee80211_tx_result res; + +	skb_queue_walk(&tx->skbs, skb) { +		info  = IEEE80211_SKB_CB(skb); + +		/* handle hw-only algorithm */ +		if (!info->control.hw_key) +			return TX_DROP; + +		if (tx->key->sta->cipher_scheme) { +			res = ieee80211_crypto_cs_encrypt(tx, skb); +			if (res != TX_CONTINUE) +				return res; +		} +	} + +	ieee80211_tx_set_protected(tx); + +	return TX_CONTINUE; +} + +ieee80211_rx_result +ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx) +{ +	if (rx->sta->cipher_scheme) +		return ieee80211_crypto_cs_decrypt(rx); + +	return RX_DROP_UNUSABLE; +} diff --git a/net/mac80211/wpa.h b/net/mac80211/wpa.h index baba0608313..62e5a12dfe0 100644 --- a/net/mac80211/wpa.h +++ b/net/mac80211/wpa.h @@ -32,5 +32,9 @@ ieee80211_tx_result  ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx);  ieee80211_rx_result  ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx); +ieee80211_tx_result +ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx); +ieee80211_rx_result +ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx);  #endif /* WPA_H */  | 
