aboutsummaryrefslogtreecommitdiff
path: root/net/mac80211
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/Kconfig174
-rw-r--r--net/mac80211/Makefile25
-rw-r--r--net/mac80211/aes_ccm.c53
-rw-r--r--net/mac80211/aes_ccm.h6
-rw-r--r--net/mac80211/cfg.c228
-rw-r--r--net/mac80211/debugfs.c62
-rw-r--r--net/mac80211/debugfs_key.c17
-rw-r--r--net/mac80211/debugfs_netdev.c137
-rw-r--r--net/mac80211/debugfs_netdev.h5
-rw-r--r--net/mac80211/debugfs_sta.c80
-rw-r--r--net/mac80211/event.c5
-rw-r--r--net/mac80211/ht.c992
-rw-r--r--net/mac80211/ieee80211_i.h560
-rw-r--r--net/mac80211/iface.c896
-rw-r--r--net/mac80211/key.c33
-rw-r--r--net/mac80211/key.h54
-rw-r--r--net/mac80211/main.c1391
-rw-r--r--net/mac80211/mesh.c406
-rw-r--r--net/mac80211/mesh.h85
-rw-r--r--net/mac80211/mesh_hwmp.c239
-rw-r--r--net/mac80211/mesh_pathtbl.c270
-rw-r--r--net/mac80211/mesh_plink.c186
-rw-r--r--net/mac80211/michael.c116
-rw-r--r--net/mac80211/michael.h8
-rw-r--r--net/mac80211/mlme.c4118
-rw-r--r--net/mac80211/rate.c81
-rw-r--r--net/mac80211/rate.h131
-rw-r--r--net/mac80211/rc80211_minstrel.c583
-rw-r--r--net/mac80211/rc80211_minstrel.h85
-rw-r--r--net/mac80211/rc80211_minstrel_debugfs.c164
-rw-r--r--net/mac80211/rc80211_pid.h8
-rw-r--r--net/mac80211/rc80211_pid_algo.c213
-rw-r--r--net/mac80211/rc80211_pid_debugfs.c8
-rw-r--r--net/mac80211/rx.c1121
-rw-r--r--net/mac80211/scan.c938
-rw-r--r--net/mac80211/spectmgmt.c86
-rw-r--r--net/mac80211/sta_info.c158
-rw-r--r--net/mac80211/sta_info.h193
-rw-r--r--net/mac80211/tkip.c284
-rw-r--r--net/mac80211/tkip.h8
-rw-r--r--net/mac80211/tx.c1460
-rw-r--r--net/mac80211/util.c466
-rw-r--r--net/mac80211/wep.c86
-rw-r--r--net/mac80211/wep.h2
-rw-r--r--net/mac80211/wext.c308
-rw-r--r--net/mac80211/wme.c685
-rw-r--r--net/mac80211/wme.h46
-rw-r--r--net/mac80211/wpa.c401
48 files changed, 9243 insertions, 8418 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index a24b459dd45..7f710a27e91 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -7,7 +7,6 @@ config MAC80211
select CRC32
select WIRELESS_EXT
select CFG80211
- select NET_SCH_FIFO
---help---
This option enables the hardware independent IEEE 802.11
networking stack.
@@ -15,6 +14,19 @@ config MAC80211
menu "Rate control algorithm selection"
depends on MAC80211 != n
+config MAC80211_RC_PID
+ bool "PID controller based rate control algorithm" if EMBEDDED
+ default y
+ ---help---
+ This option enables a TX rate control algorithm for
+ mac80211 that uses a PID controller to select the TX
+ rate.
+
+config MAC80211_RC_MINSTREL
+ bool "Minstrel"
+ ---help---
+ This option enables the 'minstrel' TX rate control algorithm
+
choice
prompt "Default rate control algorithm"
default MAC80211_RC_DEFAULT_PID
@@ -26,40 +38,27 @@ choice
config MAC80211_RC_DEFAULT_PID
bool "PID controller based rate control algorithm"
- select MAC80211_RC_PID
+ depends on MAC80211_RC_PID
---help---
Select the PID controller based rate control as the
default rate control algorithm. You should choose
this unless you know what you are doing.
-config MAC80211_RC_DEFAULT_NONE
- bool "No default algorithm"
- depends on EMBEDDED
- help
- Selecting this option will select no default algorithm
- and allow you to not build any. Do not choose this
- option unless you know your driver comes with another
- suitable algorithm.
-endchoice
+config MAC80211_RC_DEFAULT_MINSTREL
+ bool "Minstrel"
+ depends on MAC80211_RC_MINSTREL
+ ---help---
+ Select Minstrel as the default rate control algorithm.
+
-comment "Selecting 'y' for an algorithm will"
-comment "build the algorithm into mac80211."
+endchoice
config MAC80211_RC_DEFAULT
string
default "pid" if MAC80211_RC_DEFAULT_PID
+ default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL
default ""
-config MAC80211_RC_PID
- tristate "PID controller based rate control algorithm"
- ---help---
- This option enables a TX rate control algorithm for
- mac80211 that uses a PID controller to select the TX
- rate.
-
- Say Y or M unless you're sure you want to use a
- different rate control algorithm.
-
endmenu
config MAC80211_MESH
@@ -89,10 +88,16 @@ config MAC80211_DEBUGFS
Say N unless you know you need this.
+menuconfig MAC80211_DEBUG_MENU
+ bool "Select mac80211 debugging features"
+ depends on MAC80211
+ ---help---
+ This option collects various mac80211 debug settings.
+
config MAC80211_DEBUG_PACKET_ALIGNMENT
bool "Enable packet alignment debugging"
- depends on MAC80211
- help
+ depends on MAC80211_DEBUG_MENU
+ ---help---
This option is recommended for driver authors and strongly
discouraged for everybody else, it will trigger a warning
when a driver hands mac80211 a buffer that is aligned in
@@ -101,67 +106,106 @@ config MAC80211_DEBUG_PACKET_ALIGNMENT
Say N unless you're writing a mac80211 based driver.
-config MAC80211_DEBUG
- bool "Enable debugging output"
- depends on MAC80211
+config MAC80211_NOINLINE
+ bool "Do not inline TX/RX handlers"
+ depends on MAC80211_DEBUG_MENU
+ ---help---
+ This option affects code generation in mac80211, when
+ selected some functions are marked "noinline" to allow
+ easier debugging of problems in the transmit and receive
+ paths.
+
+ This option increases code size a bit and inserts a lot
+ of function calls in the code, but is otherwise safe to
+ enable.
+
+ If unsure, say N unless you expect to be finding problems
+ in mac80211.
+
+config MAC80211_VERBOSE_DEBUG
+ bool "Verbose debugging output"
+ depends on MAC80211_DEBUG_MENU
---help---
- This option will enable debug tracing output for the
- ieee80211 network stack.
+ Selecting this option causes mac80211 to print out
+ many debugging messages. It should not be selected
+ on production systems as some of the messages are
+ remotely triggerable.
- If you are not trying to debug or develop the ieee80211
- subsystem, you most likely want to say N here.
+ Do not select this option.
config MAC80211_HT_DEBUG
- bool "Enable HT debugging output"
- depends on MAC80211_DEBUG
+ bool "Verbose HT debugging"
+ depends on MAC80211_DEBUG_MENU
---help---
This option enables 802.11n High Throughput features
debug tracing output.
- If you are not trying to debug of develop the ieee80211
- subsystem, you most likely want to say N here.
+ It should not be selected on production systems as some
+ of the messages are remotely triggerable.
-config MAC80211_VERBOSE_DEBUG
- bool "Verbose debugging output"
- depends on MAC80211_DEBUG
+ Do not select this option.
-config MAC80211_LOWTX_FRAME_DUMP
- bool "Debug frame dumping"
- depends on MAC80211_DEBUG
+config MAC80211_TKIP_DEBUG
+ bool "Verbose TKIP debugging"
+ depends on MAC80211_DEBUG_MENU
---help---
- Selecting this option will cause the stack to
- print a message for each frame that is handed
- to the lowlevel driver for transmission. This
- message includes all MAC addresses and the
- frame control field.
+ 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.
- If unsure, say N and insert the debugging code
- you require into the driver you are debugging.
-
-config TKIP_DEBUG
- bool "TKIP debugging"
- depends on MAC80211_DEBUG
-
-config MAC80211_DEBUG_COUNTERS
- bool "Extra statistics for TX/RX debugging"
- depends on MAC80211_DEBUG
+ Do not select this option.
config MAC80211_IBSS_DEBUG
- bool "Support for IBSS testing"
- depends on MAC80211_DEBUG
+ bool "Verbose IBSS debugging"
+ depends on MAC80211_DEBUG_MENU
---help---
- Say Y here if you intend to debug the IBSS code.
+ Selecting this option causes mac80211 to print out
+ very verbose IBSS debugging messages. It should not
+ be selected on production systems as those messages
+ are remotely triggerable.
+
+ Do not select this option.
config MAC80211_VERBOSE_PS_DEBUG
bool "Verbose powersave mode debugging"
- depends on MAC80211_DEBUG
+ depends on MAC80211_DEBUG_MENU
---help---
- Say Y here to print out verbose powersave
- mode debug messages.
+ Selecting this option causes mac80211 to print out very
+ verbose power save mode debugging messages (when mac80211
+ is an AP and has power saving stations.)
+ It should not be selected on production systems as those
+ messages are remotely triggerable.
+
+ Do not select this option.
config MAC80211_VERBOSE_MPL_DEBUG
bool "Verbose mesh peer link debugging"
- depends on MAC80211_DEBUG && MAC80211_MESH
+ depends on MAC80211_DEBUG_MENU
+ depends on MAC80211_MESH
+ ---help---
+ Selecting this option causes mac80211 to print out very
+ verbose mesh peer link 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_DEBUG_COUNTERS
+ bool "Extra statistics for TX/RX debugging"
+ depends on MAC80211_DEBUG_MENU
+ depends on MAC80211_DEBUGFS
+ ---help---
+ Selecting this option causes mac80211 to keep additional
+ and very verbose statistics about TX and RX handler use
+ and show them in debugfs.
+
+ If unsure, say N.
+
+config MAC80211_VERBOSE_SPECT_MGMT_DEBUG
+ bool "Verbose Spectrum Management (IEEE 802.11h)debugging"
+ depends on MAC80211_DEBUG_MENU
---help---
- Say Y here to print out verbose mesh peer link
+ Say Y here to print out verbose Spectrum Management (IEEE 802.11h)
debug messages.
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 4e5847fd316..31cfd1f89a7 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -1,13 +1,5 @@
obj-$(CONFIG_MAC80211) += mac80211.o
-# objects for PID algorithm
-rc80211_pid-y := rc80211_pid_algo.o
-rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o
-
-# build helper for PID algorithm
-rc-pid-y := $(rc80211_pid-y)
-rc-pid-m := rc80211_pid.o
-
# mac80211 objects
mac80211-y := \
main.o \
@@ -15,6 +7,8 @@ mac80211-y := \
sta_info.o \
wep.o \
wpa.o \
+ scan.o \
+ ht.o \
mlme.o \
iface.o \
rate.o \
@@ -23,13 +17,14 @@ mac80211-y := \
aes_ccm.o \
cfg.o \
rx.o \
+ spectmgmt.o \
tx.o \
key.o \
util.o \
+ wme.o \
event.o
mac80211-$(CONFIG_MAC80211_LEDS) += led.o
-mac80211-$(CONFIG_NET_SCHED) += wme.o
mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
debugfs.o \
debugfs_sta.o \
@@ -42,10 +37,12 @@ mac80211-$(CONFIG_MAC80211_MESH) += \
mesh_plink.o \
mesh_hwmp.o
+# objects for PID algorithm
+rc80211_pid-y := rc80211_pid_algo.o
+rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o
-# Build rate control algorithm(s)
-CFLAGS_rc80211_pid_algo.o += -DRC80211_PID_COMPILE
-mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc-pid-$(CONFIG_MAC80211_RC_PID))
+rc80211_minstrel-y := rc80211_minstrel.o
+rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o
-# Modular rate algorithms are assigned to mac80211-m - make separate modules
-obj-m += $(mac80211-m)
+mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y)
+mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)
diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c
index 59f1691f62c..a87cb3ba2df 100644
--- a/net/mac80211/aes_ccm.c
+++ b/net/mac80211/aes_ccm.c
@@ -16,31 +16,28 @@
#include "key.h"
#include "aes_ccm.h"
-
-static void ieee80211_aes_encrypt(struct crypto_cipher *tfm,
- const u8 pt[16], u8 ct[16])
-{
- crypto_cipher_encrypt_one(tfm, ct, pt);
-}
-
-
-static inline void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *b_0, u8 *aad,
- u8 *b, u8 *s_0, u8 *a)
+static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a)
{
int i;
+ u8 *b_0, *aad, *b, *s_0;
- ieee80211_aes_encrypt(tfm, b_0, b);
+ 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);
/* Extra Authenticate-only data (always two AES blocks) */
for (i = 0; i < AES_BLOCK_LEN; i++)
aad[i] ^= b[i];
- ieee80211_aes_encrypt(tfm, aad, b);
+ crypto_cipher_encrypt_one(tfm, b, aad);
aad += AES_BLOCK_LEN;
for (i = 0; i < AES_BLOCK_LEN; i++)
aad[i] ^= b[i];
- ieee80211_aes_encrypt(tfm, aad, a);
+ crypto_cipher_encrypt_one(tfm, a, aad);
/* Mask out bits from auth-only-b_0 */
b_0[0] &= 0x07;
@@ -48,24 +45,26 @@ static inline void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *b_0, u8 *aad,
/* S_0 is used to encrypt T (= MIC) */
b_0[14] = 0;
b_0[15] = 0;
- ieee80211_aes_encrypt(tfm, b_0, s_0);
+ crypto_cipher_encrypt_one(tfm, s_0, b_0);
}
void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
- u8 *b_0, u8 *aad, u8 *data, size_t data_len,
+ u8 *data, size_t data_len,
u8 *cdata, u8 *mic)
{
int i, j, last_len, num_blocks;
- u8 *pos, *cpos, *b, *s_0, *e;
+ 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, b_0, aad, b, s_0, b);
+ aes_ccm_prepare(tfm, scratch, b);
/* Process payload blocks */
pos = data;
@@ -77,11 +76,11 @@ void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
/* Authentication followed by encryption */
for (i = 0; i < blen; i++)
b[i] ^= pos[i];
- ieee80211_aes_encrypt(tfm, b, b);
+ crypto_cipher_encrypt_one(tfm, b, b);
b_0[14] = (j >> 8) & 0xff;
b_0[15] = j & 0xff;
- ieee80211_aes_encrypt(tfm, b_0, e);
+ crypto_cipher_encrypt_one(tfm, e, b_0);
for (i = 0; i < blen; i++)
*cpos++ = *pos++ ^ e[i];
}
@@ -92,19 +91,20 @@ void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
- u8 *b_0, u8 *aad, u8 *cdata, size_t data_len,
- u8 *mic, u8 *data)
+ u8 *cdata, size_t data_len, u8 *mic, u8 *data)
{
int i, j, last_len, num_blocks;
- u8 *pos, *cpos, *b, *s_0, *a;
+ 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, b_0, aad, b, s_0, a);
+ aes_ccm_prepare(tfm, scratch, a);
/* Process payload blocks */
cpos = cdata;
@@ -116,13 +116,12 @@ int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
/* Decryption followed by authentication */
b_0[14] = (j >> 8) & 0xff;
b_0[15] = j & 0xff;
- ieee80211_aes_encrypt(tfm, b_0, b);
+ crypto_cipher_encrypt_one(tfm, b, b_0);
for (i = 0; i < blen; i++) {
*pos = *cpos++ ^ b[i];
a[i] ^= *pos++;
}
-
- ieee80211_aes_encrypt(tfm, a, a);
+ crypto_cipher_encrypt_one(tfm, a, a);
}
for (i = 0; i < CCMP_MIC_LEN; i++) {
@@ -134,7 +133,7 @@ int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
}
-struct crypto_cipher * ieee80211_aes_key_setup_encrypt(const u8 key[])
+struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[])
{
struct crypto_cipher *tfm;
diff --git a/net/mac80211/aes_ccm.h b/net/mac80211/aes_ccm.h
index 885f19030b2..6e7820ef344 100644
--- a/net/mac80211/aes_ccm.h
+++ b/net/mac80211/aes_ccm.h
@@ -14,12 +14,12 @@
#define AES_BLOCK_LEN 16
-struct crypto_cipher * ieee80211_aes_key_setup_encrypt(const u8 key[]);
+struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]);
void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
- u8 *b_0, u8 *aad, u8 *data, size_t data_len,
+ u8 *data, size_t data_len,
u8 *cdata, u8 *mic);
int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
- u8 *b_0, u8 *aad, u8 *cdata, size_t data_len,
+ u8 *cdata, size_t data_len,
u8 *mic, u8 *data);
void ieee80211_aes_key_free(struct crypto_cipher *tfm);
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index a9fce4afdf2..855126a3039 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -17,26 +17,26 @@
#include "rate.h"
#include "mesh.h"
-static enum ieee80211_if_types
-nl80211_type_to_mac80211_type(enum nl80211_iftype type)
+struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ return &local->hw;
+}
+EXPORT_SYMBOL(wiphy_to_hw);
+
+static bool nl80211_type_check(enum nl80211_iftype type)
{
switch (type) {
- case NL80211_IFTYPE_UNSPECIFIED:
- return IEEE80211_IF_TYPE_STA;
case NL80211_IFTYPE_ADHOC:
- return IEEE80211_IF_TYPE_IBSS;
case NL80211_IFTYPE_STATION:
- return IEEE80211_IF_TYPE_STA;
case NL80211_IFTYPE_MONITOR:
- return IEEE80211_IF_TYPE_MNTR;
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
- return IEEE80211_IF_TYPE_MESH_POINT;
#endif
case NL80211_IFTYPE_WDS:
- return IEEE80211_IF_TYPE_WDS;
+ return true;
default:
- return IEEE80211_IF_TYPE_INVALID;
+ return false;
}
}
@@ -45,20 +45,15 @@ static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
struct vif_params *params)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
- enum ieee80211_if_types itype;
struct net_device *dev;
struct ieee80211_sub_if_data *sdata;
int err;
- if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
- return -ENODEV;
-
- itype = nl80211_type_to_mac80211_type(type);
- if (itype == IEEE80211_IF_TYPE_INVALID)
+ if (!nl80211_type_check(type))
return -EINVAL;
- err = ieee80211_if_add(local->mdev, name, &dev, itype, params);
- if (err || itype != IEEE80211_IF_TYPE_MNTR || !flags)
+ err = ieee80211_if_add(local, name, &dev, type, params);
+ if (err || type != NL80211_IFTYPE_MONITOR || !flags)
return err;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -68,61 +63,52 @@ static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex)
{
- struct ieee80211_local *local = wiphy_priv(wiphy);
struct net_device *dev;
- char *name;
-
- if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
- return -ENODEV;
+ struct ieee80211_sub_if_data *sdata;
/* we're under RTNL */
dev = __dev_get_by_index(&init_net, ifindex);
if (!dev)
- return 0;
+ return -ENODEV;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- name = dev->name;
+ ieee80211_if_remove(sdata);
- return ieee80211_if_remove(local->mdev, name, -1);
+ return 0;
}
static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
enum nl80211_iftype type, u32 *flags,
struct vif_params *params)
{
- struct ieee80211_local *local = wiphy_priv(wiphy);
struct net_device *dev;
- enum ieee80211_if_types itype;
struct ieee80211_sub_if_data *sdata;
-
- if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
- return -ENODEV;
+ int ret;
/* we're under RTNL */
dev = __dev_get_by_index(&init_net, ifindex);
if (!dev)
return -ENODEV;
- if (netif_running(dev))
- return -EBUSY;
-
- itype = nl80211_type_to_mac80211_type(type);
- if (itype == IEEE80211_IF_TYPE_INVALID)
+ if (!nl80211_type_check(type))
return -EINVAL;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
- return -EOPNOTSUPP;
+ ret = ieee80211_if_change_type(sdata, type);
+ if (ret)
+ return ret;
- ieee80211_if_reinit(dev);
- ieee80211_if_set_type(dev, itype);
+ if (netif_running(sdata->dev))
+ return -EBUSY;
if (ieee80211_vif_is_mesh(&sdata->vif) && params->mesh_id_len)
- ieee80211_if_sta_set_mesh_id(&sdata->u.sta,
- params->mesh_id_len,
- params->mesh_id);
+ ieee80211_sdata_set_mesh_id(sdata,
+ params->mesh_id_len,
+ params->mesh_id);
- if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR || !flags)
+ if (sdata->vif.type != NL80211_IFTYPE_MONITOR || !flags)
return 0;
sdata->u.mntr_flags = *flags;
@@ -227,7 +213,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
void (*callback)(void *cookie,
struct key_params *params))
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *sdata;
struct sta_info *sta = NULL;
u8 seq[6] = {0};
struct key_params params;
@@ -236,6 +222,8 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
u16 iv16;
int err = -ENOENT;
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
rcu_read_lock();
if (mac_addr) {
@@ -256,8 +244,8 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
case ALG_TKIP:
params.cipher = WLAN_CIPHER_SUITE_TKIP;
- iv32 = key->u.tkip.iv32;
- iv16 = key->u.tkip.iv16;
+ iv32 = key->u.tkip.tx.iv32;
+ iv16 = key->u.tkip.tx.iv16;
if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
sdata->local->ops->get_tkip_seq)
@@ -359,7 +347,7 @@ static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
sta = sta_info_get_by_idx(local, idx, dev);
if (sta) {
ret = 0;
- memcpy(mac, sta->addr, ETH_ALEN);
+ memcpy(mac, sta->sta.addr, ETH_ALEN);
sta_set_sinfo(sta, sinfo);
}
@@ -485,16 +473,18 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
kfree(old);
- return ieee80211_if_config_beacon(sdata->dev);
+ return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
}
static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
struct beacon_parameters *params)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *sdata;
struct beacon_data *old;
- if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
return -EINVAL;
old = sdata->u.ap.beacon;
@@ -508,10 +498,12 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev,
struct beacon_parameters *params)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *sdata;
struct beacon_data *old;
- if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
return -EINVAL;
old = sdata->u.ap.beacon;
@@ -524,10 +516,12 @@ static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *sdata;
struct beacon_data *old;
- if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
return -EINVAL;
old = sdata->u.ap.beacon;
@@ -539,7 +533,7 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
synchronize_rcu();
kfree(old);
- return ieee80211_if_config_beacon(dev);
+ return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
}
/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
@@ -570,7 +564,7 @@ static void ieee80211_send_layer2_update(struct sta_info *sta)
* Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */
memset(msg->da, 0xff, ETH_ALEN);
- memcpy(msg->sa, sta->addr, ETH_ALEN);
+ memcpy(msg->sa, sta->sta.addr, ETH_ALEN);
msg->len = htons(6);
msg->dsap = 0;
msg->ssap = 0x01; /* NULL LSAP, CR Bit: Response */
@@ -602,6 +596,7 @@ static void sta_apply_parameters(struct ieee80211_local *local,
*/
if (params->station_flags & STATION_FLAG_CHANGED) {
+ spin_lock_bh(&sta->lock);
sta->flags &= ~WLAN_STA_AUTHORIZED;
if (params->station_flags & STATION_FLAG_AUTHORIZED)
sta->flags |= WLAN_STA_AUTHORIZED;
@@ -613,6 +608,7 @@ static void sta_apply_parameters(struct ieee80211_local *local,
sta->flags &= ~WLAN_STA_WME;
if (params->station_flags & STATION_FLAG_WME)
sta->flags |= WLAN_STA_WME;
+ spin_unlock_bh(&sta->lock);
}
/*
@@ -623,9 +619,9 @@ static void sta_apply_parameters(struct ieee80211_local *local,
*/
if (params->aid) {
- sta->aid = params->aid;
- if (sta->aid > IEEE80211_MAX_AID)
- sta->aid = 0; /* XXX: should this be an error? */
+ sta->sta.aid = params->aid;
+ if (sta->sta.aid > IEEE80211_MAX_AID)
+ sta->sta.aid = 0; /* XXX: should this be an error? */
}
if (params->listen_interval >= 0)
@@ -642,7 +638,12 @@ static void sta_apply_parameters(struct ieee80211_local *local,
rates |= BIT(j);
}
}
- sta->supp_rates[local->oper_channel->band] = rates;
+ sta->sta.supp_rates[local->oper_channel->band] = rates;
+ }
+
+ if (params->ht_capa) {
+ ieee80211_ht_cap_ie_to_ht_info(params->ht_capa,
+ &sta->sta.ht_info);
}
if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) {
@@ -660,7 +661,7 @@ static void sta_apply_parameters(struct ieee80211_local *local,
static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_parameters *params)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = wiphy_priv(wiphy);
struct sta_info *sta;
struct ieee80211_sub_if_data *sdata;
int err;
@@ -672,8 +673,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
if (params->vlan) {
sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
- if (sdata->vif.type != IEEE80211_IF_TYPE_VLAN &&
- sdata->vif.type != IEEE80211_IF_TYPE_AP)
+ if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+ sdata->vif.type != NL80211_IFTYPE_AP)
return -EINVAL;
} else
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -692,7 +693,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
sta_apply_parameters(local, sta, params);
- rate_control_rate_init(sta, local);
+ rate_control_rate_init(sta);
rcu_read_lock();
@@ -703,8 +704,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
return err;
}
- if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
- sdata->vif.type == IEEE80211_IF_TYPE_AP)
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+ sdata->vif.type == NL80211_IFTYPE_AP)
ieee80211_send_layer2_update(sta);
rcu_read_unlock();
@@ -715,10 +716,12 @@ 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)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = sdata->local;
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
if (mac) {
rcu_read_lock();
@@ -744,7 +747,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
u8 *mac,
struct station_parameters *params)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = wiphy_priv(wiphy);
struct sta_info *sta;
struct ieee80211_sub_if_data *vlansdata;
@@ -760,13 +763,13 @@ static int ieee80211_change_station(struct wiphy *wiphy,
if (params->vlan && params->vlan != sta->sdata->dev) {
vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
- if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN &&
- vlansdata->vif.type != IEEE80211_IF_TYPE_AP) {
+ if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+ vlansdata->vif.type != NL80211_IFTYPE_AP) {
rcu_read_unlock();
return -EINVAL;
}
- sta->sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
+ sta->sdata = vlansdata;
ieee80211_send_layer2_update(sta);
}
@@ -781,8 +784,8 @@ static int ieee80211_change_station(struct wiphy *wiphy,
static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
u8 *dst, u8 *next_hop)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_sub_if_data *sdata;
struct mesh_path *mpath;
struct sta_info *sta;
int err;
@@ -790,7 +793,9 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
if (!netif_running(dev))
return -ENETDOWN;
- if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
return -ENOTSUPP;
rcu_read_lock();
@@ -800,13 +805,13 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
return -ENOENT;
}
- err = mesh_path_add(dst, dev);
+ err = mesh_path_add(dst, sdata);
if (err) {
rcu_read_unlock();
return err;
}
- mpath = mesh_path_lookup(dst, dev);
+ mpath = mesh_path_lookup(dst, sdata);
if (!mpath) {
rcu_read_unlock();
return -ENXIO;
@@ -820,10 +825,12 @@ 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)
{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
if (dst)
- return mesh_path_del(dst, dev);
+ return mesh_path_del(dst, sdata);
- mesh_path_flush(dev);
+ mesh_path_flush(sdata);
return 0;
}
@@ -831,15 +838,17 @@ static int ieee80211_change_mpath(struct wiphy *wiphy,
struct net_device *dev,
u8 *dst, u8 *next_hop)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_sub_if_data *sdata;
struct mesh_path *mpath;
struct sta_info *sta;
if (!netif_running(dev))
return -ENETDOWN;
- if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
return -ENOTSUPP;
rcu_read_lock();
@@ -850,7 +859,7 @@ static int ieee80211_change_mpath(struct wiphy *wiphy,
return -ENOENT;
}
- mpath = mesh_path_lookup(dst, dev);
+ mpath = mesh_path_lookup(dst, sdata);
if (!mpath) {
rcu_read_unlock();
return -ENOENT;
@@ -866,7 +875,7 @@ 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->addr, ETH_ALEN);
+ memcpy(next_hop, mpath->next_hop->sta.addr, ETH_ALEN);
else
memset(next_hop, 0, ETH_ALEN);
@@ -905,14 +914,16 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *sdata;
struct mesh_path *mpath;
- if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
return -ENOTSUPP;
rcu_read_lock();
- mpath = mesh_path_lookup(dst, dev);
+ mpath = mesh_path_lookup(dst, sdata);
if (!mpath) {
rcu_read_unlock();
return -ENOENT;
@@ -927,14 +938,16 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *dst, u8 *next_hop,
struct mpath_info *pinfo)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *sdata;
struct mesh_path *mpath;
- if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
return -ENOTSUPP;
rcu_read_lock();
- mpath = mesh_path_lookup_by_idx(idx, dev);
+ mpath = mesh_path_lookup_by_idx(idx, sdata);
if (!mpath) {
rcu_read_unlock();
return -ENOENT;
@@ -946,6 +959,38 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
}
#endif
+static int ieee80211_change_bss(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct bss_parameters *params)
+{
+ struct ieee80211_sub_if_data *sdata;
+ u32 changed = 0;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
+ return -EINVAL;
+
+ if (params->use_cts_prot >= 0) {
+ sdata->bss_conf.use_cts_prot = params->use_cts_prot;
+ changed |= BSS_CHANGED_ERP_CTS_PROT;
+ }
+ if (params->use_short_preamble >= 0) {
+ sdata->bss_conf.use_short_preamble =
+ params->use_short_preamble;
+ changed |= BSS_CHANGED_ERP_PREAMBLE;
+ }
+ if (params->use_short_slot_time >= 0) {
+ sdata->bss_conf.use_short_slot =
+ params->use_short_slot_time;
+ changed |= BSS_CHANGED_ERP_SLOT;
+ }
+
+ ieee80211_bss_info_change_notify(sdata, changed);
+
+ return 0;
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -969,4 +1014,5 @@ struct cfg80211_ops mac80211_config_ops = {
.get_mpath = ieee80211_get_mpath,
.dump_mpath = ieee80211_dump_mpath,
#endif
+ .change_bss = ieee80211_change_bss,
};
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 1cccbfd781f..24ce5446331 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -51,8 +51,6 @@ DEBUGFS_READONLY_FILE(antenna_sel_tx, 20, "%d",
local->hw.conf.antenna_sel_tx);
DEBUGFS_READONLY_FILE(antenna_sel_rx, 20, "%d",
local->hw.conf.antenna_sel_rx);
-DEBUGFS_READONLY_FILE(bridge_packets, 20, "%d",
- local->bridge_packets);
DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
local->rts_threshold);
DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
@@ -70,16 +68,6 @@ DEBUGFS_READONLY_FILE(rate_ctrl_alg, 100, "%s",
/* statistics stuff */
-static inline int rtnl_lock_local(struct ieee80211_local *local)
-{
- rtnl_lock();
- if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED)) {
- rtnl_unlock();
- return -ENODEV;
- }
- return 0;
-}
-
#define DEBUGFS_STATS_FILE(name, buflen, fmt, value...) \
DEBUGFS_READONLY_FILE(stats_ ##name, buflen, fmt, ##value)
@@ -96,10 +84,7 @@ static ssize_t format_devstat_counter(struct ieee80211_local *local,
if (!local->ops->get_stats)
return -EOPNOTSUPP;
- res = rtnl_lock_local(local);
- if (res)
- return res;
-
+ rtnl_lock();
res = local->ops->get_stats(local_to_hw(local), &stats);
rtnl_unlock();
if (!res)
@@ -197,45 +182,6 @@ DEBUGFS_STATS_FILE(rx_handlers_fragments, 20, "%u",
DEBUGFS_STATS_FILE(tx_status_drop, 20, "%u",
local->tx_status_drop);
-static ssize_t stats_wme_rx_queue_read(struct file *file,
- char __user *userbuf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_local *local = file->private_data;
- char buf[NUM_RX_DATA_QUEUES*15], *p = buf;
- int i;
-
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
- p += scnprintf(p, sizeof(buf)+buf-p,
- "%u\n", local->wme_rx_queue[i]);
-
- return simple_read_from_buffer(userbuf, count, ppos, buf, p-buf);
-}
-
-static const struct file_operations stats_wme_rx_queue_ops = {
- .read = stats_wme_rx_queue_read,
- .open = mac80211_open_file_generic,
-};
-
-static ssize_t stats_wme_tx_queue_read(struct file *file,
- char __user *userbuf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_local *local = file->private_data;
- char buf[NUM_TX_DATA_QUEUES*15], *p = buf;
- int i;
-
- for (i = 0; i < NUM_TX_DATA_QUEUES; i++)
- p += scnprintf(p, sizeof(buf)+buf-p,
- "%u\n", local->wme_tx_queue[i]);
-
- return simple_read_from_buffer(userbuf, count, ppos, buf, p-buf);
-}
-
-static const struct file_operations stats_wme_tx_queue_ops = {
- .read = stats_wme_tx_queue_read,
- .open = mac80211_open_file_generic,
-};
#endif
DEBUGFS_DEVSTATS_FILE(dot11ACKFailureCount);
@@ -258,7 +204,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_ADD(frequency);
DEBUGFS_ADD(antenna_sel_tx);
DEBUGFS_ADD(antenna_sel_rx);
- DEBUGFS_ADD(bridge_packets);
DEBUGFS_ADD(rts_threshold);
DEBUGFS_ADD(fragmentation_threshold);
DEBUGFS_ADD(short_retry_limit);
@@ -303,8 +248,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_STATS_ADD(rx_expand_skb_head2);
DEBUGFS_STATS_ADD(rx_handlers_fragments);
DEBUGFS_STATS_ADD(tx_status_drop);
- DEBUGFS_STATS_ADD(wme_tx_queue);
- DEBUGFS_STATS_ADD(wme_rx_queue);
#endif
DEBUGFS_STATS_ADD(dot11ACKFailureCount);
DEBUGFS_STATS_ADD(dot11RTSFailureCount);
@@ -317,7 +260,6 @@ void debugfs_hw_del(struct ieee80211_local *local)
DEBUGFS_DEL(frequency);
DEBUGFS_DEL(antenna_sel_tx);
DEBUGFS_DEL(antenna_sel_rx);
- DEBUGFS_DEL(bridge_packets);
DEBUGFS_DEL(rts_threshold);
DEBUGFS_DEL(fragmentation_threshold);
DEBUGFS_DEL(short_retry_limit);
@@ -356,8 +298,6 @@ void debugfs_hw_del(struct ieee80211_local *local)
DEBUGFS_STATS_DEL(rx_expand_skb_head2);
DEBUGFS_STATS_DEL(rx_handlers_fragments);
DEBUGFS_STATS_DEL(tx_status_drop);
- DEBUGFS_STATS_DEL(wme_tx_queue);
- DEBUGFS_STATS_DEL(wme_rx_queue);
#endif
DEBUGFS_STATS_DEL(dot11ACKFailureCount);
DEBUGFS_STATS_DEL(dot11RTSFailureCount);
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 19efc3a6a93..a3294d10932 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -97,8 +97,8 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,
break;
case ALG_TKIP:
len = scnprintf(buf, sizeof(buf), "%08x %04x\n",
- key->u.tkip.iv32,
- key->u.tkip.iv16);
+ key->u.tkip.tx.iv32,
+ key->u.tkip.tx.iv16);
break;
case ALG_CCMP:
tpn = key->u.ccmp.tx_pn;
@@ -128,8 +128,8 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
p += scnprintf(p, sizeof(buf)+buf-p,
"%08x %04x\n",
- key->u.tkip.iv32_rx[i],
- key->u.tkip.iv16_rx[i]);
+ key->u.tkip.rx[i].iv32,
+ key->u.tkip.rx[i].iv16);
len = p - buf;
break;
case ALG_CCMP:
@@ -206,7 +206,8 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
rcu_read_lock();
sta = rcu_dereference(key->sta);
if (sta)
- sprintf(buf, "../../stations/%s", print_mac(mac, sta->addr));
+ sprintf(buf, "../../stations/%s",
+ print_mac(mac, sta->sta.addr));
rcu_read_unlock();
/* using sta as a boolean is fine outside RCU lock */
@@ -265,7 +266,7 @@ void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata)
key = sdata->default_key;
if (key) {
sprintf(buf, "../keys/%d", key->debugfs.cnt);
- sdata->debugfs.default_key =
+ sdata->common_debugfs.default_key =
debugfs_create_symlink("default_key",
sdata->debugfsdir, buf);
} else
@@ -277,8 +278,8 @@ void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata)
if (!sdata)
return;
- debugfs_remove(sdata->debugfs.default_key);
- sdata->debugfs.default_key = NULL;
+ debugfs_remove(sdata->common_debugfs.default_key);
+ sdata->common_debugfs.default_key = NULL;
}
void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key,
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index e3326d04694..2ad504fc341 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -155,8 +155,9 @@ static const struct file_operations name##_ops = { \
__IEEE80211_IF_WFILE(name)
/* common attributes */
-IEEE80211_IF_FILE(channel_use, channel_use, DEC);
IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
+IEEE80211_IF_FILE(force_unicast_rateidx, force_unicast_rateidx, DEC);
+IEEE80211_IF_FILE(max_ratectrl_rateidx, max_ratectrl_rateidx, DEC);
/* STA/IBSS attributes */
IEEE80211_IF_FILE(state, u.sta.state, DEC);
@@ -172,7 +173,6 @@ IEEE80211_IF_FILE(assoc_tries, u.sta.assoc_tries, DEC);
IEEE80211_IF_FILE(auth_algs, u.sta.auth_algs, HEX);
IEEE80211_IF_FILE(auth_alg, u.sta.auth_alg, DEC);
IEEE80211_IF_FILE(auth_transaction, u.sta.auth_transaction, DEC);
-IEEE80211_IF_FILE(num_beacons_sta, u.sta.num_beacons, DEC);
static ssize_t ieee80211_if_fmt_flags(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
@@ -191,9 +191,6 @@ __IEEE80211_IF_FILE(flags);
/* 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_beacons, u.ap.num_beacons, DEC);
-IEEE80211_IF_FILE(force_unicast_rateidx, u.ap.force_unicast_rateidx, DEC);
-IEEE80211_IF_FILE(max_ratectrl_rateidx, u.ap.max_ratectrl_rateidx, DEC);
static ssize_t ieee80211_if_fmt_num_buffered_multicast(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
@@ -208,37 +205,37 @@ IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
#ifdef CONFIG_MAC80211_MESH
/* Mesh stats attributes */
-IEEE80211_IF_FILE(fwded_frames, u.sta.mshstats.fwded_frames, DEC);
-IEEE80211_IF_FILE(dropped_frames_ttl, u.sta.mshstats.dropped_frames_ttl, 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_no_route,
- u.sta.mshstats.dropped_frames_no_route, DEC);
-IEEE80211_IF_FILE(estab_plinks, u.sta.mshstats.estab_plinks, ATOMIC);
+ u.mesh.mshstats.dropped_frames_no_route, DEC);
+IEEE80211_IF_FILE(estab_plinks, u.mesh.mshstats.estab_plinks, ATOMIC);
/* Mesh parameters */
IEEE80211_IF_WFILE(dot11MeshMaxRetries,
- u.sta.mshcfg.dot11MeshMaxRetries, DEC, u8);
+ u.mesh.mshcfg.dot11MeshMaxRetries, DEC, u8);
IEEE80211_IF_WFILE(dot11MeshRetryTimeout,
- u.sta.mshcfg.dot11MeshRetryTimeout, DEC, u16);
+ u.mesh.mshcfg.dot11MeshRetryTimeout, DEC, u16);
IEEE80211_IF_WFILE(dot11MeshConfirmTimeout,
- u.sta.mshcfg.dot11MeshConfirmTimeout, DEC, u16);
+ u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC, u16);
IEEE80211_IF_WFILE(dot11MeshHoldingTimeout,
- u.sta.mshcfg.dot11MeshHoldingTimeout, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshTTL, u.sta.mshcfg.dot11MeshTTL, DEC, u8);
-IEEE80211_IF_WFILE(auto_open_plinks, u.sta.mshcfg.auto_open_plinks, DEC, u8);
+ u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC, u16);
+IEEE80211_IF_WFILE(dot11MeshTTL, u.mesh.mshcfg.dot11MeshTTL, DEC, u8);
+IEEE80211_IF_WFILE(auto_open_plinks, u.mesh.mshcfg.auto_open_plinks, DEC, u8);
IEEE80211_IF_WFILE(dot11MeshMaxPeerLinks,
- u.sta.mshcfg.dot11MeshMaxPeerLinks, DEC, u16);
+ u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC, u16);
IEEE80211_IF_WFILE(dot11MeshHWMPactivePathTimeout,
- u.sta.mshcfg.dot11MeshHWMPactivePathTimeout, DEC, u32);
+ u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC, u32);
IEEE80211_IF_WFILE(dot11MeshHWMPpreqMinInterval,
- u.sta.mshcfg.dot11MeshHWMPpreqMinInterval, DEC, u16);
+ u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC, u16);
IEEE80211_IF_WFILE(dot11MeshHWMPnetDiameterTraversalTime,
- u.sta.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC, u16);
+ u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC, u16);
IEEE80211_IF_WFILE(dot11MeshHWMPmaxPREQretries,
- u.sta.mshcfg.dot11MeshHWMPmaxPREQretries, DEC, u8);
+ u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC, u8);
IEEE80211_IF_WFILE(path_refresh_time,
- u.sta.mshcfg.path_refresh_time, DEC, u32);
+ u.mesh.mshcfg.path_refresh_time, DEC, u32);
IEEE80211_IF_WFILE(min_discovery_timeout,
- u.sta.mshcfg.min_discovery_timeout, DEC, u16);
+ u.mesh.mshcfg.min_discovery_timeout, DEC, u16);
#endif
@@ -248,8 +245,10 @@ IEEE80211_IF_WFILE(min_discovery_timeout,
static void add_sta_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_ADD(channel_use, sta);
DEBUGFS_ADD(drop_unencrypted, sta);
+ DEBUGFS_ADD(force_unicast_rateidx, sta);
+ DEBUGFS_ADD(max_ratectrl_rateidx, sta);
+
DEBUGFS_ADD(state, sta);
DEBUGFS_ADD(bssid, sta);
DEBUGFS_ADD(prev_bssid, sta);
@@ -264,32 +263,33 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(auth_alg, sta);
DEBUGFS_ADD(auth_transaction, sta);
DEBUGFS_ADD(flags, sta);
- DEBUGFS_ADD(num_beacons_sta, sta);
}
static void add_ap_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_ADD(channel_use, ap);
DEBUGFS_ADD(drop_unencrypted, ap);
- DEBUGFS_ADD(num_sta_ps, ap);
- DEBUGFS_ADD(dtim_count, ap);
- DEBUGFS_ADD(num_beacons, ap);
DEBUGFS_ADD(force_unicast_rateidx, ap);
DEBUGFS_ADD(max_ratectrl_rateidx, ap);
+
+ DEBUGFS_ADD(num_sta_ps, ap);
+ DEBUGFS_ADD(dtim_count, ap);
DEBUGFS_ADD(num_buffered_multicast, ap);
}
static void add_wds_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_ADD(channel_use, wds);
DEBUGFS_ADD(drop_unencrypted, wds);
+ DEBUGFS_ADD(force_unicast_rateidx, wds);
+ DEBUGFS_ADD(max_ratectrl_rateidx, wds);
+
DEBUGFS_ADD(peer, wds);
}
static void add_vlan_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_ADD(channel_use, vlan);
DEBUGFS_ADD(drop_unencrypted, vlan);
+ DEBUGFS_ADD(force_unicast_rateidx, vlan);
+ DEBUGFS_ADD(max_ratectrl_rateidx, vlan);
}
static void add_monitor_files(struct ieee80211_sub_if_data *sdata)
@@ -341,26 +341,26 @@ static void add_files(struct ieee80211_sub_if_data *sdata)
return;
switch (sdata->vif.type) {
- case IEEE80211_IF_TYPE_MESH_POINT:
+ case NL80211_IFTYPE_MESH_POINT:
#ifdef CONFIG_MAC80211_MESH
add_mesh_stats(sdata);
add_mesh_config(sdata);
#endif
- /* fall through */
- case IEEE80211_IF_TYPE_STA:
- case IEEE80211_IF_TYPE_IBSS:
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
add_sta_files(sdata);
break;
- case IEEE80211_IF_TYPE_AP:
+ case NL80211_IFTYPE_AP:
add_ap_files(sdata);
break;
- case IEEE80211_IF_TYPE_WDS:
+ case NL80211_IFTYPE_WDS:
add_wds_files(sdata);
break;
- case IEEE80211_IF_TYPE_MNTR:
+ case NL80211_IFTYPE_MONITOR:
add_monitor_files(sdata);
break;
- case IEEE80211_IF_TYPE_VLAN:
+ case NL80211_IFTYPE_AP_VLAN:
add_vlan_files(sdata);
break;
default:
@@ -376,8 +376,10 @@ static void add_files(struct ieee80211_sub_if_data *sdata)
static void del_sta_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_DEL(channel_use, sta);
DEBUGFS_DEL(drop_unencrypted, sta);
+ DEBUGFS_DEL(force_unicast_rateidx, sta);
+ DEBUGFS_DEL(max_ratectrl_rateidx, sta);
+
DEBUGFS_DEL(state, sta);
DEBUGFS_DEL(bssid, sta);
DEBUGFS_DEL(prev_bssid, sta);
@@ -392,32 +394,33 @@ static void del_sta_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_DEL(auth_alg, sta);
DEBUGFS_DEL(auth_transaction, sta);
DEBUGFS_DEL(flags, sta);
- DEBUGFS_DEL(num_beacons_sta, sta);
}
static void del_ap_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_DEL(channel_use, ap);
DEBUGFS_DEL(drop_unencrypted, ap);
- DEBUGFS_DEL(num_sta_ps, ap);
- DEBUGFS_DEL(dtim_count, ap);
- DEBUGFS_DEL(num_beacons, ap);
DEBUGFS_DEL(force_unicast_rateidx, ap);
DEBUGFS_DEL(max_ratectrl_rateidx, ap);
+
+ DEBUGFS_DEL(num_sta_ps, ap);
+ DEBUGFS_DEL(dtim_count, ap);
DEBUGFS_DEL(num_buffered_multicast, ap);
}
static void del_wds_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_DEL(channel_use, wds);
DEBUGFS_DEL(drop_unencrypted, wds);
+ DEBUGFS_DEL(force_unicast_rateidx, wds);
+ DEBUGFS_DEL(max_ratectrl_rateidx, wds);
+
DEBUGFS_DEL(peer, wds);
}
static void del_vlan_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_DEL(channel_use, vlan);
DEBUGFS_DEL(drop_unencrypted, vlan);
+ DEBUGFS_DEL(force_unicast_rateidx, vlan);
+ DEBUGFS_DEL(max_ratectrl_rateidx, vlan);
}
static void del_monitor_files(struct ieee80211_sub_if_data *sdata)
@@ -467,32 +470,32 @@ static void del_mesh_config(struct ieee80211_sub_if_data *sdata)
}
#endif
-static void del_files(struct ieee80211_sub_if_data *sdata, int type)
+static void del_files(struct ieee80211_sub_if_data *sdata)
{
if (!sdata->debugfsdir)
return;
- switch (type) {
- case IEEE80211_IF_TYPE_MESH_POINT:
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_MESH_POINT:
#ifdef CONFIG_MAC80211_MESH
del_mesh_stats(sdata);
del_mesh_config(sdata);
#endif
- /* fall through */
- case IEEE80211_IF_TYPE_STA:
- case IEEE80211_IF_TYPE_IBSS:
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
del_sta_files(sdata);
break;
- case IEEE80211_IF_TYPE_AP:
+ case NL80211_IFTYPE_AP:
del_ap_files(sdata);
break;
- case IEEE80211_IF_TYPE_WDS:
+ case NL80211_IFTYPE_WDS:
del_wds_files(sdata);
break;
- case IEEE80211_IF_TYPE_MNTR:
+ case NL80211_IFTYPE_MONITOR:
del_monitor_files(sdata);
break;
- case IEEE80211_IF_TYPE_VLAN:
+ case NL80211_IFTYPE_AP_VLAN:
del_vlan_files(sdata);
break;
default:
@@ -512,29 +515,23 @@ void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata)
sprintf(buf, "netdev:%s", sdata->dev->name);
sdata->debugfsdir = debugfs_create_dir(buf,
sdata->local->hw.wiphy->debugfsdir);
+ add_files(sdata);
}
void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata)
{
- del_files(sdata, sdata->vif.type);
+ del_files(sdata);
debugfs_remove(sdata->debugfsdir);
sdata->debugfsdir = NULL;
}
-void ieee80211_debugfs_change_if_type(struct ieee80211_sub_if_data *sdata,
- int oldtype)
-{
- del_files(sdata, oldtype);
- add_files(sdata);
-}
-
-static int netdev_notify(struct notifier_block * nb,
+static int netdev_notify(struct notifier_block *nb,
unsigned long state,
void *ndev)
{
struct net_device *dev = ndev;
struct dentry *dir;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *sdata;
char buf[10+IFNAMSIZ];
if (state != NETDEV_CHANGENAME)
@@ -546,8 +543,14 @@ static int netdev_notify(struct notifier_block * nb,
if (dev->ieee80211_ptr->wiphy->privid != mac80211_wiphy_privid)
return 0;
- sprintf(buf, "netdev:%s", dev->name);
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
dir = sdata->debugfsdir;
+
+ if (!dir)
+ return 0;
+
+ sprintf(buf, "netdev:%s", dev->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);
diff --git a/net/mac80211/debugfs_netdev.h b/net/mac80211/debugfs_netdev.h
index a690071fde8..7af731f0b73 100644
--- a/net/mac80211/debugfs_netdev.h
+++ b/net/mac80211/debugfs_netdev.h
@@ -6,8 +6,6 @@
#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);
-void ieee80211_debugfs_change_if_type(struct ieee80211_sub_if_data *sdata,
- int oldtype);
void ieee80211_debugfs_netdev_init(void);
void ieee80211_debugfs_netdev_exit(void);
#else
@@ -17,9 +15,6 @@ static inline void ieee80211_debugfs_add_netdev(
static inline void ieee80211_debugfs_remove_netdev(
struct ieee80211_sub_if_data *sdata)
{}
-static inline void ieee80211_debugfs_change_if_type(
- struct ieee80211_sub_if_data *sdata, int oldtype)
-{}
static inline void ieee80211_debugfs_netdev_init(void)
{}
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 6d47a1d31b3..189d0bafa91 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -50,7 +50,7 @@ static const struct file_operations sta_ ##name## _ops = { \
STA_READ_##format(name, field) \
STA_OPS(name)
-STA_FILE(aid, aid, D);
+STA_FILE(aid, sta.aid, D);
STA_FILE(dev, sdata->dev->name, S);
STA_FILE(rx_packets, rx_packets, LU);
STA_FILE(tx_packets, tx_packets, LU);
@@ -63,10 +63,9 @@ STA_FILE(tx_fragments, tx_fragments, LU);
STA_FILE(tx_filtered, tx_filtered_count, LU);
STA_FILE(tx_retry_failed, tx_retry_failed, LU);
STA_FILE(tx_retry_count, tx_retry_count, LU);
-STA_FILE(last_rssi, last_rssi, D);
STA_FILE(last_signal, last_signal, D);
+STA_FILE(last_qual, last_qual, D);
STA_FILE(last_noise, last_noise, D);
-STA_FILE(channel_use, channel_use, D);
STA_FILE(wep_weak_iv_count, wep_weak_iv_count, LU);
static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
@@ -74,14 +73,15 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
{
char buf[100];
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",
- sta->flags & WLAN_STA_AUTH ? "AUTH\n" : "",
- sta->flags & WLAN_STA_ASSOC ? "ASSOC\n" : "",
- sta->flags & WLAN_STA_PS ? "PS\n" : "",
- sta->flags & WLAN_STA_AUTHORIZED ? "AUTHORIZED\n" : "",
- sta->flags & WLAN_STA_SHORT_PREAMBLE ? "SHORT PREAMBLE\n" : "",
- sta->flags & WLAN_STA_WME ? "WME\n" : "",
- sta->flags & WLAN_STA_WDS ? "WDS\n" : "");
+ staflags & WLAN_STA_AUTH ? "AUTH\n" : "",
+ staflags & WLAN_STA_ASSOC ? "ASSOC\n" : "",
+ staflags & WLAN_STA_PS ? "PS\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" : "");
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
STA_OPS(flags);
@@ -123,36 +123,6 @@ static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf,
}
STA_OPS(last_seq_ctrl);
-#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
-static ssize_t sta_wme_rx_queue_read(struct file *file, char __user *userbuf,
- size_t count, loff_t *ppos)
-{
- char buf[15*NUM_RX_DATA_QUEUES], *p = buf;
- int i;
- struct sta_info *sta = file->private_data;
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
- p += scnprintf(p, sizeof(buf)+buf-p, "%u ",
- sta->wme_rx_queue[i]);
- p += scnprintf(p, sizeof(buf)+buf-p, "\n");
- return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
-}
-STA_OPS(wme_rx_queue);
-
-static ssize_t sta_wme_tx_queue_read(struct file *file, char __user *userbuf,
- size_t count, loff_t *ppos)
-{
- char buf[15*NUM_TX_DATA_QUEUES], *p = buf;
- int i;
- struct sta_info *sta = file->private_data;
- for (i = 0; i < NUM_TX_DATA_QUEUES; i++)
- p += scnprintf(p, sizeof(buf)+buf-p, "%u ",
- sta->wme_tx_queue[i]);
- p += scnprintf(p, sizeof(buf)+buf-p, "\n");
- return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
-}
-STA_OPS(wme_tx_queue);
-#endif
-
static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
@@ -203,10 +173,9 @@ static ssize_t sta_agg_status_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
struct sta_info *sta = file->private_data;
- struct net_device *dev = sta->sdata->dev;
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = sta->sdata->local;
struct ieee80211_hw *hw = &local->hw;
- u8 *da = sta->addr;
+ u8 *da = sta->sta.addr;
static int tid_static_tx[16] = {0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0};
static int tid_static_rx[16] = {1, 1, 1, 1, 1, 1, 1, 1,
@@ -231,7 +200,7 @@ static ssize_t sta_agg_status_write(struct file *file,
tid_num = tid_num - 100;
if (tid_static_rx[tid_num] == 1) {
strcpy(state, "off ");
- ieee80211_sta_stop_rx_ba_session(dev, da, tid_num, 0,
+ ieee80211_sta_stop_rx_ba_session(sta->sdata, da, tid_num, 0,
WLAN_REASON_QSTA_REQUIRE_SETUP);
sta->ampdu_mlme.tid_state_rx[tid_num] |=
HT_AGG_STATE_DEBUGFS_CTL;
@@ -280,11 +249,22 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DECLARE_MAC_BUF(mbuf);
u8 *mac;
+ sta->debugfs.add_has_run = true;
+
if (!stations_dir)
return;
- mac = print_mac(mbuf, sta->addr);
-
+ mac = print_mac(mbuf, sta->sta.addr);
+
+ /*
+ * This might fail due to a race condition:
+ * When mac80211 unlinks a station, the debugfs entries
+ * remain, but it is already possible to link a new
+ * station with the same address which triggers adding
+ * it to debugfs; therefore, if the old station isn't
+ * destroyed quickly enough the old station's debugfs
+ * dir might still be around.
+ */
sta->debugfs.dir = debugfs_create_dir(mac, stations_dir);
if (!sta->debugfs.dir)
return;
@@ -293,10 +273,6 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD(num_ps_buf_frames);
DEBUGFS_ADD(inactive_ms);
DEBUGFS_ADD(last_seq_ctrl);
-#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
- DEBUGFS_ADD(wme_rx_queue);
- DEBUGFS_ADD(wme_tx_queue);
-#endif
DEBUGFS_ADD(agg_status);
}
@@ -306,10 +282,6 @@ void ieee80211_sta_debugfs_remove(struct sta_info *sta)
DEBUGFS_DEL(num_ps_buf_frames);
DEBUGFS_DEL(inactive_ms);
DEBUGFS_DEL(last_seq_ctrl);
-#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
- DEBUGFS_DEL(wme_rx_queue);
- DEBUGFS_DEL(wme_tx_queue);
-#endif
DEBUGFS_DEL(agg_status);
debugfs_remove(sta->debugfs.dir);
diff --git a/net/mac80211/event.c b/net/mac80211/event.c
index 2280f40b456..8de60de70bc 100644
--- a/net/mac80211/event.c
+++ b/net/mac80211/event.c
@@ -8,7 +8,6 @@
* mac80211 - events
*/
-#include <linux/netdevice.h>
#include <net/iw_handler.h>
#include "ieee80211_i.h"
@@ -17,7 +16,7 @@
* (in the variable hdr) must be long enough to extract the TKIP
* fields like TSC
*/
-void mac80211_ev_michael_mic_failure(struct net_device *dev, int keyidx,
+void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
struct ieee80211_hdr *hdr)
{
union iwreq_data wrqu;
@@ -32,7 +31,7 @@ void mac80211_ev_michael_mic_failure(struct net_device *dev, int keyidx,
print_mac(mac, hdr->addr2));
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = strlen(buf);
- wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
+ wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf);
kfree(buf);
}
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
new file mode 100644
index 00000000000..dc7d9a3d70d
--- /dev/null
+++ b/net/mac80211/ht.c
@@ -0,0 +1,992 @@
+/*
+ * HT handling
+ *
+ * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright 2002-2005, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2007-2008, Intel Corporation
+ *
+ * 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 <net/wireless.h>
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+#include "sta_info.h"
+#include "wme.h"
+
+int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
+ struct ieee80211_ht_info *ht_info)
+{
+
+ if (ht_info == NULL)
+ return -EINVAL;
+
+ memset(ht_info, 0, sizeof(*ht_info));
+
+ if (ht_cap_ie) {
+ u8 ampdu_info = ht_cap_ie->ampdu_params_info;
+
+ ht_info->ht_supported = 1;
+ ht_info->cap = le16_to_cpu(ht_cap_ie->cap_info);
+ ht_info->ampdu_factor =
+ ampdu_info & IEEE80211_HT_CAP_AMPDU_FACTOR;
+ ht_info->ampdu_density =
+ (ampdu_info & IEEE80211_HT_CAP_AMPDU_DENSITY) >> 2;
+ memcpy(ht_info->supp_mcs_set, ht_cap_ie->supp_mcs_set, 16);
+ } else
+ ht_info->ht_supported = 0;
+
+ return 0;
+}
+
+int ieee80211_ht_addt_info_ie_to_ht_bss_info(
+ struct ieee80211_ht_addt_info *ht_add_info_ie,
+ struct ieee80211_ht_bss_info *bss_info)
+{
+ if (bss_info == NULL)
+ return -EINVAL;
+
+ memset(bss_info, 0, sizeof(*bss_info));
+
+ if (ht_add_info_ie) {
+ u16 op_mode;
+ op_mode = le16_to_cpu(ht_add_info_ie->operation_mode);
+
+ bss_info->primary_channel = ht_add_info_ie->control_chan;
+ bss_info->bss_cap = ht_add_info_ie->ht_param;
+ bss_info->bss_op_mode = (u8)(op_mode & 0xff);
+ }
+
+ return 0;
+}
+
+static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
+ const u8 *da, u16 tid,
+ u8 dialog_token, u16 start_seq_num,
+ u16 agg_size, u16 timeout)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ u16 capab;
+
+ 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->dev->name);
+ 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->dev->dev_addr, ETH_ALEN);
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
+ else
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION);
+
+ skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_req));
+
+ mgmt->u.action.category = WLAN_CATEGORY_BACK;
+ mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ;
+
+ mgmt->u.action.u.addba_req.dialog_token = dialog_token;
+ capab = (u16)(1 << 1); /* bit 1 aggregation policy */
+ capab |= (u16)(tid << 2); /* bit 5:2 TID number */
+ capab |= (u16)(agg_size << 6); /* bit 15:6 max size of aggergation */
+
+ mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab);
+
+ mgmt->u.action.u.addba_req.timeout = cpu_to_le16(timeout);
+ mgmt->u.action.u.addba_req.start_seq_num =
+ cpu_to_le16(start_seq_num << 4);
+
+ ieee80211_tx_skb(sdata, skb, 0);
+}
+
+static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *da, u16 tid,
+ u8 dialog_token, u16 status, u16 policy,
+ u16 buf_size, u16 timeout)
+{
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ 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->dev->name);
+ 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->dev->dev_addr, ETH_ALEN);
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
+ else
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION);
+
+ skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_resp));
+ mgmt->u.action.category = WLAN_CATEGORY_BACK;
+ mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP;
+ mgmt->u.action.u.addba_resp.dialog_token = dialog_token;
+
+ capab = (u16)(policy << 1); /* bit 1 aggregation policy */
+ capab |= (u16)(tid << 2); /* bit 5:2 TID number */
+ capab |= (u16)(buf_size << 6); /* bit 15:6 max size of aggregation */
+
+ mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab);
+ mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout);
+ mgmt->u.action.u.addba_resp.status = cpu_to_le16(status);
+
+ ieee80211_tx_skb(sdata, skb, 0);
+}
+
+static void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
+ const u8 *da, u16 tid,
+ u16 initiator, u16 reason_code)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ 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->dev->name);
+ 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->dev->dev_addr, ETH_ALEN);
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
+ else
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION);
+
+ skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba));
+
+ mgmt->u.action.category = WLAN_CATEGORY_BACK;
+ mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA;
+ params = (u16)(initiator << 11); /* bit 11 initiator */
+ params |= (u16)(tid << 12); /* bit 15:12 TID number */
+
+ mgmt->u.action.u.delba.params = cpu_to_le16(params);
+ mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);
+
+ ieee80211_tx_skb(sdata, skb, 0);
+}
+
+void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn)
+{
+ 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->dev->name);
+ return;
+ }
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+ bar = (struct ieee80211_bar *)skb_put(skb, sizeof(*bar));
+ memset(bar, 0, sizeof(*bar));
+ bar->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
+ IEEE80211_STYPE_BACK_REQ);
+ memcpy(bar->ra, ra, ETH_ALEN);
+ memcpy(bar->ta, sdata->dev->dev_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 = cpu_to_le16(bar_control);
+ bar->start_seq_num = cpu_to_le16(ssn);
+
+ ieee80211_tx_skb(sdata, skb, 0);
+}
+
+void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid,
+ u16 initiator, u16 reason)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_hw *hw = &local->hw;
+ struct sta_info *sta;
+ int ret, i;
+ DECLARE_MAC_BUF(mac);
+
+ rcu_read_lock();
+
+ sta = sta_info_get(local, ra);
+ if (!sta) {
+ rcu_read_unlock();
+ return;
+ }
+
+ /* check if TID is in operational state */
+ spin_lock_bh(&sta->lock);
+ if (sta->ampdu_mlme.tid_state_rx[tid]
+ != HT_AGG_STATE_OPERATIONAL) {
+ spin_unlock_bh(&sta->lock);
+ rcu_read_unlock();
+ return;
+ }
+ sta->ampdu_mlme.tid_state_rx[tid] =
+ HT_AGG_STATE_REQ_STOP_BA_MSK |
+ (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
+ spin_unlock_bh(&sta->lock);
+
+ /* stop HW Rx aggregation. ampdu_action existence
+ * already verified in session init so we add the BUG_ON */
+ BUG_ON(!local->ops->ampdu_action);
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Rx BA session stop requested for %s tid %u\n",
+ print_mac(mac, ra), tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+ ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP,
+ &sta->sta, tid, NULL);
+ if (ret)
+ printk(KERN_DEBUG "HW problem - can not stop rx "
+ "aggregation for tid %d\n", tid);
+
+ /* shutdown timer has not expired */
+ if (initiator != WLAN_BACK_TIMER)
+ del_timer_sync(&sta->ampdu_mlme.tid_rx[tid]->session_timer);
+
+ /* check if this is a self generated aggregation halt */
+ if (initiator == WLAN_BACK_RECIPIENT || initiator == WLAN_BACK_TIMER)
+ ieee80211_send_delba(sdata, ra, tid, 0, reason);
+
+ /* free the reordering buffer */
+ for (i = 0; i < sta->ampdu_mlme.tid_rx[tid]->buf_size; i++) {
+ if (sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]) {
+ /* release the reordered frames */
+ dev_kfree_skb(sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]);
+ sta->ampdu_mlme.tid_rx[tid]->stored_mpdu_num--;
+ sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i] = NULL;
+ }
+ }
+ /* free resources */
+ kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_buf);
+ kfree(sta->ampdu_mlme.tid_rx[tid]);
+ sta->ampdu_mlme.tid_rx[tid] = NULL;
+ sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_IDLE;
+
+ rcu_read_unlock();
+}
+
+
+/*
+ * After sending add Block Ack request we activated a timer until
+ * add Block Ack response will arrive from the recipient.
+ * If this timer expires sta_addba_resp_timer_expired will be executed.
+ */
+static void sta_addba_resp_timer_expired(unsigned long data)
+{
+ /* not an elegant detour, but there is no choice as the timer passes
+ * only one argument, and both sta_info and TID are needed, so init
+ * flow in sta_info_create gives the TID as data, while the timer_to_id
+ * array gives the sta through container_of */
+ u16 tid = *(u8 *)data;
+ struct sta_info *temp_sta = container_of((void *)data,
+ struct sta_info, timer_to_tid[tid]);
+
+ struct ieee80211_local *local = temp_sta->local;
+ struct ieee80211_hw *hw = &local->hw;
+ struct sta_info *sta;
+ u8 *state;
+
+ rcu_read_lock();
+
+ sta = sta_info_get(local, temp_sta->sta.addr);
+ if (!sta) {
+ rcu_read_unlock();
+ return;
+ }
+
+ state = &sta->ampdu_mlme.tid_state_tx[tid];
+ /* check if the TID waits for addBA response */
+ spin_lock_bh(&sta->lock);
+ if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
+ spin_unlock_bh(&sta->lock);
+ *state = HT_AGG_STATE_IDLE;
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "timer expired on tid %d but we are not "
+ "expecting addBA response there", tid);
+#endif
+ goto timer_expired_exit;
+ }
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "addBA response timer expired on tid %d\n", tid);
+#endif
+
+ /* go through the state check in stop_BA_session */
+ *state = HT_AGG_STATE_OPERATIONAL;
+ spin_unlock_bh(&sta->lock);
+ ieee80211_stop_tx_ba_session(hw, temp_sta->sta.addr, tid,
+ WLAN_BACK_INITIATOR);
+
+timer_expired_exit:
+ rcu_read_unlock();
+}
+
+void ieee80211_sta_tear_down_BA_sessions(struct ieee80211_sub_if_data *sdata, u8 *addr)
+{
+ struct ieee80211_local *local = sdata->local;
+ int i;
+
+ for (i = 0; i < STA_TID_NUM; i++) {
+ ieee80211_stop_tx_ba_session(&local->hw, addr, i,
+ WLAN_BACK_INITIATOR);
+ ieee80211_sta_stop_rx_ba_session(sdata, addr, i,
+ WLAN_BACK_RECIPIENT,
+ WLAN_REASON_QSTA_LEAVE_QBSS);
+ }
+}
+
+int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct sta_info *sta;
+ struct ieee80211_sub_if_data *sdata;
+ u16 start_seq_num;
+ u8 *state;
+ int ret;
+ DECLARE_MAC_BUF(mac);
+
+ if (tid >= STA_TID_NUM)
+ return -EINVAL;
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Open BA session requested for %s tid %u\n",
+ print_mac(mac, ra), tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+ rcu_read_lock();
+
+ sta = sta_info_get(local, ra);
+ if (!sta) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Could not find the station\n");
+#endif
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ spin_lock_bh(&sta->lock);
+
+ /* we have tried too many times, receiver does not want A-MPDU */
+ if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) {
+ ret = -EBUSY;
+ goto err_unlock_sta;
+ }
+
+ state = &sta->ampdu_mlme.tid_state_tx[tid];
+ /* check if the TID is not in aggregation flow already */
+ if (*state != HT_AGG_STATE_IDLE) {
+#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 */
+ ret = -EAGAIN;
+ goto err_unlock_sta;
+ }
+
+ /* prepare A-MPDU MLME for Tx aggregation */
+ sta->ampdu_mlme.tid_tx[tid] =
+ kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC);
+ if (!sta->ampdu_mlme.tid_tx[tid]) {
+#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;
+ }
+ /* Tx timer */
+ sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function =
+ sta_addba_resp_timer_expired;
+ sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.data =
+ (unsigned long)&sta->timer_to_tid[tid];
+ init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
+
+ /* create a new queue for this aggregation */
+ ret = ieee80211_ht_agg_queue_add(local, sta, tid);
+
+ /* case no queue is available to aggregation
+ * don't switch to aggregation */
+ if (ret) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "BA request denied - queue unavailable for"
+ " tid %d\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+ goto err_unlock_queue;
+ }
+ sdata = sta->sdata;
+
+ /* Ok, the Addba frame hasn't been sent yet, but if the driver calls the
+ * call back right away, it must see that the flow has begun */
+ *state |= HT_ADDBA_REQUESTED_MSK;
+
+ /* This is slightly racy because the queue isn't stopped */
+ start_seq_num = sta->tid_seq[tid];
+
+ if (local->ops->ampdu_action)
+ ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START,
+ &sta->sta, tid, &start_seq_num);
+
+ if (ret) {
+ /* No need to requeue the packets in the agg queue, since we
+ * held the tx lock: no packet could be enqueued to the newly
+ * allocated queue */
+ ieee80211_ht_agg_queue_remove(local, sta, tid, 0);
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "BA request denied - HW unavailable for"
+ " tid %d\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+ *state = HT_AGG_STATE_IDLE;
+ goto err_unlock_queue;
+ }
+
+ /* Will put all the packets in the new SW queue */
+ ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
+ spin_unlock_bh(&sta->lock);
+
+ /* send an addBA request */
+ sta->ampdu_mlme.dialog_token_allocator++;
+ sta->ampdu_mlme.tid_tx[tid]->dialog_token =
+ sta->ampdu_mlme.dialog_token_allocator;
+ sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num;
+
+
+ ieee80211_send_addba_request(sta->sdata, ra, tid,
+ sta->ampdu_mlme.tid_tx[tid]->dialog_token,
+ sta->ampdu_mlme.tid_tx[tid]->ssn,
+ 0x40, 5000);
+ /* activate the timer for the recipient's addBA response */
+ sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.expires =
+ jiffies + ADDBA_RESP_INTERVAL;
+ add_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid);
+#endif
+ goto exit;
+
+err_unlock_queue:
+ kfree(sta->ampdu_mlme.tid_tx[tid]);
+ sta->ampdu_mlme.tid_tx[tid] = NULL;
+ ret = -EBUSY;
+err_unlock_sta:
+ spin_unlock_bh(&sta->lock);
+exit:
+ rcu_read_unlock();
+ return ret;
+}
+EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
+
+int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
+ u8 *ra, u16 tid,
+ enum ieee80211_back_parties initiator)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct sta_info *sta;
+ u8 *state;
+ int ret = 0;
+ DECLARE_MAC_BUF(mac);
+
+ if (tid >= STA_TID_NUM)
+ return -EINVAL;
+
+ rcu_read_lock();
+ sta = sta_info_get(local, ra);
+ if (!sta) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+
+ /* check if the TID is in aggregation */
+ state = &sta->ampdu_mlme.tid_state_tx[tid];
+ spin_lock_bh(&sta->lock);
+
+ if (*state != HT_AGG_STATE_OPERATIONAL) {
+ ret = -ENOENT;
+ goto stop_BA_exit;
+ }
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Tx BA session stop requested for %s tid %u\n",
+ print_mac(mac, ra), tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+ ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]);
+
+ *state = HT_AGG_STATE_REQ_STOP_BA_MSK |
+ (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
+
+ if (local->ops->ampdu_action)
+ ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_STOP,
+ &sta->sta, tid, NULL);
+
+ /* case HW denied going back to legacy */
+ if (ret) {
+ WARN_ON(ret != -EBUSY);
+ *state = HT_AGG_STATE_OPERATIONAL;
+ ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ goto stop_BA_exit;
+ }
+
+stop_BA_exit:
+ spin_unlock_bh(&sta->lock);
+ rcu_read_unlock();
+ return ret;
+}
+EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
+
+void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct sta_info *sta;
+ u8 *state;
+ DECLARE_MAC_BUF(mac);
+
+ 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
+ return;
+ }
+
+ rcu_read_lock();
+ sta = sta_info_get(local, ra);
+ if (!sta) {
+ rcu_read_unlock();
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Could not find station: %s\n",
+ print_mac(mac, ra));
+#endif
+ return;
+ }
+
+ state = &sta->ampdu_mlme.tid_state_tx[tid];
+ spin_lock_bh(&sta->lock);
+
+ if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
+ *state);
+#endif
+ spin_unlock_bh(&sta->lock);
+ rcu_read_unlock();
+ return;
+ }
+
+ WARN_ON_ONCE(*state & HT_ADDBA_DRV_READY_MSK);
+
+ *state |= HT_ADDBA_DRV_READY_MSK;
+
+ if (*state == HT_AGG_STATE_OPERATIONAL) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
+#endif
+ ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ }
+ spin_unlock_bh(&sta->lock);
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
+
+void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct sta_info *sta;
+ u8 *state;
+ int agg_queue;
+ DECLARE_MAC_BUF(mac);
+
+ 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
+ return;
+ }
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Stopping Tx BA session for %s tid %d\n",
+ print_mac(mac, ra), tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+ rcu_read_lock();
+ sta = sta_info_get(local, ra);
+ if (!sta) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Could not find station: %s\n",
+ print_mac(mac, ra));
+#endif
+ rcu_read_unlock();
+ return;
+ }
+ state = &sta->ampdu_mlme.tid_state_tx[tid];
+
+ /* NOTE: no need to use sta->lock in this state check, as
+ * ieee80211_stop_tx_ba_session will let only one stop call to
+ * pass through per sta/tid
+ */
+ if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n");
+#endif
+ rcu_read_unlock();
+ return;
+ }
+
+ if (*state & HT_AGG_STATE_INITIATOR_MSK)
+ ieee80211_send_delba(sta->sdata, ra, tid,
+ WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
+
+ agg_queue = sta->tid_to_tx_q[tid];
+
+ ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
+
+ /* We just requeued the all the frames that were in the
+ * removed queue, and since we might miss a softirq we do
+ * netif_schedule_queue. ieee80211_wake_queue is not used
+ * here as this queue is not necessarily stopped
+ */
+ netif_schedule_queue(netdev_get_tx_queue(local->mdev, agg_queue));
+ spin_lock_bh(&sta->lock);
+ *state = HT_AGG_STATE_IDLE;
+ sta->ampdu_mlme.addba_req_num[tid] = 0;
+ kfree(sta->ampdu_mlme.tid_tx[tid]);
+ sta->ampdu_mlme.tid_tx[tid] = NULL;
+ spin_unlock_bh(&sta->lock);
+
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb);
+
+void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
+ const u8 *ra, u16 tid)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ 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", skb->dev->name);
+#endif
+ return;
+ }
+ ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
+ memcpy(&ra_tid->ra, ra, ETH_ALEN);
+ ra_tid->tid = tid;
+
+ skb->pkt_type = IEEE80211_ADDBA_MSG;
+ skb_queue_tail(&local->skb_queue, skb);
+ tasklet_schedule(&local->tasklet);
+}
+EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe);
+
+void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
+ const u8 *ra, u16 tid)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ 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", skb->dev->name);
+#endif
+ return;
+ }
+ ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
+ memcpy(&ra_tid->ra, ra, ETH_ALEN);
+ ra_tid->tid = tid;
+
+ skb->pkt_type = IEEE80211_DELBA_MSG;
+ skb_queue_tail(&local->skb_queue, skb);
+ tasklet_schedule(&local->tasklet);
+}
+EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb_irqsafe);
+
+/*
+ * After accepting the AddBA Request we activated a timer,
+ * resetting it after each frame that arrives from the originator.
+ * if this timer expires ieee80211_sta_stop_rx_ba_session will be executed.
+ */
+static void sta_rx_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]);
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid);
+#endif
+ ieee80211_sta_stop_rx_ba_session(sta->sdata, sta->sta.addr,
+ (u16)*ptid, WLAN_BACK_TIMER,
+ WLAN_REASON_QSTA_TIMEOUT);
+}
+
+void ieee80211_process_addba_request(struct ieee80211_local *local,
+ struct sta_info *sta,
+ 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;
+ int ret = -EOPNOTSUPP;
+ DECLARE_MAC_BUF(mac);
+
+ /* extract session parameters from addba request frame */
+ dialog_token = mgmt->u.action.u.addba_req.dialog_token;
+ timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
+ start_seq_num =
+ le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
+
+ capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
+ ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
+ tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+ buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
+
+ status = WLAN_STATUS_REQUEST_DECLINED;
+
+ /* sanity check for incoming parameters:
+ * check if configuration can support the BA policy
+ * and if buffer size does not exceeds max value */
+ if (((ba_policy != 1)
+ && (!(conf->ht_conf.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 "
+ "%s on tid %u. policy %d, buffer size %d\n",
+ print_mac(mac, mgmt->sa), tid, ba_policy,
+ buf_size);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+ 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_info.ampdu_factor;
+ }
+
+
+ /* examine state machine */
+ spin_lock_bh(&sta->lock);
+
+ if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_IDLE) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ if (net_ratelimit())
+ printk(KERN_DEBUG "unexpected AddBA Req from "
+ "%s on tid %u\n",
+ print_mac(mac, mgmt->sa), tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+ goto end;
+ }
+
+ /* prepare A-MPDU MLME for Rx aggregation */
+ sta->ampdu_mlme.tid_rx[tid] =
+ kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC);
+ if (!sta->ampdu_mlme.tid_rx[tid]) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ if (net_ratelimit())
+ printk(KERN_ERR "allocate rx mlme to tid %d failed\n",
+ tid);
+#endif
+ goto end;
+ }
+ /* rx timer */
+ sta->ampdu_mlme.tid_rx[tid]->session_timer.function =
+ sta_rx_agg_session_timer_expired;
+ sta->ampdu_mlme.tid_rx[tid]->session_timer.data =
+ (unsigned long)&sta->timer_to_tid[tid];
+ init_timer(&sta->ampdu_mlme.tid_rx[tid]->session_timer);
+
+ tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
+
+ /* prepare reordering buffer */
+ tid_agg_rx->reorder_buf =
+ kmalloc(buf_size * sizeof(struct sk_buff *), GFP_ATOMIC);
+ if (!tid_agg_rx->reorder_buf) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ if (net_ratelimit())
+ printk(KERN_ERR "can not allocate reordering buffer "
+ "to tid %d\n", tid);
+#endif
+ kfree(sta->ampdu_mlme.tid_rx[tid]);
+ goto end;
+ }
+ memset(tid_agg_rx->reorder_buf, 0,
+ buf_size * sizeof(struct sk_buff *));
+
+ if (local->ops->ampdu_action)
+ ret = local->ops->ampdu_action(hw, 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 */
+
+ if (ret) {
+ kfree(tid_agg_rx->reorder_buf);
+ kfree(tid_agg_rx);
+ sta->ampdu_mlme.tid_rx[tid] = NULL;
+ goto end;
+ }
+
+ /* change state and send addba resp */
+ sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_OPERATIONAL;
+ tid_agg_rx->dialog_token = dialog_token;
+ tid_agg_rx->ssn = start_seq_num;
+ tid_agg_rx->head_seq_num = start_seq_num;
+ tid_agg_rx->buf_size = buf_size;
+ tid_agg_rx->timeout = timeout;
+ tid_agg_rx->stored_mpdu_num = 0;
+ status = WLAN_STATUS_SUCCESS;
+end:
+ spin_unlock_bh(&sta->lock);
+
+end_no_lock:
+ ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
+ dialog_token, status, 1, buf_size, timeout);
+}
+
+void ieee80211_process_addba_resp(struct ieee80211_local *local,
+ struct sta_info *sta,
+ struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct ieee80211_hw *hw = &local->hw;
+ u16 capab;
+ u16 tid;
+ u8 *state;
+
+ capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
+ tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+
+ state = &sta->ampdu_mlme.tid_state_tx[tid];
+
+ spin_lock_bh(&sta->lock);
+
+ if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
+ spin_unlock_bh(&sta->lock);
+ return;
+ }
+
+ if (mgmt->u.action.u.addba_resp.dialog_token !=
+ sta->ampdu_mlme.tid_tx[tid]->dialog_token) {
+ spin_unlock_bh(&sta->lock);
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "wrong addBA response token, tid %d\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+ return;
+ }
+
+ del_timer_sync(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "switched off addBA timer for tid %d \n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+ if (le16_to_cpu(mgmt->u.action.u.addba_resp.status)
+ == WLAN_STATUS_SUCCESS) {
+ *state |= HT_ADDBA_RECEIVED_MSK;
+ sta->ampdu_mlme.addba_req_num[tid] = 0;
+
+ if (*state == HT_AGG_STATE_OPERATIONAL)
+ ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+
+ spin_unlock_bh(&sta->lock);
+ } else {
+ sta->ampdu_mlme.addba_req_num[tid]++;
+ /* this will allow the state check in stop_BA_session */
+ *state = HT_AGG_STATE_OPERATIONAL;
+ spin_unlock_bh(&sta->lock);
+ ieee80211_stop_tx_ba_session(hw, sta->sta.addr, tid,
+ WLAN_BACK_INITIATOR);
+ }
+}
+
+void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct ieee80211_local *local = sdata->local;
+ u16 tid, params;
+ u16 initiator;
+ DECLARE_MAC_BUF(mac);
+
+ params = le16_to_cpu(mgmt->u.action.u.delba.params);
+ 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 %s (%s) tid %d reason code %d\n",
+ print_mac(mac, mgmt->sa),
+ initiator ? "initiator" : "recipient", tid,
+ mgmt->u.action.u.delba.reason_code);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+ if (initiator == WLAN_BACK_INITIATOR)
+ ieee80211_sta_stop_rx_ba_session(sdata, sta->sta.addr, tid,
+ WLAN_BACK_INITIATOR, 0);
+ else { /* WLAN_BACK_RECIPIENT */
+ spin_lock_bh(&sta->lock);
+ sta->ampdu_mlme.tid_state_tx[tid] =
+ HT_AGG_STATE_OPERATIONAL;
+ spin_unlock_bh(&sta->lock);
+ ieee80211_stop_tx_ba_session(&local->hw, sta->sta.addr, tid,
+ WLAN_BACK_RECIPIENT);
+ }
+}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 006486b2672..156e42a003a 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2,6 +2,7 @@
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2005, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007-2008 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
@@ -23,20 +24,11 @@
#include <linux/spinlock.h>
#include <linux/etherdevice.h>
#include <net/wireless.h>
+#include <net/iw_handler.h>
+#include <net/mac80211.h>
#include "key.h"
#include "sta_info.h"
-/* ieee80211.o internal definitions, etc. These are not included into
- * low-level drivers. */
-
-#ifndef ETH_P_PAE
-#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
-#endif /* ETH_P_PAE */
-
-#define WLAN_FC_DATA_PRESENT(fc) (((fc) & 0x4c) == 0x08)
-
-#define IEEE80211_FC(type, subtype) cpu_to_le16(type | subtype)
-
struct ieee80211_local;
/* Maximum number of broadcast/multicast frames to buffer when some of the
@@ -58,6 +50,12 @@ struct ieee80211_local;
* increased memory use (about 2 kB of RAM per entry). */
#define IEEE80211_FRAGMENT_MAX 4
+/*
+ * Time after which we ignore scan results and no longer report/use
+ * them in any way.
+ */
+#define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ)
+
struct ieee80211_fragment_entry {
unsigned long first_frag_time;
unsigned int seq;
@@ -70,27 +68,25 @@ struct ieee80211_fragment_entry {
};
-struct ieee80211_sta_bss {
+struct ieee80211_bss {
struct list_head list;
- struct ieee80211_sta_bss *hnext;
+ struct ieee80211_bss *hnext;
size_t ssid_len;
atomic_t users;
u8 bssid[ETH_ALEN];
u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 dtim_period;
u16 capability; /* host byte order */
enum ieee80211_band band;
int freq;
- int rssi, signal, noise;
- u8 *wpa_ie;
- size_t wpa_ie_len;
- u8 *rsn_ie;
- size_t rsn_ie_len;
- u8 *wmm_ie;
- size_t wmm_ie_len;
- u8 *ht_ie;
- size_t ht_ie_len;
+ int signal, noise, qual;
+ u8 *ies; /* all information elements from the last Beacon or Probe
+ * Response frames; note Beacon frame is not allowed to
+ * override values from Probe Response */
+ size_t ies_len;
+ bool wmm_used;
#ifdef CONFIG_MAC80211_MESH
u8 *mesh_id;
size_t mesh_id_len;
@@ -102,7 +98,7 @@ struct ieee80211_sta_bss {
u64 timestamp;
int beacon_int;
- bool probe_resp;
+ unsigned long last_probe_resp;
unsigned long last_update;
/* during assocation, we save an ERP value from a probe response so
@@ -113,7 +109,7 @@ struct ieee80211_sta_bss {
u8 erp_value;
};
-static inline u8 *bss_mesh_cfg(struct ieee80211_sta_bss *bss)
+static inline u8 *bss_mesh_cfg(struct ieee80211_bss *bss)
{
#ifdef CONFIG_MAC80211_MESH
return bss->mesh_cfg;
@@ -121,7 +117,7 @@ static inline u8 *bss_mesh_cfg(struct ieee80211_sta_bss *bss)
return NULL;
}
-static inline u8 *bss_mesh_id(struct ieee80211_sta_bss *bss)
+static inline u8 *bss_mesh_id(struct ieee80211_bss *bss)
{
#ifdef CONFIG_MAC80211_MESH
return bss->mesh_id;
@@ -129,7 +125,7 @@ static inline u8 *bss_mesh_id(struct ieee80211_sta_bss *bss)
return NULL;
}
-static inline u8 bss_mesh_id_len(struct ieee80211_sta_bss *bss)
+static inline u8 bss_mesh_id_len(struct ieee80211_bss *bss)
{
#ifdef CONFIG_MAC80211_MESH
return bss->mesh_id_len;
@@ -147,7 +143,6 @@ typedef unsigned __bitwise__ ieee80211_tx_result;
#define IEEE80211_TX_UNICAST BIT(1)
#define IEEE80211_TX_PS_BUFFERED BIT(2)
#define IEEE80211_TX_PROBE_LAST_FRAG BIT(3)
-#define IEEE80211_TX_INJECTED BIT(4)
struct ieee80211_tx_data {
struct sk_buff *skb;
@@ -157,20 +152,19 @@ struct ieee80211_tx_data {
struct sta_info *sta;
struct ieee80211_key *key;
- struct ieee80211_tx_control *control;
struct ieee80211_channel *channel;
- struct ieee80211_rate *rate;
+ s8 rate_idx;
/* use this rate (if set) for last fragment; rate can
* be set to lower rate for the first fragments, e.g.,
* when using CTS protection with IEEE 802.11g. */
- struct ieee80211_rate *last_frag_rate;
+ s8 last_frag_rate_idx;
/* Extra fragments (in addition to the first fragment
* in skb) */
struct sk_buff **extra_frag;
int num_extra_frag;
- u16 fc, ethertype;
+ u16 ethertype;
unsigned int flags;
};
@@ -198,36 +192,20 @@ struct ieee80211_rx_data {
struct ieee80211_rx_status *status;
struct ieee80211_rate *rate;
- u16 fc, ethertype;
+ u16 ethertype;
unsigned int flags;
int sent_ps_buffered;
int queue;
- int load;
u32 tkip_iv32;
u16 tkip_iv16;
};
-/* flags used in struct ieee80211_tx_packet_data.flags */
-#define IEEE80211_TXPD_REQ_TX_STATUS BIT(0)
-#define IEEE80211_TXPD_DO_NOT_ENCRYPT BIT(1)
-#define IEEE80211_TXPD_REQUEUE BIT(2)
-#define IEEE80211_TXPD_EAPOL_FRAME BIT(3)
-#define IEEE80211_TXPD_AMPDU BIT(4)
-/* Stored in sk_buff->cb */
-struct ieee80211_tx_packet_data {
- int ifindex;
- unsigned long jiffies;
- unsigned int flags;
- u8 queue;
-};
-
struct ieee80211_tx_stored_packet {
- struct ieee80211_tx_control control;
struct sk_buff *skb;
struct sk_buff **extra_frag;
- struct ieee80211_rate *last_frag_rate;
+ s8 last_frag_rate_idx;
int num_extra_frag;
- unsigned int last_frag_rate_ctrl_probe;
+ bool last_frag_rate_ctrl_probe;
};
struct beacon_data {
@@ -251,9 +229,6 @@ struct ieee80211_if_ap {
struct sk_buff_head ps_bc_buf;
atomic_t num_sta_ps; /* number of stations in PS mode */
int dtim_count;
- int force_unicast_rateidx; /* forced TX rateidx for unicast frames */
- int max_ratectrl_rateidx; /* max TX rateidx for rate control */
- int num_beacons; /* number of TXed beacon frames for this BSS */
};
struct ieee80211_if_wds {
@@ -262,7 +237,6 @@ struct ieee80211_if_wds {
};
struct ieee80211_if_vlan {
- struct ieee80211_sub_if_data *ap;
struct list_head list;
};
@@ -315,48 +289,37 @@ struct mesh_config {
#define IEEE80211_STA_AUTO_BSSID_SEL BIT(11)
#define IEEE80211_STA_AUTO_CHANNEL_SEL BIT(12)
#define IEEE80211_STA_PRIVACY_INVOKED BIT(13)
+/* flags for MLME request */
+#define IEEE80211_STA_REQ_SCAN 0
+#define IEEE80211_STA_REQ_DIRECT_PROBE 1
+#define IEEE80211_STA_REQ_AUTH 2
+#define IEEE80211_STA_REQ_RUN 3
+
+/* STA/IBSS MLME states */
+enum ieee80211_sta_mlme_state {
+ IEEE80211_STA_MLME_DISABLED,
+ IEEE80211_STA_MLME_DIRECT_PROBE,
+ IEEE80211_STA_MLME_AUTHENTICATE,
+ IEEE80211_STA_MLME_ASSOCIATE,
+ IEEE80211_STA_MLME_ASSOCIATED,
+ IEEE80211_STA_MLME_IBSS_SEARCH,
+ IEEE80211_STA_MLME_IBSS_JOINED,
+};
+
+/* bitfield of allowed auth algs */
+#define IEEE80211_AUTH_ALG_OPEN BIT(0)
+#define IEEE80211_AUTH_ALG_SHARED_KEY BIT(1)
+#define IEEE80211_AUTH_ALG_LEAP BIT(2)
+
struct ieee80211_if_sta {
struct timer_list timer;
struct work_struct work;
u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
u8 ssid[IEEE80211_MAX_SSID_LEN];
- enum {
- IEEE80211_DISABLED, IEEE80211_AUTHENTICATE,
- IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED,
- IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED,
- IEEE80211_MESH_UP
- } state;
+ enum ieee80211_sta_mlme_state state;
size_t ssid_len;
u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
size_t scan_ssid_len;
-#ifdef CONFIG_MAC80211_MESH
- struct timer_list mesh_path_timer;
- u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
- size_t mesh_id_len;
- /* Active Path Selection Protocol Identifier */
- u8 mesh_pp_id[4];
- /* Active Path Selection Metric Identifier */
- u8 mesh_pm_id[4];
- /* Congestion Control Mode Identifier */
- u8 mesh_cc_id[4];
- /* Local mesh Destination Sequence Number */
- u32 dsn;
- /* Last used PREQ ID */
- u32 preq_id;
- atomic_t mpaths;
- /* Timestamp of last DSN update */
- unsigned long last_dsn_update;
- /* Timestamp of last DSN sent */
- unsigned long last_preq;
- struct mesh_rmc *rmc;
- spinlock_t mesh_preq_queue_lock;
- struct mesh_preq_queue preq_queue;
- int preq_queue_len;
- struct mesh_stats mshstats;
- struct mesh_config mshcfg;
- u32 mesh_seqnum;
- bool accepting_plinks;
-#endif
u16 aid;
u16 ap_capab, capab;
u8 *extra_ie; /* to be added to the end of AssocReq */
@@ -368,20 +331,17 @@ struct ieee80211_if_sta {
struct sk_buff_head skb_queue;
- int auth_tries, assoc_tries;
+ int assoc_scan_tries; /* number of scans done pre-association */
+ int direct_probe_tries; /* retries for direct probes */
+ int auth_tries; /* retries for auth req */
+ int assoc_tries; /* retries for assoc req */
unsigned long request;
unsigned long last_probe;
unsigned int flags;
-#define IEEE80211_STA_REQ_SCAN 0
-#define IEEE80211_STA_REQ_AUTH 1
-#define IEEE80211_STA_REQ_RUN 2
-#define IEEE80211_AUTH_ALG_OPEN BIT(0)
-#define IEEE80211_AUTH_ALG_SHARED_KEY BIT(1)
-#define IEEE80211_AUTH_ALG_LEAP BIT(2)
unsigned int auth_algs; /* bitfield of allowed auth algs */
int auth_alg; /* currently used IEEE 802.11 authentication algorithm */
int auth_transaction;
@@ -391,31 +351,70 @@ struct ieee80211_if_sta {
u32 supp_rates_bits[IEEE80211_NUM_BANDS];
int wmm_last_param_set;
- int num_beacons; /* number of TXed beacon frames by this STA */
};
-static inline void ieee80211_if_sta_set_mesh_id(struct ieee80211_if_sta *ifsta,
- u8 mesh_id_len, u8 *mesh_id)
-{
-#ifdef CONFIG_MAC80211_MESH
- ifsta->mesh_id_len = mesh_id_len;
- memcpy(ifsta->mesh_id, mesh_id, mesh_id_len);
-#endif
-}
+struct ieee80211_if_mesh {
+ struct work_struct work;
+ struct timer_list housekeeping_timer;
+ struct timer_list mesh_path_timer;
+ struct sk_buff_head skb_queue;
+
+ bool housekeeping;
+
+ u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
+ size_t mesh_id_len;
+ /* Active Path Selection Protocol Identifier */
+ u8 mesh_pp_id[4];
+ /* Active Path Selection Metric Identifier */
+ u8 mesh_pm_id[4];
+ /* Congestion Control Mode Identifier */
+ u8 mesh_cc_id[4];
+ /* Local mesh Destination Sequence Number */
+ u32 dsn;
+ /* Last used PREQ ID */
+ u32 preq_id;
+ atomic_t mpaths;
+ /* Timestamp of last DSN update */
+ unsigned long last_dsn_update;
+ /* Timestamp of last DSN sent */
+ unsigned long last_preq;
+ struct mesh_rmc *rmc;
+ spinlock_t mesh_preq_queue_lock;
+ struct mesh_preq_queue preq_queue;
+ int preq_queue_len;
+ struct mesh_stats mshstats;
+ struct mesh_config mshcfg;
+ u32 mesh_seqnum;
+ bool accepting_plinks;
+};
#ifdef CONFIG_MAC80211_MESH
-#define IEEE80211_IFSTA_MESH_CTR_INC(sta, name) \
- do { (sta)->mshstats.name++; } while (0)
+#define IEEE80211_IFSTA_MESH_CTR_INC(msh, name) \
+ do { (msh)->mshstats.name++; } while (0)
#else
-#define IEEE80211_IFSTA_MESH_CTR_INC(sta, name) \
+#define IEEE80211_IFSTA_MESH_CTR_INC(msh, name) \
do { } while (0)
#endif
-/* flags used in struct ieee80211_sub_if_data.flags */
-#define IEEE80211_SDATA_ALLMULTI BIT(0)
-#define IEEE80211_SDATA_PROMISC BIT(1)
-#define IEEE80211_SDATA_USERSPACE_MLME BIT(2)
-#define IEEE80211_SDATA_OPERATING_GMODE BIT(3)
+/**
+ * enum ieee80211_sub_if_data_flags - virtual interface flags
+ *
+ * @IEEE80211_SDATA_ALLMULTI: interface wants all multicast packets
+ * @IEEE80211_SDATA_PROMISC: interface is promisc
+ * @IEEE80211_SDATA_USERSPACE_MLME: userspace MLME is active
+ * @IEEE80211_SDATA_OPERATING_GMODE: operating in G-only mode
+ * @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.
+ */
+enum ieee80211_sub_if_data_flags {
+ IEEE80211_SDATA_ALLMULTI = BIT(0),
+ IEEE80211_SDATA_PROMISC = BIT(1),
+ IEEE80211_SDATA_USERSPACE_MLME = BIT(2),
+ IEEE80211_SDATA_OPERATING_GMODE = BIT(3),
+ IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(4),
+};
+
struct ieee80211_sub_if_data {
struct list_head list;
@@ -431,13 +430,6 @@ struct ieee80211_sub_if_data {
int drop_unencrypted;
- /*
- * basic rates of this AP or the AP we're associated to
- */
- u64 basic_rates;
-
- u16 sequence;
-
/* Fragment table for host-based reassembly */
struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX];
unsigned int fragment_next;
@@ -446,32 +438,34 @@ struct ieee80211_sub_if_data {
struct ieee80211_key *keys[NUM_DEFAULT_KEYS];
struct ieee80211_key *default_key;
+ /* BSS configuration for this interface. */
+ struct ieee80211_bss_conf bss_conf;
+
/*
- * BSS configuration for this interface.
- *
- * FIXME: I feel bad putting this here when we already have a
- * bss pointer, but the bss pointer is just wrong when
- * you have multiple virtual STA mode interfaces...
- * This needs to be fixed.
+ * AP this belongs to: self in AP mode and
+ * corresponding AP in VLAN mode, NULL for
+ * all others (might be needed later in IBSS)
*/
- struct ieee80211_bss_conf bss_conf;
- struct ieee80211_if_ap *bss; /* BSS that this device belongs to */
+ struct ieee80211_if_ap *bss;
+
+ int force_unicast_rateidx; /* forced TX rateidx for unicast frames */
+ int max_ratectrl_rateidx; /* max TX rateidx for rate control */
union {
struct ieee80211_if_ap ap;
struct ieee80211_if_wds wds;
struct ieee80211_if_vlan vlan;
struct ieee80211_if_sta sta;
+#ifdef CONFIG_MAC80211_MESH
+ struct ieee80211_if_mesh mesh;
+#endif
u32 mntr_flags;
} u;
- int channel_use;
- int channel_use_raw;
#ifdef CONFIG_MAC80211_DEBUGFS
struct dentry *debugfsdir;
union {
struct {
- struct dentry *channel_use;
struct dentry *drop_unencrypted;
struct dentry *state;
struct dentry *bssid;
@@ -487,32 +481,35 @@ struct ieee80211_sub_if_data {
struct dentry *auth_alg;
struct dentry *auth_transaction;
struct dentry *flags;
- struct dentry *num_beacons_sta;
+ struct dentry *force_unicast_rateidx;
+ struct dentry *max_ratectrl_rateidx;
} sta;
struct {
- struct dentry *channel_use;
struct dentry *drop_unencrypted;
struct dentry *num_sta_ps;
struct dentry *dtim_count;
- struct dentry *num_beacons;
struct dentry *force_unicast_rateidx;
struct dentry *max_ratectrl_rateidx;
struct dentry *num_buffered_multicast;
} ap;
struct {
- struct dentry *channel_use;
struct dentry *drop_unencrypted;
struct dentry *peer;
+ struct dentry *force_unicast_rateidx;
+ struct dentry *max_ratectrl_rateidx;
} wds;
struct {
- struct dentry *channel_use;
struct dentry *drop_unencrypted;
+ struct dentry *force_unicast_rateidx;
+ struct dentry *max_ratectrl_rateidx;
} vlan;
struct {
struct dentry *mode;
} monitor;
- struct dentry *default_key;
} debugfs;
+ struct {
+ struct dentry *default_key;
+ } common_debugfs;
#ifdef CONFIG_MAC80211_MESH
struct dentry *mesh_stats_dir;
@@ -553,7 +550,18 @@ struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p)
return container_of(p, struct ieee80211_sub_if_data, vif);
}
-#define IEEE80211_DEV_TO_SUB_IF(dev) netdev_priv(dev)
+static inline void
+ieee80211_sdata_set_mesh_id(struct ieee80211_sub_if_data *sdata,
+ u8 mesh_id_len, u8 *mesh_id)
+{
+#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
+}
enum {
IEEE80211_RX_MSG = 1,
@@ -562,6 +570,13 @@ enum {
IEEE80211_ADDBA_MSG = 4,
};
+/* maximum number of hardware queues we support. */
+#define QD_MAX_QUEUES (IEEE80211_MAX_AMPDU_QUEUES + IEEE80211_MAX_QUEUES)
+
+struct ieee80211_master_priv {
+ struct ieee80211_local *local;
+};
+
struct ieee80211_local {
/* embed the driver visible part.
* don't cast (use the static inlines below), but we keep
@@ -570,6 +585,8 @@ struct ieee80211_local {
const struct ieee80211_ops *ops;
+ unsigned long queue_pool[BITS_TO_LONGS(QD_MAX_QUEUES)];
+
struct net_device *mdev; /* wmaster# - "master" 802.11 device */
int open_count;
int monitors, cooked_mntrs;
@@ -581,12 +598,6 @@ struct ieee80211_local {
bool tim_in_locked_section; /* see ieee80211_beacon_get() */
int tx_headroom; /* required headroom for hardware/radiotap */
- enum {
- IEEE80211_DEV_UNINITIALIZED = 0,
- IEEE80211_DEV_REGISTERED,
- IEEE80211_DEV_UNREGISTERED,
- } reg_state;
-
/* Tasklet and skb queue to process calls from IRQ mode. All frames
* added to skb_queue will be processed, but frames in
* skb_queue_unreliable may be dropped if the total length of these
@@ -610,8 +621,9 @@ struct ieee80211_local {
struct sta_info *sta_hash[STA_HASH_SIZE];
struct timer_list sta_cleanup;
- unsigned long state[NUM_TX_DATA_QUEUES_AMPDU];
- struct ieee80211_tx_stored_packet pending_packet[NUM_TX_DATA_QUEUES_AMPDU];
+ unsigned long queues_pending[BITS_TO_LONGS(IEEE80211_MAX_QUEUES)];
+ unsigned long queues_pending_run[BITS_TO_LONGS(IEEE80211_MAX_QUEUES)];
+ struct ieee80211_tx_stored_packet pending_packet[IEEE80211_MAX_QUEUES];
struct tasklet_struct tx_pending_tasklet;
/* number of interfaces with corresponding IFF_ flags */
@@ -628,10 +640,6 @@ struct ieee80211_local {
struct crypto_blkcipher *wep_rx_tfm;
u32 wep_iv;
- int bridge_packets; /* bridge packets between associated stations and
- * deliver multicast frames both back to wireless
- * media and to the local net stack */
-
struct list_head interfaces;
/*
@@ -641,21 +649,21 @@ struct ieee80211_local {
spinlock_t key_lock;
- bool sta_sw_scanning;
- bool sta_hw_scanning;
+ /* Scanning and BSS list */
+ bool sw_scanning, hw_scanning;
int scan_channel_idx;
enum ieee80211_band scan_band;
enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
unsigned long last_scan_completed;
struct delayed_work scan_work;
- struct net_device *scan_dev;
+ struct ieee80211_sub_if_data *scan_sdata;
struct ieee80211_channel *oper_channel, *scan_channel;
u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
size_t scan_ssid_len;
- struct list_head sta_bss_list;
- struct ieee80211_sta_bss *sta_bss_hash[STA_HASH_SIZE];
- spinlock_t sta_bss_lock;
+ struct list_head bss_list;
+ struct ieee80211_bss *bss_hash[STA_HASH_SIZE];
+ spinlock_t bss_lock;
/* SNMP counters */
/* dot11CountersTable */
@@ -677,9 +685,6 @@ struct ieee80211_local {
assoc_led_name[32], radio_led_name[32];
#endif
- u32 channel_use;
- u32 channel_use_raw;
-
#ifdef CONFIG_MAC80211_DEBUGFS
struct work_struct sta_debugfs_add;
#endif
@@ -705,8 +710,6 @@ struct ieee80211_local {
unsigned int rx_expand_skb_head2;
unsigned int rx_handlers_fragments;
unsigned int tx_status_drop;
- unsigned int wme_rx_queue[NUM_RX_DATA_QUEUES];
- unsigned int wme_tx_queue[NUM_RX_DATA_QUEUES];
#define I802_DEBUG_INC(c) (c)++
#else /* CONFIG_MAC80211_DEBUG_COUNTERS */
#define I802_DEBUG_INC(c) do { } while (0)
@@ -721,10 +724,11 @@ struct ieee80211_local {
#ifdef CONFIG_MAC80211_DEBUGFS
struct local_debugfsdentries {
+ struct dentry *rcdir;
+ struct dentry *rcname;
struct dentry *frequency;
struct dentry *antenna_sel_tx;
struct dentry *antenna_sel_rx;
- struct dentry *bridge_packets;
struct dentry *rts_threshold;
struct dentry *fragmentation_threshold;
struct dentry *short_retry_limit;
@@ -764,8 +768,6 @@ struct ieee80211_local {
struct dentry *rx_expand_skb_head2;
struct dentry *rx_handlers_fragments;
struct dentry *tx_status_drop;
- struct dentry *wme_tx_queue;
- struct dentry *wme_rx_queue;
#endif
struct dentry *dot11ACKFailureCount;
struct dentry *dot11RTSFailureCount;
@@ -778,6 +780,16 @@ struct ieee80211_local {
#endif
};
+static inline struct ieee80211_sub_if_data *
+IEEE80211_DEV_TO_SUB_IF(struct net_device *dev)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+
+ BUG_ON(!local || local->mdev == dev);
+
+ return netdev_priv(dev);
+}
+
/* this struct represents 802.11n's RA/TID combination */
struct ieee80211_ra_tid {
u8 ra[ETH_ALEN];
@@ -786,6 +798,9 @@ struct ieee80211_ra_tid {
/* Parsed Information Elements */
struct ieee802_11_elems {
+ u8 *ie_start;
+ size_t total_len;
+
/* pointers to IEs */
u8 *ssid;
u8 *supp_rates;
@@ -801,14 +816,18 @@ struct ieee802_11_elems {
u8 *ext_supp_rates;
u8 *wmm_info;
u8 *wmm_param;
- u8 *ht_cap_elem;
- u8 *ht_info_elem;
+ struct ieee80211_ht_cap *ht_cap_elem;
+ struct ieee80211_ht_addt_info *ht_info_elem;
u8 *mesh_config;
u8 *mesh_id;
u8 *peer_link;
u8 *preq;
u8 *prep;
u8 *perr;
+ u8 *ch_switch_elem;
+ u8 *country_elem;
+ u8 *pwr_constr_elem;
+ u8 *quiet_elem; /* first quite element */
/* length of them, respectively */
u8 ssid_len;
@@ -825,14 +844,17 @@ struct ieee802_11_elems {
u8 ext_supp_rates_len;
u8 wmm_info_len;
u8 wmm_param_len;
- u8 ht_cap_elem_len;
- u8 ht_info_elem_len;
u8 mesh_config_len;
u8 mesh_id_len;
u8 peer_link_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 */
};
static inline struct ieee80211_local *hw_to_local(
@@ -847,11 +869,6 @@ static inline struct ieee80211_hw *local_to_hw(
return &local->hw;
}
-enum ieee80211_link_state_t {
- IEEE80211_LINK_STATE_XOFF = 0,
- IEEE80211_LINK_STATE_PENDING,
-};
-
struct sta_attribute {
struct attribute attr;
ssize_t (*show)(const struct sta_info *, char *buf);
@@ -865,110 +882,83 @@ static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
}
-/* ieee80211.c */
int ieee80211_hw_config(struct ieee80211_local *local);
-int ieee80211_if_config(struct net_device *dev);
-int ieee80211_if_config_beacon(struct net_device *dev);
+int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed);
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
-void ieee80211_if_setup(struct net_device *dev);
u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht,
struct ieee80211_ht_info *req_ht_cap,
struct ieee80211_ht_bss_info *req_bss_cap);
+void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
+ u32 changed);
+void ieee80211_configure_filter(struct ieee80211_local *local);
-/* ieee80211_ioctl.c */
+/* wireless extensions */
extern const struct iw_handler_def ieee80211_iw_handler_def;
-
-/* Least common multiple of the used rates (in 100 kbps). This is used to
- * calculate rate_inv values for each rate so that only integers are needed. */
-#define CHAN_UTIL_RATE_LCM 95040
-/* 1 usec is 1/8 * (95040/10) = 1188 */
-#define CHAN_UTIL_PER_USEC 1188
-/* Amount of bits to shift the result right to scale the total utilization
- * to values that will not wrap around 32-bit integers. */
-#define CHAN_UTIL_SHIFT 9
-/* Theoretical maximum of channel utilization counter in 10 ms (stat_time=1):
- * (CHAN_UTIL_PER_USEC * 10000) >> CHAN_UTIL_SHIFT = 23203. So dividing the
- * raw value with about 23 should give utilization in 10th of a percentage
- * (1/1000). However, utilization is only estimated and not all intervals
- * between frames etc. are calculated. 18 seems to give numbers that are closer
- * to the real maximum. */
-#define CHAN_UTIL_PER_10MS 18
-#define CHAN_UTIL_HDR_LONG (202 * CHAN_UTIL_PER_USEC)
-#define CHAN_UTIL_HDR_SHORT (40 * CHAN_UTIL_PER_USEC)
-
-
-/* ieee80211_ioctl.c */
-int ieee80211_set_freq(struct net_device *dev, int freq);
-/* ieee80211_sta.c */
-void ieee80211_sta_timer(unsigned long data);
-void ieee80211_sta_work(struct work_struct *work);
-void ieee80211_sta_scan_work(struct work_struct *work);
-void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
+/* STA/IBSS code */
+void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata);
+void ieee80211_scan_work(struct work_struct *work);
+void ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
struct ieee80211_rx_status *rx_status);
-int ieee80211_sta_set_ssid(struct net_device *dev, char *ssid, size_t len);
-int ieee80211_sta_get_ssid(struct net_device *dev, char *ssid, size_t *len);
-int ieee80211_sta_set_bssid(struct net_device *dev, u8 *bssid);
-int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len);
-void ieee80211_sta_req_auth(struct net_device *dev,
+int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len);
+int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len);
+int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid);
+void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta);
-int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len);
-ieee80211_rx_result ieee80211_sta_rx_scan(
- struct net_device *dev, struct sk_buff *skb,
- struct ieee80211_rx_status *rx_status);
-void ieee80211_rx_bss_list_init(struct net_device *dev);
-void ieee80211_rx_bss_list_deinit(struct net_device *dev);
-int ieee80211_sta_set_extra_ie(struct net_device *dev, char *ie, size_t len);
-struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev,
- struct sk_buff *skb, u8 *bssid,
- u8 *addr);
-int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason);
-int ieee80211_sta_disassociate(struct net_device *dev, u16 reason);
-void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
- u32 changed);
-void ieee80211_reset_erp_info(struct net_device *dev);
-int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
- struct ieee80211_ht_info *ht_info);
-int ieee80211_ht_addt_info_ie_to_ht_bss_info(
- struct ieee80211_ht_addt_info *ht_add_info_ie,
- struct ieee80211_ht_bss_info *bss_info);
-void ieee80211_send_addba_request(struct net_device *dev, const u8 *da,
- u16 tid, u8 dialog_token, u16 start_seq_num,
- u16 agg_size, u16 timeout);
-void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid,
- u16 initiator, u16 reason_code);
-
-void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *da,
- u16 tid, u16 initiator, u16 reason);
-void sta_rx_agg_session_timer_expired(unsigned long data);
-void sta_addba_resp_timer_expired(unsigned long data);
-void ieee80211_sta_tear_down_BA_sessions(struct net_device *dev, u8 *addr);
+struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u8 *bssid,
+ u8 *addr, u64 supp_rates);
+int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason);
+int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason);
+u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
u64 ieee80211_sta_get_rates(struct ieee80211_local *local,
struct ieee802_11_elems *elems,
enum ieee80211_band band);
-void ieee80211_sta_tx(struct net_device *dev, struct sk_buff *skb,
- int encrypt);
-void ieee802_11_parse_elems(u8 *start, size_t len,
- struct ieee802_11_elems *elems);
-
-#ifdef CONFIG_MAC80211_MESH
-void ieee80211_start_mesh(struct net_device *dev);
-#else
-static inline void ieee80211_start_mesh(struct net_device *dev)
-{}
-#endif
-
-/* ieee80211_iface.c */
-int ieee80211_if_add(struct net_device *dev, const char *name,
- struct net_device **new_dev, int type,
+void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
+ u8 *ssid, size_t ssid_len);
+
+/* scan/BSS handling */
+int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
+ u8 *ssid, size_t ssid_len);
+int ieee80211_scan_results(struct ieee80211_local *local,
+ struct iw_request_info *info,
+ char *buf, size_t len);
+ieee80211_rx_result
+ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb,
+ struct ieee80211_rx_status *rx_status);
+void ieee80211_rx_bss_list_init(struct ieee80211_local *local);
+void ieee80211_rx_bss_list_deinit(struct ieee80211_local *local);
+int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata,
+ char *ie, size_t len);
+
+void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local);
+int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
+ u8 *ssid, size_t ssid_len);
+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 ieee802_11_elems *elems,
+ int freq, bool beacon);
+struct ieee80211_bss *
+ieee80211_rx_bss_add(struct ieee80211_local *local, u8 *bssid, int freq,
+ u8 *ssid, u8 ssid_len);
+struct ieee80211_bss *
+ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
+ u8 *ssid, u8 ssid_len);
+void ieee80211_rx_bss_put(struct ieee80211_local *local,
+ struct ieee80211_bss *bss);
+
+/* interface handling */
+int ieee80211_if_add(struct ieee80211_local *local, const char *name,
+ struct net_device **new_dev, enum nl80211_iftype type,
struct vif_params *params);
-void ieee80211_if_set_type(struct net_device *dev, int type);
-void ieee80211_if_reinit(struct net_device *dev);
-void __ieee80211_if_del(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata);
-int ieee80211_if_remove(struct net_device *dev, const char *name, int id);
-void ieee80211_if_free(struct net_device *dev);
-void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata);
+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);
/* tx handling */
void ieee80211_clear_tx_pending(struct ieee80211_local *local);
@@ -977,15 +967,57 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev);
int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
+/* HT */
+int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
+ struct ieee80211_ht_info *ht_info);
+int ieee80211_ht_addt_info_ie_to_ht_bss_info(
+ struct ieee80211_ht_addt_info *ht_add_info_ie,
+ struct ieee80211_ht_bss_info *bss_info);
+void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn);
+
+void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da,
+ u16 tid, u16 initiator, u16 reason);
+void ieee80211_sta_tear_down_BA_sessions(struct ieee80211_sub_if_data *sdata, u8 *addr);
+void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct ieee80211_mgmt *mgmt, size_t len);
+void ieee80211_process_addba_resp(struct ieee80211_local *local,
+ struct sta_info *sta,
+ struct ieee80211_mgmt *mgmt,
+ size_t len);
+void ieee80211_process_addba_request(struct ieee80211_local *local,
+ struct sta_info *sta,
+ struct ieee80211_mgmt *mgmt,
+ size_t len);
+
+/* Spectrum management */
+void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ size_t len);
+
/* utility functions/constants */
extern void *mac80211_wiphy_privid; /* for wiphy privid */
extern const unsigned char rfc1042_header[6];
extern const unsigned char bridge_tunnel_header[6];
u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
- enum ieee80211_if_types type);
+ enum nl80211_iftype type);
int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
int rate, int erp, int short_preamble);
-void mac80211_ev_michael_mic_failure(struct net_device *dev, int keyidx,
+void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
struct ieee80211_hdr *hdr);
+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,
+ int encrypt);
+void ieee802_11_parse_elems(u8 *start, size_t len,
+ struct ieee802_11_elems *elems);
+int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freq);
+u64 ieee80211_mandatory_rates(struct ieee80211_local *local,
+ enum ieee80211_band band);
+
+#ifdef CONFIG_MAC80211_NOINLINE
+#define debug_noinline noinline
+#else
+#define debug_noinline
+#endif
#endif /* IEEE80211_I_H */
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 06e88a5a036..8336fee68d3 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1,7 +1,10 @@
/*
+ * Interface handling (except master interface)
+ *
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2008, 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
@@ -16,226 +19,563 @@
#include "sta_info.h"
#include "debugfs_netdev.h"
#include "mesh.h"
+#include "led.h"
-void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata)
+static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
{
- int i;
+ int meshhdrlen;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- /* Default values for sub-interface parameters */
- sdata->drop_unencrypted = 0;
- for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
- skb_queue_head_init(&sdata->fragments[i].skb_list);
+ meshhdrlen = (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ? 5 : 0;
- INIT_LIST_HEAD(&sdata->key_list);
+ /* 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) {
+ 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 void ieee80211_if_sdata_deinit(struct ieee80211_sub_if_data *sdata)
+static inline int identical_mac_addr_allowed(int type1, int type2)
{
- int i;
-
- for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) {
- __skb_queue_purge(&sdata->fragments[i].skb_list);
- }
+ return type1 == NL80211_IFTYPE_MONITOR ||
+ type2 == NL80211_IFTYPE_MONITOR ||
+ (type1 == NL80211_IFTYPE_AP && type2 == NL80211_IFTYPE_WDS) ||
+ (type1 == NL80211_IFTYPE_WDS &&
+ (type2 == NL80211_IFTYPE_WDS ||
+ type2 == NL80211_IFTYPE_AP)) ||
+ (type1 == NL80211_IFTYPE_AP && type2 == NL80211_IFTYPE_AP_VLAN) ||
+ (type1 == NL80211_IFTYPE_AP_VLAN &&
+ (type2 == NL80211_IFTYPE_AP ||
+ type2 == NL80211_IFTYPE_AP_VLAN));
}
-/* Must be called with rtnl lock held. */
-int ieee80211_if_add(struct net_device *dev, const char *name,
- struct net_device **new_dev, int type,
- struct vif_params *params)
+static int ieee80211_open(struct net_device *dev)
{
- struct net_device *ndev;
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata = NULL;
- int ret;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *nsdata;
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
+ struct ieee80211_if_init_conf conf;
+ u32 changed = 0;
+ int res;
+ bool need_hw_reconfig = 0;
+ u8 null_addr[ETH_ALEN] = {0};
+
+ /* fail early if user set an invalid address */
+ if (compare_ether_addr(dev->dev_addr, null_addr) &&
+ !is_valid_ether_addr(dev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ /* 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 && netif_running(ndev)) {
+ /*
+ * Allow only a single IBSS interface to be up at any
+ * time. This is restricted because beacon distribution
+ * cannot work properly if both are in the same IBSS.
+ *
+ * To remove this restriction we'd have to disallow them
+ * from setting the same SSID on different IBSS interfaces
+ * belonging to the same hardware. Then, however, we're
+ * faced with having to adopt two different TSF timers...
+ */
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
+ nsdata->vif.type == NL80211_IFTYPE_ADHOC)
+ 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))
+ continue;
+
+ /*
+ * check whether it may have the same address
+ */
+ if (!identical_mac_addr_allowed(sdata->vif.type,
+ nsdata->vif.type))
+ return -ENOTUNIQ;
+
+ /*
+ * can only add VLANs to enabled APs
+ */
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+ nsdata->vif.type == NL80211_IFTYPE_AP)
+ sdata->bss = &nsdata->u.ap;
+ }
+ }
- ASSERT_RTNL();
- ndev = alloc_netdev(sizeof(*sdata) + local->hw.vif_data_size,
- name, ieee80211_if_setup);
- if (!ndev)
- return -ENOMEM;
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_WDS:
+ if (!is_valid_ether_addr(sdata->u.wds.remote_addr))
+ return -ENOLINK;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ if (!sdata->bss)
+ return -ENOLINK;
+ list_add(&sdata->u.vlan.list, &sdata->bss->vlans);
+ 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:
+ /* no special treatment */
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case __NL80211_IFTYPE_AFTER_LAST:
+ /* cannot happen */
+ WARN_ON(1);
+ break;
+ }
- 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;
+ if (local->open_count == 0) {
+ res = 0;
+ if (local->ops->start)
+ res = local->ops->start(local_to_hw(local));
+ if (res)
+ goto err_del_bss;
+ need_hw_reconfig = 1;
+ ieee80211_led_radio(local, local->hw.conf.radio_enabled);
+ }
- ret = dev_alloc_name(ndev, ndev->name);
- if (ret < 0)
- goto fail;
+ /*
+ * Check all interfaces and copy the hopefully now-present
+ * MAC address to those that have the special null one.
+ */
+ list_for_each_entry(nsdata, &local->interfaces, list) {
+ struct net_device *ndev = nsdata->dev;
+
+ /*
+ * No need to check netif_running since we do not allow
+ * it to start up with this invalid address.
+ */
+ if (compare_ether_addr(null_addr, ndev->dev_addr) == 0)
+ memcpy(ndev->dev_addr,
+ local->hw.wiphy->perm_addr,
+ ETH_ALEN);
+ }
- memcpy(ndev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN);
- ndev->base_addr = dev->base_addr;
- ndev->irq = dev->irq;
- ndev->mem_start = dev->mem_start;
- ndev->mem_end = dev->mem_end;
- SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));
+ if (compare_ether_addr(null_addr, local->mdev->dev_addr) == 0)
+ memcpy(local->mdev->dev_addr, local->hw.wiphy->perm_addr,
+ ETH_ALEN);
- sdata = IEEE80211_DEV_TO_SUB_IF(ndev);
- ndev->ieee80211_ptr = &sdata->wdev;
- sdata->wdev.wiphy = local->hw.wiphy;
- sdata->vif.type = IEEE80211_IF_TYPE_AP;
- sdata->dev = ndev;
- sdata->local = local;
- ieee80211_if_sdata_init(sdata);
+ /*
+ * Validate the MAC address for this device.
+ */
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ if (!local->open_count && local->ops->stop)
+ local->ops->stop(local_to_hw(local));
+ return -EADDRNOTAVAIL;
+ }
- ret = register_netdevice(ndev);
- if (ret)
- goto fail;
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_AP_VLAN:
+ /* no need to tell driver */
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) {
+ local->cooked_mntrs++;
+ break;
+ }
- ieee80211_debugfs_add_netdev(sdata);
- ieee80211_if_set_type(ndev, type);
+ /* must be before the call to ieee80211_configure_filter */
+ local->monitors++;
+ if (local->monitors == 1)
+ local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
+
+ if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL)
+ local->fif_fcsfail++;
+ if (sdata->u.mntr_flags & MONITOR_FLAG_PLCPFAIL)
+ local->fif_plcpfail++;
+ if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL)
+ local->fif_control++;
+ if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS)
+ local->fif_other_bss++;
+
+ netif_addr_lock_bh(local->mdev);
+ ieee80211_configure_filter(local);
+ netif_addr_unlock_bh(local->mdev);
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ sdata->u.sta.flags &= ~IEEE80211_STA_PREV_BSSID_SET;
+ /* fall through */
+ default:
+ conf.vif = &sdata->vif;
+ conf.type = sdata->vif.type;
+ conf.mac_addr = dev->dev_addr;
+ res = local->ops->add_interface(local_to_hw(local), &conf);
+ if (res)
+ goto err_stop;
- if (ieee80211_vif_is_mesh(&sdata->vif) &&
- params && params->mesh_id_len)
- ieee80211_if_sta_set_mesh_id(&sdata->u.sta,
- params->mesh_id_len,
- params->mesh_id);
-
- /* we're under RTNL so all this is fine */
- if (unlikely(local->reg_state == IEEE80211_DEV_UNREGISTERED)) {
- __ieee80211_if_del(local, sdata);
- return -ENODEV;
+ if (ieee80211_vif_is_mesh(&sdata->vif))
+ ieee80211_start_mesh(sdata);
+ changed |= ieee80211_reset_erp_info(sdata);
+ ieee80211_bss_info_change_notify(sdata, changed);
+ ieee80211_enable_keys(sdata);
+
+ if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+ !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
+ netif_carrier_off(dev);
+ else
+ netif_carrier_on(dev);
}
- list_add_tail_rcu(&sdata->list, &local->interfaces);
- if (new_dev)
- *new_dev = ndev;
+ if (sdata->vif.type == NL80211_IFTYPE_WDS) {
+ /* Create STA entry for the WDS peer */
+ sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr,
+ GFP_KERNEL);
+ if (!sta) {
+ res = -ENOMEM;
+ goto err_del_interface;
+ }
- return 0;
+ /* no locking required since STA is not live yet */
+ sta->flags |= WLAN_STA_AUTHORIZED;
-fail:
- free_netdev(ndev);
- return ret;
+ res = sta_info_insert(sta);
+ if (res) {
+ /* STA has been freed */
+ goto err_del_interface;
+ }
+ }
+
+ if (local->open_count == 0) {
+ res = dev_open(local->mdev);
+ WARN_ON(res);
+ if (res)
+ goto err_del_interface;
+ tasklet_enable(&local->tx_pending_tasklet);
+ tasklet_enable(&local->tasklet);
+ }
+
+ /*
+ * set_multicast_list will be invoked by the networking core
+ * which will check whether any increments here were done in
+ * error and sync them down to the hardware as filter flags.
+ */
+ if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
+ atomic_inc(&local->iff_allmultis);
+
+ if (sdata->flags & IEEE80211_SDATA_PROMISC)
+ atomic_inc(&local->iff_promiscs);
+
+ local->open_count++;
+ if (need_hw_reconfig) {
+ ieee80211_hw_config(local);
+ /*
+ * 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_sta_work is disabled while network interface
+ * is down. Therefore, some configuration changes may not
+ * yet be effective. Trigger execution of ieee80211_sta_work
+ * to fix this.
+ */
+ if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ queue_work(local->hw.workqueue, &ifsta->work);
+ }
+
+ netif_tx_start_all_queues(dev);
+
+ return 0;
+ err_del_interface:
+ local->ops->remove_interface(local_to_hw(local), &conf);
+ err_stop:
+ if (!local->open_count && local->ops->stop)
+ local->ops->stop(local_to_hw(local));
+ err_del_bss:
+ sdata->bss = NULL;
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ list_del(&sdata->u.vlan.list);
+ return res;
}
-void ieee80211_if_set_type(struct net_device *dev, int type)
+static int ieee80211_stop(struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- int oldtype = sdata->vif.type;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_init_conf conf;
+ struct sta_info *sta;
/*
- * We need to call this function on the master interface
- * which already has a hard_start_xmit routine assigned
- * which must not be changed.
+ * Stop TX on this interface first.
*/
- if (dev != sdata->local->mdev)
- dev->hard_start_xmit = ieee80211_subif_start_xmit;
+ netif_tx_stop_all_queues(dev);
/*
- * Called even when register_netdevice fails, it would
- * oops if assigned before initialising the rest.
+ * Now delete all active aggregation sessions.
*/
- dev->uninit = ieee80211_if_reinit;
+ rcu_read_lock();
- /* most have no BSS pointer */
- sdata->bss = NULL;
- sdata->vif.type = type;
+ list_for_each_entry_rcu(sta, &local->sta_list, list) {
+ if (sta->sdata == sdata)
+ ieee80211_sta_tear_down_BA_sessions(sdata,
+ sta->sta.addr);
+ }
- sdata->basic_rates = 0;
+ rcu_read_unlock();
- switch (type) {
- case IEEE80211_IF_TYPE_WDS:
- /* nothing special */
- break;
- case IEEE80211_IF_TYPE_VLAN:
- sdata->u.vlan.ap = NULL;
- break;
- case IEEE80211_IF_TYPE_AP:
- sdata->u.ap.force_unicast_rateidx = -1;
- sdata->u.ap.max_ratectrl_rateidx = -1;
- skb_queue_head_init(&sdata->u.ap.ps_bc_buf);
- sdata->bss = &sdata->u.ap;
- INIT_LIST_HEAD(&sdata->u.ap.vlans);
- break;
- case IEEE80211_IF_TYPE_MESH_POINT:
- case IEEE80211_IF_TYPE_STA:
- case IEEE80211_IF_TYPE_IBSS: {
- struct ieee80211_sub_if_data *msdata;
- struct ieee80211_if_sta *ifsta;
-
- ifsta = &sdata->u.sta;
- INIT_WORK(&ifsta->work, ieee80211_sta_work);
- setup_timer(&ifsta->timer, ieee80211_sta_timer,
- (unsigned long) sdata);
- skb_queue_head_init(&ifsta->skb_queue);
-
- ifsta->capab = WLAN_CAPABILITY_ESS;
- ifsta->auth_algs = IEEE80211_AUTH_ALG_OPEN |
- IEEE80211_AUTH_ALG_SHARED_KEY;
- ifsta->flags |= IEEE80211_STA_CREATE_IBSS |
- IEEE80211_STA_WMM_ENABLED |
- IEEE80211_STA_AUTO_BSSID_SEL |
- IEEE80211_STA_AUTO_CHANNEL_SEL;
-
- msdata = IEEE80211_DEV_TO_SUB_IF(sdata->local->mdev);
- sdata->bss = &msdata->u.ap;
+ /*
+ * Remove all stations associated with this interface.
+ *
+ * This must be done before calling ops->remove_interface()
+ * because otherwise we can later invoke ops->sta_notify()
+ * whenever the STAs are removed, and that invalidates driver
+ * assumptions about always getting a vif pointer that is valid
+ * (because if we remove a STA after ops->remove_interface()
+ * the driver will have removed the vif info already!)
+ *
+ * We could relax this and only unlink the stations from the
+ * hash table and list but keep them on a per-sdata list that
+ * will be inserted back again when the interface is brought
+ * up again, but I don't currently see a use case for that,
+ * except with WDS which gets a STA entry created when it is
+ * brought up.
+ */
+ sta_info_flush(local, sdata);
- if (ieee80211_vif_is_mesh(&sdata->vif))
- ieee80211_mesh_init_sdata(sdata);
- break;
+ /*
+ * 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.
+ */
+ if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
+ atomic_dec(&local->iff_allmultis);
+
+ if (sdata->flags & IEEE80211_SDATA_PROMISC)
+ atomic_dec(&local->iff_promiscs);
+
+ dev_mc_unsync(local->mdev, dev);
+
+ /* APs need special treatment */
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ struct ieee80211_sub_if_data *vlan, *tmp;
+ struct beacon_data *old_beacon = sdata->u.ap.beacon;
+
+ /* remove beacon */
+ rcu_assign_pointer(sdata->u.ap.beacon, NULL);
+ synchronize_rcu();
+ kfree(old_beacon);
+
+ /* down all dependent devices, that is VLANs */
+ list_for_each_entry_safe(vlan, tmp, &sdata->u.ap.vlans,
+ u.vlan.list)
+ dev_close(vlan->dev);
+ WARN_ON(!list_empty(&sdata->u.ap.vlans));
}
- case IEEE80211_IF_TYPE_MNTR:
- dev->type = ARPHRD_IEEE80211_RADIOTAP;
- dev->hard_start_xmit = ieee80211_monitor_start_xmit;
- sdata->u.mntr_flags = MONITOR_FLAG_CONTROL |
- MONITOR_FLAG_OTHER_BSS;
+
+ local->open_count--;
+
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_AP_VLAN:
+ list_del(&sdata->u.vlan.list);
+ /* no need to tell driver */
break;
+ case NL80211_IFTYPE_MONITOR:
+ if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) {
+ local->cooked_mntrs--;
+ break;
+ }
+
+ local->monitors--;
+ if (local->monitors == 0)
+ local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
+
+ if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL)
+ local->fif_fcsfail--;
+ if (sdata->u.mntr_flags & MONITOR_FLAG_PLCPFAIL)
+ local->fif_plcpfail--;
+ if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL)
+ local->fif_control--;
+ if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS)
+ local->fif_other_bss--;
+
+ netif_addr_lock_bh(local->mdev);
+ ieee80211_configure_filter(local);
+ netif_addr_unlock_bh(local->mdev);
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ sdata->u.sta.state = IEEE80211_STA_MLME_DISABLED;
+ memset(sdata->u.sta.bssid, 0, ETH_ALEN);
+ del_timer_sync(&sdata->u.sta.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->u.sta.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.
+ */
+ synchronize_rcu();
+ skb_queue_purge(&sdata->u.sta.skb_queue);
+
+ sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
+ kfree(sdata->u.sta.extra_ie);
+ sdata->u.sta.extra_ie = NULL;
+ sdata->u.sta.extra_ie_len = 0;
+ /* fall through */
+ case NL80211_IFTYPE_MESH_POINT:
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ /* allmulti is always set on mesh ifaces */
+ atomic_dec(&local->iff_allmultis);
+ ieee80211_stop_mesh(sdata);
+ }
+ /* fall through */
default:
- printk(KERN_WARNING "%s: %s: Unknown interface type 0x%x",
- dev->name, __func__, type);
+ if (local->scan_sdata == sdata) {
+ if (!local->ops->hw_scan)
+ cancel_delayed_work_sync(&local->scan_work);
+ /*
+ * The software scan can no longer run now, so we can
+ * clear out the scan_sdata reference. However, the
+ * hardware scan may still be running. The complete
+ * function must be prepared to handle a NULL value.
+ */
+ local->scan_sdata = NULL;
+ /*
+ * The memory barrier guarantees that another CPU
+ * that is hardware-scanning will now see the fact
+ * that this interface is gone.
+ */
+ smp_mb();
+ /*
+ * If software scanning, complete the scan but since
+ * the scan_sdata is NULL already don't send out a
+ * scan event to userspace -- the scan is incomplete.
+ */
+ if (local->sw_scanning)
+ ieee80211_scan_completed(&local->hw);
+ }
+
+ conf.vif = &sdata->vif;
+ conf.type = sdata->vif.type;
+ conf.mac_addr = dev->dev_addr;
+ /* disable all keys for as long as this netdev is down */
+ ieee80211_disable_keys(sdata);
+ local->ops->remove_interface(local_to_hw(local), &conf);
+ }
+
+ sdata->bss = NULL;
+
+ if (local->open_count == 0) {
+ if (netif_running(local->mdev))
+ dev_close(local->mdev);
+
+ if (local->ops->stop)
+ local->ops->stop(local_to_hw(local));
+
+ ieee80211_led_radio(local, 0);
+
+ flush_workqueue(local->hw.workqueue);
+
+ tasklet_disable(&local->tx_pending_tasklet);
+ tasklet_disable(&local->tasklet);
}
- ieee80211_debugfs_change_if_type(sdata, oldtype);
+
+ return 0;
}
-/* Must be called with rtnl lock held. */
-void ieee80211_if_reinit(struct net_device *dev)
+static void ieee80211_set_multicast_list(struct net_device *dev)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ int allmulti, promisc, sdata_allmulti, sdata_promisc;
+
+ allmulti = !!(dev->flags & IFF_ALLMULTI);
+ promisc = !!(dev->flags & IFF_PROMISC);
+ sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI);
+ sdata_promisc = !!(sdata->flags & IEEE80211_SDATA_PROMISC);
+
+ if (allmulti != sdata_allmulti) {
+ if (dev->flags & IFF_ALLMULTI)
+ atomic_inc(&local->iff_allmultis);
+ else
+ atomic_dec(&local->iff_allmultis);
+ sdata->flags ^= IEEE80211_SDATA_ALLMULTI;
+ }
+
+ if (promisc != sdata_promisc) {
+ if (dev->flags & IFF_PROMISC)
+ atomic_inc(&local->iff_promiscs);
+ else
+ atomic_dec(&local->iff_promiscs);
+ sdata->flags ^= IEEE80211_SDATA_PROMISC;
+ }
+
+ dev_mc_sync(local->mdev, dev);
+}
+
+static void ieee80211_if_setup(struct net_device *dev)
+{
+ ether_setup(dev);
+ dev->hard_start_xmit = ieee80211_subif_start_xmit;
+ dev->wireless_handlers = &ieee80211_iw_handler_def;
+ dev->set_multicast_list = ieee80211_set_multicast_list;
+ dev->change_mtu = ieee80211_change_mtu;
+ dev->open = ieee80211_open;
+ dev->stop = ieee80211_stop;
+ dev->destructor = free_netdev;
+ /* we will validate the address ourselves in ->open */
+ dev->validate_addr = NULL;
+}
+/*
+ * 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)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct beacon_data *beacon;
struct sk_buff *skb;
int flushed;
+ int i;
- ASSERT_RTNL();
-
+ /* free extra data */
ieee80211_free_keys(sdata);
- ieee80211_if_sdata_deinit(sdata);
+ ieee80211_debugfs_remove_netdev(sdata);
- /* Need to handle mesh specially to allow eliding the function call */
- if (ieee80211_vif_is_mesh(&sdata->vif))
- mesh_rmc_free(dev);
+ for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
+ __skb_queue_purge(&sdata->fragments[i].skb_list);
+ sdata->fragment_next = 0;
switch (sdata->vif.type) {
- case IEEE80211_IF_TYPE_INVALID:
- /* cannot happen */
- WARN_ON(1);
- break;
- case IEEE80211_IF_TYPE_AP: {
- /* Remove all virtual interfaces that use this BSS
- * as their sdata->bss */
- struct ieee80211_sub_if_data *tsdata, *n;
- struct beacon_data *beacon;
-
- list_for_each_entry_safe(tsdata, n, &local->interfaces, list) {
- if (tsdata != sdata && tsdata->bss == &sdata->u.ap) {
- printk(KERN_DEBUG "%s: removing virtual "
- "interface %s because its BSS interface"
- " is being removed\n",
- sdata->dev->name, tsdata->dev->name);
- list_del_rcu(&tsdata->list);
- /*
- * We have lots of time and can afford
- * to sync for each interface
- */
- synchronize_rcu();
- __ieee80211_if_del(local, tsdata);
- }
- }
-
+ case NL80211_IFTYPE_AP:
beacon = sdata->u.ap.beacon;
rcu_assign_pointer(sdata->u.ap.beacon, NULL);
synchronize_rcu();
@@ -247,77 +587,205 @@ void ieee80211_if_reinit(struct net_device *dev)
}
break;
- }
- case IEEE80211_IF_TYPE_WDS:
- /* nothing to do */
+ case NL80211_IFTYPE_MESH_POINT:
+ if (ieee80211_vif_is_mesh(&sdata->vif))
+ mesh_rmc_free(sdata);
break;
- case IEEE80211_IF_TYPE_MESH_POINT:
- case IEEE80211_IF_TYPE_STA:
- case IEEE80211_IF_TYPE_IBSS:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
kfree(sdata->u.sta.extra_ie);
- sdata->u.sta.extra_ie = NULL;
kfree(sdata->u.sta.assocreq_ies);
- sdata->u.sta.assocreq_ies = NULL;
kfree(sdata->u.sta.assocresp_ies);
- sdata->u.sta.assocresp_ies = NULL;
- if (sdata->u.sta.probe_resp) {
- dev_kfree_skb(sdata->u.sta.probe_resp);
- sdata->u.sta.probe_resp = NULL;
- }
-
+ kfree_skb(sdata->u.sta.probe_resp);
break;
- case IEEE80211_IF_TYPE_MNTR:
- dev->type = ARPHRD_ETHER;
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_MONITOR:
break;
- case IEEE80211_IF_TYPE_VLAN:
- sdata->u.vlan.ap = NULL;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case __NL80211_IFTYPE_AFTER_LAST:
+ BUG();
break;
}
flushed = sta_info_flush(local, sdata);
WARN_ON(flushed);
+}
+/*
+ * Helper function to initialise an interface to a specific type.
+ */
+static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
+ enum nl80211_iftype type)
+{
+ /* clear type-dependent union */
memset(&sdata->u, 0, sizeof(sdata->u));
- ieee80211_if_sdata_init(sdata);
+
+ /* and set some type-dependent values */
+ sdata->vif.type = type;
+ sdata->dev->hard_start_xmit = ieee80211_subif_start_xmit;
+ sdata->wdev.iftype = type;
+
+ /* only monitor differs */
+ sdata->dev->type = ARPHRD_ETHER;
+
+ switch (type) {
+ case NL80211_IFTYPE_AP:
+ skb_queue_head_init(&sdata->u.ap.ps_bc_buf);
+ INIT_LIST_HEAD(&sdata->u.ap.vlans);
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ ieee80211_sta_setup_sdata(sdata);
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ if (ieee80211_vif_is_mesh(&sdata->vif))
+ ieee80211_mesh_init_sdata(sdata);
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ sdata->dev->type = ARPHRD_IEEE80211_RADIOTAP;
+ sdata->dev->hard_start_xmit = ieee80211_monitor_start_xmit;
+ sdata->u.mntr_flags = MONITOR_FLAG_CONTROL |
+ MONITOR_FLAG_OTHER_BSS;
+ break;
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_AP_VLAN:
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case __NL80211_IFTYPE_AFTER_LAST:
+ BUG();
+ break;
+ }
+
+ ieee80211_debugfs_add_netdev(sdata);
}
-/* Must be called with rtnl lock held. */
-void __ieee80211_if_del(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata)
+int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
+ enum nl80211_iftype type)
{
- struct net_device *dev = sdata->dev;
+ ASSERT_RTNL();
- ieee80211_debugfs_remove_netdev(sdata);
- unregister_netdevice(dev);
- /* Except master interface, the net_device will be freed by
- * net_device->destructor (i. e. ieee80211_if_free). */
+ if (type == sdata->vif.type)
+ return 0;
+
+ /*
+ * We could, here, on changes between IBSS/STA/MESH modes,
+ * invoke an MLME function instead that disassociates etc.
+ * and goes into the requested mode.
+ */
+
+ if (netif_running(sdata->dev))
+ return -EBUSY;
+
+ /* Purge and reset type-dependent state. */
+ ieee80211_teardown_sdata(sdata->dev);
+ ieee80211_setup_sdata(sdata, type);
+
+ /* reset some values that shouldn't be kept across type changes */
+ sdata->bss_conf.basic_rates =
+ ieee80211_mandatory_rates(sdata->local,
+ sdata->local->hw.conf.channel->band);
+ sdata->drop_unencrypted = 0;
+
+ return 0;
}
-/* Must be called with rtnl lock held. */
-int ieee80211_if_remove(struct net_device *dev, const char *name, int id)
+int ieee80211_if_add(struct ieee80211_local *local, const char *name,
+ struct net_device **new_dev, enum nl80211_iftype type,
+ struct vif_params *params)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata, *n;
+ struct net_device *ndev;
+ struct ieee80211_sub_if_data *sdata = NULL;
+ int ret, i;
ASSERT_RTNL();
- list_for_each_entry_safe(sdata, n, &local->interfaces, list) {
- if ((sdata->vif.type == id || id == -1) &&
- strcmp(name, sdata->dev->name) == 0 &&
- sdata->dev != local->mdev) {
- list_del_rcu(&sdata->list);
- synchronize_rcu();
- __ieee80211_if_del(local, sdata);
- return 0;
- }
- }
- return -ENODEV;
+ ndev = alloc_netdev(sizeof(*sdata) + local->hw.vif_data_size,
+ name, ieee80211_if_setup);
+ if (!ndev)
+ return -ENOMEM;
+
+ 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;
+
+ memcpy(ndev->dev_addr, local->hw.wiphy->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;
+
+ /* initialise type-independent data */
+ sdata->wdev.wiphy = local->hw.wiphy;
+ sdata->local = local;
+ sdata->dev = ndev;
+
+ for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
+ skb_queue_head_init(&sdata->fragments[i].skb_list);
+
+ INIT_LIST_HEAD(&sdata->key_list);
+
+ sdata->force_unicast_rateidx = -1;
+ sdata->max_ratectrl_rateidx = -1;
+
+ /* setup type-dependent data */
+ ieee80211_setup_sdata(sdata, type);
+
+ ret = register_netdevice(ndev);
+ if (ret)
+ goto fail;
+
+ ndev->uninit = ieee80211_teardown_sdata;
+
+ 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);
+
+ list_add_tail_rcu(&sdata->list, &local->interfaces);
+
+ if (new_dev)
+ *new_dev = ndev;
+
+ return 0;
+
+ fail:
+ free_netdev(ndev);
+ return ret;
}
-void ieee80211_if_free(struct net_device *dev)
+void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ ASSERT_RTNL();
- ieee80211_if_sdata_deinit(sdata);
- free_netdev(dev);
+ list_del_rcu(&sdata->list);
+ synchronize_rcu();
+ unregister_netdevice(sdata->dev);
+}
+
+/*
+ * Remove all interfaces, may only be called at hardware unregistration
+ * time because it doesn't do RCU-safe list removals.
+ */
+void ieee80211_remove_interfaces(struct ieee80211_local *local)
+{
+ struct ieee80211_sub_if_data *sdata, *tmp;
+
+ ASSERT_RTNL();
+
+ list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
+ list_del(&sdata->list);
+ unregister_netdevice(sdata->dev);
+ }
}
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 220e83be3ef..a5b06fe7198 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -118,12 +118,12 @@ static const u8 *get_mac_for_key(struct ieee80211_key *key)
* address to indicate a transmit-only key.
*/
if (key->conf.alg != ALG_WEP &&
- (key->sdata->vif.type == IEEE80211_IF_TYPE_AP ||
- key->sdata->vif.type == IEEE80211_IF_TYPE_VLAN))
+ (key->sdata->vif.type == NL80211_IFTYPE_AP ||
+ key->sdata->vif.type == NL80211_IFTYPE_AP_VLAN))
addr = zero_addr;
if (key->sta)
- addr = key->sta->addr;
+ addr = key->sta->sta.addr;
return addr;
}
@@ -281,6 +281,20 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
key->conf.alg = alg;
key->conf.keyidx = idx;
key->conf.keylen = key_len;
+ switch (alg) {
+ case ALG_WEP:
+ key->conf.iv_len = WEP_IV_LEN;
+ key->conf.icv_len = WEP_ICV_LEN;
+ break;
+ case ALG_TKIP:
+ key->conf.iv_len = TKIP_IV_LEN;
+ key->conf.icv_len = TKIP_ICV_LEN;
+ break;
+ case ALG_CCMP:
+ key->conf.iv_len = CCMP_HDR_LEN;
+ key->conf.icv_len = CCMP_MIC_LEN;
+ break;
+ }
memcpy(key->conf.key, key_data, key_len);
INIT_LIST_HEAD(&key->list);
INIT_LIST_HEAD(&key->todo);
@@ -321,10 +335,17 @@ void ieee80211_key_link(struct ieee80211_key *key,
* some hardware cannot handle TKIP with QoS, so
* we indicate whether QoS could be in use.
*/
- if (sta->flags & WLAN_STA_WME)
+ if (test_sta_flags(sta, WLAN_STA_WME))
key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA;
+
+ /*
+ * This key is for a specific sta interface,
+ * inform the driver that it should try to store
+ * this key as pairwise key.
+ */
+ key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
} else {
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
struct sta_info *ap;
/*
@@ -335,7 +356,7 @@ void ieee80211_key_link(struct ieee80211_key *key,
/* same here, the AP could be using QoS */
ap = sta_info_get(key->local, key->sdata->u.sta.bssid);
if (ap) {
- if (ap->flags & WLAN_STA_WME)
+ if (test_sta_flags(ap, WLAN_STA_WME))
key->conf.flags |=
IEEE80211_KEY_FLAG_WMM_STA;
}
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index f52c3df1fe9..425816e0996 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -16,31 +16,18 @@
#include <linux/rcupdate.h>
#include <net/mac80211.h>
-/* ALG_TKIP
- * struct ieee80211_key::key is encoded as a 256-bit (32 byte) data block:
- * Temporal Encryption Key (128 bits)
- * Temporal Authenticator Tx MIC Key (64 bits)
- * Temporal Authenticator Rx MIC Key (64 bits)
- */
-
-#define WEP_IV_LEN 4
-#define WEP_ICV_LEN 4
-
-#define ALG_TKIP_KEY_LEN 32
-/* Starting offsets for each key */
-#define ALG_TKIP_TEMP_ENCR_KEY 0
-#define ALG_TKIP_TEMP_AUTH_TX_MIC_KEY 16
-#define ALG_TKIP_TEMP_AUTH_RX_MIC_KEY 24
-#define TKIP_IV_LEN 8
-#define TKIP_ICV_LEN 4
-
-#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 NUM_RX_DATA_QUEUES 17
+#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
struct ieee80211_local;
struct ieee80211_sub_if_data;
@@ -69,6 +56,13 @@ enum ieee80211_internal_key_flags {
KEY_FLAG_TODO_ADD_DEBUGFS = BIT(5),
};
+struct tkip_ctx {
+ u32 iv32;
+ u16 iv16;
+ u16 p1k[5];
+ int initialized;
+};
+
struct ieee80211_key {
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
@@ -85,16 +79,10 @@ struct ieee80211_key {
union {
struct {
/* last used TSC */
- u32 iv32;
- u16 iv16;
- u16 p1k[5];
- int tx_initialized;
+ struct tkip_ctx tx;
/* last received RSC */
- u32 iv32_rx[NUM_RX_DATA_QUEUES];
- u16 iv16_rx[NUM_RX_DATA_QUEUES];
- u16 p1k_rx[NUM_RX_DATA_QUEUES][5];
- int rx_initialized[NUM_RX_DATA_QUEUES];
+ struct tkip_ctx rx[NUM_RX_DATA_QUEUES];
} tkip;
struct {
u8 tx_pn[6];
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index df0836ff1a2..ae62ad40ad6 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -35,8 +35,6 @@
#include "debugfs.h"
#include "debugfs_netdev.h"
-#define SUPP_MCS_SET_LEN 16
-
/*
* For seeing transmitted packets on monitor interfaces
* we have a radiotap header too.
@@ -47,16 +45,9 @@ struct ieee80211_tx_status_rtap_hdr {
u8 data_retries;
} __attribute__ ((packed));
-/* common interface routines */
-
-static int header_parse_80211(const struct sk_buff *skb, unsigned char *haddr)
-{
- memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */
- return ETH_ALEN;
-}
/* must be called under mdev tx lock */
-static void ieee80211_configure_filter(struct ieee80211_local *local)
+void ieee80211_configure_filter(struct ieee80211_local *local)
{
unsigned int changed_flags;
unsigned int new_flags = 0;
@@ -99,30 +90,52 @@ static void ieee80211_configure_filter(struct ieee80211_local *local)
/* master interface */
+static int header_parse_80211(const struct sk_buff *skb, unsigned char *haddr)
+{
+ memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */
+ return ETH_ALEN;
+}
+
+static const struct header_ops ieee80211_header_ops = {
+ .create = eth_header,
+ .parse = header_parse_80211,
+ .rebuild = eth_rebuild_header,
+ .cache = eth_header_cache,
+ .cache_update = eth_header_cache_update,
+};
+
static int ieee80211_master_open(struct net_device *dev)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_master_priv *mpriv = netdev_priv(dev);
+ struct ieee80211_local *local = mpriv->local;
struct ieee80211_sub_if_data *sdata;
int res = -EOPNOTSUPP;
/* we hold the RTNL here so can safely walk the list */
list_for_each_entry(sdata, &local->interfaces, list) {
- if (sdata->dev != dev && netif_running(sdata->dev)) {
+ if (netif_running(sdata->dev)) {
res = 0;
break;
}
}
- return res;
+
+ if (res)
+ return res;
+
+ netif_tx_start_all_queues(local->mdev);
+
+ return 0;
}
static int ieee80211_master_stop(struct net_device *dev)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_master_priv *mpriv = netdev_priv(dev);
+ struct ieee80211_local *local = mpriv->local;
struct ieee80211_sub_if_data *sdata;
/* we hold the RTNL here so can safely walk the list */
list_for_each_entry(sdata, &local->interfaces, list)
- if (sdata->dev != dev && netif_running(sdata->dev))
+ if (netif_running(sdata->dev))
dev_close(sdata->dev);
return 0;
@@ -130,898 +143,58 @@ static int ieee80211_master_stop(struct net_device *dev)
static void ieee80211_master_set_multicast_list(struct net_device *dev)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_master_priv *mpriv = netdev_priv(dev);
+ struct ieee80211_local *local = mpriv->local;
ieee80211_configure_filter(local);
}
-/* regular interfaces */
-
-static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
-{
- int meshhdrlen;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- meshhdrlen = (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) ? 5 : 0;
-
- /* 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) {
- printk(KERN_WARNING "%s: invalid MTU %d\n",
- dev->name, new_mtu);
- 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 inline int identical_mac_addr_allowed(int type1, int type2)
-{
- return (type1 == IEEE80211_IF_TYPE_MNTR ||
- type2 == IEEE80211_IF_TYPE_MNTR ||
- (type1 == IEEE80211_IF_TYPE_AP &&
- type2 == IEEE80211_IF_TYPE_WDS) ||
- (type1 == IEEE80211_IF_TYPE_WDS &&
- (type2 == IEEE80211_IF_TYPE_WDS ||
- type2 == IEEE80211_IF_TYPE_AP)) ||
- (type1 == IEEE80211_IF_TYPE_AP &&
- type2 == IEEE80211_IF_TYPE_VLAN) ||
- (type1 == IEEE80211_IF_TYPE_VLAN &&
- (type2 == IEEE80211_IF_TYPE_AP ||
- type2 == IEEE80211_IF_TYPE_VLAN)));
-}
-
-static int ieee80211_open(struct net_device *dev)
-{
- struct ieee80211_sub_if_data *sdata, *nsdata;
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_if_init_conf conf;
- int res;
- bool need_hw_reconfig = 0;
- struct sta_info *sta;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- /* 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 && ndev != local->mdev && netif_running(ndev)) {
- /*
- * Allow only a single IBSS interface to be up at any
- * time. This is restricted because beacon distribution
- * cannot work properly if both are in the same IBSS.
- *
- * To remove this restriction we'd have to disallow them
- * from setting the same SSID on different IBSS interfaces
- * belonging to the same hardware. Then, however, we're
- * faced with having to adopt two different TSF timers...
- */
- if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS &&
- nsdata->vif.type == IEEE80211_IF_TYPE_IBSS)
- return -EBUSY;
-
- /*
- * Disallow multiple IBSS/STA mode interfaces.
- *
- * This is a technical restriction, it is possible although
- * most likely not IEEE 802.11 compliant to have multiple
- * STAs with just a single hardware (the TSF timer will not
- * be adjusted properly.)
- *
- * However, because mac80211 uses the master device's BSS
- * information for each STA/IBSS interface, doing this will
- * currently corrupt that BSS information completely, unless,
- * a not very useful case, both STAs are associated to the
- * same BSS.
- *
- * To remove this restriction, the BSS information needs to
- * be embedded in the STA/IBSS mode sdata instead of using
- * the master device's BSS structure.
- */
- if ((sdata->vif.type == IEEE80211_IF_TYPE_STA ||
- sdata->vif.type == IEEE80211_IF_TYPE_IBSS) &&
- (nsdata->vif.type == IEEE80211_IF_TYPE_STA ||
- nsdata->vif.type == IEEE80211_IF_TYPE_IBSS))
- 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))
- continue;
-
- /*
- * check whether it may have the same address
- */
- if (!identical_mac_addr_allowed(sdata->vif.type,
- nsdata->vif.type))
- return -ENOTUNIQ;
-
- /*
- * can only add VLANs to enabled APs
- */
- if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN &&
- nsdata->vif.type == IEEE80211_IF_TYPE_AP)
- sdata->u.vlan.ap = nsdata;
- }
- }
-
- switch (sdata->vif.type) {
- case IEEE80211_IF_TYPE_WDS:
- if (!is_valid_ether_addr(sdata->u.wds.remote_addr))
- return -ENOLINK;
- break;
- case IEEE80211_IF_TYPE_VLAN:
- if (!sdata->u.vlan.ap)
- return -ENOLINK;
- break;
- case IEEE80211_IF_TYPE_AP:
- case IEEE80211_IF_TYPE_STA:
- case IEEE80211_IF_TYPE_MNTR:
- case IEEE80211_IF_TYPE_IBSS:
- case IEEE80211_IF_TYPE_MESH_POINT:
- /* no special treatment */
- break;
- case IEEE80211_IF_TYPE_INVALID:
- /* cannot happen */
- WARN_ON(1);
- break;
- }
-
- if (local->open_count == 0) {
- res = 0;
- if (local->ops->start)
- res = local->ops->start(local_to_hw(local));
- if (res)
- return res;
- need_hw_reconfig = 1;
- ieee80211_led_radio(local, local->hw.conf.radio_enabled);
- }
-
- switch (sdata->vif.type) {
- case IEEE80211_IF_TYPE_VLAN:
- list_add(&sdata->u.vlan.list, &sdata->u.vlan.ap->u.ap.vlans);
- /* no need to tell driver */
- break;
- case IEEE80211_IF_TYPE_MNTR:
- if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) {
- local->cooked_mntrs++;
- break;
- }
-
- /* must be before the call to ieee80211_configure_filter */
- local->monitors++;
- if (local->monitors == 1)
- local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
-
- if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL)
- local->fif_fcsfail++;
- if (sdata->u.mntr_flags & MONITOR_FLAG_PLCPFAIL)
- local->fif_plcpfail++;
- if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL)
- local->fif_control++;
- if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS)
- local->fif_other_bss++;
-
- netif_tx_lock_bh(local->mdev);
- ieee80211_configure_filter(local);
- netif_tx_unlock_bh(local->mdev);
- break;
- case IEEE80211_IF_TYPE_STA:
- case IEEE80211_IF_TYPE_IBSS:
- sdata->u.sta.flags &= ~IEEE80211_STA_PREV_BSSID_SET;
- /* fall through */
- default:
- conf.vif = &sdata->vif;
- conf.type = sdata->vif.type;
- conf.mac_addr = dev->dev_addr;
- res = local->ops->add_interface(local_to_hw(local), &conf);
- if (res)
- goto err_stop;
-
- ieee80211_if_config(dev);
- ieee80211_reset_erp_info(dev);
- ieee80211_enable_keys(sdata);
-
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA &&
- !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
- netif_carrier_off(dev);
- else
- netif_carrier_on(dev);
- }
-
- if (sdata->vif.type == IEEE80211_IF_TYPE_WDS) {
- /* Create STA entry for the WDS peer */
- sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr,
- GFP_KERNEL);
- if (!sta) {
- res = -ENOMEM;
- goto err_del_interface;
- }
-
- sta->flags |= WLAN_STA_AUTHORIZED;
-
- res = sta_info_insert(sta);
- if (res) {
- /* STA has been freed */
- goto err_del_interface;
- }
- }
-
- if (local->open_count == 0) {
- res = dev_open(local->mdev);
- WARN_ON(res);
- if (res)
- goto err_del_interface;
- tasklet_enable(&local->tx_pending_tasklet);
- tasklet_enable(&local->tasklet);
- }
-
- /*
- * set_multicast_list will be invoked by the networking core
- * which will check whether any increments here were done in
- * error and sync them down to the hardware as filter flags.
- */
- if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
- atomic_inc(&local->iff_allmultis);
-
- if (sdata->flags & IEEE80211_SDATA_PROMISC)
- atomic_inc(&local->iff_promiscs);
-
- local->open_count++;
- if (need_hw_reconfig)
- ieee80211_hw_config(local);
-
- /*
- * ieee80211_sta_work is disabled while network interface
- * is down. Therefore, some configuration changes may not
- * yet be effective. Trigger execution of ieee80211_sta_work
- * to fix this.
- */
- if(sdata->vif.type == IEEE80211_IF_TYPE_STA ||
- sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- queue_work(local->hw.workqueue, &ifsta->work);
- }
-
- netif_start_queue(dev);
-
- return 0;
- err_del_interface:
- local->ops->remove_interface(local_to_hw(local), &conf);
- err_stop:
- if (!local->open_count && local->ops->stop)
- local->ops->stop(local_to_hw(local));
- return res;
-}
+/* everything else */
-static int ieee80211_stop(struct net_device *dev)
+int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
- struct ieee80211_if_init_conf conf;
- struct sta_info *sta;
-
- /*
- * Stop TX on this interface first.
- */
- netif_stop_queue(dev);
-
- /*
- * Now delete all active aggregation sessions.
- */
- rcu_read_lock();
-
- list_for_each_entry_rcu(sta, &local->sta_list, list) {
- if (sta->sdata == sdata)
- ieee80211_sta_tear_down_BA_sessions(dev, sta->addr);
- }
-
- rcu_read_unlock();
-
- /*
- * Remove all stations associated with this interface.
- *
- * This must be done before calling ops->remove_interface()
- * because otherwise we can later invoke ops->sta_notify()
- * whenever the STAs are removed, and that invalidates driver
- * assumptions about always getting a vif pointer that is valid
- * (because if we remove a STA after ops->remove_interface()
- * the driver will have removed the vif info already!)
- *
- * We could relax this and only unlink the stations from the
- * hash table and list but keep them on a per-sdata list that
- * will be inserted back again when the interface is brought
- * up again, but I don't currently see a use case for that,
- * except with WDS which gets a STA entry created when it is
- * brought up.
- */
- sta_info_flush(local, sdata);
-
- /*
- * 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.
- */
- if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
- atomic_dec(&local->iff_allmultis);
-
- if (sdata->flags & IEEE80211_SDATA_PROMISC)
- atomic_dec(&local->iff_promiscs);
-
- dev_mc_unsync(local->mdev, dev);
-
- /* APs need special treatment */
- if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
- struct ieee80211_sub_if_data *vlan, *tmp;
- struct beacon_data *old_beacon = sdata->u.ap.beacon;
-
- /* remove beacon */
- rcu_assign_pointer(sdata->u.ap.beacon, NULL);
- synchronize_rcu();
- kfree(old_beacon);
-
- /* down all dependent devices, that is VLANs */
- list_for_each_entry_safe(vlan, tmp, &sdata->u.ap.vlans,
- u.vlan.list)
- dev_close(vlan->dev);
- WARN_ON(!list_empty(&sdata->u.ap.vlans));
- }
-
- local->open_count--;
-
- switch (sdata->vif.type) {
- case IEEE80211_IF_TYPE_VLAN:
- list_del(&sdata->u.vlan.list);
- sdata->u.vlan.ap = NULL;
- /* no need to tell driver */
- break;
- case IEEE80211_IF_TYPE_MNTR:
- if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) {
- local->cooked_mntrs--;
- break;
- }
-
- local->monitors--;
- if (local->monitors == 0)
- local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
-
- if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL)
- local->fif_fcsfail--;
- if (sdata->u.mntr_flags & MONITOR_FLAG_PLCPFAIL)
- local->fif_plcpfail--;
- if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL)
- local->fif_control--;
- if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS)
- local->fif_other_bss--;
-
- netif_tx_lock_bh(local->mdev);
- ieee80211_configure_filter(local);
- netif_tx_unlock_bh(local->mdev);
- break;
- case IEEE80211_IF_TYPE_MESH_POINT:
- case IEEE80211_IF_TYPE_STA:
- case IEEE80211_IF_TYPE_IBSS:
- sdata->u.sta.state = IEEE80211_DISABLED;
- memset(sdata->u.sta.bssid, 0, ETH_ALEN);
- del_timer_sync(&sdata->u.sta.timer);
- /*
- * 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.
- */
- synchronize_rcu();
- skb_queue_purge(&sdata->u.sta.skb_queue);
-
- if (local->scan_dev == sdata->dev) {
- if (!local->ops->hw_scan) {
- local->sta_sw_scanning = 0;
- cancel_delayed_work(&local->scan_work);
- } else
- local->sta_hw_scanning = 0;
- }
-
- sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
- kfree(sdata->u.sta.extra_ie);
- sdata->u.sta.extra_ie = NULL;
- sdata->u.sta.extra_ie_len = 0;
- /* fall through */
- default:
- conf.vif = &sdata->vif;
- conf.type = sdata->vif.type;
- conf.mac_addr = dev->dev_addr;
- /* disable all keys for as long as this netdev is down */
- ieee80211_disable_keys(sdata);
- local->ops->remove_interface(local_to_hw(local), &conf);
- }
-
- if (local->open_count == 0) {
- if (netif_running(local->mdev))
- dev_close(local->mdev);
-
- if (local->ops->stop)
- local->ops->stop(local_to_hw(local));
-
- ieee80211_led_radio(local, 0);
-
- flush_workqueue(local->hw.workqueue);
-
- tasklet_disable(&local->tx_pending_tasklet);
- tasklet_disable(&local->tasklet);
- }
-
- return 0;
-}
-
-int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- struct sta_info *sta;
- struct ieee80211_sub_if_data *sdata;
- u16 start_seq_num = 0;
- u8 *state;
- int ret;
- DECLARE_MAC_BUF(mac);
-
- if (tid >= STA_TID_NUM)
- return -EINVAL;
-
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Open BA session requested for %s tid %u\n",
- print_mac(mac, ra), tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-
- rcu_read_lock();
-
- sta = sta_info_get(local, ra);
- if (!sta) {
- printk(KERN_DEBUG "Could not find the station\n");
- rcu_read_unlock();
- return -ENOENT;
- }
-
- spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
-
- /* we have tried too many times, receiver does not want A-MPDU */
- if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) {
- ret = -EBUSY;
- goto start_ba_exit;
- }
-
- state = &sta->ampdu_mlme.tid_state_tx[tid];
- /* check if the TID is not in aggregation flow already */
- if (*state != HT_AGG_STATE_IDLE) {
-#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 */
- ret = -EAGAIN;
- goto start_ba_exit;
- }
-
- /* prepare A-MPDU MLME for Tx aggregation */
- sta->ampdu_mlme.tid_tx[tid] =
- kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC);
- if (!sta->ampdu_mlme.tid_tx[tid]) {
- if (net_ratelimit())
- printk(KERN_ERR "allocate tx mlme to tid %d failed\n",
- tid);
- ret = -ENOMEM;
- goto start_ba_exit;
- }
- /* Tx timer */
- sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function =
- sta_addba_resp_timer_expired;
- sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.data =
- (unsigned long)&sta->timer_to_tid[tid];
- init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
-
- /* ensure that TX flow won't interrupt us
- * until the end of the call to requeue function */
- spin_lock_bh(&local->mdev->queue_lock);
-
- /* create a new queue for this aggregation */
- ret = ieee80211_ht_agg_queue_add(local, sta, tid);
-
- /* case no queue is available to aggregation
- * don't switch to aggregation */
- if (ret) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "BA request denied - queue unavailable for"
- " tid %d\n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
- goto start_ba_err;
- }
- sdata = sta->sdata;
-
- /* Ok, the Addba frame hasn't been sent yet, but if the driver calls the
- * call back right away, it must see that the flow has begun */
- *state |= HT_ADDBA_REQUESTED_MSK;
-
- if (local->ops->ampdu_action)
- ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START,
- ra, tid, &start_seq_num);
-
- if (ret) {
- /* No need to requeue the packets in the agg queue, since we
- * held the tx lock: no packet could be enqueued to the newly
- * allocated queue */
- ieee80211_ht_agg_queue_remove(local, sta, tid, 0);
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "BA request denied - HW unavailable for"
- " tid %d\n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
- *state = HT_AGG_STATE_IDLE;
- goto start_ba_err;
- }
-
- /* Will put all the packets in the new SW queue */
- ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
- spin_unlock_bh(&local->mdev->queue_lock);
-
- /* send an addBA request */
- sta->ampdu_mlme.dialog_token_allocator++;
- sta->ampdu_mlme.tid_tx[tid]->dialog_token =
- sta->ampdu_mlme.dialog_token_allocator;
- sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num;
-
- ieee80211_send_addba_request(sta->sdata->dev, ra, tid,
- sta->ampdu_mlme.tid_tx[tid]->dialog_token,
- sta->ampdu_mlme.tid_tx[tid]->ssn,
- 0x40, 5000);
-
- /* activate the timer for the recipient's addBA response */
- sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.expires =
- jiffies + ADDBA_RESP_INTERVAL;
- add_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
- printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid);
- goto start_ba_exit;
-
-start_ba_err:
- kfree(sta->ampdu_mlme.tid_tx[tid]);
- sta->ampdu_mlme.tid_tx[tid] = NULL;
- spin_unlock_bh(&local->mdev->queue_lock);
- ret = -EBUSY;
-start_ba_exit:
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
- rcu_read_unlock();
- return ret;
-}
-EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
+ struct ieee80211_if_conf conf;
-int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
- u8 *ra, u16 tid,
- enum ieee80211_back_parties initiator)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- struct sta_info *sta;
- u8 *state;
- int ret = 0;
- DECLARE_MAC_BUF(mac);
+ if (WARN_ON(!netif_running(sdata->dev)))
+ return 0;
- if (tid >= STA_TID_NUM)
+ if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN))
return -EINVAL;
- rcu_read_lock();
- sta = sta_info_get(local, ra);
- if (!sta) {
- rcu_read_unlock();
- return -ENOENT;
- }
-
- /* check if the TID is in aggregation */
- state = &sta->ampdu_mlme.tid_state_tx[tid];
- spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
-
- if (*state != HT_AGG_STATE_OPERATIONAL) {
- ret = -ENOENT;
- goto stop_BA_exit;
- }
-
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Tx BA session stop requested for %s tid %u\n",
- print_mac(mac, ra), tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-
- ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]);
-
- *state = HT_AGG_STATE_REQ_STOP_BA_MSK |
- (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
-
- if (local->ops->ampdu_action)
- ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_STOP,
- ra, tid, NULL);
-
- /* case HW denied going back to legacy */
- if (ret) {
- WARN_ON(ret != -EBUSY);
- *state = HT_AGG_STATE_OPERATIONAL;
- ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
- goto stop_BA_exit;
- }
-
-stop_BA_exit:
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
- rcu_read_unlock();
- return ret;
-}
-EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
-
-void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- struct sta_info *sta;
- u8 *state;
- DECLARE_MAC_BUF(mac);
-
- if (tid >= STA_TID_NUM) {
- printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
- tid, STA_TID_NUM);
- return;
- }
-
- rcu_read_lock();
- sta = sta_info_get(local, ra);
- if (!sta) {
- rcu_read_unlock();
- printk(KERN_DEBUG "Could not find station: %s\n",
- print_mac(mac, ra));
- return;
- }
-
- state = &sta->ampdu_mlme.tid_state_tx[tid];
- spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
-
- if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
- printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
- *state);
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
- rcu_read_unlock();
- return;
- }
-
- WARN_ON_ONCE(*state & HT_ADDBA_DRV_READY_MSK);
-
- *state |= HT_ADDBA_DRV_READY_MSK;
-
- if (*state == HT_AGG_STATE_OPERATIONAL) {
- printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
- ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
- }
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
- rcu_read_unlock();
-}
-EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
-
-void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- struct sta_info *sta;
- u8 *state;
- int agg_queue;
- DECLARE_MAC_BUF(mac);
-
- if (tid >= STA_TID_NUM) {
- printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
- tid, STA_TID_NUM);
- return;
- }
-
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Stopping Tx BA session for %s tid %d\n",
- print_mac(mac, ra), tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-
- rcu_read_lock();
- sta = sta_info_get(local, ra);
- if (!sta) {
- printk(KERN_DEBUG "Could not find station: %s\n",
- print_mac(mac, ra));
- rcu_read_unlock();
- return;
- }
- state = &sta->ampdu_mlme.tid_state_tx[tid];
-
- spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
- if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) {
- printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n");
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
- rcu_read_unlock();
- return;
- }
-
- if (*state & HT_AGG_STATE_INITIATOR_MSK)
- ieee80211_send_delba(sta->sdata->dev, ra, tid,
- WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
-
- agg_queue = sta->tid_to_tx_q[tid];
-
- /* avoid ordering issues: we are the only one that can modify
- * the content of the qdiscs */
- spin_lock_bh(&local->mdev->queue_lock);
- /* remove the queue for this aggregation */
- ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
- spin_unlock_bh(&local->mdev->queue_lock);
-
- /* we just requeued the all the frames that were in the removed
- * queue, and since we might miss a softirq we do netif_schedule.
- * ieee80211_wake_queue is not used here as this queue is not
- * necessarily stopped */
- netif_schedule(local->mdev);
- *state = HT_AGG_STATE_IDLE;
- sta->ampdu_mlme.addba_req_num[tid] = 0;
- kfree(sta->ampdu_mlme.tid_tx[tid]);
- sta->ampdu_mlme.tid_tx[tid] = NULL;
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
-
- rcu_read_unlock();
-}
-EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb);
-
-void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
- const u8 *ra, u16 tid)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_ra_tid *ra_tid;
- struct sk_buff *skb = dev_alloc_skb(0);
-
- if (unlikely(!skb)) {
- if (net_ratelimit())
- printk(KERN_WARNING "%s: Not enough memory, "
- "dropping start BA session", skb->dev->name);
- return;
- }
- ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
- memcpy(&ra_tid->ra, ra, ETH_ALEN);
- ra_tid->tid = tid;
-
- skb->pkt_type = IEEE80211_ADDBA_MSG;
- skb_queue_tail(&local->skb_queue, skb);
- tasklet_schedule(&local->tasklet);
-}
-EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe);
-
-void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
- const u8 *ra, u16 tid)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_ra_tid *ra_tid;
- struct sk_buff *skb = dev_alloc_skb(0);
-
- if (unlikely(!skb)) {
- if (net_ratelimit())
- printk(KERN_WARNING "%s: Not enough memory, "
- "dropping stop BA session", skb->dev->name);
- return;
- }
- ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
- memcpy(&ra_tid->ra, ra, ETH_ALEN);
- ra_tid->tid = tid;
-
- skb->pkt_type = IEEE80211_DELBA_MSG;
- skb_queue_tail(&local->skb_queue, skb);
- tasklet_schedule(&local->tasklet);
-}
-EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb_irqsafe);
-
-static void ieee80211_set_multicast_list(struct net_device *dev)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- int allmulti, promisc, sdata_allmulti, sdata_promisc;
-
- allmulti = !!(dev->flags & IFF_ALLMULTI);
- promisc = !!(dev->flags & IFF_PROMISC);
- sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI);
- sdata_promisc = !!(sdata->flags & IEEE80211_SDATA_PROMISC);
-
- if (allmulti != sdata_allmulti) {
- if (dev->flags & IFF_ALLMULTI)
- atomic_inc(&local->iff_allmultis);
- else
- atomic_dec(&local->iff_allmultis);
- sdata->flags ^= IEEE80211_SDATA_ALLMULTI;
- }
-
- if (promisc != sdata_promisc) {
- if (dev->flags & IFF_PROMISC)
- atomic_inc(&local->iff_promiscs);
- else
- atomic_dec(&local->iff_promiscs);
- sdata->flags ^= IEEE80211_SDATA_PROMISC;
- }
-
- dev_mc_sync(local->mdev, dev);
-}
-
-static const struct header_ops ieee80211_header_ops = {
- .create = eth_header,
- .parse = header_parse_80211,
- .rebuild = eth_rebuild_header,
- .cache = eth_header_cache,
- .cache_update = eth_header_cache_update,
-};
-
-/* Must not be called for mdev */
-void ieee80211_if_setup(struct net_device *dev)
-{
- ether_setup(dev);
- dev->hard_start_xmit = ieee80211_subif_start_xmit;
- dev->wireless_handlers = &ieee80211_iw_handler_def;
- dev->set_multicast_list = ieee80211_set_multicast_list;
- dev->change_mtu = ieee80211_change_mtu;
- dev->open = ieee80211_open;
- dev->stop = ieee80211_stop;
- dev->destructor = ieee80211_if_free;
-}
-
-/* everything else */
-
-static int __ieee80211_if_config(struct net_device *dev,
- struct sk_buff *beacon,
- struct ieee80211_tx_control *control)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_if_conf conf;
-
- if (!local->ops->config_interface || !netif_running(dev))
+ if (!local->ops->config_interface)
return 0;
memset(&conf, 0, sizeof(conf));
- conf.type = sdata->vif.type;
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
- sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
+ conf.changed = changed;
+
+ if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC) {
conf.bssid = sdata->u.sta.bssid;
conf.ssid = sdata->u.sta.ssid;
conf.ssid_len = sdata->u.sta.ssid_len;
- } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
- conf.beacon = beacon;
- conf.beacon_control = control;
- ieee80211_start_mesh(dev);
- } else if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
+ } else if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ conf.bssid = sdata->dev->dev_addr;
conf.ssid = sdata->u.ap.ssid;
conf.ssid_len = sdata->u.ap.ssid_len;
- conf.beacon = beacon;
- conf.beacon_control = control;
+ } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ u8 zero[ETH_ALEN] = { 0 };
+ conf.bssid = zero;
+ conf.ssid = zero;
+ conf.ssid_len = 0;
+ } else {
+ WARN_ON(1);
+ return -EINVAL;
}
- return local->ops->config_interface(local_to_hw(local),
- &sdata->vif, &conf);
-}
-int ieee80211_if_config(struct net_device *dev)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT &&
- (local->hw.flags & IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE))
- return ieee80211_if_config_beacon(dev);
- return __ieee80211_if_config(dev, NULL, NULL);
-}
+ if (WARN_ON(!conf.bssid && (changed & IEEE80211_IFCC_BSSID)))
+ return -EINVAL;
-int ieee80211_if_config_beacon(struct net_device *dev)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_tx_control control;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct sk_buff *skb;
+ if (WARN_ON(!conf.ssid && (changed & IEEE80211_IFCC_SSID)))
+ return -EINVAL;
- if (!(local->hw.flags & IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE))
- return 0;
- skb = ieee80211_beacon_get(local_to_hw(local), &sdata->vif,
- &control);
- if (!skb)
- return -ENOMEM;
- return __ieee80211_if_config(dev, skb, &control);
+ return local->ops->config_interface(local_to_hw(local),
+ &sdata->vif, &conf);
}
int ieee80211_hw_config(struct ieee80211_local *local)
@@ -1029,7 +202,7 @@ int ieee80211_hw_config(struct ieee80211_local *local)
struct ieee80211_channel *chan;
int ret = 0;
- if (local->sta_sw_scanning)
+ if (local->sw_scanning)
chan = local->scan_channel;
else
chan = local->oper_channel;
@@ -1068,56 +241,84 @@ u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht,
struct ieee80211_supported_band *sband;
struct ieee80211_ht_info ht_conf;
struct ieee80211_ht_bss_info ht_bss_conf;
- int i;
u32 changed = 0;
+ int i;
+ u8 max_tx_streams = IEEE80211_HT_CAP_MAX_STREAMS;
+ u8 tx_mcs_set_cap;
sband = local->hw.wiphy->bands[conf->channel->band];
+ memset(&ht_conf, 0, sizeof(struct ieee80211_ht_info));
+ memset(&ht_bss_conf, 0, sizeof(struct ieee80211_ht_bss_info));
+
/* HT is not supported */
if (!sband->ht_info.ht_supported) {
conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE;
- return 0;
+ goto out;
}
- memset(&ht_conf, 0, sizeof(struct ieee80211_ht_info));
- memset(&ht_bss_conf, 0, sizeof(struct ieee80211_ht_bss_info));
-
- if (enable_ht) {
- if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE))
+ /* disable HT */
+ if (!enable_ht) {
+ if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)
changed |= BSS_CHANGED_HT;
+ conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE;
+ conf->ht_conf.ht_supported = 0;
+ goto out;
+ }
- conf->flags |= IEEE80211_CONF_SUPPORT_HT_MODE;
- ht_conf.ht_supported = 1;
- ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap;
- ht_conf.cap &= ~(IEEE80211_HT_CAP_MIMO_PS);
- ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS;
+ if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE))
+ changed |= BSS_CHANGED_HT;
- for (i = 0; i < SUPP_MCS_SET_LEN; i++)
- ht_conf.supp_mcs_set[i] =
- sband->ht_info.supp_mcs_set[i] &
- req_ht_cap->supp_mcs_set[i];
+ conf->flags |= IEEE80211_CONF_SUPPORT_HT_MODE;
+ ht_conf.ht_supported = 1;
- ht_bss_conf.primary_channel = req_bss_cap->primary_channel;
- ht_bss_conf.bss_cap = req_bss_cap->bss_cap;
- ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode;
+ ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap;
+ ht_conf.cap &= ~(IEEE80211_HT_CAP_SM_PS);
+ ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_SM_PS;
+ ht_bss_conf.primary_channel = req_bss_cap->primary_channel;
+ ht_bss_conf.bss_cap = req_bss_cap->bss_cap;
+ ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode;
- ht_conf.ampdu_factor = req_ht_cap->ampdu_factor;
- ht_conf.ampdu_density = req_ht_cap->ampdu_density;
+ ht_conf.ampdu_factor = req_ht_cap->ampdu_factor;
+ ht_conf.ampdu_density = req_ht_cap->ampdu_density;
- /* if bss configuration changed store the new one */
- if (memcmp(&conf->ht_conf, &ht_conf, sizeof(ht_conf)) ||
- memcmp(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf))) {
- changed |= BSS_CHANGED_HT;
- memcpy(&conf->ht_conf, &ht_conf, sizeof(ht_conf));
- memcpy(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf));
- }
- } else {
- if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)
- changed |= BSS_CHANGED_HT;
- conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE;
- }
+ /* Bits 96-100 */
+ tx_mcs_set_cap = sband->ht_info.supp_mcs_set[12];
+
+ /* configure suppoerted Tx MCS according to requested MCS
+ * (based in most cases on Rx capabilities of peer) and self
+ * Tx MCS capabilities (as defined by low level driver HW
+ * Tx capabilities) */
+ if (!(tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_DEFINED))
+ goto check_changed;
+
+ /* Counting from 0 therfore + 1 */
+ if (tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_RX_DIFF)
+ max_tx_streams = ((tx_mcs_set_cap &
+ IEEE80211_HT_CAP_MCS_TX_STREAMS) >> 2) + 1;
+
+ for (i = 0; i < max_tx_streams; i++)
+ ht_conf.supp_mcs_set[i] =
+ sband->ht_info.supp_mcs_set[i] &
+ req_ht_cap->supp_mcs_set[i];
+
+ if (tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_UEQM)
+ for (i = IEEE80211_SUPP_MCS_SET_UEQM;
+ i < IEEE80211_SUPP_MCS_SET_LEN; i++)
+ ht_conf.supp_mcs_set[i] =
+ sband->ht_info.supp_mcs_set[i] &
+ req_ht_cap->supp_mcs_set[i];
+check_changed:
+ /* if bss configuration changed store the new one */
+ if (memcmp(&conf->ht_conf, &ht_conf, sizeof(ht_conf)) ||
+ memcmp(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf))) {
+ changed |= BSS_CHANGED_HT;
+ memcpy(&conf->ht_conf, &ht_conf, sizeof(ht_conf));
+ memcpy(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf));
+ }
+out:
return changed;
}
@@ -1126,6 +327,9 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
+ if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN))
+ return;
+
if (!changed)
return;
@@ -1136,50 +340,28 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
changed);
}
-void ieee80211_reset_erp_info(struct net_device *dev)
+u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
sdata->bss_conf.use_cts_prot = 0;
sdata->bss_conf.use_short_preamble = 0;
- ieee80211_bss_info_change_notify(sdata,
- BSS_CHANGED_ERP_CTS_PROT |
- BSS_CHANGED_ERP_PREAMBLE);
+ return BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE;
}
void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
- struct sk_buff *skb,
- struct ieee80211_tx_status *status)
+ struct sk_buff *skb)
{
struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_tx_status *saved;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int tmp;
skb->dev = local->mdev;
- saved = kmalloc(sizeof(struct ieee80211_tx_status), GFP_ATOMIC);
- if (unlikely(!saved)) {
- if (net_ratelimit())
- printk(KERN_WARNING "%s: Not enough memory, "
- "dropping tx status", skb->dev->name);
- /* should be dev_kfree_skb_irq, but due to this function being
- * named _irqsafe instead of just _irq we can't be sure that
- * people won't call it from non-irq contexts */
- dev_kfree_skb_any(skb);
- return;
- }
- memcpy(saved, status, sizeof(struct ieee80211_tx_status));
- /* copy pointer to saved status into skb->cb for use by tasklet */
- memcpy(skb->cb, &saved, sizeof(saved));
-
skb->pkt_type = IEEE80211_TX_STATUS_MSG;
- skb_queue_tail(status->control.flags & IEEE80211_TXCTL_REQ_TX_STATUS ?
+ skb_queue_tail(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS ?
&local->skb_queue : &local->skb_queue_unreliable, skb);
tmp = skb_queue_len(&local->skb_queue) +
skb_queue_len(&local->skb_queue_unreliable);
while (tmp > IEEE80211_IRQSAFE_QUEUE_LIMIT &&
(skb = skb_dequeue(&local->skb_queue_unreliable))) {
- memcpy(&saved, skb->cb, sizeof(saved));
- kfree(saved);
dev_kfree_skb_irq(skb);
tmp--;
I802_DEBUG_INC(local->tx_status_drop);
@@ -1193,7 +375,6 @@ static void ieee80211_tasklet_handler(unsigned long data)
struct ieee80211_local *local = (struct ieee80211_local *) data;
struct sk_buff *skb;
struct ieee80211_rx_status rx_status;
- struct ieee80211_tx_status *tx_status;
struct ieee80211_ra_tid *ra_tid;
while ((skb = skb_dequeue(&local->skb_queue)) ||
@@ -1208,12 +389,8 @@ static void ieee80211_tasklet_handler(unsigned long data)
__ieee80211_rx(local_to_hw(local), skb, &rx_status);
break;
case IEEE80211_TX_STATUS_MSG:
- /* get pointer to saved status out of skb->cb */
- memcpy(&tx_status, skb->cb, sizeof(tx_status));
skb->pkt_type = 0;
- ieee80211_tx_status(local_to_hw(local),
- skb, tx_status);
- kfree(tx_status);
+ ieee80211_tx_status(local_to_hw(local), skb);
break;
case IEEE80211_DELBA_MSG:
ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
@@ -1227,9 +404,8 @@ static void ieee80211_tasklet_handler(unsigned long data)
ra_tid->ra, ra_tid->tid);
dev_kfree_skb(skb);
break ;
- default: /* should never get here! */
- printk(KERN_ERR "%s: Unknown message type (%d)\n",
- wiphy_name(local->hw.wiphy), skb->pkt_type);
+ default:
+ WARN_ON(1);
dev_kfree_skb(skb);
break;
}
@@ -1239,29 +415,15 @@ static void ieee80211_tasklet_handler(unsigned long data)
/* Remove added headers (e.g., QoS control), encryption header/MIC, etc. to
* make a prepared TX frame (one that has been given to hw) to look like brand
* new IEEE 802.11 frame that is ready to go through TX processing again.
- * Also, tx_packet_data in cb is restored from tx_control. */
+ */
static void ieee80211_remove_tx_extra(struct ieee80211_local *local,
struct ieee80211_key *key,
- struct sk_buff *skb,
- struct ieee80211_tx_control *control)
+ struct sk_buff *skb)
{
- int hdrlen, iv_len, mic_len;
- struct ieee80211_tx_packet_data *pkt_data;
-
- pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
- pkt_data->ifindex = vif_to_sdata(control->vif)->dev->ifindex;
- pkt_data->flags = 0;
- if (control->flags & IEEE80211_TXCTL_REQ_TX_STATUS)
- pkt_data->flags |= IEEE80211_TXPD_REQ_TX_STATUS;
- if (control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)
- pkt_data->flags |= IEEE80211_TXPD_DO_NOT_ENCRYPT;
- if (control->flags & IEEE80211_TXCTL_REQUEUE)
- pkt_data->flags |= IEEE80211_TXPD_REQUEUE;
- if (control->flags & IEEE80211_TXCTL_EAPOL_FRAME)
- pkt_data->flags |= IEEE80211_TXPD_EAPOL_FRAME;
- pkt_data->queue = control->queue;
-
- hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+ unsigned int hdrlen, iv_len, mic_len;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (!key)
goto no_key;
@@ -1283,32 +445,29 @@ static void ieee80211_remove_tx_extra(struct ieee80211_local *local,
goto no_key;
}
- if (skb->len >= mic_len &&
+ if (skb->len >= hdrlen + mic_len &&
!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
skb_trim(skb, skb->len - mic_len);
- if (skb->len >= iv_len && skb->len > hdrlen) {
+ if (skb->len >= hdrlen + iv_len) {
memmove(skb->data + iv_len, skb->data, hdrlen);
- skb_pull(skb, iv_len);
+ hdr = (struct ieee80211_hdr *)skb_pull(skb, iv_len);
}
no_key:
- {
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u16 fc = le16_to_cpu(hdr->frame_control);
- if ((fc & 0x8C) == 0x88) /* QoS Control Field */ {
- fc &= ~IEEE80211_STYPE_QOS_DATA;
- hdr->frame_control = cpu_to_le16(fc);
- memmove(skb->data + 2, skb->data, hdrlen - 2);
- skb_pull(skb, 2);
- }
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ hdr->frame_control &= ~cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
+ memmove(skb->data + IEEE80211_QOS_CTL_LEN, skb->data,
+ hdrlen - IEEE80211_QOS_CTL_LEN);
+ skb_pull(skb, IEEE80211_QOS_CTL_LEN);
}
}
static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
struct sta_info *sta,
- struct sk_buff *skb,
- struct ieee80211_tx_status *status)
+ struct sk_buff *skb)
{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
sta->tx_filtered_count++;
/*
@@ -1316,7 +475,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
* packet. If the STA went to power save mode, this will happen
* when it wakes up for the next time.
*/
- sta->flags |= WLAN_STA_CLEAR_PS_FILT;
+ set_sta_flags(sta, WLAN_STA_CLEAR_PS_FILT);
/*
* This code races in the following way:
@@ -1348,84 +507,91 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
* can be unknown, for example with different interrupt status
* bits.
*/
- if (sta->flags & WLAN_STA_PS &&
+ if (test_sta_flags(sta, WLAN_STA_PS) &&
skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) {
- ieee80211_remove_tx_extra(local, sta->key, skb,
- &status->control);
+ ieee80211_remove_tx_extra(local, sta->key, skb);
skb_queue_tail(&sta->tx_filtered, skb);
return;
}
- if (!(sta->flags & WLAN_STA_PS) &&
- !(status->control.flags & IEEE80211_TXCTL_REQUEUE)) {
+ if (!test_sta_flags(sta, WLAN_STA_PS) &&
+ !(info->flags & IEEE80211_TX_CTL_REQUEUE)) {
/* Software retry the packet once */
- status->control.flags |= IEEE80211_TXCTL_REQUEUE;
- ieee80211_remove_tx_extra(local, sta->key, skb,
- &status->control);
+ info->flags |= IEEE80211_TX_CTL_REQUEUE;
+ ieee80211_remove_tx_extra(local, sta->key, skb);
dev_queue_xmit(skb);
return;
}
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "%s: dropped TX filtered frame, "
"queue_len=%d PS=%d @%lu\n",
wiphy_name(local->hw.wiphy),
skb_queue_len(&sta->tx_filtered),
- !!(sta->flags & WLAN_STA_PS), jiffies);
+ !!test_sta_flags(sta, WLAN_STA_PS), jiffies);
+#endif
dev_kfree_skb(skb);
}
-void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
- struct ieee80211_tx_status *status)
+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;
-
- if (!status) {
- printk(KERN_ERR
- "%s: ieee80211_tx_status called with NULL status\n",
- wiphy_name(local->hw.wiphy));
- dev_kfree_skb(skb);
- return;
- }
+ struct sta_info *sta;
rcu_read_lock();
- if (status->excessive_retries) {
- struct sta_info *sta;
- sta = sta_info_get(local, hdr->addr1);
- if (sta) {
- if (sta->flags & WLAN_STA_PS) {
- /*
- * The STA is in power save mode, so assume
- * that this TX packet failed because of that.
- */
- status->excessive_retries = 0;
- status->flags |= IEEE80211_TX_STATUS_TX_FILTERED;
- ieee80211_handle_filtered_frame(local, sta,
- skb, status);
- rcu_read_unlock();
- return;
- }
+ sta = sta_info_get(local, hdr->addr1);
+
+ if (sta) {
+ if (info->status.excessive_retries &&
+ test_sta_flags(sta, WLAN_STA_PS)) {
+ /*
+ * The STA is in power save mode, so assume
+ * that this TX packet failed because of that.
+ */
+ ieee80211_handle_filtered_frame(local, sta, skb);
+ rcu_read_unlock();
+ return;
}
- }
- if (status->flags & IEEE80211_TX_STATUS_TX_FILTERED) {
- struct sta_info *sta;
- sta = sta_info_get(local, hdr->addr1);
- if (sta) {
- ieee80211_handle_filtered_frame(local, sta, skb,
- status);
+ fc = hdr->frame_control;
+
+ if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
+ (ieee80211_is_data_qos(fc))) {
+ u16 tid, ssn;
+ u8 *qc;
+
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = qc[0] & 0xf;
+ ssn = ((le16_to_cpu(hdr->seq_ctrl) + 0x10)
+ & IEEE80211_SCTL_SEQ);
+ ieee80211_send_bar(sta->sdata, hdr->addr1,
+ tid, ssn);
+ }
+
+ if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
+ ieee80211_handle_filtered_frame(local, sta, skb);
rcu_read_unlock();
return;
+ } else {
+ if (info->status.excessive_retries)
+ sta->tx_retry_failed++;
+ sta->tx_retry_count += info->status.retry_count;
}
- } else
- rate_control_tx_status(local->mdev, skb, status);
+
+ sband = local->hw.wiphy->bands[info->band];
+ rate_control_tx_status(local, sband, sta, skb);
+ }
rcu_read_unlock();
@@ -1439,14 +605,14 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
frag = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG;
type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE;
- if (status->flags & IEEE80211_TX_STATUS_ACK) {
+ if (info->flags & IEEE80211_TX_STAT_ACK) {
if (frag == 0) {
local->dot11TransmittedFrameCount++;
if (is_multicast_ether_addr(hdr->addr1))
local->dot11MulticastTransmittedFrameCount++;
- if (status->retry_count > 0)
+ if (info->status.retry_count > 0)
local->dot11RetryCount++;
- if (status->retry_count > 1)
+ if (info->status.retry_count > 1)
local->dot11MultipleRetryCount++;
}
@@ -1483,7 +649,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
return;
}
- rthdr = (struct ieee80211_tx_status_rtap_hdr*)
+ rthdr = (struct ieee80211_tx_status_rtap_hdr *)
skb_push(skb, sizeof(*rthdr));
memset(rthdr, 0, sizeof(*rthdr));
@@ -1492,17 +658,17 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) |
(1 << IEEE80211_RADIOTAP_DATA_RETRIES));
- if (!(status->flags & IEEE80211_TX_STATUS_ACK) &&
+ if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
!is_multicast_ether_addr(hdr->addr1))
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_FAIL);
- if ((status->control.flags & IEEE80211_TXCTL_USE_RTS_CTS) &&
- (status->control.flags & IEEE80211_TXCTL_USE_CTS_PROTECT))
+ if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) &&
+ (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT))
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_CTS);
- else if (status->control.flags & IEEE80211_TXCTL_USE_RTS_CTS)
+ else if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS)
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_RTS);
- rthdr->data_retries = status->retry_count;
+ rthdr->data_retries = info->status.retry_count;
/* XXX: is this sufficient for BPF? */
skb_set_mac_header(skb, 0);
@@ -1513,7 +679,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
- if (sdata->vif.type == IEEE80211_IF_TYPE_MNTR) {
+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
if (!netif_running(sdata->dev))
continue;
@@ -1589,8 +755,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->hw.queues = 1; /* default */
- local->bridge_packets = 1;
-
local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
local->short_retry_limit = 7;
@@ -1601,7 +765,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
spin_lock_init(&local->key_lock);
- INIT_DELAYED_WORK(&local->scan_work, ieee80211_sta_scan_work);
+ INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
sta_info_init(local);
@@ -1628,7 +792,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
int result;
enum ieee80211_band band;
struct net_device *mdev;
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_master_priv *mpriv;
/*
* generic code guarantees at least one band,
@@ -1648,23 +812,39 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
}
}
+ /* 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);
+
result = wiphy_register(local->hw.wiphy);
if (result < 0)
return result;
- /* for now, mdev needs sub_if_data :/ */
- mdev = alloc_netdev(sizeof(struct ieee80211_sub_if_data),
- "wmaster%d", ether_setup);
+ /*
+ * We use the number of queues for feature tests (QoS, HT) internally
+ * so restrict them appropriately.
+ */
+ if (hw->queues > IEEE80211_MAX_QUEUES)
+ hw->queues = IEEE80211_MAX_QUEUES;
+ if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES)
+ hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES;
+ if (hw->queues < 4)
+ hw->ampdu_queues = 0;
+
+ mdev = alloc_netdev_mq(sizeof(struct ieee80211_master_priv),
+ "wmaster%d", ether_setup,
+ ieee80211_num_queues(hw));
if (!mdev)
goto fail_mdev_alloc;
- sdata = IEEE80211_DEV_TO_SUB_IF(mdev);
- mdev->ieee80211_ptr = &sdata->wdev;
- sdata->wdev.wiphy = local->hw.wiphy;
-
+ mpriv = netdev_priv(mdev);
+ mpriv->local = local;
local->mdev = mdev;
- ieee80211_rx_bss_list_init(mdev);
+ ieee80211_rx_bss_list_init(local);
mdev->hard_start_xmit = ieee80211_master_start_xmit;
mdev->open = ieee80211_master_open;
@@ -1673,18 +853,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
mdev->header_ops = &ieee80211_header_ops;
mdev->set_multicast_list = ieee80211_master_set_multicast_list;
- sdata->vif.type = IEEE80211_IF_TYPE_AP;
- sdata->dev = mdev;
- sdata->local = local;
- sdata->u.ap.force_unicast_rateidx = -1;
- sdata->u.ap.max_ratectrl_rateidx = -1;
- ieee80211_if_sdata_init(sdata);
-
- /* no RCU needed since we're still during init phase */
- list_add_tail(&sdata->list, &local->interfaces);
-
name = wiphy_dev(local->hw.wiphy)->driver->name;
- local->hw.workqueue = create_singlethread_workqueue(name);
+ local->hw.workqueue = create_freezeable_workqueue(name);
if (!local->hw.workqueue) {
result = -ENOMEM;
goto fail_workqueue;
@@ -1700,15 +870,21 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
debugfs_hw_add(local);
- local->hw.conf.beacon_int = 1000;
+ if (local->hw.conf.beacon_int < 10)
+ local->hw.conf.beacon_int = 100;
+
+ if (local->hw.max_listen_interval == 0)
+ local->hw.max_listen_interval = 1;
+
+ local->hw.conf.listen_interval = local->hw.max_listen_interval;
- local->wstats_flags |= local->hw.max_rssi ?
- IW_QUAL_LEVEL_UPDATED : IW_QUAL_LEVEL_INVALID;
- local->wstats_flags |= local->hw.max_signal ?
+ local->wstats_flags |= local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC |
+ IEEE80211_HW_SIGNAL_DB |
+ IEEE80211_HW_SIGNAL_DBM) ?
IW_QUAL_QUAL_UPDATED : IW_QUAL_QUAL_INVALID;
- local->wstats_flags |= local->hw.max_noise ?
+ local->wstats_flags |= local->hw.flags & IEEE80211_HW_NOISE_DBM ?
IW_QUAL_NOISE_UPDATED : IW_QUAL_NOISE_INVALID;
- if (local->hw.max_rssi < 0 || local->hw.max_noise < 0)
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
local->wstats_flags |= IW_QUAL_DBM;
result = sta_info_start(local);
@@ -1727,9 +903,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (result < 0)
goto fail_dev;
- ieee80211_debugfs_add_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev));
- ieee80211_if_set_type(local->mdev, IEEE80211_IF_TYPE_AP);
-
result = ieee80211_init_rate_ctrl_alg(local,
hw->rate_control_algorithm);
if (result < 0) {
@@ -1741,21 +914,20 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
result = ieee80211_wep_init(local);
if (result < 0) {
- printk(KERN_DEBUG "%s: Failed to initialize wep\n",
- wiphy_name(local->hw.wiphy));
+ printk(KERN_DEBUG "%s: Failed to initialize wep: %d\n",
+ wiphy_name(local->hw.wiphy), result);
goto fail_wep;
}
- ieee80211_install_qdisc(local->mdev);
+ local->mdev->select_queue = ieee80211_select_queue;
/* add one default STA interface */
- result = ieee80211_if_add(local->mdev, "wlan%d", NULL,
- IEEE80211_IF_TYPE_STA, NULL);
+ result = ieee80211_if_add(local, "wlan%d", NULL,
+ NL80211_IFTYPE_STATION, NULL);
if (result)
printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
wiphy_name(local->hw.wiphy));
- local->reg_state = IEEE80211_DEV_REGISTERED;
rtnl_unlock();
ieee80211_led_init(local);
@@ -1765,7 +937,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
fail_wep:
rate_control_deinitialize(local);
fail_rate:
- ieee80211_debugfs_remove_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev));
unregister_netdevice(local->mdev);
local->mdev = NULL;
fail_dev:
@@ -1775,10 +946,8 @@ fail_sta_info:
debugfs_hw_del(local);
destroy_workqueue(local->hw.workqueue);
fail_workqueue:
- if (local->mdev != NULL) {
- ieee80211_if_free(local->mdev);
- local->mdev = NULL;
- }
+ if (local->mdev)
+ free_netdev(local->mdev);
fail_mdev_alloc:
wiphy_unregister(local->hw.wiphy);
return result;
@@ -1788,42 +957,27 @@ EXPORT_SYMBOL(ieee80211_register_hw);
void ieee80211_unregister_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_sub_if_data *sdata, *tmp;
tasklet_kill(&local->tx_pending_tasklet);
tasklet_kill(&local->tasklet);
rtnl_lock();
- BUG_ON(local->reg_state != IEEE80211_DEV_REGISTERED);
-
- local->reg_state = IEEE80211_DEV_UNREGISTERED;
-
/*
* At this point, interface list manipulations are fine
* because the driver cannot be handing us frames any
* more and the tasklet is killed.
*/
- /*
- * First, we remove all non-master interfaces. Do this because they
- * may have bss pointer dependency on the master, and when we free
- * the master these would be freed as well, breaking our list
- * iteration completely.
- */
- list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
- if (sdata->dev == local->mdev)
- continue;
- list_del(&sdata->list);
- __ieee80211_if_del(local, sdata);
- }
+ /* First, we remove all virtual interfaces. */
+ ieee80211_remove_interfaces(local);
/* then, finally, remove the master interface */
- __ieee80211_if_del(local, IEEE80211_DEV_TO_SUB_IF(local->mdev));
+ unregister_netdevice(local->mdev);
rtnl_unlock();
- ieee80211_rx_bss_list_deinit(local->mdev);
+ ieee80211_rx_bss_list_deinit(local);
ieee80211_clear_tx_pending(local);
sta_info_stop(local);
rate_control_deinitialize(local);
@@ -1840,8 +994,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
wiphy_unregister(local->hw.wiphy);
ieee80211_wep_free(local);
ieee80211_led_exit(local);
- ieee80211_if_free(local->mdev);
- local->mdev = NULL;
+ free_netdev(local->mdev);
}
EXPORT_SYMBOL(ieee80211_unregister_hw);
@@ -1858,32 +1011,27 @@ static int __init ieee80211_init(void)
struct sk_buff *skb;
int ret;
- BUILD_BUG_ON(sizeof(struct ieee80211_tx_packet_data) > sizeof(skb->cb));
+ BUILD_BUG_ON(sizeof(struct ieee80211_tx_info) > sizeof(skb->cb));
+ BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, driver_data) +
+ IEEE80211_TX_INFO_DRIVER_DATA_SIZE > sizeof(skb->cb));
- ret = rc80211_pid_init();
+ ret = rc80211_minstrel_init();
if (ret)
- goto out;
+ return ret;
- ret = ieee80211_wme_register();
- if (ret) {
- printk(KERN_DEBUG "ieee80211_init: failed to "
- "initialize WME (err=%d)\n", ret);
- goto out_cleanup_pid;
- }
+ ret = rc80211_pid_init();
+ if (ret)
+ return ret;
ieee80211_debugfs_netdev_init();
return 0;
-
- out_cleanup_pid:
- rc80211_pid_exit();
- out:
- return ret;
}
static void __exit ieee80211_exit(void)
{
rc80211_pid_exit();
+ rc80211_minstrel_exit();
/*
* For key todo, it'll be empty by now but the work
@@ -1894,7 +1042,6 @@ static void __exit ieee80211_exit(void)
if (mesh_allocated)
ieee80211s_stop();
- ieee80211_wme_unregister();
ieee80211_debugfs_netdev_exit();
}
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 697ef67f96b..8013277924f 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -12,6 +12,9 @@
#include "ieee80211_i.h"
#include "mesh.h"
+#define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ)
+#define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ)
+
#define PP_OFFSET 1 /* Path Selection Protocol */
#define PM_OFFSET 5 /* Path Selection Metric */
#define CC_OFFSET 9 /* Congestion Control Mode */
@@ -35,19 +38,28 @@ void ieee80211s_stop(void)
kmem_cache_destroy(rm_cache);
}
+static void ieee80211_mesh_housekeeping_timer(unsigned long data)
+{
+ struct ieee80211_sub_if_data *sdata = (void *) data;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+ ifmsh->housekeeping = true;
+ queue_work(local->hw.workqueue, &ifmsh->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
- * @dev: local mesh interface
+ * @sdata: local mesh subif
*
* 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 net_device *dev)
+bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_if_sta *sta = &sdata->u.sta;
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
/*
* As support for each feature is added, check for matching
@@ -59,11 +71,11 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct net_device *dev)
* - MDA enabled
* - Power management control on fc
*/
- if (sta->mesh_id_len == ie->mesh_id_len &&
- memcmp(sta->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 &&
- memcmp(sta->mesh_pp_id, ie->mesh_config + PP_OFFSET, 4) == 0 &&
- memcmp(sta->mesh_pm_id, ie->mesh_config + PM_OFFSET, 4) == 0 &&
- memcmp(sta->mesh_cc_id, ie->mesh_config + CC_OFFSET, 4) == 0)
+ if (ifmsh->mesh_id_len == ie->mesh_id_len &&
+ memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 &&
+ memcmp(ifmsh->mesh_pp_id, ie->mesh_config + PP_OFFSET, 4) == 0 &&
+ memcmp(ifmsh->mesh_pm_id, ie->mesh_config + PM_OFFSET, 4) == 0 &&
+ memcmp(ifmsh->mesh_cc_id, ie->mesh_config + CC_OFFSET, 4) == 0)
return true;
return false;
@@ -73,10 +85,8 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct net_device *dev)
* mesh_peer_accepts_plinks - check if an mp is willing to establish peer links
*
* @ie: information elements of a management frame from the mesh peer
- * @dev: local mesh interface
*/
-bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie,
- struct net_device *dev)
+bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie)
{
return (*(ie->mesh_config + CAPAB_OFFSET) & ACCEPT_PLINKS) != 0;
}
@@ -98,11 +108,11 @@ void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata)
*/
free_plinks = mesh_plink_availables(sdata);
- if (free_plinks != sdata->u.sta.accepting_plinks)
- ieee80211_sta_timer((unsigned long) sdata);
+ if (free_plinks != sdata->u.mesh.accepting_plinks)
+ ieee80211_mesh_housekeeping_timer((unsigned long) sdata);
}
-void mesh_ids_set_default(struct ieee80211_if_sta *sta)
+void mesh_ids_set_default(struct ieee80211_if_mesh *sta)
{
u8 def_id[4] = {0x00, 0x0F, 0xAC, 0xff};
@@ -111,28 +121,26 @@ void mesh_ids_set_default(struct ieee80211_if_sta *sta)
memcpy(sta->mesh_cc_id, def_id, 4);
}
-int mesh_rmc_init(struct net_device *dev)
+int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
int i;
- sdata->u.sta.rmc = kmalloc(sizeof(struct mesh_rmc), GFP_KERNEL);
- if (!sdata->u.sta.rmc)
+ sdata->u.mesh.rmc = kmalloc(sizeof(struct mesh_rmc), GFP_KERNEL);
+ if (!sdata->u.mesh.rmc)
return -ENOMEM;
- sdata->u.sta.rmc->idx_mask = RMC_BUCKETS - 1;
+ sdata->u.mesh.rmc->idx_mask = RMC_BUCKETS - 1;
for (i = 0; i < RMC_BUCKETS; i++)
- INIT_LIST_HEAD(&sdata->u.sta.rmc->bucket[i].list);
+ INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i].list);
return 0;
}
-void mesh_rmc_free(struct net_device *dev)
+void mesh_rmc_free(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct mesh_rmc *rmc = sdata->u.sta.rmc;
+ struct mesh_rmc *rmc = sdata->u.mesh.rmc;
struct rmc_entry *p, *n;
int i;
- if (!sdata->u.sta.rmc)
+ if (!sdata->u.mesh.rmc)
return;
for (i = 0; i < RMC_BUCKETS; i++)
@@ -142,7 +150,7 @@ void mesh_rmc_free(struct net_device *dev)
}
kfree(rmc);
- sdata->u.sta.rmc = NULL;
+ sdata->u.mesh.rmc = NULL;
}
/**
@@ -158,10 +166,9 @@ void mesh_rmc_free(struct net_device *dev)
* it.
*/
int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr,
- struct net_device *dev)
+ struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct mesh_rmc *rmc = sdata->u.sta.rmc;
+ struct mesh_rmc *rmc = sdata->u.mesh.rmc;
u32 seqnum = 0;
int entries = 0;
u8 idx;
@@ -194,10 +201,9 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr,
return 0;
}
-void mesh_mgmt_ies_add(struct sk_buff *skb, struct net_device *dev)
+void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
u8 *pos;
int len, i, rate;
@@ -224,11 +230,11 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct net_device *dev)
}
}
- pos = skb_put(skb, 2 + sdata->u.sta.mesh_id_len);
+ pos = skb_put(skb, 2 + sdata->u.mesh.mesh_id_len);
*pos++ = WLAN_EID_MESH_ID;
- *pos++ = sdata->u.sta.mesh_id_len;
- if (sdata->u.sta.mesh_id_len)
- memcpy(pos, sdata->u.sta.mesh_id, sdata->u.sta.mesh_id_len);
+ *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);
pos = skb_put(skb, 21);
*pos++ = WLAN_EID_MESH_CONFIG;
@@ -237,15 +243,15 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct net_device *dev)
*pos++ = 1;
/* Active path selection protocol ID */
- memcpy(pos, sdata->u.sta.mesh_pp_id, 4);
+ memcpy(pos, sdata->u.mesh.mesh_pp_id, 4);
pos += 4;
/* Active path selection metric ID */
- memcpy(pos, sdata->u.sta.mesh_pm_id, 4);
+ memcpy(pos, sdata->u.mesh.mesh_pm_id, 4);
pos += 4;
/* Congestion control mode identifier */
- memcpy(pos, sdata->u.sta.mesh_cc_id, 4);
+ memcpy(pos, sdata->u.mesh.mesh_cc_id, 4);
pos += 4;
/* Channel precedence:
@@ -255,17 +261,17 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct net_device *dev)
pos += 4;
/* Mesh capability */
- sdata->u.sta.accepting_plinks = mesh_plink_availables(sdata);
- *pos++ = sdata->u.sta.accepting_plinks ? ACCEPT_PLINKS : 0x00;
+ sdata->u.mesh.accepting_plinks = mesh_plink_availables(sdata);
+ *pos++ = sdata->u.mesh.accepting_plinks ? ACCEPT_PLINKS : 0x00;
*pos++ = 0x00;
return;
}
-u32 mesh_table_hash(u8 *addr, struct net_device *dev, struct mesh_table *tbl)
+u32 mesh_table_hash(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), dev->ifindex, tbl->hash_rnd)
+ return jhash_2words(*(u32 *)(addr+2), sdata->dev->ifindex, tbl->hash_rnd)
& tbl->hash_mask;
}
@@ -315,6 +321,13 @@ struct mesh_table *mesh_table_alloc(int size_order)
return newtbl;
}
+static void __mesh_table_free(struct mesh_table *tbl)
+{
+ kfree(tbl->hash_buckets);
+ kfree(tbl->hashwlock);
+ kfree(tbl);
+}
+
void mesh_table_free(struct mesh_table *tbl, bool free_leafs)
{
struct hlist_head *mesh_hash;
@@ -330,40 +343,33 @@ void mesh_table_free(struct mesh_table *tbl, bool free_leafs)
}
spin_unlock(&tbl->hashwlock[i]);
}
- kfree(tbl->hash_buckets);
- kfree(tbl->hashwlock);
- kfree(tbl);
+ __mesh_table_free(tbl);
}
static void ieee80211_mesh_path_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- struct ieee80211_local *local = wdev_priv(&sdata->wdev);
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct ieee80211_local *local = sdata->local;
- queue_work(local->hw.workqueue, &ifsta->work);
+ queue_work(local->hw.workqueue, &ifmsh->work);
}
struct mesh_table *mesh_table_grow(struct mesh_table *tbl)
{
struct mesh_table *newtbl;
struct hlist_head *oldhash;
- struct hlist_node *p;
- int err = 0;
+ struct hlist_node *p, *q;
int i;
if (atomic_read(&tbl->entries)
- < tbl->mean_chain_len * (tbl->hash_mask + 1)) {
- err = -EPERM;
+ < tbl->mean_chain_len * (tbl->hash_mask + 1))
goto endgrow;
- }
newtbl = mesh_table_alloc(tbl->size_order + 1);
- if (!newtbl) {
- err = -ENOMEM;
+ if (!newtbl)
goto endgrow;
- }
newtbl->free_node = tbl->free_node;
newtbl->mean_chain_len = tbl->mean_chain_len;
@@ -373,13 +379,19 @@ struct mesh_table *mesh_table_grow(struct mesh_table *tbl)
oldhash = tbl->hash_buckets;
for (i = 0; i <= tbl->hash_mask; i++)
hlist_for_each(p, &oldhash[i])
- tbl->copy_node(p, newtbl);
+ if (tbl->copy_node(p, newtbl) < 0)
+ goto errcopy;
+ return newtbl;
+
+errcopy:
+ for (i = 0; i <= newtbl->hash_mask; i++) {
+ hlist_for_each_safe(p, q, &newtbl->hash_buckets[i])
+ tbl->free_node(p, 0);
+ }
+ __mesh_table_free(newtbl);
endgrow:
- if (err)
- return NULL;
- else
- return newtbl;
+ return NULL;
}
/**
@@ -393,50 +405,264 @@ int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
struct ieee80211_sub_if_data *sdata)
{
meshhdr->flags = 0;
- meshhdr->ttl = sdata->u.sta.mshcfg.dot11MeshTTL;
- put_unaligned(cpu_to_le32(sdata->u.sta.mesh_seqnum), &meshhdr->seqnum);
- sdata->u.sta.mesh_seqnum++;
+ meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
+ put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum);
+ sdata->u.mesh.mesh_seqnum++;
return 6;
}
+static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_if_mesh *ifmsh)
+{
+ bool free_plinks;
+
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ printk(KERN_DEBUG "%s: running mesh housekeeping\n",
+ sdata->dev->name);
+#endif
+
+ ieee80211_sta_expire(sdata, IEEE80211_MESH_PEER_INACTIVITY_LIMIT);
+ mesh_path_expire(sdata);
+
+ free_plinks = mesh_plink_availables(sdata);
+ if (free_plinks != sdata->u.mesh.accepting_plinks)
+ ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
+
+ ifmsh->housekeeping = false;
+ mod_timer(&ifmsh->housekeeping_timer,
+ round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL));
+}
+
+
+void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct ieee80211_local *local = sdata->local;
+
+ ifmsh->housekeeping = true;
+ queue_work(local->hw.workqueue, &ifmsh->work);
+ ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
+}
+
+void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
+{
+ del_timer_sync(&sdata->u.mesh.housekeeping_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->u.mesh.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.
+ */
+ synchronize_rcu();
+ skb_queue_purge(&sdata->u.mesh.skb_queue);
+}
+
+static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
+ u16 stype,
+ struct ieee80211_mgmt *mgmt,
+ size_t len,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct ieee80211_local *local= sdata->local;
+ struct ieee802_11_elems elems;
+ struct ieee80211_channel *channel;
+ u64 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->dev->dev_addr))
+ return;
+
+ 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);
+
+ 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)
+ return;
+
+ if (elems.mesh_id && elems.mesh_config &&
+ mesh_matches_local(&elems, sdata)) {
+ supp_rates = ieee80211_sta_get_rates(local, &elems, band);
+
+ mesh_neighbour_update(mgmt->sa, supp_rates, sdata,
+ mesh_peer_accepts_plinks(&elems));
+ }
+}
+
+static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ size_t len,
+ struct ieee80211_rx_status *rx_status)
+{
+ switch (mgmt->u.action.category) {
+ case PLINK_CATEGORY:
+ mesh_rx_plink_frame(sdata, mgmt, len, rx_status);
+ break;
+ case MESH_PATH_SEL_CATEGORY:
+ mesh_rx_path_sel_frame(sdata, mgmt, len);
+ break;
+ }
+}
+
+static 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;
+
+ rx_status = (struct ieee80211_rx_status *) skb->cb;
+ mgmt = (struct ieee80211_mgmt *) skb->data;
+ stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
+
+ switch (stype) {
+ case IEEE80211_STYPE_PROBE_RESP:
+ case IEEE80211_STYPE_BEACON:
+ ieee80211_mesh_rx_bcn_presp(sdata, stype, mgmt, skb->len,
+ rx_status);
+ break;
+ case IEEE80211_STYPE_ACTION:
+ ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status);
+ break;
+ }
+
+ kfree_skb(skb);
+}
+
+static void ieee80211_mesh_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data, u.mesh.work);
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct sk_buff *skb;
+
+ if (!netif_running(sdata->dev))
+ return;
+
+ if (local->sw_scanning || local->hw_scanning)
+ return;
+
+ while ((skb = skb_dequeue(&ifmsh->skb_queue)))
+ ieee80211_mesh_rx_queued_mgmt(sdata, skb);
+
+ if (ifmsh->preq_queue_len &&
+ time_after(jiffies,
+ ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval)))
+ mesh_path_start_discovery(sdata);
+
+ if (ifmsh->housekeeping)
+ ieee80211_mesh_housekeeping(sdata, ifmsh);
+}
+
+void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list)
+ if (ieee80211_vif_is_mesh(&sdata->vif))
+ queue_work(local->hw.workqueue, &sdata->u.mesh.work);
+ rcu_read_unlock();
+}
+
void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
-
- ifsta->mshcfg.dot11MeshRetryTimeout = MESH_RET_T;
- ifsta->mshcfg.dot11MeshConfirmTimeout = MESH_CONF_T;
- ifsta->mshcfg.dot11MeshHoldingTimeout = MESH_HOLD_T;
- ifsta->mshcfg.dot11MeshMaxRetries = MESH_MAX_RETR;
- ifsta->mshcfg.dot11MeshTTL = MESH_TTL;
- ifsta->mshcfg.auto_open_plinks = true;
- ifsta->mshcfg.dot11MeshMaxPeerLinks =
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+ INIT_WORK(&ifmsh->work, ieee80211_mesh_work);
+ setup_timer(&ifmsh->housekeeping_timer,
+ ieee80211_mesh_housekeeping_timer,
+ (unsigned long) sdata);
+ skb_queue_head_init(&sdata->u.mesh.skb_queue);
+
+ 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;
- ifsta->mshcfg.dot11MeshHWMPactivePathTimeout =
+ ifmsh->mshcfg.dot11MeshHWMPactivePathTimeout =
MESH_PATH_TIMEOUT;
- ifsta->mshcfg.dot11MeshHWMPpreqMinInterval =
+ ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval =
MESH_PREQ_MIN_INT;
- ifsta->mshcfg.dot11MeshHWMPnetDiameterTraversalTime =
+ ifmsh->mshcfg.dot11MeshHWMPnetDiameterTraversalTime =
MESH_DIAM_TRAVERSAL_TIME;
- ifsta->mshcfg.dot11MeshHWMPmaxPREQretries =
+ ifmsh->mshcfg.dot11MeshHWMPmaxPREQretries =
MESH_MAX_PREQ_RETRIES;
- ifsta->mshcfg.path_refresh_time =
+ ifmsh->mshcfg.path_refresh_time =
MESH_PATH_REFRESH_TIME;
- ifsta->mshcfg.min_discovery_timeout =
+ ifmsh->mshcfg.min_discovery_timeout =
MESH_MIN_DISCOVERY_TIMEOUT;
- ifsta->accepting_plinks = true;
- ifsta->preq_id = 0;
- ifsta->dsn = 0;
- atomic_set(&ifsta->mpaths, 0);
- mesh_rmc_init(sdata->dev);
- ifsta->last_preq = jiffies;
+ ifmsh->accepting_plinks = true;
+ ifmsh->preq_id = 0;
+ ifmsh->dsn = 0;
+ atomic_set(&ifmsh->mpaths, 0);
+ mesh_rmc_init(sdata);
+ ifmsh->last_preq = jiffies;
/* Allocate all mesh structures when creating the first mesh interface. */
if (!mesh_allocated)
ieee80211s_init();
- mesh_ids_set_default(ifsta);
- setup_timer(&ifsta->mesh_path_timer,
+ mesh_ids_set_default(ifmsh);
+ setup_timer(&ifmsh->mesh_path_timer,
ieee80211_mesh_path_timer,
(unsigned long) sdata);
- INIT_LIST_HEAD(&ifsta->preq_queue.list);
- spin_lock_init(&ifsta->mesh_preq_queue_lock);
+ INIT_LIST_HEAD(&ifmsh->preq_queue.list);
+ spin_lock_init(&ifmsh->mesh_preq_queue_lock);
+}
+
+ieee80211_rx_result
+ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct ieee80211_mgmt *mgmt;
+ u16 fc;
+
+ if (skb->len < 24)
+ return RX_DROP_MONITOR;
+
+ mgmt = (struct ieee80211_mgmt *) skb->data;
+ fc = le16_to_cpu(mgmt->frame_control);
+
+ switch (fc & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_PROBE_RESP:
+ case IEEE80211_STYPE_BEACON:
+ case IEEE80211_STYPE_ACTION:
+ memcpy(skb->cb, rx_status, sizeof(*rx_status));
+ skb_queue_tail(&ifmsh->skb_queue, skb);
+ queue_work(local->hw.workqueue, &ifmsh->work);
+ return RX_QUEUED;
+ }
+
+ return RX_CONTINUE;
}
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 2e161f6d828..e10471c6ba4 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -47,7 +47,7 @@ enum mesh_path_flags {
* struct mesh_path - mac80211 mesh path structure
*
* @dst: mesh path destination mac address
- * @dev: mesh path device
+ * @sdata: mesh subif
* @next_hop: mesh neighbor to which frames for this destination will be
* forwarded
* @timer: mesh path discovery timer
@@ -64,14 +64,15 @@ enum mesh_path_flags {
* @state_lock: mesh pat state lock
*
*
- * The combination of dst and dev is unique in the mesh path table. Since the
+ * The combination of dst and sdata is unique in the mesh path table. Since the
* next_hop STA is only protected by RCU as well, deleting the STA must also
* remove/substitute the mesh_path structure and wait until that is no longer
* reachable before destroying the STA completely.
*/
struct mesh_path {
u8 dst[ETH_ALEN];
- struct net_device *dev;
+ u8 mpp[ETH_ALEN]; /* used for MPP or MAP */
+ struct ieee80211_sub_if_data *sdata;
struct sta_info *next_hop;
struct timer_list timer;
struct sk_buff_head frame_queue;
@@ -109,7 +110,7 @@ struct mesh_table {
__u32 hash_rnd; /* Used for hash generation */
atomic_t entries; /* Up to MAX_MESH_NEIGHBOURS */
void (*free_node) (struct hlist_node *p, bool free_leafs);
- void (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl);
+ int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl);
int size_order;
int mean_chain_len;
};
@@ -203,68 +204,82 @@ int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr);
int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
struct ieee80211_sub_if_data *sdata);
int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr,
- struct net_device *dev);
-bool mesh_matches_local(struct ieee802_11_elems *ie, struct net_device *dev);
-void mesh_ids_set_default(struct ieee80211_if_sta *sta);
-void mesh_mgmt_ies_add(struct sk_buff *skb, struct net_device *dev);
-void mesh_rmc_free(struct net_device *dev);
-int mesh_rmc_init(struct net_device *dev);
+ struct ieee80211_sub_if_data *sdata);
+bool mesh_matches_local(struct ieee802_11_elems *ie,
+ struct ieee80211_sub_if_data *sdata);
+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);
+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_stop(void);
void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata);
+ieee80211_rx_result
+ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+ struct ieee80211_rx_status *rx_status);
+void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
+void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);
/* Mesh paths */
-int mesh_nexthop_lookup(u8 *next_hop, struct sk_buff *skb,
- struct net_device *dev);
-void mesh_path_start_discovery(struct net_device *dev);
-struct mesh_path *mesh_path_lookup(u8 *dst, struct net_device *dev);
-struct mesh_path *mesh_path_lookup_by_idx(int idx, struct net_device *dev);
+int mesh_nexthop_lookup(struct sk_buff *skb,
+ struct ieee80211_sub_if_data *sdata);
+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);
void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop);
-void mesh_path_expire(struct net_device *dev);
-void mesh_path_flush(struct net_device *dev);
-void mesh_rx_path_sel_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
- size_t len);
-int mesh_path_add(u8 *dst, struct net_device *dev);
+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);
/* Mesh plinks */
-void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev,
- bool add);
-bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie,
- struct net_device *dev);
+void mesh_neighbour_update(u8 *hw_addr, u64 rates,
+ struct ieee80211_sub_if_data *sdata, bool add);
+bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie);
void 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);
int mesh_plink_close(struct sta_info *sta);
void mesh_plink_block(struct sta_info *sta);
-void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
- size_t len, struct ieee80211_rx_status *rx_status);
+void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ struct ieee80211_rx_status *rx_status);
/* Private interfaces */
/* Mesh tables */
struct mesh_table *mesh_table_alloc(int size_order);
void mesh_table_free(struct mesh_table *tbl, bool free_leafs);
struct mesh_table *mesh_table_grow(struct mesh_table *tbl);
-u32 mesh_table_hash(u8 *addr, struct net_device *dev, struct mesh_table *tbl);
+u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata,
+ struct mesh_table *tbl);
/* Mesh paths */
int mesh_path_error_tx(u8 *dest, __le32 dest_dsn, u8 *ra,
- struct net_device *dev);
+ struct ieee80211_sub_if_data *sdata);
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 net_device *dev);
+int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata);
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 net_device *dev);
+void mesh_path_discard_frame(struct sk_buff *skb,
+ struct ieee80211_sub_if_data *sdata);
#ifdef CONFIG_MAC80211_MESH
extern int mesh_allocated;
static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata)
{
- return sdata->u.sta.mshcfg.dot11MeshMaxPeerLinks -
- atomic_read(&sdata->u.sta.mshstats.estab_plinks);
+ return sdata->u.mesh.mshcfg.dot11MeshMaxPeerLinks -
+ atomic_read(&sdata->u.mesh.mshstats.estab_plinks);
}
static inline bool mesh_plink_availables(struct ieee80211_sub_if_data *sdata)
@@ -282,10 +297,12 @@ static inline void mesh_path_activate(struct mesh_path *mpath)
for (i = 0; i <= x->hash_mask; i++) \
hlist_for_each_entry_rcu(node, p, &x->hash_buckets[i], list)
+void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local);
+
#else
#define mesh_allocated 0
+static inline void
+ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {}
#endif
-#define MESH_PREQ(skb) (skb->cb + 30)
-
#endif /* IEEE80211S_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index af0cd1e3e21..501c7831adb 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -26,7 +26,7 @@ static inline u32 u32_field_get(u8 *preq_elem, int offset, bool ae)
{
if (ae)
offset += 6;
- return le32_to_cpu(get_unaligned((__le32 *) (preq_elem + offset)));
+ return get_unaligned_le32(preq_elem + offset);
}
/* HWMP IE processing macros */
@@ -64,14 +64,14 @@ static inline u32 u32_field_get(u8 *preq_elem, int offset, bool ae)
#define DSN_LT(x, y) ((long) (x) - (long) (y) < 0)
#define net_traversal_jiffies(s) \
- msecs_to_jiffies(s->u.sta.mshcfg.dot11MeshHWMPnetDiameterTraversalTime)
+ msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime)
#define default_lifetime(s) \
- MSEC_TO_TU(s->u.sta.mshcfg.dot11MeshHWMPactivePathTimeout)
+ MSEC_TO_TU(s->u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout)
#define min_preq_int_jiff(s) \
- (msecs_to_jiffies(s->u.sta.mshcfg.dot11MeshHWMPpreqMinInterval))
-#define max_preq_retries(s) (s->u.sta.mshcfg.dot11MeshHWMPmaxPREQretries)
+ (msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval))
+#define max_preq_retries(s) (s->u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries)
#define disc_timeout_jiff(s) \
- msecs_to_jiffies(sdata->u.sta.mshcfg.min_discovery_timeout)
+ msecs_to_jiffies(sdata->u.mesh.mshcfg.min_discovery_timeout)
enum mpath_frame_type {
MPATH_PREQ = 0,
@@ -82,9 +82,9 @@ enum mpath_frame_type {
static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
u8 *orig_addr, __le32 orig_dsn, u8 dst_flags, u8 *dst,
__le32 dst_dsn, u8 *da, u8 hop_count, u8 ttl, __le32 lifetime,
- __le32 metric, __le32 preq_id, struct net_device *dev)
+ __le32 metric, __le32 preq_id, struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = sdata->local;
struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
struct ieee80211_mgmt *mgmt;
u8 *pos;
@@ -99,11 +99,11 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
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));
- mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
- IEEE80211_STYPE_ACTION);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION);
memcpy(mgmt->da, da, ETH_ALEN);
- memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
/* BSSID is left zeroed, wildcard value */
mgmt->u.action.category = MESH_PATH_SEL_CATEGORY;
mgmt->u.action.u.mesh_action.action_code = action;
@@ -149,7 +149,7 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
pos += ETH_ALEN;
memcpy(pos, &dst_dsn, 4);
- ieee80211_sta_tx(dev, skb, 0);
+ ieee80211_tx_skb(sdata, skb, 0);
return 0;
}
@@ -161,9 +161,9 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
* @ra: node this frame is addressed to
*/
int mesh_path_error_tx(u8 *dst, __le32 dst_dsn, u8 *ra,
- struct net_device *dev)
+ struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = sdata->local;
struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
struct ieee80211_mgmt *mgmt;
u8 *pos;
@@ -178,11 +178,11 @@ int mesh_path_error_tx(u8 *dst, __le32 dst_dsn, u8 *ra,
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));
- mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
- IEEE80211_STYPE_ACTION);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION);
memcpy(mgmt->da, ra, ETH_ALEN);
- memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
/* BSSID is left zeroed, wildcard value */
mgmt->u.action.category = MESH_PATH_SEL_CATEGORY;
mgmt->u.action.u.mesh_action.action_code = MPATH_PERR;
@@ -198,7 +198,7 @@ int mesh_path_error_tx(u8 *dst, __le32 dst_dsn, u8 *ra,
pos += ETH_ALEN;
memcpy(pos, &dst_dsn, 4);
- ieee80211_sta_tx(dev, skb, 0);
+ ieee80211_tx_skb(sdata, skb, 0);
return 0;
}
@@ -223,7 +223,7 @@ 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->txrate_idx].bitrate;
+ rate = sband->bitrates[sta->last_txrate_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) ;
@@ -233,7 +233,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
/**
* hwmp_route_info_get - Update routing info to originator and transmitter
*
- * @dev: local mesh interface
+ * @sdata: local mesh subif
* @mgmt: mesh management frame
* @hwmp_ie: hwmp information element (PREP or PREQ)
*
@@ -246,11 +246,11 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
* Notes: this function is the only place (besides user-provided info) where
* path routing information is updated.
*/
-static u32 hwmp_route_info_get(struct net_device *dev,
+static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
u8 *hwmp_ie)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = sdata->local;
struct mesh_path *mpath;
struct sta_info *sta;
bool fresh_info;
@@ -301,14 +301,14 @@ static u32 hwmp_route_info_get(struct net_device *dev,
new_metric = MAX_METRIC;
exp_time = TU_TO_EXP_TIME(orig_lifetime);
- if (memcmp(orig_addr, dev->dev_addr, ETH_ALEN) == 0) {
+ if (memcmp(orig_addr, sdata->dev->dev_addr, ETH_ALEN) == 0) {
/* 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, dev);
+ mpath = mesh_path_lookup(orig_addr, sdata);
if (mpath) {
spin_lock_bh(&mpath->state_lock);
if (mpath->flags & MESH_PATH_FIXED)
@@ -324,8 +324,8 @@ static u32 hwmp_route_info_get(struct net_device *dev,
}
}
} else {
- mesh_path_add(orig_addr, dev);
- mpath = mesh_path_lookup(orig_addr, dev);
+ mesh_path_add(orig_addr, sdata);
+ mpath = mesh_path_lookup(orig_addr, sdata);
if (!mpath) {
rcu_read_unlock();
return 0;
@@ -357,7 +357,7 @@ static u32 hwmp_route_info_get(struct net_device *dev,
else {
fresh_info = true;
- mpath = mesh_path_lookup(ta, dev);
+ mpath = mesh_path_lookup(ta, sdata);
if (mpath) {
spin_lock_bh(&mpath->state_lock);
if ((mpath->flags & MESH_PATH_FIXED) ||
@@ -365,8 +365,8 @@ static u32 hwmp_route_info_get(struct net_device *dev,
(last_hop_metric > mpath->metric)))
fresh_info = false;
} else {
- mesh_path_add(ta, dev);
- mpath = mesh_path_lookup(ta, dev);
+ mesh_path_add(ta, sdata);
+ mpath = mesh_path_lookup(ta, sdata);
if (!mpath) {
rcu_read_unlock();
return 0;
@@ -392,11 +392,10 @@ static u32 hwmp_route_info_get(struct net_device *dev,
return process ? new_metric : 0;
}
-static void hwmp_preq_frame_process(struct net_device *dev,
+static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
u8 *preq_elem, u32 metric) {
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_path *mpath;
u8 *dst_addr, *orig_addr;
u8 dst_flags, ttl;
@@ -411,19 +410,19 @@ static void hwmp_preq_frame_process(struct net_device *dev,
orig_dsn = PREQ_IE_ORIG_DSN(preq_elem);
dst_flags = PREQ_IE_DST_F(preq_elem);
- if (memcmp(dst_addr, dev->dev_addr, ETH_ALEN) == 0) {
+ if (memcmp(dst_addr, sdata->dev->dev_addr, ETH_ALEN) == 0) {
forward = false;
reply = true;
metric = 0;
- if (time_after(jiffies, ifsta->last_dsn_update +
+ if (time_after(jiffies, ifmsh->last_dsn_update +
net_traversal_jiffies(sdata)) ||
- time_before(jiffies, ifsta->last_dsn_update)) {
- dst_dsn = ++ifsta->dsn;
- ifsta->last_dsn_update = jiffies;
+ time_before(jiffies, ifmsh->last_dsn_update)) {
+ dst_dsn = ++ifmsh->dsn;
+ ifmsh->last_dsn_update = jiffies;
}
} else {
rcu_read_lock();
- mpath = mesh_path_lookup(dst_addr, dev);
+ mpath = mesh_path_lookup(dst_addr, sdata);
if (mpath) {
if ((!(mpath->flags & MESH_PATH_DSN_VALID)) ||
DSN_LT(mpath->dsn, dst_dsn)) {
@@ -445,15 +444,15 @@ static void hwmp_preq_frame_process(struct net_device *dev,
if (reply) {
lifetime = PREQ_IE_LIFETIME(preq_elem);
- ttl = ifsta->mshcfg.dot11MeshTTL;
+ ttl = ifmsh->mshcfg.dot11MeshTTL;
if (ttl != 0)
mesh_path_sel_frame_tx(MPATH_PREP, 0, dst_addr,
cpu_to_le32(dst_dsn), 0, orig_addr,
cpu_to_le32(orig_dsn), mgmt->sa, 0, ttl,
cpu_to_le32(lifetime), cpu_to_le32(metric),
- 0, dev);
+ 0, sdata);
else
- ifsta->mshstats.dropped_frames_ttl++;
+ ifmsh->mshstats.dropped_frames_ttl++;
}
if (forward) {
@@ -463,7 +462,7 @@ static void hwmp_preq_frame_process(struct net_device *dev,
ttl = PREQ_IE_TTL(preq_elem);
lifetime = PREQ_IE_LIFETIME(preq_elem);
if (ttl <= 1) {
- ifsta->mshstats.dropped_frames_ttl++;
+ ifmsh->mshstats.dropped_frames_ttl++;
return;
}
--ttl;
@@ -472,20 +471,19 @@ static void hwmp_preq_frame_process(struct net_device *dev,
hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1;
mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr,
cpu_to_le32(orig_dsn), dst_flags, dst_addr,
- cpu_to_le32(dst_dsn), dev->broadcast,
+ cpu_to_le32(dst_dsn), sdata->dev->broadcast,
hopcount, ttl, cpu_to_le32(lifetime),
cpu_to_le32(metric), cpu_to_le32(preq_id),
- dev);
- ifsta->mshstats.fwded_frames++;
+ sdata);
+ ifmsh->mshstats.fwded_frames++;
}
}
-static void hwmp_prep_frame_process(struct net_device *dev,
+static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
u8 *prep_elem, u32 metric)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct mesh_path *mpath;
u8 *dst_addr, *orig_addr;
u8 ttl, hopcount, flags;
@@ -499,18 +497,18 @@ static void hwmp_prep_frame_process(struct net_device *dev,
* replies
*/
dst_addr = PREP_IE_DST_ADDR(prep_elem);
- if (memcmp(dst_addr, dev->dev_addr, ETH_ALEN) == 0)
+ if (memcmp(dst_addr, sdata->dev->dev_addr, ETH_ALEN) == 0)
/* destination, no forwarding required */
return;
ttl = PREP_IE_TTL(prep_elem);
if (ttl <= 1) {
- sdata->u.sta.mshstats.dropped_frames_ttl++;
+ sdata->u.mesh.mshstats.dropped_frames_ttl++;
return;
}
rcu_read_lock();
- mpath = mesh_path_lookup(dst_addr, dev);
+ mpath = mesh_path_lookup(dst_addr, sdata);
if (mpath)
spin_lock_bh(&mpath->state_lock);
else
@@ -519,7 +517,7 @@ static void hwmp_prep_frame_process(struct net_device *dev,
spin_unlock_bh(&mpath->state_lock);
goto fail;
}
- memcpy(next_hop, mpath->next_hop->addr, ETH_ALEN);
+ memcpy(next_hop, mpath->next_hop->sta.addr, ETH_ALEN);
spin_unlock_bh(&mpath->state_lock);
--ttl;
flags = PREP_IE_FLAGS(prep_elem);
@@ -531,20 +529,20 @@ static void hwmp_prep_frame_process(struct net_device *dev,
mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr,
cpu_to_le32(orig_dsn), 0, dst_addr,
- cpu_to_le32(dst_dsn), mpath->next_hop->addr, hopcount, ttl,
+ cpu_to_le32(dst_dsn), mpath->next_hop->sta.addr, hopcount, ttl,
cpu_to_le32(lifetime), cpu_to_le32(metric),
- 0, dev);
+ 0, sdata);
rcu_read_unlock();
- sdata->u.sta.mshstats.fwded_frames++;
+ sdata->u.mesh.mshstats.fwded_frames++;
return;
fail:
rcu_read_unlock();
- sdata->u.sta.mshstats.dropped_frames_no_route++;
+ sdata->u.mesh.mshstats.dropped_frames_no_route++;
return;
}
-static void hwmp_perr_frame_process(struct net_device *dev,
+static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, u8 *perr_elem)
{
struct mesh_path *mpath;
@@ -555,18 +553,18 @@ static void hwmp_perr_frame_process(struct net_device *dev,
dst_addr = PERR_IE_DST_ADDR(perr_elem);
dst_dsn = PERR_IE_DST_DSN(perr_elem);
rcu_read_lock();
- mpath = mesh_path_lookup(dst_addr, dev);
+ mpath = mesh_path_lookup(dst_addr, sdata);
if (mpath) {
spin_lock_bh(&mpath->state_lock);
if (mpath->flags & MESH_PATH_ACTIVE &&
- memcmp(ta, mpath->next_hop->addr, ETH_ALEN) == 0 &&
+ memcmp(ta, mpath->next_hop->sta.addr, ETH_ALEN) == 0 &&
(!(mpath->flags & MESH_PATH_DSN_VALID) ||
DSN_GT(dst_dsn, mpath->dsn))) {
mpath->flags &= ~MESH_PATH_ACTIVE;
mpath->dsn = dst_dsn;
spin_unlock_bh(&mpath->state_lock);
mesh_path_error_tx(dst_addr, cpu_to_le32(dst_dsn),
- dev->broadcast, dev);
+ sdata->dev->broadcast, sdata);
} else
spin_unlock_bh(&mpath->state_lock);
}
@@ -575,7 +573,7 @@ static void hwmp_perr_frame_process(struct net_device *dev,
-void mesh_rx_path_sel_frame(struct net_device *dev,
+void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
size_t len)
{
@@ -583,6 +581,10 @@ void mesh_rx_path_sel_frame(struct net_device *dev,
size_t baselen;
u32 last_hop_metric;
+ /* need action_code */
+ if (len < IEEE80211_MIN_ACTION_SIZE + 1)
+ return;
+
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);
@@ -592,25 +594,25 @@ void mesh_rx_path_sel_frame(struct net_device *dev,
if (!elems.preq || elems.preq_len != 37)
/* Right now we support just 1 destination and no AE */
return;
- last_hop_metric = hwmp_route_info_get(dev, mgmt, elems.preq);
+ last_hop_metric = hwmp_route_info_get(sdata, mgmt, elems.preq);
if (!last_hop_metric)
return;
- hwmp_preq_frame_process(dev, mgmt, elems.preq, last_hop_metric);
+ hwmp_preq_frame_process(sdata, mgmt, elems.preq, last_hop_metric);
break;
case MPATH_PREP:
if (!elems.prep || elems.prep_len != 31)
/* Right now we support no AE */
return;
- last_hop_metric = hwmp_route_info_get(dev, mgmt, elems.prep);
+ last_hop_metric = hwmp_route_info_get(sdata, mgmt, elems.prep);
if (!last_hop_metric)
return;
- hwmp_prep_frame_process(dev, mgmt, elems.prep, last_hop_metric);
+ hwmp_prep_frame_process(sdata, mgmt, elems.prep, last_hop_metric);
break;
case MPATH_PERR:
if (!elems.perr || elems.perr_len != 12)
/* Right now we support only one destination per PERR */
return;
- hwmp_perr_frame_process(dev, mgmt, elems.perr);
+ hwmp_perr_frame_process(sdata, mgmt, elems.perr);
default:
return;
}
@@ -628,9 +630,8 @@ void mesh_rx_path_sel_frame(struct net_device *dev,
*/
static void mesh_queue_preq(struct mesh_path *mpath, u8 flags)
{
- struct ieee80211_sub_if_data *sdata =
- IEEE80211_DEV_TO_SUB_IF(mpath->dev);
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_sub_if_data *sdata = mpath->sdata;
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_preq_queue *preq_node;
preq_node = kmalloc(sizeof(struct mesh_preq_queue), GFP_KERNEL);
@@ -639,9 +640,9 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags)
return;
}
- spin_lock(&ifsta->mesh_preq_queue_lock);
- if (ifsta->preq_queue_len == MAX_PREQ_QUEUE_LEN) {
- spin_unlock(&ifsta->mesh_preq_queue_lock);
+ spin_lock(&ifmsh->mesh_preq_queue_lock);
+ if (ifmsh->preq_queue_len == MAX_PREQ_QUEUE_LEN) {
+ spin_unlock(&ifmsh->mesh_preq_queue_lock);
kfree(preq_node);
if (printk_ratelimit())
printk(KERN_DEBUG "Mesh HWMP: PREQ node queue full\n");
@@ -651,55 +652,53 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags)
memcpy(preq_node->dst, mpath->dst, ETH_ALEN);
preq_node->flags = flags;
- list_add_tail(&preq_node->list, &ifsta->preq_queue.list);
- ++ifsta->preq_queue_len;
- spin_unlock(&ifsta->mesh_preq_queue_lock);
+ list_add_tail(&preq_node->list, &ifmsh->preq_queue.list);
+ ++ifmsh->preq_queue_len;
+ spin_unlock(&ifmsh->mesh_preq_queue_lock);
- if (time_after(jiffies, ifsta->last_preq + min_preq_int_jiff(sdata)))
- queue_work(sdata->local->hw.workqueue, &ifsta->work);
+ if (time_after(jiffies, ifmsh->last_preq + min_preq_int_jiff(sdata)))
+ queue_work(sdata->local->hw.workqueue, &ifmsh->work);
- else if (time_before(jiffies, ifsta->last_preq)) {
+ else if (time_before(jiffies, ifmsh->last_preq)) {
/* avoid long wait if did not send preqs for a long time
* and jiffies wrapped around
*/
- ifsta->last_preq = jiffies - min_preq_int_jiff(sdata) - 1;
- queue_work(sdata->local->hw.workqueue, &ifsta->work);
+ ifmsh->last_preq = jiffies - min_preq_int_jiff(sdata) - 1;
+ queue_work(sdata->local->hw.workqueue, &ifmsh->work);
} else
- mod_timer(&ifsta->mesh_path_timer, ifsta->last_preq +
+ mod_timer(&ifmsh->mesh_path_timer, ifmsh->last_preq +
min_preq_int_jiff(sdata));
}
/**
* mesh_path_start_discovery - launch a path discovery from the PREQ queue
*
- * @dev: local mesh interface
+ * @sdata: local mesh subif
*/
-void mesh_path_start_discovery(struct net_device *dev)
+void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata =
- IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_preq_queue *preq_node;
struct mesh_path *mpath;
u8 ttl, dst_flags;
u32 lifetime;
- spin_lock(&ifsta->mesh_preq_queue_lock);
- if (!ifsta->preq_queue_len ||
- time_before(jiffies, ifsta->last_preq +
+ spin_lock(&ifmsh->mesh_preq_queue_lock);
+ if (!ifmsh->preq_queue_len ||
+ time_before(jiffies, ifmsh->last_preq +
min_preq_int_jiff(sdata))) {
- spin_unlock(&ifsta->mesh_preq_queue_lock);
+ spin_unlock(&ifmsh->mesh_preq_queue_lock);
return;
}
- preq_node = list_first_entry(&ifsta->preq_queue.list,
+ preq_node = list_first_entry(&ifmsh->preq_queue.list,
struct mesh_preq_queue, list);
list_del(&preq_node->list);
- --ifsta->preq_queue_len;
- spin_unlock(&ifsta->mesh_preq_queue_lock);
+ --ifmsh->preq_queue_len;
+ spin_unlock(&ifmsh->mesh_preq_queue_lock);
rcu_read_lock();
- mpath = mesh_path_lookup(preq_node->dst, dev);
+ mpath = mesh_path_lookup(preq_node->dst, sdata);
if (!mpath)
goto enddiscovery;
@@ -721,18 +720,18 @@ void mesh_path_start_discovery(struct net_device *dev)
goto enddiscovery;
}
- ifsta->last_preq = jiffies;
+ ifmsh->last_preq = jiffies;
- if (time_after(jiffies, ifsta->last_dsn_update +
+ if (time_after(jiffies, ifmsh->last_dsn_update +
net_traversal_jiffies(sdata)) ||
- time_before(jiffies, ifsta->last_dsn_update)) {
- ++ifsta->dsn;
- sdata->u.sta.last_dsn_update = jiffies;
+ time_before(jiffies, ifmsh->last_dsn_update)) {
+ ++ifmsh->dsn;
+ sdata->u.mesh.last_dsn_update = jiffies;
}
lifetime = default_lifetime(sdata);
- ttl = sdata->u.sta.mshcfg.dot11MeshTTL;
+ ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
if (ttl == 0) {
- sdata->u.sta.mshstats.dropped_frames_ttl++;
+ sdata->u.mesh.mshstats.dropped_frames_ttl++;
spin_unlock_bh(&mpath->state_lock);
goto enddiscovery;
}
@@ -743,11 +742,11 @@ void mesh_path_start_discovery(struct net_device *dev)
dst_flags = MP_F_RF;
spin_unlock_bh(&mpath->state_lock);
- mesh_path_sel_frame_tx(MPATH_PREQ, 0, dev->dev_addr,
- cpu_to_le32(ifsta->dsn), dst_flags, mpath->dst,
- cpu_to_le32(mpath->dsn), dev->broadcast, 0,
+ mesh_path_sel_frame_tx(MPATH_PREQ, 0, sdata->dev->dev_addr,
+ cpu_to_le32(ifmsh->dsn), dst_flags, mpath->dst,
+ cpu_to_le32(mpath->dsn), sdata->dev->broadcast, 0,
ttl, cpu_to_le32(lifetime), 0,
- cpu_to_le32(ifsta->preq_id++), dev);
+ cpu_to_le32(ifmsh->preq_id++), sdata);
mod_timer(&mpath->timer, jiffies + mpath->discovery_timeout);
enddiscovery:
@@ -758,32 +757,33 @@ enddiscovery:
/**
* ieee80211s_lookup_nexthop - put the appropriate next hop on a mesh frame
*
- * @next_hop: output argument for next hop address
- * @skb: frame to be sent
- * @dev: network device the frame will be sent through
+ * @skb: 802.11 frame to be sent
+ * @sdata: network subif the frame will be sent through
+ * @fwd_frame: true if this frame was originally from a different host
*
* 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.
*/
-int mesh_nexthop_lookup(u8 *next_hop, struct sk_buff *skb,
- struct net_device *dev)
+int mesh_nexthop_lookup(struct sk_buff *skb,
+ struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct sk_buff *skb_to_free = NULL;
struct mesh_path *mpath;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ u8 *dst_addr = hdr->addr3;
int err = 0;
rcu_read_lock();
- mpath = mesh_path_lookup(skb->data, dev);
+ mpath = mesh_path_lookup(dst_addr, sdata);
if (!mpath) {
- mesh_path_add(skb->data, dev);
- mpath = mesh_path_lookup(skb->data, dev);
+ mesh_path_add(dst_addr, sdata);
+ mpath = mesh_path_lookup(dst_addr, sdata);
if (!mpath) {
dev_kfree_skb(skb);
- sdata->u.sta.mshstats.dropped_frames_no_route++;
+ sdata->u.mesh.mshstats.dropped_frames_no_route++;
err = -ENOSPC;
goto endlookup;
}
@@ -791,14 +791,15 @@ int mesh_nexthop_lookup(u8 *next_hop, struct sk_buff *skb,
if (mpath->flags & MESH_PATH_ACTIVE) {
if (time_after(jiffies, mpath->exp_time -
- msecs_to_jiffies(sdata->u.sta.mshcfg.path_refresh_time))
- && skb->pkt_type != PACKET_OTHERHOST
+ msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time))
+ && !memcmp(sdata->dev->dev_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(next_hop, mpath->next_hop->addr,
+ memcpy(hdr->addr1, mpath->next_hop->sta.addr,
ETH_ALEN);
} else {
if (!(mpath->flags & MESH_PATH_RESOLVING)) {
@@ -814,7 +815,7 @@ int mesh_nexthop_lookup(u8 *next_hop, struct sk_buff *skb,
skb_queue_tail(&mpath->frame_queue, skb);
if (skb_to_free)
- mesh_path_discard_frame(skb_to_free, dev);
+ mesh_path_discard_frame(skb_to_free, sdata);
err = -ENOENT;
}
@@ -834,7 +835,7 @@ void mesh_path_timer(unsigned long data)
if (!mpath)
goto endmpathtimer;
spin_lock_bh(&mpath->state_lock);
- sdata = IEEE80211_DEV_TO_SUB_IF(mpath->dev);
+ sdata = mpath->sdata;
if (mpath->flags & MESH_PATH_RESOLVED ||
(!(mpath->flags & MESH_PATH_RESOLVING)))
mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED);
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index 99c2d360888..3c72557df45 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -9,7 +9,6 @@
#include <linux/etherdevice.h>
#include <linux/list.h>
-#include <linux/netdevice.h>
#include <linux/random.h>
#include <linux/spinlock.h>
#include <linux/string.h>
@@ -37,6 +36,7 @@ struct mpath_node {
};
static struct mesh_table *mesh_paths;
+static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */
/* 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
@@ -62,13 +62,13 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta)
/**
* mesh_path_lookup - look up a path in the mesh path table
* @dst: hardware address (ETH_ALEN length) of destination
- * @dev: local interface
+ * @sdata: local subif
*
* 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(u8 *dst, struct net_device *dev)
+struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata)
{
struct mesh_path *mpath;
struct hlist_node *n;
@@ -78,10 +78,10 @@ struct mesh_path *mesh_path_lookup(u8 *dst, struct net_device *dev)
tbl = rcu_dereference(mesh_paths);
- bucket = &tbl->hash_buckets[mesh_table_hash(dst, dev, tbl)];
+ bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)];
hlist_for_each_entry_rcu(node, n, bucket, list) {
mpath = node->mpath;
- if (mpath->dev == dev &&
+ if (mpath->sdata == sdata &&
memcmp(dst, mpath->dst, ETH_ALEN) == 0) {
if (MPATH_EXPIRED(mpath)) {
spin_lock_bh(&mpath->state_lock);
@@ -95,16 +95,44 @@ struct mesh_path *mesh_path_lookup(u8 *dst, struct net_device *dev)
return NULL;
}
+struct mesh_path *mpp_path_lookup(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) {
+ 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;
+ }
+ }
+ return NULL;
+}
+
+
/**
* mesh_path_lookup_by_idx - look up a path in the mesh path table by its index
* @idx: index
- * @dev: local interface, or NULL for all entries
+ * @sdata: local subif, or NULL for all entries
*
* 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_by_idx(int idx, struct net_device *dev)
+struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata)
{
struct mpath_node *node;
struct hlist_node *p;
@@ -112,7 +140,7 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct net_device *dev)
int j = 0;
for_each_mesh_entry(mesh_paths, p, node, i) {
- if (dev && node->mpath->dev != dev)
+ if (sdata && node->mpath->sdata != sdata)
continue;
if (j++ == idx) {
if (MPATH_EXPIRED(node->mpath)) {
@@ -131,15 +159,14 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct net_device *dev)
/**
* mesh_path_add - allocate and add a new path to the mesh path table
* @addr: destination address of the path (ETH_ALEN length)
- * @dev: local interface
+ * @sdata: local subif
*
* Returns: 0 on sucess
*
* State: the initial state of the new path is set to 0
*/
-int mesh_path_add(u8 *dst, struct net_device *dev)
+int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct mesh_path *mpath, *new_mpath;
struct mpath_node *node, *new_node;
struct hlist_head *bucket;
@@ -148,33 +175,28 @@ int mesh_path_add(u8 *dst, struct net_device *dev)
int err = 0;
u32 hash_idx;
- if (memcmp(dst, dev->dev_addr, ETH_ALEN) == 0)
+ if (memcmp(dst, sdata->dev->dev_addr, ETH_ALEN) == 0)
/* never add ourselves as neighbours */
return -ENOTSUPP;
if (is_multicast_ether_addr(dst))
return -ENOTSUPP;
- if (atomic_add_unless(&sdata->u.sta.mpaths, 1, MESH_MAX_MPATHS) == 0)
+ if (atomic_add_unless(&sdata->u.mesh.mpaths, 1, MESH_MAX_MPATHS) == 0)
return -ENOSPC;
+ err = -ENOMEM;
new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL);
- if (!new_mpath) {
- atomic_dec(&sdata->u.sta.mpaths);
- err = -ENOMEM;
- goto endadd2;
- }
+ if (!new_mpath)
+ goto err_path_alloc;
+
new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
- if (!new_node) {
- kfree(new_mpath);
- atomic_dec(&sdata->u.sta.mpaths);
- err = -ENOMEM;
- goto endadd2;
- }
+ if (!new_node)
+ goto err_node_alloc;
read_lock(&pathtbl_resize_lock);
memcpy(new_mpath->dst, dst, ETH_ALEN);
- new_mpath->dev = dev;
+ new_mpath->sdata = sdata;
new_mpath->flags = 0;
skb_queue_head_init(&new_mpath->frame_queue);
new_node->mpath = new_mpath;
@@ -184,21 +206,16 @@ int mesh_path_add(u8 *dst, struct net_device *dev)
spin_lock_init(&new_mpath->state_lock);
init_timer(&new_mpath->timer);
- hash_idx = mesh_table_hash(dst, dev, mesh_paths);
+ 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->dev == dev && memcmp(dst, mpath->dst, ETH_ALEN)
- == 0) {
- err = -EEXIST;
- atomic_dec(&sdata->u.sta.mpaths);
- kfree(new_node);
- kfree(new_mpath);
- goto endadd;
- }
+ if (mpath->sdata == sdata && memcmp(dst, mpath->dst, ETH_ALEN) == 0)
+ goto err_exists;
}
hlist_add_head_rcu(&new_node->list, bucket);
@@ -206,10 +223,9 @@ int mesh_path_add(u8 *dst, struct net_device *dev)
mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1))
grow = 1;
-endadd:
spin_unlock(&mesh_paths->hashwlock[hash_idx]);
read_unlock(&pathtbl_resize_lock);
- if (!err && grow) {
+ if (grow) {
struct mesh_table *oldtbl, *newtbl;
write_lock(&pathtbl_resize_lock);
@@ -217,7 +233,7 @@ endadd:
newtbl = mesh_table_grow(mesh_paths);
if (!newtbl) {
write_unlock(&pathtbl_resize_lock);
- return -ENOMEM;
+ return 0;
}
rcu_assign_pointer(mesh_paths, newtbl);
write_unlock(&pathtbl_resize_lock);
@@ -225,7 +241,101 @@ endadd:
synchronize_rcu();
mesh_table_free(oldtbl, false);
}
-endadd2:
+ return 0;
+
+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;
+}
+
+
+int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
+{
+ 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->dev->dev_addr, ETH_ALEN) == 0)
+ /* never add ourselves as neighbours */
+ return -ENOTSUPP;
+
+ if (is_multicast_ether_addr(dst))
+ return -ENOTSUPP;
+
+ err = -ENOMEM;
+ new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL);
+ if (!new_mpath)
+ goto err_path_alloc;
+
+ new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
+ if (!new_node)
+ goto err_node_alloc;
+
+ read_lock(&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;
+ 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];
+
+ spin_lock(&mpp_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(&mpp_paths->entries) >=
+ mpp_paths->mean_chain_len * (mpp_paths->hash_mask + 1))
+ grow = 1;
+
+ spin_unlock(&mpp_paths->hashwlock[hash_idx]);
+ read_unlock(&pathtbl_resize_lock);
+ if (grow) {
+ 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 0;
+ }
+ rcu_assign_pointer(mpp_paths, newtbl);
+ write_unlock(&pathtbl_resize_lock);
+
+ synchronize_rcu();
+ mesh_table_free(oldtbl, false);
+ }
+ return 0;
+
+err_exists:
+ spin_unlock(&mpp_paths->hashwlock[hash_idx]);
+ read_unlock(&pathtbl_resize_lock);
+ kfree(new_node);
+err_node_alloc:
+ kfree(new_mpath);
+err_path_alloc:
return err;
}
@@ -243,7 +353,7 @@ void mesh_plink_broken(struct sta_info *sta)
struct mesh_path *mpath;
struct mpath_node *node;
struct hlist_node *p;
- struct net_device *dev = sta->sdata->dev;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
int i;
rcu_read_lock();
@@ -258,13 +368,12 @@ void mesh_plink_broken(struct sta_info *sta)
spin_unlock_bh(&mpath->state_lock);
mesh_path_error_tx(mpath->dst,
cpu_to_le32(mpath->dsn),
- dev->broadcast, dev);
+ sdata->dev->broadcast, sdata);
} else
spin_unlock_bh(&mpath->state_lock);
}
rcu_read_unlock();
}
-EXPORT_SYMBOL(mesh_plink_broken);
/**
* mesh_path_flush_by_nexthop - Deletes mesh paths if their next hop matches
@@ -287,11 +396,11 @@ void mesh_path_flush_by_nexthop(struct sta_info *sta)
for_each_mesh_entry(mesh_paths, p, node, i) {
mpath = node->mpath;
if (mpath->next_hop == sta)
- mesh_path_del(mpath->dst, mpath->dev);
+ mesh_path_del(mpath->dst, mpath->sdata);
}
}
-void mesh_path_flush(struct net_device *dev)
+void mesh_path_flush(struct ieee80211_sub_if_data *sdata)
{
struct mesh_path *mpath;
struct mpath_node *node;
@@ -300,19 +409,18 @@ void mesh_path_flush(struct net_device *dev)
for_each_mesh_entry(mesh_paths, p, node, i) {
mpath = node->mpath;
- if (mpath->dev == dev)
- mesh_path_del(mpath->dst, mpath->dev);
+ if (mpath->sdata == sdata)
+ mesh_path_del(mpath->dst, mpath->sdata);
}
}
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 =
- IEEE80211_DEV_TO_SUB_IF(node->mpath->dev);
+ struct ieee80211_sub_if_data *sdata = node->mpath->sdata;
del_timer_sync(&node->mpath->timer);
- atomic_dec(&sdata->u.sta.mpaths);
+ atomic_dec(&sdata->u.mesh.mpaths);
kfree(node->mpath);
kfree(node);
}
@@ -321,11 +429,11 @@ static void mesh_path_node_reclaim(struct rcu_head *rp)
* mesh_path_del - delete a mesh path from the table
*
* @addr: dst address (ETH_ALEN length)
- * @dev: local interface
+ * @sdata: local subif
*
* Returns: 0 if succesful
*/
-int mesh_path_del(u8 *addr, struct net_device *dev)
+int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)
{
struct mesh_path *mpath;
struct mpath_node *node;
@@ -335,13 +443,13 @@ int mesh_path_del(u8 *addr, struct net_device *dev)
int err = 0;
read_lock(&pathtbl_resize_lock);
- hash_idx = mesh_table_hash(addr, dev, mesh_paths);
+ hash_idx = mesh_table_hash(addr, sdata, mesh_paths);
bucket = &mesh_paths->hash_buckets[hash_idx];
spin_lock(&mesh_paths->hashwlock[hash_idx]);
hlist_for_each_entry(node, n, bucket, list) {
mpath = node->mpath;
- if (mpath->dev == dev &&
+ if (mpath->sdata == sdata &&
memcmp(addr, mpath->dst, ETH_ALEN) == 0) {
spin_lock_bh(&mpath->state_lock);
mpath->flags |= MESH_PATH_RESOLVING;
@@ -381,36 +489,33 @@ void mesh_path_tx_pending(struct mesh_path *mpath)
* mesh_path_discard_frame - discard a frame whose path could not be resolved
*
* @skb: frame to discard
- * @dev: network device the frame was to be sent through
+ * @sdata: network subif the frame was to be sent through
*
* If the frame was beign forwarded from another MP, a PERR frame will be sent
* to the precursor.
*
* Locking: the function must me called within a rcu_read_lock region
*/
-void mesh_path_discard_frame(struct sk_buff *skb, struct net_device *dev)
+void mesh_path_discard_frame(struct sk_buff *skb,
+ struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct mesh_path *mpath;
u32 dsn = 0;
- if (skb->pkt_type == PACKET_OTHERHOST) {
- struct ieee80211s_hdr *prev_meshhdr;
- int mshhdrlen;
+ if (memcmp(hdr->addr4, sdata->dev->dev_addr, ETH_ALEN) != 0) {
u8 *ra, *da;
- prev_meshhdr = ((struct ieee80211s_hdr *)skb->cb);
- mshhdrlen = ieee80211_get_mesh_hdrlen(prev_meshhdr);
- da = skb->data;
- ra = MESH_PREQ(skb);
- mpath = mesh_path_lookup(da, dev);
+ da = hdr->addr3;
+ ra = hdr->addr2;
+ mpath = mesh_path_lookup(da, sdata);
if (mpath)
dsn = ++mpath->dsn;
- mesh_path_error_tx(skb->data, cpu_to_le32(dsn), ra, dev);
+ mesh_path_error_tx(skb->data, cpu_to_le32(dsn), ra, sdata);
}
kfree_skb(skb);
- sdata->u.sta.mshstats.dropped_frames_no_route++;
+ sdata->u.mesh.mshstats.dropped_frames_no_route++;
}
/**
@@ -422,14 +527,11 @@ void mesh_path_discard_frame(struct sk_buff *skb, struct net_device *dev)
*/
void mesh_path_flush_pending(struct mesh_path *mpath)
{
- struct ieee80211_sub_if_data *sdata;
struct sk_buff *skb;
- sdata = IEEE80211_DEV_TO_SUB_IF(mpath->dev);
-
while ((skb = skb_dequeue(&mpath->frame_queue)) &&
(mpath->flags & MESH_PATH_ACTIVE))
- mesh_path_discard_frame(skb, mpath->dev);
+ mesh_path_discard_frame(skb, mpath->sdata);
}
/**
@@ -460,39 +562,52 @@ 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);
- synchronize_rcu();
if (free_leafs)
kfree(mpath);
kfree(node);
}
-static void mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl)
+static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl)
{
struct mesh_path *mpath;
struct mpath_node *node, *new_node;
u32 hash_idx;
+ new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC);
+ if (new_node == NULL)
+ return -ENOMEM;
+
node = hlist_entry(p, struct mpath_node, list);
mpath = node->mpath;
- new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
new_node->mpath = mpath;
- hash_idx = mesh_table_hash(mpath->dst, mpath->dev, newtbl);
+ hash_idx = mesh_table_hash(mpath->dst, mpath->sdata, newtbl);
hlist_add_head(&new_node->list,
&newtbl->hash_buckets[hash_idx]);
+ return 0;
}
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;
- if (!mesh_paths)
+
+ mpp_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER);
+ if (!mpp_paths) {
+ mesh_table_free(mesh_paths, true);
return -ENOMEM;
+ }
+ mpp_paths->free_node = &mesh_path_node_free;
+ mpp_paths->copy_node = &mesh_path_node_copy;
+ mpp_paths->mean_chain_len = MEAN_CHAIN_LEN;
+
return 0;
}
-void mesh_path_expire(struct net_device *dev)
+void mesh_path_expire(struct ieee80211_sub_if_data *sdata)
{
struct mesh_path *mpath;
struct mpath_node *node;
@@ -501,7 +616,7 @@ void mesh_path_expire(struct net_device *dev)
read_lock(&pathtbl_resize_lock);
for_each_mesh_entry(mesh_paths, p, node, i) {
- if (node->mpath->dev != dev)
+ if (node->mpath->sdata != sdata)
continue;
mpath = node->mpath;
spin_lock_bh(&mpath->state_lock);
@@ -510,7 +625,7 @@ void mesh_path_expire(struct net_device *dev)
time_after(jiffies,
mpath->exp_time + MESH_PATH_EXPIRE)) {
spin_unlock_bh(&mpath->state_lock);
- mesh_path_del(mpath->dst, mpath->dev);
+ mesh_path_del(mpath->dst, mpath->sdata);
} else
spin_unlock_bh(&mpath->state_lock);
}
@@ -520,4 +635,5 @@ void mesh_path_expire(struct net_device *dev)
void mesh_pathtbl_unregister(void)
{
mesh_table_free(mesh_paths, true);
+ mesh_table_free(mpp_paths, true);
}
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 37f0c2b94ae..faac101c0f8 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -36,11 +36,11 @@
#define MESH_SECURITY_AUTHENTICATION_IMPOSSIBLE 9
#define MESH_SECURITY_FAILED_VERIFICATION 10
-#define dot11MeshMaxRetries(s) (s->u.sta.mshcfg.dot11MeshMaxRetries)
-#define dot11MeshRetryTimeout(s) (s->u.sta.mshcfg.dot11MeshRetryTimeout)
-#define dot11MeshConfirmTimeout(s) (s->u.sta.mshcfg.dot11MeshConfirmTimeout)
-#define dot11MeshHoldingTimeout(s) (s->u.sta.mshcfg.dot11MeshHoldingTimeout)
-#define dot11MeshMaxPeerLinks(s) (s->u.sta.mshcfg.dot11MeshMaxPeerLinks)
+#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,
@@ -63,14 +63,14 @@ enum plink_event {
static inline
void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
{
- atomic_inc(&sdata->u.sta.mshstats.estab_plinks);
+ atomic_inc(&sdata->u.mesh.mshstats.estab_plinks);
mesh_accept_plinks_update(sdata);
}
static inline
void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
{
- atomic_dec(&sdata->u.sta.mshstats.estab_plinks);
+ atomic_dec(&sdata->u.mesh.mshstats.estab_plinks);
mesh_accept_plinks_update(sdata);
}
@@ -79,7 +79,7 @@ void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
*
* @sta: mes peer link to restart
*
- * Locking: this function must be called holding sta->plink_lock
+ * Locking: this function must be called holding sta->lock
*/
static inline void mesh_plink_fsm_restart(struct sta_info *sta)
{
@@ -105,8 +105,8 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
if (!sta)
return NULL;
- sta->flags |= WLAN_STA_AUTHORIZED;
- sta->supp_rates[local->hw.conf.channel->band] = rates;
+ sta->flags = WLAN_STA_AUTHORIZED;
+ sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
return sta;
}
@@ -118,7 +118,7 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
*
* All mesh paths with this peer as next hop will be flushed
*
- * Locking: the caller must hold sta->plink_lock
+ * Locking: the caller must hold sta->lock
*/
static void __mesh_plink_deactivate(struct sta_info *sta)
{
@@ -139,15 +139,15 @@ static void __mesh_plink_deactivate(struct sta_info *sta)
*/
void mesh_plink_deactivate(struct sta_info *sta)
{
- spin_lock_bh(&sta->plink_lock);
+ spin_lock_bh(&sta->lock);
__mesh_plink_deactivate(sta);
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
}
-static int mesh_plink_frame_tx(struct net_device *dev,
+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) {
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = sdata->local;
struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
struct ieee80211_mgmt *mgmt;
bool include_plid = false;
@@ -163,10 +163,10 @@ static int mesh_plink_frame_tx(struct net_device *dev,
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));
- mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
- IEEE80211_STYPE_ACTION);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION);
memcpy(mgmt->da, da, ETH_ALEN);
- memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
/* BSSID is left zeroed, wildcard value */
mgmt->u.action.category = PLINK_CATEGORY;
mgmt->u.action.u.plink_action.action_code = action;
@@ -180,7 +180,7 @@ static int mesh_plink_frame_tx(struct net_device *dev,
/* two-byte status code followed by two-byte AID */
memset(pos, 0, 4);
}
- mesh_mgmt_ies_add(skb, dev);
+ mesh_mgmt_ies_add(skb, sdata);
}
/* Add Peer Link Management element */
@@ -217,15 +217,14 @@ static int mesh_plink_frame_tx(struct net_device *dev,
memcpy(pos, &reason, 2);
}
- ieee80211_sta_tx(dev, skb, 0);
+ ieee80211_tx_skb(sdata, skb, 0);
return 0;
}
-void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev,
+void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct ieee80211_sub_if_data *sdata,
bool peer_accepting_plinks)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
rcu_read_lock();
@@ -244,10 +243,10 @@ void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev,
}
sta->last_rx = jiffies;
- sta->supp_rates[local->hw.conf.channel->band] = rates;
+ sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
if (peer_accepting_plinks && sta->plink_state == PLINK_LISTEN &&
- sdata->u.sta.accepting_plinks &&
- sdata->u.sta.mshcfg.auto_open_plinks)
+ sdata->u.mesh.accepting_plinks &&
+ sdata->u.mesh.mshcfg.auto_open_plinks)
mesh_plink_open(sta);
rcu_read_unlock();
@@ -257,7 +256,6 @@ static void mesh_plink_timer(unsigned long data)
{
struct sta_info *sta;
__le16 llid, plid, reason;
- struct net_device *dev = NULL;
struct ieee80211_sub_if_data *sdata;
#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
DECLARE_MAC_BUF(mac);
@@ -270,19 +268,18 @@ static void mesh_plink_timer(unsigned long data)
*/
sta = (struct sta_info *) data;
- spin_lock_bh(&sta->plink_lock);
+ spin_lock_bh(&sta->lock);
if (sta->ignore_plink_timer) {
sta->ignore_plink_timer = false;
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
return;
}
mpl_dbg("Mesh plink timer for %s fired on state %d\n",
- print_mac(mac, sta->addr), sta->plink_state);
+ print_mac(mac, sta->sta.addr), sta->plink_state);
reason = 0;
llid = sta->llid;
plid = sta->plid;
sdata = sta->sdata;
- dev = sdata->dev;
switch (sta->plink_state) {
case PLINK_OPN_RCVD:
@@ -291,15 +288,15 @@ static void mesh_plink_timer(unsigned long data)
if (sta->plink_retries < dot11MeshMaxRetries(sdata)) {
u32 rand;
mpl_dbg("Mesh plink for %s (retry, timeout): %d %d\n",
- print_mac(mac, sta->addr),
+ print_mac(mac, sta->sta.addr),
sta->plink_retries, sta->plink_timeout);
get_random_bytes(&rand, sizeof(u32));
sta->plink_timeout = sta->plink_timeout +
rand % sta->plink_timeout;
++sta->plink_retries;
mod_plink_timer(sta, sta->plink_timeout);
- spin_unlock_bh(&sta->plink_lock);
- mesh_plink_frame_tx(dev, PLINK_OPEN, sta->addr, llid,
+ spin_unlock_bh(&sta->lock);
+ mesh_plink_frame_tx(sdata, PLINK_OPEN, sta->sta.addr, llid,
0, 0);
break;
}
@@ -311,18 +308,18 @@ static void mesh_plink_timer(unsigned long data)
reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT);
sta->plink_state = PLINK_HOLDING;
mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
- spin_unlock_bh(&sta->plink_lock);
- mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, plid,
+ spin_unlock_bh(&sta->lock);
+ mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, plid,
reason);
break;
case PLINK_HOLDING:
/* holding timer */
del_timer(&sta->plink_timer);
mesh_plink_fsm_restart(sta);
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
break;
default:
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
break;
}
}
@@ -344,21 +341,21 @@ int mesh_plink_open(struct sta_info *sta)
DECLARE_MAC_BUF(mac);
#endif
- spin_lock_bh(&sta->plink_lock);
+ spin_lock_bh(&sta->lock);
get_random_bytes(&llid, 2);
sta->llid = llid;
if (sta->plink_state != PLINK_LISTEN) {
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
return -EBUSY;
}
sta->plink_state = PLINK_OPN_SNT;
mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
mpl_dbg("Mesh plink: starting establishment with %s\n",
- print_mac(mac, sta->addr));
+ print_mac(mac, sta->sta.addr));
- return mesh_plink_frame_tx(sdata->dev, PLINK_OPEN,
- sta->addr, llid, 0, 0);
+ return mesh_plink_frame_tx(sdata, PLINK_OPEN,
+ sta->sta.addr, llid, 0, 0);
}
void mesh_plink_block(struct sta_info *sta)
@@ -367,10 +364,10 @@ void mesh_plink_block(struct sta_info *sta)
DECLARE_MAC_BUF(mac);
#endif
- spin_lock_bh(&sta->plink_lock);
+ spin_lock_bh(&sta->lock);
__mesh_plink_deactivate(sta);
sta->plink_state = PLINK_BLOCKED;
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
}
int mesh_plink_close(struct sta_info *sta)
@@ -382,15 +379,15 @@ int mesh_plink_close(struct sta_info *sta)
#endif
mpl_dbg("Mesh plink: closing link with %s\n",
- print_mac(mac, sta->addr));
- spin_lock_bh(&sta->plink_lock);
+ print_mac(mac, sta->sta.addr));
+ spin_lock_bh(&sta->lock);
sta->reason = cpu_to_le16(MESH_LINK_CANCELLED);
reason = sta->reason;
if (sta->plink_state == PLINK_LISTEN ||
sta->plink_state == PLINK_BLOCKED) {
mesh_plink_fsm_restart(sta);
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
return 0;
} else if (sta->plink_state == PLINK_ESTAB) {
__mesh_plink_deactivate(sta);
@@ -402,16 +399,15 @@ int mesh_plink_close(struct sta_info *sta)
sta->plink_state = PLINK_HOLDING;
llid = sta->llid;
plid = sta->plid;
- spin_unlock_bh(&sta->plink_lock);
- mesh_plink_frame_tx(sta->sdata->dev, PLINK_CLOSE, sta->addr, llid,
+ spin_unlock_bh(&sta->lock);
+ mesh_plink_frame_tx(sta->sdata, PLINK_CLOSE, sta->sta.addr, llid,
plid, reason);
return 0;
}
-void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
+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 ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
struct ieee802_11_elems elems;
struct sta_info *sta;
@@ -425,6 +421,10 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
DECLARE_MAC_BUF(mac);
#endif
+ /* need action_code, aux */
+ if (len < IEEE80211_MIN_ACTION_SIZE + 3)
+ return;
+
if (is_multicast_ether_addr(mgmt->da)) {
mpl_dbg("Mesh plink: ignore frame from multicast address");
return;
@@ -478,7 +478,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
/* Now we will figure out the appropriate event... */
event = PLINK_UNDEFINED;
- if (ftype != PLINK_CLOSE && (!mesh_matches_local(&elems, dev))) {
+ if (ftype != PLINK_CLOSE && (!mesh_matches_local(&elems, sdata))) {
switch (ftype) {
case PLINK_OPEN:
event = OPN_RJCT;
@@ -490,7 +490,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
/* avoid warning */
break;
}
- spin_lock_bh(&sta->plink_lock);
+ spin_lock_bh(&sta->lock);
} else if (!sta) {
/* ftype == PLINK_OPEN */
u64 rates;
@@ -512,9 +512,9 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
return;
}
event = OPN_ACPT;
- spin_lock_bh(&sta->plink_lock);
+ spin_lock_bh(&sta->lock);
} else {
- spin_lock_bh(&sta->plink_lock);
+ spin_lock_bh(&sta->lock);
switch (ftype) {
case PLINK_OPEN:
if (!mesh_plink_free_count(sdata) ||
@@ -551,7 +551,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
break;
default:
mpl_dbg("Mesh plink: unknown frame subtype\n");
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
rcu_read_unlock();
return;
}
@@ -568,7 +568,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
switch (event) {
case CLS_ACPT:
mesh_plink_fsm_restart(sta);
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
break;
case OPN_ACPT:
sta->plink_state = PLINK_OPN_RCVD;
@@ -576,14 +576,14 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
get_random_bytes(&llid, 2);
sta->llid = llid;
mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
- spin_unlock_bh(&sta->plink_lock);
- mesh_plink_frame_tx(dev, PLINK_OPEN, sta->addr, llid,
+ spin_unlock_bh(&sta->lock);
+ mesh_plink_frame_tx(sdata, PLINK_OPEN, sta->sta.addr, llid,
0, 0);
- mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr,
+ mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr,
llid, plid, 0);
break;
default:
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
break;
}
break;
@@ -603,8 +603,8 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
sta->ignore_plink_timer = true;
llid = sta->llid;
- spin_unlock_bh(&sta->plink_lock);
- mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
+ spin_unlock_bh(&sta->lock);
+ mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid,
plid, reason);
break;
case OPN_ACPT:
@@ -612,8 +612,8 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
sta->plink_state = PLINK_OPN_RCVD;
sta->plid = plid;
llid = sta->llid;
- spin_unlock_bh(&sta->plink_lock);
- mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid,
+ spin_unlock_bh(&sta->lock);
+ mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid,
plid, 0);
break;
case CNF_ACPT:
@@ -622,10 +622,10 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
dot11MeshConfirmTimeout(sdata)))
sta->ignore_plink_timer = true;
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
break;
default:
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
break;
}
break;
@@ -645,26 +645,26 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
sta->ignore_plink_timer = true;
llid = sta->llid;
- spin_unlock_bh(&sta->plink_lock);
- mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
+ spin_unlock_bh(&sta->lock);
+ mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid,
plid, reason);
break;
case OPN_ACPT:
llid = sta->llid;
- spin_unlock_bh(&sta->plink_lock);
- mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid,
+ spin_unlock_bh(&sta->lock);
+ mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid,
plid, 0);
break;
case CNF_ACPT:
del_timer(&sta->plink_timer);
sta->plink_state = PLINK_ESTAB;
mesh_plink_inc_estab_count(sdata);
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
mpl_dbg("Mesh plink with %s ESTABLISHED\n",
- print_mac(mac, sta->addr));
+ print_mac(mac, sta->sta.addr));
break;
default:
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
break;
}
break;
@@ -684,22 +684,22 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
sta->ignore_plink_timer = true;
llid = sta->llid;
- spin_unlock_bh(&sta->plink_lock);
- mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
+ spin_unlock_bh(&sta->lock);
+ mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid,
plid, reason);
break;
case OPN_ACPT:
del_timer(&sta->plink_timer);
sta->plink_state = PLINK_ESTAB;
mesh_plink_inc_estab_count(sdata);
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
mpl_dbg("Mesh plink with %s ESTABLISHED\n",
- print_mac(mac, sta->addr));
- mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid,
+ print_mac(mac, sta->sta.addr));
+ mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid,
plid, 0);
break;
default:
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
break;
}
break;
@@ -713,18 +713,18 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
sta->plink_state = PLINK_HOLDING;
llid = sta->llid;
mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
- spin_unlock_bh(&sta->plink_lock);
- mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
+ spin_unlock_bh(&sta->lock);
+ mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid,
plid, reason);
break;
case OPN_ACPT:
llid = sta->llid;
- spin_unlock_bh(&sta->plink_lock);
- mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid,
+ spin_unlock_bh(&sta->lock);
+ mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid,
plid, 0);
break;
default:
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
break;
}
break;
@@ -734,7 +734,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
if (del_timer(&sta->plink_timer))
sta->ignore_plink_timer = 1;
mesh_plink_fsm_restart(sta);
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
break;
case OPN_ACPT:
case CNF_ACPT:
@@ -742,19 +742,19 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
case CNF_RJCT:
llid = sta->llid;
reason = sta->reason;
- spin_unlock_bh(&sta->plink_lock);
- mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
- plid, reason);
+ spin_unlock_bh(&sta->lock);
+ mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr,
+ llid, plid, reason);
break;
default:
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
}
break;
default:
/* should not get here, PLINK_BLOCKED is dealt with at the
* beggining of the function
*/
- spin_unlock_bh(&sta->plink_lock);
+ spin_unlock_bh(&sta->lock);
break;
}
diff --git a/net/mac80211/michael.c b/net/mac80211/michael.c
index 0f844f7895f..408649bd470 100644
--- a/net/mac80211/michael.c
+++ b/net/mac80211/michael.c
@@ -6,85 +6,68 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-
#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/ieee80211.h>
+#include <asm/unaligned.h>
#include "michael.h"
-static inline u32 rotr(u32 val, int bits)
-{
- return (val >> bits) | (val << (32 - bits));
-}
-
-
-static inline u32 rotl(u32 val, int bits)
-{
- return (val << bits) | (val >> (32 - bits));
-}
-
-
-static inline u32 xswap(u32 val)
-{
- return ((val & 0xff00ff00) >> 8) | ((val & 0x00ff00ff) << 8);
-}
-
-
-#define michael_block(l, r) \
-do { \
- r ^= rotl(l, 17); \
- l += r; \
- r ^= xswap(l); \
- l += r; \
- r ^= rotl(l, 3); \
- l += r; \
- r ^= rotr(l, 2); \
- l += r; \
-} while (0)
-
-
-static inline u32 michael_get32(u8 *data)
+static void michael_block(struct michael_mic_ctx *mctx, u32 val)
{
- return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+ mctx->l ^= val;
+ mctx->r ^= rol32(mctx->l, 17);
+ mctx->l += mctx->r;
+ mctx->r ^= ((mctx->l & 0xff00ff00) >> 8) |
+ ((mctx->l & 0x00ff00ff) << 8);
+ mctx->l += mctx->r;
+ mctx->r ^= rol32(mctx->l, 3);
+ mctx->l += mctx->r;
+ mctx->r ^= ror32(mctx->l, 2);
+ mctx->l += mctx->r;
}
-
-static inline void michael_put32(u32 val, u8 *data)
+static void michael_mic_hdr(struct michael_mic_ctx *mctx, const u8 *key,
+ struct ieee80211_hdr *hdr)
{
- data[0] = val & 0xff;
- data[1] = (val >> 8) & 0xff;
- data[2] = (val >> 16) & 0xff;
- data[3] = (val >> 24) & 0xff;
+ u8 *da, *sa, tid;
+
+ da = ieee80211_get_DA(hdr);
+ sa = ieee80211_get_SA(hdr);
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+ else
+ tid = 0;
+
+ mctx->l = get_unaligned_le32(key);
+ mctx->r = get_unaligned_le32(key + 4);
+
+ /*
+ * A pseudo header (DA, SA, Priority, 0, 0, 0) is used in Michael MIC
+ * calculation, but it is _not_ transmitted
+ */
+ michael_block(mctx, get_unaligned_le32(da));
+ michael_block(mctx, get_unaligned_le16(&da[4]) |
+ (get_unaligned_le16(sa) << 16));
+ michael_block(mctx, get_unaligned_le32(&sa[2]));
+ michael_block(mctx, tid);
}
-
-void michael_mic(u8 *key, u8 *da, u8 *sa, u8 priority,
- u8 *data, size_t data_len, u8 *mic)
+void michael_mic(const u8 *key, struct ieee80211_hdr *hdr,
+ const u8 *data, size_t data_len, u8 *mic)
{
- u32 l, r, val;
+ u32 val;
size_t block, blocks, left;
+ struct michael_mic_ctx mctx;
- l = michael_get32(key);
- r = michael_get32(key + 4);
-
- /* A pseudo header (DA, SA, Priority, 0, 0, 0) is used in Michael MIC
- * calculation, but it is _not_ transmitted */
- l ^= michael_get32(da);
- michael_block(l, r);
- l ^= da[4] | (da[5] << 8) | (sa[0] << 16) | (sa[1] << 24);
- michael_block(l, r);
- l ^= michael_get32(&sa[2]);
- michael_block(l, r);
- l ^= priority;
- michael_block(l, r);
+ michael_mic_hdr(&mctx, key, hdr);
/* Real data */
blocks = data_len / 4;
left = data_len % 4;
- for (block = 0; block < blocks; block++) {
- l ^= michael_get32(&data[block * 4]);
- michael_block(l, r);
- }
+ for (block = 0; block < blocks; block++)
+ michael_block(&mctx, get_unaligned_le32(&data[block * 4]));
/* Partial block of 0..3 bytes and padding: 0x5a + 4..7 zeros to make
* total length a multiple of 4. */
@@ -94,11 +77,10 @@ void michael_mic(u8 *key, u8 *da, u8 *sa, u8 priority,
left--;
val |= data[blocks * 4 + left];
}
- l ^= val;
- michael_block(l, r);
- /* last block is zero, so l ^ 0 = l */
- michael_block(l, r);
- michael_put32(l, mic);
- michael_put32(r, mic + 4);
+ michael_block(&mctx, val);
+ michael_block(&mctx, 0);
+
+ put_unaligned_le32(mctx.l, mic);
+ put_unaligned_le32(mctx.r, mic + 4);
}
diff --git a/net/mac80211/michael.h b/net/mac80211/michael.h
index 2e6aebabeea..3b848dad958 100644
--- a/net/mac80211/michael.h
+++ b/net/mac80211/michael.h
@@ -14,7 +14,11 @@
#define MICHAEL_MIC_LEN 8
-void michael_mic(u8 *key, u8 *da, u8 *sa, u8 priority,
- u8 *data, size_t data_len, u8 *mic);
+struct michael_mic_ctx {
+ u32 l, r;
+};
+
+void michael_mic(const u8 *key, struct ieee80211_hdr *hdr,
+ const u8 *data, size_t data_len, u8 *mic);
#endif /* MICHAEL_H */
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index b404537c0bc..87665d7bb4f 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -11,11 +11,6 @@
* published by the Free Software Foundation.
*/
-/* TODO:
- * order BSS list by RSSI(?) ("quality of AP")
- * scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE,
- * SSID)
- */
#include <linux/delay.h>
#include <linux/if_ether.h>
#include <linux/skbuff.h>
@@ -26,208 +21,460 @@
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include <net/iw_handler.h>
-#include <asm/types.h>
-
#include <net/mac80211.h>
+#include <asm/unaligned.h>
+
#include "ieee80211_i.h"
#include "rate.h"
#include "led.h"
-#include "mesh.h"
+#define IEEE80211_ASSOC_SCANS_MAX_TRIES 2
#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_MONITORING_INTERVAL (2 * HZ)
-#define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ)
#define IEEE80211_PROBE_INTERVAL (60 * HZ)
#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)
#define IEEE80211_SCAN_INTERVAL (2 * HZ)
#define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ)
#define IEEE80211_IBSS_JOIN_TIMEOUT (7 * HZ)
-#define IEEE80211_PROBE_DELAY (HZ / 33)
-#define IEEE80211_CHANNEL_TIME (HZ / 33)
-#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5)
-#define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ)
#define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ)
#define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ)
-#define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ)
#define IEEE80211_IBSS_MAX_STA_ENTRIES 128
-#define ERP_INFO_USE_PROTECTION BIT(1)
-
-/* mgmt header + 1 byte action code */
-#define IEEE80211_MIN_ACTION_SIZE (24 + 1)
-
-#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
-#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C
-#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0
-#define IEEE80211_DELBA_PARAM_TID_MASK 0xF000
-#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800
-
-/* next values represent the buffer size for A-MPDU frame.
- * According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2) */
-#define IEEE80211_MIN_AMPDU_BUF 0x8
-#define IEEE80211_MAX_AMPDU_BUF 0x40
-
-static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst,
- u8 *ssid, size_t ssid_len);
-static struct ieee80211_sta_bss *
-ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid, int freq,
- u8 *ssid, u8 ssid_len);
-static void ieee80211_rx_bss_put(struct net_device *dev,
- struct ieee80211_sta_bss *bss);
-static int ieee80211_sta_find_ibss(struct net_device *dev,
- struct ieee80211_if_sta *ifsta);
-static int ieee80211_sta_wep_configured(struct net_device *dev);
-static int ieee80211_sta_start_scan(struct net_device *dev,
- u8 *ssid, size_t ssid_len);
-static int ieee80211_sta_config_auth(struct net_device *dev,
- struct ieee80211_if_sta *ifsta);
-
-
-void ieee802_11_parse_elems(u8 *start, size_t len,
- struct ieee802_11_elems *elems)
+/* utils */
+static int ecw2cw(int ecw)
{
- size_t left = len;
- u8 *pos = start;
+ return (1 << ecw) - 1;
+}
- memset(elems, 0, sizeof(*elems));
+static u8 *ieee80211_bss_get_ie(struct ieee80211_bss *bss, u8 ie)
+{
+ u8 *end, *pos;
- while (left >= 2) {
- u8 id, elen;
+ pos = bss->ies;
+ if (pos == NULL)
+ return NULL;
+ end = pos + bss->ies_len;
- id = *pos++;
- elen = *pos++;
- left -= 2;
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == ie)
+ return pos;
+ pos += 2 + pos[1];
+ }
- if (elen > left)
- return;
+ return NULL;
+}
- switch (id) {
- case WLAN_EID_SSID:
- elems->ssid = pos;
- elems->ssid_len = elen;
- break;
- case WLAN_EID_SUPP_RATES:
- 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;
- break;
- case WLAN_EID_TIM:
- elems->tim = pos;
- elems->tim_len = elen;
- break;
- case WLAN_EID_IBSS_PARAMS:
- elems->ibss_params = pos;
- elems->ibss_params_len = elen;
- break;
- case WLAN_EID_CHALLENGE:
- elems->challenge = pos;
- elems->challenge_len = elen;
- break;
- case WLAN_EID_WPA:
- if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
- pos[2] == 0xf2) {
- /* Microsoft OUI (00:50:F2) */
- if (pos[3] == 1) {
- /* OUI Type 1 - WPA IE */
- elems->wpa = pos;
- elems->wpa_len = elen;
- } else if (elen >= 5 && pos[3] == 2) {
- if (pos[4] == 0) {
- elems->wmm_info = pos;
- elems->wmm_info_len = elen;
- } else if (pos[4] == 1) {
- elems->wmm_param = pos;
- elems->wmm_param_len = elen;
- }
- }
+static int ieee80211_compatible_rates(struct ieee80211_bss *bss,
+ struct ieee80211_supported_band *sband,
+ u64 *rates)
+{
+ int i, j, count;
+ *rates = 0;
+ count = 0;
+ for (i = 0; i < bss->supp_rates_len; i++) {
+ int rate = (bss->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;
+}
+
+/* also used by mesh code */
+u64 ieee80211_sta_get_rates(struct ieee80211_local *local,
+ struct ieee802_11_elems *elems,
+ enum ieee80211_band band)
+{
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *bitrates;
+ size_t num_rates;
+ u64 supp_rates;
+ int i, j;
+ sband = local->hw.wiphy->bands[band];
+
+ if (!sband) {
+ WARN_ON(1);
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+ }
+
+ 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;
+ 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)
+ supp_rates |= BIT(j);
+ }
+ return supp_rates;
+}
+
+/* frame sending functions */
+
+/* also used by scanning code */
+void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
+ u8 *ssid, size_t ssid_len)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_supported_band *sband;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ u8 *pos, *supp_rates, *esupp_rates = NULL;
+ int i;
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200);
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
+ "request\n", sdata->dev->name);
+ return;
+ }
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_REQ);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+ if (dst) {
+ memcpy(mgmt->da, dst, ETH_ALEN);
+ memcpy(mgmt->bssid, dst, ETH_ALEN);
+ } else {
+ memset(mgmt->da, 0xff, ETH_ALEN);
+ memset(mgmt->bssid, 0xff, ETH_ALEN);
+ }
+ pos = skb_put(skb, 2 + ssid_len);
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = ssid_len;
+ memcpy(pos, ssid, ssid_len);
+
+ supp_rates = skb_put(skb, 2);
+ supp_rates[0] = WLAN_EID_SUPP_RATES;
+ supp_rates[1] = 0;
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ struct ieee80211_rate *rate = &sband->bitrates[i];
+ if (esupp_rates) {
+ pos = skb_put(skb, 1);
+ esupp_rates[1]++;
+ } else if (supp_rates[1] == 8) {
+ esupp_rates = skb_put(skb, 3);
+ esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES;
+ esupp_rates[1] = 1;
+ pos = &esupp_rates[2];
+ } else {
+ pos = skb_put(skb, 1);
+ supp_rates[1]++;
+ }
+ *pos = rate->bitrate / 5;
+ }
+
+ ieee80211_tx_skb(sdata, skb, 0);
+}
+
+static void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_if_sta *ifsta,
+ int transaction, u8 *extra, size_t extra_len,
+ int encrypt)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+
+ 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->dev->name);
+ return;
+ }
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ 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);
+ if (encrypt)
+ mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->u.auth.auth_alg = cpu_to_le16(ifsta->auth_alg);
+ mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
+ ifsta->auth_transaction = transaction + 1;
+ mgmt->u.auth.status_code = cpu_to_le16(0);
+ if (extra)
+ memcpy(skb_put(skb, extra_len), extra, extra_len);
+
+ ieee80211_tx_skb(sdata, skb, encrypt);
+}
+
+static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_if_sta *ifsta)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ u8 *pos, *ies, *ht_add_ie;
+ int i, len, count, rates_len, supp_rates_len;
+ u16 capab;
+ struct ieee80211_bss *bss;
+ int wmm = 0;
+ struct ieee80211_supported_band *sband;
+ u64 rates = 0;
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+ sizeof(*mgmt) + 200 + ifsta->extra_ie_len +
+ ifsta->ssid_len);
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
+ "frame\n", sdata->dev->name);
+ return;
+ }
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+
+ capab = ifsta->capab;
+
+ if (local->hw.conf.channel->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;
+ }
+
+ bss = ieee80211_rx_bss_get(local, ifsta->bssid,
+ local->hw.conf.channel->center_freq,
+ ifsta->ssid, ifsta->ssid_len);
+ if (bss) {
+ if (bss->capability & WLAN_CAPABILITY_PRIVACY)
+ capab |= WLAN_CAPABILITY_PRIVACY;
+ if (bss->wmm_used)
+ wmm = 1;
+
+ /* 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(bss, sband, &rates);
+
+ if ((bss->capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
+ (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
+ capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+
+ ieee80211_rx_bss_put(local, bss);
+ } else {
+ rates = ~0;
+ rates_len = sband->n_bitrates;
+ }
+
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+
+ if (ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) {
+ 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, ifsta->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.reassoc_req.listen_interval =
+ cpu_to_le16(local->hw.conf.listen_interval);
+ }
+
+ /* SSID */
+ ies = pos = skb_put(skb, 2 + ifsta->ssid_len);
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = ifsta->ssid_len;
+ memcpy(pos, ifsta->ssid, ifsta->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++ = local->hw.conf.channel->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 (ifsta->extra_ie) {
+ pos = skb_put(skb, ifsta->extra_ie_len);
+ memcpy(pos, ifsta->extra_ie, ifsta->extra_ie_len);
+ }
+
+ if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
+ 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++ = 0;
+ }
+
+ /* wmm support is a must to HT */
+ if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED) &&
+ sband->ht_info.ht_supported &&
+ (ht_add_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_EXTRA_INFO))) {
+ struct ieee80211_ht_addt_info *ht_add_info =
+ (struct ieee80211_ht_addt_info *)ht_add_ie;
+ u16 cap = sband->ht_info.cap;
+ __le16 tmp;
+ u32 flags = local->hw.conf.channel->flags;
+
+ switch (ht_add_info->ht_param & IEEE80211_HT_IE_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_IE_CHA_SEC_ABOVE:
+ if (flags & IEEE80211_CHAN_NO_FAT_ABOVE) {
+ cap &= ~IEEE80211_HT_CAP_SUP_WIDTH;
+ cap &= ~IEEE80211_HT_CAP_SGI_40;
}
break;
- case WLAN_EID_RSN:
- elems->rsn = pos;
- elems->rsn_len = elen;
- break;
- case WLAN_EID_ERP_INFO:
- elems->erp_info = pos;
- elems->erp_info_len = elen;
- break;
- case WLAN_EID_EXT_SUPP_RATES:
- elems->ext_supp_rates = pos;
- elems->ext_supp_rates_len = elen;
- break;
- case WLAN_EID_HT_CAPABILITY:
- elems->ht_cap_elem = pos;
- elems->ht_cap_elem_len = elen;
- break;
- case WLAN_EID_HT_EXTRA_INFO:
- elems->ht_info_elem = pos;
- elems->ht_info_elem_len = elen;
- break;
- case WLAN_EID_MESH_ID:
- elems->mesh_id = pos;
- elems->mesh_id_len = elen;
- break;
- case WLAN_EID_MESH_CONFIG:
- elems->mesh_config = pos;
- elems->mesh_config_len = elen;
- break;
- case WLAN_EID_PEER_LINK:
- elems->peer_link = pos;
- elems->peer_link_len = elen;
- break;
- case WLAN_EID_PREQ:
- elems->preq = pos;
- elems->preq_len = elen;
- break;
- case WLAN_EID_PREP:
- elems->prep = pos;
- elems->prep_len = elen;
- break;
- case WLAN_EID_PERR:
- elems->perr = pos;
- elems->perr_len = elen;
- break;
- default:
+ case IEEE80211_HT_IE_CHA_SEC_BELOW:
+ if (flags & IEEE80211_CHAN_NO_FAT_BELOW) {
+ cap &= ~IEEE80211_HT_CAP_SUP_WIDTH;
+ cap &= ~IEEE80211_HT_CAP_SGI_40;
+ }
break;
}
- left -= elen;
- pos += elen;
+ tmp = cpu_to_le16(cap);
+ 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));
+ memcpy(pos, &tmp, sizeof(u16));
+ pos += sizeof(u16);
+ /* TODO: needs a define here for << 2 */
+ *pos++ = sband->ht_info.ampdu_factor |
+ (sband->ht_info.ampdu_density << 2);
+ memcpy(pos, sband->ht_info.supp_mcs_set, 16);
}
+
+ kfree(ifsta->assocreq_ies);
+ ifsta->assocreq_ies_len = (skb->data + skb->len) - ies;
+ ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_KERNEL);
+ if (ifsta->assocreq_ies)
+ memcpy(ifsta->assocreq_ies, ies, ifsta->assocreq_ies_len);
+
+ ieee80211_tx_skb(sdata, skb, 0);
}
-static int ecw2cw(int ecw)
+static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
+ u16 stype, u16 reason)
{
- return (1 << ecw) - 1;
-}
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ 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->dev->name);
+ return;
+ }
+ skb_reserve(skb, local->hw.extra_tx_headroom);
-static void ieee80211_sta_def_wmm_params(struct net_device *dev,
- struct ieee80211_sta_bss *bss,
- int ibss)
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->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);
+
+ ieee80211_tx_skb(sdata, skb, 0);
+}
+
+/* MLME */
+static void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_bss *bss)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
int i, have_higher_than_11mbit = 0;
-
/* cf. IEEE 802.11 9.2.12 */
for (i = 0; i < bss->supp_rates_len; i++)
if ((bss->supp_rates[i] & 0x7f) * 5 > 110)
@@ -239,49 +486,24 @@ static void ieee80211_sta_def_wmm_params(struct net_device *dev,
else
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
-
- if (local->ops->conf_tx) {
- struct ieee80211_tx_queue_params qparam;
-
- memset(&qparam, 0, sizeof(qparam));
-
- qparam.aifs = 2;
-
- if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
- !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE))
- qparam.cw_min = 31;
- else
- qparam.cw_min = 15;
-
- qparam.cw_max = 1023;
- qparam.txop = 0;
-
- for (i = IEEE80211_TX_QUEUE_DATA0; i < NUM_TX_DATA_QUEUES; i++)
- local->ops->conf_tx(local_to_hw(local),
- i + IEEE80211_TX_QUEUE_DATA0,
- &qparam);
-
- if (ibss) {
- /* IBSS uses different parameters for Beacon sending */
- qparam.cw_min++;
- qparam.cw_min *= 2;
- qparam.cw_min--;
- local->ops->conf_tx(local_to_hw(local),
- IEEE80211_TX_QUEUE_BEACON, &qparam);
- }
- }
+ ieee80211_set_wmm_default(sdata);
}
-static void ieee80211_sta_wmm_params(struct net_device *dev,
+static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
struct ieee80211_if_sta *ifsta,
u8 *wmm_param, size_t wmm_param_len)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_tx_queue_params params;
size_t left;
int count;
u8 *pos;
+ if (!(ifsta->flags & IEEE80211_STA_WMM_ENABLED))
+ return;
+
+ if (!wmm_param)
+ return;
+
if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1)
return;
count = wmm_param[6] & 0x0f;
@@ -305,47 +527,43 @@ static void ieee80211_sta_wmm_params(struct net_device *dev,
switch (aci) {
case 1:
- queue = IEEE80211_TX_QUEUE_DATA3;
- if (acm) {
+ queue = 3;
+ if (acm)
local->wmm_acm |= BIT(0) | BIT(3);
- }
break;
case 2:
- queue = IEEE80211_TX_QUEUE_DATA1;
- if (acm) {
+ queue = 1;
+ if (acm)
local->wmm_acm |= BIT(4) | BIT(5);
- }
break;
case 3:
- queue = IEEE80211_TX_QUEUE_DATA0;
- if (acm) {
+ queue = 0;
+ if (acm)
local->wmm_acm |= BIT(6) | BIT(7);
- }
break;
case 0:
default:
- queue = IEEE80211_TX_QUEUE_DATA2;
- if (acm) {
+ queue = 2;
+ if (acm)
local->wmm_acm |= BIT(1) | BIT(2);
- }
break;
}
params.aifs = pos[0] & 0x0f;
params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
params.cw_min = ecw2cw(pos[1] & 0x0f);
- params.txop = pos[2] | (pos[3] << 8);
-#ifdef CONFIG_MAC80211_DEBUG
+ params.txop = get_unaligned_le16(pos + 2);
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: WMM queue=%d aci=%d acm=%d aifs=%d "
"cWmin=%d cWmax=%d txop=%d\n",
- dev->name, queue, aci, acm, params.aifs, params.cw_min,
+ local->mdev->name, queue, aci, acm, params.aifs, params.cw_min,
params.cw_max, params.txop);
#endif
/* TODO: handle ACM (block TX, fallback to next lowest allowed
* AC for now) */
if (local->ops->conf_tx(local_to_hw(local), queue, &params)) {
printk(KERN_DEBUG "%s: failed to set TX queue "
- "parameters for queue %d\n", dev->name, queue);
+ "parameters for queue %d\n", local->mdev->name, queue);
}
}
}
@@ -355,11 +573,14 @@ static u32 ieee80211_handle_protect_preamb(struct ieee80211_sub_if_data *sdata,
bool use_short_preamble)
{
struct ieee80211_bss_conf *bss_conf = &sdata->bss_conf;
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
DECLARE_MAC_BUF(mac);
+#endif
u32 changed = 0;
if (use_protection != bss_conf->use_cts_prot) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: CTS protection %s (BSSID="
"%s)\n",
@@ -367,11 +588,13 @@ static u32 ieee80211_handle_protect_preamb(struct ieee80211_sub_if_data *sdata,
use_protection ? "enabled" : "disabled",
print_mac(mac, ifsta->bssid));
}
+#endif
bss_conf->use_cts_prot = use_protection;
changed |= BSS_CHANGED_ERP_CTS_PROT;
}
if (use_short_preamble != bss_conf->use_short_preamble) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: switched to %s barker preamble"
" (BSSID=%s)\n",
@@ -379,6 +602,7 @@ static u32 ieee80211_handle_protect_preamb(struct ieee80211_sub_if_data *sdata,
use_short_preamble ? "short" : "long",
print_mac(mac, ifsta->bssid));
}
+#endif
bss_conf->use_short_preamble = use_short_preamble;
changed |= BSS_CHANGED_ERP_PREAMBLE;
}
@@ -397,7 +621,7 @@ static u32 ieee80211_handle_erp_ie(struct ieee80211_sub_if_data *sdata,
}
static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_sta_bss *bss)
+ struct ieee80211_bss *bss)
{
u32 changed = 0;
@@ -412,53 +636,18 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
return changed;
}
-int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
- struct ieee80211_ht_info *ht_info)
-{
-
- if (ht_info == NULL)
- return -EINVAL;
-
- memset(ht_info, 0, sizeof(*ht_info));
-
- if (ht_cap_ie) {
- u8 ampdu_info = ht_cap_ie->ampdu_params_info;
-
- ht_info->ht_supported = 1;
- ht_info->cap = le16_to_cpu(ht_cap_ie->cap_info);
- ht_info->ampdu_factor =
- ampdu_info & IEEE80211_HT_CAP_AMPDU_FACTOR;
- ht_info->ampdu_density =
- (ampdu_info & IEEE80211_HT_CAP_AMPDU_DENSITY) >> 2;
- memcpy(ht_info->supp_mcs_set, ht_cap_ie->supp_mcs_set, 16);
- } else
- ht_info->ht_supported = 0;
-
- return 0;
-}
-
-int ieee80211_ht_addt_info_ie_to_ht_bss_info(
- struct ieee80211_ht_addt_info *ht_add_info_ie,
- struct ieee80211_ht_bss_info *bss_info)
+static void ieee80211_sta_send_apinfo(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_if_sta *ifsta)
{
- if (bss_info == NULL)
- return -EINVAL;
-
- memset(bss_info, 0, sizeof(*bss_info));
-
- if (ht_add_info_ie) {
- u16 op_mode;
- op_mode = le16_to_cpu(ht_add_info_ie->operation_mode);
-
- bss_info->primary_channel = ht_add_info_ie->control_chan;
- bss_info->bss_cap = ht_add_info_ie->ht_param;
- bss_info->bss_op_mode = (u8)(op_mode & 0xff);
- }
-
- return 0;
+ union iwreq_data wrqu;
+ memset(&wrqu, 0, sizeof(wrqu));
+ if (ifsta->flags & IEEE80211_STA_ASSOCIATED)
+ memcpy(wrqu.ap_addr.sa_data, sdata->u.sta.bssid, ETH_ALEN);
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL);
}
-static void ieee80211_sta_send_associnfo(struct net_device *dev,
+static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
char *buf;
@@ -501,151 +690,105 @@ static void ieee80211_sta_send_associnfo(struct net_device *dev,
}
}
- memset(&wrqu, 0, sizeof(wrqu));
- wrqu.data.length = len;
- wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
+ if (len <= IW_CUSTOM_MAX) {
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = len;
+ wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf);
+ }
kfree(buf);
}
-static void ieee80211_set_associated(struct net_device *dev,
- struct ieee80211_if_sta *ifsta,
- bool assoc)
+static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_if_sta *ifsta)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
struct ieee80211_conf *conf = &local_to_hw(local)->conf;
- union iwreq_data wrqu;
u32 changed = BSS_CHANGED_ASSOC;
- if (assoc) {
- struct ieee80211_sta_bss *bss;
-
- ifsta->flags |= IEEE80211_STA_ASSOCIATED;
+ struct ieee80211_bss *bss;
- if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
- return;
+ ifsta->flags |= IEEE80211_STA_ASSOCIATED;
- bss = ieee80211_rx_bss_get(dev, ifsta->bssid,
- conf->channel->center_freq,
- ifsta->ssid, ifsta->ssid_len);
- if (bss) {
- /* set timing information */
- sdata->bss_conf.beacon_int = bss->beacon_int;
- sdata->bss_conf.timestamp = bss->timestamp;
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return;
- changed |= ieee80211_handle_bss_capability(sdata, bss);
+ bss = ieee80211_rx_bss_get(local, ifsta->bssid,
+ conf->channel->center_freq,
+ ifsta->ssid, ifsta->ssid_len);
+ if (bss) {
+ /* set timing information */
+ sdata->bss_conf.beacon_int = bss->beacon_int;
+ sdata->bss_conf.timestamp = bss->timestamp;
+ sdata->bss_conf.dtim_period = bss->dtim_period;
- ieee80211_rx_bss_put(dev, bss);
- }
+ changed |= ieee80211_handle_bss_capability(sdata, bss);
- if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) {
- changed |= BSS_CHANGED_HT;
- sdata->bss_conf.assoc_ht = 1;
- sdata->bss_conf.ht_conf = &conf->ht_conf;
- sdata->bss_conf.ht_bss_conf = &conf->ht_bss_conf;
- }
+ ieee80211_rx_bss_put(local, bss);
+ }
- ifsta->flags |= IEEE80211_STA_PREV_BSSID_SET;
- memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN);
- memcpy(wrqu.ap_addr.sa_data, sdata->u.sta.bssid, ETH_ALEN);
- ieee80211_sta_send_associnfo(dev, ifsta);
- } else {
- netif_carrier_off(dev);
- ieee80211_sta_tear_down_BA_sessions(dev, ifsta->bssid);
- ifsta->flags &= ~IEEE80211_STA_ASSOCIATED;
- ieee80211_reset_erp_info(dev);
+ if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) {
+ changed |= BSS_CHANGED_HT;
+ sdata->bss_conf.assoc_ht = 1;
+ sdata->bss_conf.ht_conf = &conf->ht_conf;
+ sdata->bss_conf.ht_bss_conf = &conf->ht_bss_conf;
+ }
- sdata->bss_conf.assoc_ht = 0;
- sdata->bss_conf.ht_conf = NULL;
- sdata->bss_conf.ht_bss_conf = NULL;
+ ifsta->flags |= IEEE80211_STA_PREV_BSSID_SET;
+ memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN);
+ ieee80211_sta_send_associnfo(sdata, ifsta);
- memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
- }
ifsta->last_probe = jiffies;
- ieee80211_led_assoc(local, assoc);
+ ieee80211_led_assoc(local, 1);
- sdata->bss_conf.assoc = assoc;
+ sdata->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.
+ */
+ changed |= BSS_CHANGED_BASIC_RATES;
ieee80211_bss_info_change_notify(sdata, changed);
- if (assoc)
- netif_carrier_on(dev);
-
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
- wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
-}
+ netif_tx_start_all_queues(sdata->dev);
+ netif_carrier_on(sdata->dev);
-static void ieee80211_set_disassoc(struct net_device *dev,
- struct ieee80211_if_sta *ifsta, int deauth)
-{
- if (deauth)
- ifsta->auth_tries = 0;
- ifsta->assoc_tries = 0;
- ieee80211_set_associated(dev, ifsta, 0);
+ ieee80211_sta_send_apinfo(sdata, ifsta);
}
-void ieee80211_sta_tx(struct net_device *dev, struct sk_buff *skb,
- int encrypt)
+static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_if_sta *ifsta)
{
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_tx_packet_data *pkt_data;
+ DECLARE_MAC_BUF(mac);
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- skb->dev = sdata->local->mdev;
- skb_set_mac_header(skb, 0);
- skb_set_network_header(skb, 0);
- skb_set_transport_header(skb, 0);
+ ifsta->direct_probe_tries++;
+ if (ifsta->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) {
+ printk(KERN_DEBUG "%s: direct probe to AP %s timed out\n",
+ sdata->dev->name, print_mac(mac, ifsta->bssid));
+ ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ return;
+ }
- pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
- memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
- pkt_data->ifindex = sdata->dev->ifindex;
- if (!encrypt)
- pkt_data->flags |= IEEE80211_TXPD_DO_NOT_ENCRYPT;
+ printk(KERN_DEBUG "%s: direct probe to AP %s try %d\n",
+ sdata->dev->name, print_mac(mac, ifsta->bssid),
+ ifsta->direct_probe_tries);
- dev_queue_xmit(skb);
-}
+ ifsta->state = IEEE80211_STA_MLME_DIRECT_PROBE;
+ set_bit(IEEE80211_STA_REQ_DIRECT_PROBE, &ifsta->request);
-static void ieee80211_send_auth(struct net_device *dev,
- struct ieee80211_if_sta *ifsta,
- int transaction, u8 *extra, size_t extra_len,
- int encrypt)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt;
-
- 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", dev->name);
- return;
- }
- skb_reserve(skb, local->hw.extra_tx_headroom);
-
- mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6);
- memset(mgmt, 0, 24 + 6);
- mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
- IEEE80211_STYPE_AUTH);
- if (encrypt)
- mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
- memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
- memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
- mgmt->u.auth.auth_alg = cpu_to_le16(ifsta->auth_alg);
- mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
- ifsta->auth_transaction = transaction + 1;
- mgmt->u.auth.status_code = cpu_to_le16(0);
- if (extra)
- memcpy(skb_put(skb, extra_len), extra, extra_len);
+ /* 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,
+ ifsta->ssid, ifsta->ssid_len);
- ieee80211_sta_tx(dev, skb, encrypt);
+ mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
}
-static void ieee80211_authenticate(struct net_device *dev,
+static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
DECLARE_MAC_BUF(mac);
@@ -654,264 +797,95 @@ static void ieee80211_authenticate(struct net_device *dev,
if (ifsta->auth_tries > IEEE80211_AUTH_MAX_TRIES) {
printk(KERN_DEBUG "%s: authentication with AP %s"
" timed out\n",
- dev->name, print_mac(mac, ifsta->bssid));
- ifsta->state = IEEE80211_DISABLED;
+ sdata->dev->name, print_mac(mac, ifsta->bssid));
+ ifsta->state = IEEE80211_STA_MLME_DISABLED;
return;
}
- ifsta->state = IEEE80211_AUTHENTICATE;
+ ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
printk(KERN_DEBUG "%s: authenticate with AP %s\n",
- dev->name, print_mac(mac, ifsta->bssid));
+ sdata->dev->name, print_mac(mac, ifsta->bssid));
- ieee80211_send_auth(dev, ifsta, 1, NULL, 0, 0);
+ ieee80211_send_auth(sdata, ifsta, 1, NULL, 0, 0);
mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
}
-static int ieee80211_compatible_rates(struct ieee80211_sta_bss *bss,
- struct ieee80211_supported_band *sband,
- u64 *rates)
+static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_if_sta *ifsta, bool deauth,
+ bool self_disconnected, u16 reason)
{
- int i, j, count;
- *rates = 0;
- count = 0;
- for (i = 0; i < bss->supp_rates_len; i++) {
- int rate = (bss->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;
-}
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
+ u32 changed = BSS_CHANGED_ASSOC;
-static void ieee80211_send_assoc(struct net_device *dev,
- struct ieee80211_if_sta *ifsta)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt;
- u8 *pos, *ies;
- int i, len, count, rates_len, supp_rates_len;
- u16 capab;
- struct ieee80211_sta_bss *bss;
- int wmm = 0;
- struct ieee80211_supported_band *sband;
- u64 rates = 0;
+ rcu_read_lock();
- skb = dev_alloc_skb(local->hw.extra_tx_headroom +
- sizeof(*mgmt) + 200 + ifsta->extra_ie_len +
- ifsta->ssid_len);
- if (!skb) {
- printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
- "frame\n", dev->name);
+ sta = sta_info_get(local, ifsta->bssid);
+ if (!sta) {
+ rcu_read_unlock();
return;
}
- skb_reserve(skb, local->hw.extra_tx_headroom);
-
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
-
- capab = ifsta->capab;
-
- if (local->hw.conf.channel->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;
- }
-
- bss = ieee80211_rx_bss_get(dev, ifsta->bssid,
- local->hw.conf.channel->center_freq,
- ifsta->ssid, ifsta->ssid_len);
- if (bss) {
- if (bss->capability & WLAN_CAPABILITY_PRIVACY)
- capab |= WLAN_CAPABILITY_PRIVACY;
- if (bss->wmm_ie) {
- wmm = 1;
- }
-
- /* 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(bss, sband, &rates);
-
- ieee80211_rx_bss_put(dev, bss);
- } else {
- rates = ~0;
- rates_len = sband->n_bitrates;
- }
- mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
- memset(mgmt, 0, 24);
- memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
- memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
-
- if (ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) {
- skb_put(skb, 10);
- mgmt->frame_control = IEEE80211_FC(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(1);
- memcpy(mgmt->u.reassoc_req.current_ap, ifsta->prev_bssid,
- ETH_ALEN);
- } else {
- skb_put(skb, 4);
- mgmt->frame_control = IEEE80211_FC(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(1);
+ if (deauth) {
+ ifsta->direct_probe_tries = 0;
+ ifsta->auth_tries = 0;
}
+ ifsta->assoc_scan_tries = 0;
+ ifsta->assoc_tries = 0;
- /* SSID */
- ies = pos = skb_put(skb, 2 + ifsta->ssid_len);
- *pos++ = WLAN_EID_SSID;
- *pos++ = ifsta->ssid_len;
- memcpy(pos, ifsta->ssid, ifsta->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;
- }
- }
+ netif_tx_stop_all_queues(sdata->dev);
+ netif_carrier_off(sdata->dev);
- if (count == 8) {
- pos = skb_put(skb, rates_len - count + 2);
- *pos++ = WLAN_EID_EXT_SUPP_RATES;
- *pos++ = rates_len - count;
+ ieee80211_sta_tear_down_BA_sessions(sdata, sta->sta.addr);
- for (i++; i < sband->n_bitrates; i++) {
- if (BIT(i) & rates) {
- int rate = sband->bitrates[i].bitrate;
- *pos++ = (u8) (rate / 5);
- }
- }
+ if (self_disconnected) {
+ if (deauth)
+ ieee80211_send_deauth_disassoc(sdata,
+ IEEE80211_STYPE_DEAUTH, reason);
+ else
+ ieee80211_send_deauth_disassoc(sdata,
+ IEEE80211_STYPE_DISASSOC, reason);
}
- if (ifsta->extra_ie) {
- pos = skb_put(skb, ifsta->extra_ie_len);
- memcpy(pos, ifsta->extra_ie, ifsta->extra_ie_len);
- }
+ ifsta->flags &= ~IEEE80211_STA_ASSOCIATED;
+ changed |= ieee80211_reset_erp_info(sdata);
- if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
- 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++ = 0;
- }
- /* wmm support is a must to HT */
- if (wmm && sband->ht_info.ht_supported) {
- __le16 tmp = cpu_to_le16(sband->ht_info.cap);
- 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));
- memcpy(pos, &tmp, sizeof(u16));
- pos += sizeof(u16);
- /* TODO: needs a define here for << 2 */
- *pos++ = sband->ht_info.ampdu_factor |
- (sband->ht_info.ampdu_density << 2);
- memcpy(pos, sband->ht_info.supp_mcs_set, 16);
- }
+ if (sdata->bss_conf.assoc_ht)
+ changed |= BSS_CHANGED_HT;
- kfree(ifsta->assocreq_ies);
- ifsta->assocreq_ies_len = (skb->data + skb->len) - ies;
- ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_KERNEL);
- if (ifsta->assocreq_ies)
- memcpy(ifsta->assocreq_ies, ies, ifsta->assocreq_ies_len);
+ sdata->bss_conf.assoc_ht = 0;
+ sdata->bss_conf.ht_conf = NULL;
+ sdata->bss_conf.ht_bss_conf = NULL;
- ieee80211_sta_tx(dev, skb, 0);
-}
+ ieee80211_led_assoc(local, 0);
+ sdata->bss_conf.assoc = 0;
+ ieee80211_sta_send_apinfo(sdata, ifsta);
-static void ieee80211_send_deauth(struct net_device *dev,
- struct ieee80211_if_sta *ifsta, u16 reason)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt;
+ if (self_disconnected)
+ ifsta->state = IEEE80211_STA_MLME_DISABLED;
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
- if (!skb) {
- printk(KERN_DEBUG "%s: failed to allocate buffer for deauth "
- "frame\n", dev->name);
- return;
- }
- skb_reserve(skb, local->hw.extra_tx_headroom);
+ sta_info_unlink(&sta);
- mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
- memset(mgmt, 0, 24);
- memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
- memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
- IEEE80211_STYPE_DEAUTH);
- skb_put(skb, 2);
- mgmt->u.deauth.reason_code = cpu_to_le16(reason);
+ rcu_read_unlock();
- ieee80211_sta_tx(dev, skb, 0);
+ sta_info_destroy(sta);
}
-
-static void ieee80211_send_disassoc(struct net_device *dev,
- struct ieee80211_if_sta *ifsta, u16 reason)
+static int ieee80211_sta_wep_configured(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt;
-
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
- if (!skb) {
- printk(KERN_DEBUG "%s: failed to allocate buffer for disassoc "
- "frame\n", dev->name);
- return;
- }
- skb_reserve(skb, local->hw.extra_tx_headroom);
-
- mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
- memset(mgmt, 0, 24);
- memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
- memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
- IEEE80211_STYPE_DISASSOC);
- skb_put(skb, 2);
- mgmt->u.disassoc.reason_code = cpu_to_le16(reason);
-
- ieee80211_sta_tx(dev, skb, 0);
+ if (!sdata || !sdata->default_key ||
+ sdata->default_key->conf.alg != ALG_WEP)
+ return 0;
+ return 1;
}
-
-static int ieee80211_privacy_mismatch(struct net_device *dev,
+static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sta_bss *bss;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_bss *bss;
int bss_privacy;
int wep_privacy;
int privacy_invoked;
@@ -919,17 +893,17 @@ static int ieee80211_privacy_mismatch(struct net_device *dev,
if (!ifsta || (ifsta->flags & IEEE80211_STA_MIXED_CELL))
return 0;
- bss = ieee80211_rx_bss_get(dev, ifsta->bssid,
+ bss = ieee80211_rx_bss_get(local, ifsta->bssid,
local->hw.conf.channel->center_freq,
ifsta->ssid, ifsta->ssid_len);
if (!bss)
return 0;
bss_privacy = !!(bss->capability & WLAN_CAPABILITY_PRIVACY);
- wep_privacy = !!ieee80211_sta_wep_configured(dev);
+ wep_privacy = !!ieee80211_sta_wep_configured(sdata);
privacy_invoked = !!(ifsta->flags & IEEE80211_STA_PRIVACY_INVOKED);
- ieee80211_rx_bss_put(dev, bss);
+ ieee80211_rx_bss_put(local, bss);
if ((bss_privacy == wep_privacy) || (bss_privacy == privacy_invoked))
return 0;
@@ -937,8 +911,7 @@ static int ieee80211_privacy_mismatch(struct net_device *dev,
return 1;
}
-
-static void ieee80211_associate(struct net_device *dev,
+static void ieee80211_associate(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
DECLARE_MAC_BUF(mac);
@@ -947,31 +920,31 @@ static void ieee80211_associate(struct net_device *dev,
if (ifsta->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
printk(KERN_DEBUG "%s: association with AP %s"
" timed out\n",
- dev->name, print_mac(mac, ifsta->bssid));
- ifsta->state = IEEE80211_DISABLED;
+ sdata->dev->name, print_mac(mac, ifsta->bssid));
+ ifsta->state = IEEE80211_STA_MLME_DISABLED;
return;
}
- ifsta->state = IEEE80211_ASSOCIATE;
+ ifsta->state = IEEE80211_STA_MLME_ASSOCIATE;
printk(KERN_DEBUG "%s: associate with AP %s\n",
- dev->name, print_mac(mac, ifsta->bssid));
- if (ieee80211_privacy_mismatch(dev, ifsta)) {
+ sdata->dev->name, print_mac(mac, ifsta->bssid));
+ if (ieee80211_privacy_mismatch(sdata, ifsta)) {
printk(KERN_DEBUG "%s: mismatch in privacy configuration and "
- "mixed-cell disabled - abort association\n", dev->name);
- ifsta->state = IEEE80211_DISABLED;
+ "mixed-cell disabled - abort association\n", sdata->dev->name);
+ ifsta->state = IEEE80211_STA_MLME_DISABLED;
return;
}
- ieee80211_send_assoc(dev, ifsta);
+ ieee80211_send_assoc(sdata, ifsta);
mod_timer(&ifsta->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
}
-static void ieee80211_associated(struct net_device *dev,
+static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
int disassoc;
DECLARE_MAC_BUF(mac);
@@ -981,14 +954,14 @@ static void ieee80211_associated(struct net_device *dev,
* for better APs. */
/* TODO: remove expired BSSes */
- ifsta->state = IEEE80211_ASSOCIATED;
+ ifsta->state = IEEE80211_STA_MLME_ASSOCIATED;
rcu_read_lock();
sta = sta_info_get(local, ifsta->bssid);
if (!sta) {
printk(KERN_DEBUG "%s: No STA entry for own AP %s\n",
- dev->name, print_mac(mac, ifsta->bssid));
+ sdata->dev->name, print_mac(mac, ifsta->bssid));
disassoc = 1;
} else {
disassoc = 0;
@@ -998,20 +971,19 @@ static void ieee80211_associated(struct net_device *dev,
printk(KERN_DEBUG "%s: No ProbeResp from "
"current AP %s - assume out of "
"range\n",
- dev->name, print_mac(mac, ifsta->bssid));
+ sdata->dev->name, print_mac(mac, ifsta->bssid));
disassoc = 1;
- sta_info_unlink(&sta);
} else
- ieee80211_send_probe_req(dev, ifsta->bssid,
- local->scan_ssid,
- local->scan_ssid_len);
+ ieee80211_send_probe_req(sdata, ifsta->bssid,
+ ifsta->ssid,
+ ifsta->ssid_len);
ifsta->flags ^= IEEE80211_STA_PROBEREQ_POLL;
} else {
ifsta->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
if (time_after(jiffies, ifsta->last_probe +
IEEE80211_PROBE_INTERVAL)) {
ifsta->last_probe = jiffies;
- ieee80211_send_probe_req(dev, ifsta->bssid,
+ ieee80211_send_probe_req(sdata, ifsta->bssid,
ifsta->ssid,
ifsta->ssid_len);
}
@@ -1020,100 +992,25 @@ static void ieee80211_associated(struct net_device *dev,
rcu_read_unlock();
- if (disassoc && sta)
- sta_info_destroy(sta);
-
- if (disassoc) {
- ifsta->state = IEEE80211_DISABLED;
- ieee80211_set_associated(dev, ifsta, 0);
- } else {
+ if (disassoc)
+ ieee80211_set_disassoc(sdata, ifsta, true, true,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ else
mod_timer(&ifsta->timer, jiffies +
IEEE80211_MONITORING_INTERVAL);
- }
}
-static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst,
- u8 *ssid, size_t ssid_len)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_supported_band *sband;
- struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt;
- u8 *pos, *supp_rates, *esupp_rates = NULL;
- int i;
-
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200);
- if (!skb) {
- printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
- "request\n", dev->name);
- return;
- }
- skb_reserve(skb, local->hw.extra_tx_headroom);
-
- mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
- memset(mgmt, 0, 24);
- mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
- IEEE80211_STYPE_PROBE_REQ);
- memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
- if (dst) {
- memcpy(mgmt->da, dst, ETH_ALEN);
- memcpy(mgmt->bssid, dst, ETH_ALEN);
- } else {
- memset(mgmt->da, 0xff, ETH_ALEN);
- memset(mgmt->bssid, 0xff, ETH_ALEN);
- }
- pos = skb_put(skb, 2 + ssid_len);
- *pos++ = WLAN_EID_SSID;
- *pos++ = ssid_len;
- memcpy(pos, ssid, ssid_len);
-
- supp_rates = skb_put(skb, 2);
- supp_rates[0] = WLAN_EID_SUPP_RATES;
- supp_rates[1] = 0;
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
-
- for (i = 0; i < sband->n_bitrates; i++) {
- struct ieee80211_rate *rate = &sband->bitrates[i];
- if (esupp_rates) {
- pos = skb_put(skb, 1);
- esupp_rates[1]++;
- } else if (supp_rates[1] == 8) {
- esupp_rates = skb_put(skb, 3);
- esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES;
- esupp_rates[1] = 1;
- pos = &esupp_rates[2];
- } else {
- pos = skb_put(skb, 1);
- supp_rates[1]++;
- }
- *pos = rate->bitrate / 5;
- }
-
- ieee80211_sta_tx(dev, skb, 0);
-}
-
-
-static int ieee80211_sta_wep_configured(struct net_device *dev)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (!sdata || !sdata->default_key ||
- sdata->default_key->conf.alg != ALG_WEP)
- return 0;
- return 1;
-}
-
-
-static void ieee80211_auth_completed(struct net_device *dev,
+static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- printk(KERN_DEBUG "%s: authenticated\n", dev->name);
+ printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name);
ifsta->flags |= IEEE80211_STA_AUTHENTICATED;
- ieee80211_associate(dev, ifsta);
+ ieee80211_associate(sdata, ifsta);
}
-static void ieee80211_auth_challenge(struct net_device *dev,
+static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len)
@@ -1121,655 +1018,58 @@ static void ieee80211_auth_challenge(struct net_device *dev,
u8 *pos;
struct ieee802_11_elems elems;
- printk(KERN_DEBUG "%s: replying to auth challenge\n", dev->name);
pos = mgmt->u.auth.variable;
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
- if (!elems.challenge) {
- printk(KERN_DEBUG "%s: no challenge IE in shared key auth "
- "frame\n", dev->name);
+ if (!elems.challenge)
return;
- }
- ieee80211_send_auth(dev, ifsta, 3, elems.challenge - 2,
+ ieee80211_send_auth(sdata, ifsta, 3, elems.challenge - 2,
elems.challenge_len + 2, 1);
}
-static void ieee80211_send_addba_resp(struct net_device *dev, u8 *da, u16 tid,
- u8 dialog_token, u16 status, u16 policy,
- u16 buf_size, u16 timeout)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt;
- u16 capab;
-
- skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom + 1 +
- sizeof(mgmt->u.action.u.addba_resp));
- if (!skb) {
- printk(KERN_DEBUG "%s: failed to allocate buffer "
- "for addba resp frame\n", dev->name);
- 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, dev->dev_addr, ETH_ALEN);
- if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
- memcpy(mgmt->bssid, dev->dev_addr, ETH_ALEN);
- else
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
- IEEE80211_STYPE_ACTION);
-
- skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_resp));
- mgmt->u.action.category = WLAN_CATEGORY_BACK;
- mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP;
- mgmt->u.action.u.addba_resp.dialog_token = dialog_token;
-
- capab = (u16)(policy << 1); /* bit 1 aggregation policy */
- capab |= (u16)(tid << 2); /* bit 5:2 TID number */
- capab |= (u16)(buf_size << 6); /* bit 15:6 max size of aggregation */
-
- mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab);
- mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout);
- mgmt->u.action.u.addba_resp.status = cpu_to_le16(status);
-
- ieee80211_sta_tx(dev, skb, 0);
-
- return;
-}
-
-void ieee80211_send_addba_request(struct net_device *dev, const u8 *da,
- u16 tid, u8 dialog_token, u16 start_seq_num,
- u16 agg_size, u16 timeout)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt;
- u16 capab;
-
- skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom + 1 +
- sizeof(mgmt->u.action.u.addba_req));
-
-
- if (!skb) {
- printk(KERN_ERR "%s: failed to allocate buffer "
- "for addba request frame\n", dev->name);
- 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, dev->dev_addr, ETH_ALEN);
- if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
- memcpy(mgmt->bssid, dev->dev_addr, ETH_ALEN);
- else
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
-
- mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
- IEEE80211_STYPE_ACTION);
-
- skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_req));
-
- mgmt->u.action.category = WLAN_CATEGORY_BACK;
- mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ;
-
- mgmt->u.action.u.addba_req.dialog_token = dialog_token;
- capab = (u16)(1 << 1); /* bit 1 aggregation policy */
- capab |= (u16)(tid << 2); /* bit 5:2 TID number */
- capab |= (u16)(agg_size << 6); /* bit 15:6 max size of aggergation */
-
- mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab);
-
- mgmt->u.action.u.addba_req.timeout = cpu_to_le16(timeout);
- mgmt->u.action.u.addba_req.start_seq_num =
- cpu_to_le16(start_seq_num << 4);
-
- ieee80211_sta_tx(dev, skb, 0);
-}
-
-static void ieee80211_sta_process_addba_request(struct net_device *dev,
- struct ieee80211_mgmt *mgmt,
- size_t len)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_hw *hw = &local->hw;
- struct ieee80211_conf *conf = &hw->conf;
- struct sta_info *sta;
- struct tid_ampdu_rx *tid_agg_rx;
- u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
- u8 dialog_token;
- int ret = -EOPNOTSUPP;
- DECLARE_MAC_BUF(mac);
-
- rcu_read_lock();
-
- sta = sta_info_get(local, mgmt->sa);
- if (!sta) {
- rcu_read_unlock();
- return;
- }
-
- /* extract session parameters from addba request frame */
- dialog_token = mgmt->u.action.u.addba_req.dialog_token;
- timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
- start_seq_num =
- le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
-
- capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
- ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
- tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
- buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
-
- status = WLAN_STATUS_REQUEST_DECLINED;
-
- /* sanity check for incoming parameters:
- * check if configuration can support the BA policy
- * and if buffer size does not exceeds max value */
- if (((ba_policy != 1)
- && (!(conf->ht_conf.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 "
- "%s on tid %u. policy %d, buffer size %d\n",
- print_mac(mac, mgmt->sa), tid, ba_policy,
- buf_size);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
- 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_info.ampdu_factor;
- }
-
-
- /* examine state machine */
- spin_lock_bh(&sta->ampdu_mlme.ampdu_rx);
-
- if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_IDLE) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
- if (net_ratelimit())
- printk(KERN_DEBUG "unexpected AddBA Req from "
- "%s on tid %u\n",
- print_mac(mac, mgmt->sa), tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
- goto end;
- }
-
- /* prepare A-MPDU MLME for Rx aggregation */
- sta->ampdu_mlme.tid_rx[tid] =
- kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC);
- if (!sta->ampdu_mlme.tid_rx[tid]) {
- if (net_ratelimit())
- printk(KERN_ERR "allocate rx mlme to tid %d failed\n",
- tid);
- goto end;
- }
- /* rx timer */
- sta->ampdu_mlme.tid_rx[tid]->session_timer.function =
- sta_rx_agg_session_timer_expired;
- sta->ampdu_mlme.tid_rx[tid]->session_timer.data =
- (unsigned long)&sta->timer_to_tid[tid];
- init_timer(&sta->ampdu_mlme.tid_rx[tid]->session_timer);
-
- tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
-
- /* prepare reordering buffer */
- tid_agg_rx->reorder_buf =
- kmalloc(buf_size * sizeof(struct sk_buff *), GFP_ATOMIC);
- if (!tid_agg_rx->reorder_buf) {
- if (net_ratelimit())
- printk(KERN_ERR "can not allocate reordering buffer "
- "to tid %d\n", tid);
- kfree(sta->ampdu_mlme.tid_rx[tid]);
- goto end;
- }
- memset(tid_agg_rx->reorder_buf, 0,
- buf_size * sizeof(struct sk_buff *));
-
- if (local->ops->ampdu_action)
- ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_START,
- sta->addr, 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 */
-
- if (ret) {
- kfree(tid_agg_rx->reorder_buf);
- kfree(tid_agg_rx);
- sta->ampdu_mlme.tid_rx[tid] = NULL;
- goto end;
- }
-
- /* change state and send addba resp */
- sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_OPERATIONAL;
- tid_agg_rx->dialog_token = dialog_token;
- tid_agg_rx->ssn = start_seq_num;
- tid_agg_rx->head_seq_num = start_seq_num;
- tid_agg_rx->buf_size = buf_size;
- tid_agg_rx->timeout = timeout;
- tid_agg_rx->stored_mpdu_num = 0;
- status = WLAN_STATUS_SUCCESS;
-end:
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx);
-
-end_no_lock:
- ieee80211_send_addba_resp(sta->sdata->dev, sta->addr, tid,
- dialog_token, status, 1, buf_size, timeout);
- rcu_read_unlock();
-}
-
-static void ieee80211_sta_process_addba_resp(struct net_device *dev,
- struct ieee80211_mgmt *mgmt,
- size_t len)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_hw *hw = &local->hw;
- struct sta_info *sta;
- u16 capab;
- u16 tid;
- u8 *state;
-
- rcu_read_lock();
-
- sta = sta_info_get(local, mgmt->sa);
- if (!sta) {
- rcu_read_unlock();
- return;
- }
-
- capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
- tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
-
- state = &sta->ampdu_mlme.tid_state_tx[tid];
-
- spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
-
- if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
- printk(KERN_DEBUG "state not HT_ADDBA_REQUESTED_MSK:"
- "%d\n", *state);
- goto addba_resp_exit;
- }
-
- if (mgmt->u.action.u.addba_resp.dialog_token !=
- sta->ampdu_mlme.tid_tx[tid]->dialog_token) {
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "wrong addBA response token, tid %d\n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
- goto addba_resp_exit;
- }
-
- del_timer_sync(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "switched off addBA timer for tid %d \n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
- if (le16_to_cpu(mgmt->u.action.u.addba_resp.status)
- == WLAN_STATUS_SUCCESS) {
- if (*state & HT_ADDBA_RECEIVED_MSK)
- printk(KERN_DEBUG "double addBA response\n");
-
- *state |= HT_ADDBA_RECEIVED_MSK;
- sta->ampdu_mlme.addba_req_num[tid] = 0;
-
- if (*state == HT_AGG_STATE_OPERATIONAL) {
- printk(KERN_DEBUG "Aggregation on for tid %d \n", tid);
- ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
- }
-
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
- printk(KERN_DEBUG "recipient accepted agg: tid %d \n", tid);
- } else {
- printk(KERN_DEBUG "recipient rejected agg: tid %d \n", tid);
-
- sta->ampdu_mlme.addba_req_num[tid]++;
- /* this will allow the state check in stop_BA_session */
- *state = HT_AGG_STATE_OPERATIONAL;
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
- ieee80211_stop_tx_ba_session(hw, sta->addr, tid,
- WLAN_BACK_INITIATOR);
- }
-
-addba_resp_exit:
- rcu_read_unlock();
-}
-
-void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid,
- u16 initiator, u16 reason_code)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt;
- u16 params;
-
- skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom + 1 +
- sizeof(mgmt->u.action.u.delba));
-
- if (!skb) {
- printk(KERN_ERR "%s: failed to allocate buffer "
- "for delba frame\n", dev->name);
- 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, dev->dev_addr, ETH_ALEN);
- if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
- memcpy(mgmt->bssid, dev->dev_addr, ETH_ALEN);
- else
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
- IEEE80211_STYPE_ACTION);
-
- skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba));
-
- mgmt->u.action.category = WLAN_CATEGORY_BACK;
- mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA;
- params = (u16)(initiator << 11); /* bit 11 initiator */
- params |= (u16)(tid << 12); /* bit 15:12 TID number */
-
- mgmt->u.action.u.delba.params = cpu_to_le16(params);
- mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);
-
- ieee80211_sta_tx(dev, skb, 0);
-}
-
-void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *ra, u16 tid,
- u16 initiator, u16 reason)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_hw *hw = &local->hw;
- struct sta_info *sta;
- int ret, i;
- DECLARE_MAC_BUF(mac);
-
- rcu_read_lock();
-
- sta = sta_info_get(local, ra);
- if (!sta) {
- rcu_read_unlock();
- return;
- }
-
- /* check if TID is in operational state */
- spin_lock_bh(&sta->ampdu_mlme.ampdu_rx);
- if (sta->ampdu_mlme.tid_state_rx[tid]
- != HT_AGG_STATE_OPERATIONAL) {
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx);
- rcu_read_unlock();
- return;
- }
- sta->ampdu_mlme.tid_state_rx[tid] =
- HT_AGG_STATE_REQ_STOP_BA_MSK |
- (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx);
-
- /* stop HW Rx aggregation. ampdu_action existence
- * already verified in session init so we add the BUG_ON */
- BUG_ON(!local->ops->ampdu_action);
-
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Rx BA session stop requested for %s tid %u\n",
- print_mac(mac, ra), tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-
- ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP,
- ra, tid, NULL);
- if (ret)
- printk(KERN_DEBUG "HW problem - can not stop rx "
- "aggergation for tid %d\n", tid);
-
- /* shutdown timer has not expired */
- if (initiator != WLAN_BACK_TIMER)
- del_timer_sync(&sta->ampdu_mlme.tid_rx[tid]->session_timer);
-
- /* check if this is a self generated aggregation halt */
- if (initiator == WLAN_BACK_RECIPIENT || initiator == WLAN_BACK_TIMER)
- ieee80211_send_delba(dev, ra, tid, 0, reason);
-
- /* free the reordering buffer */
- for (i = 0; i < sta->ampdu_mlme.tid_rx[tid]->buf_size; i++) {
- if (sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]) {
- /* release the reordered frames */
- dev_kfree_skb(sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]);
- sta->ampdu_mlme.tid_rx[tid]->stored_mpdu_num--;
- sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i] = NULL;
- }
- }
- /* free resources */
- kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_buf);
- kfree(sta->ampdu_mlme.tid_rx[tid]);
- sta->ampdu_mlme.tid_rx[tid] = NULL;
- sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_IDLE;
-
- rcu_read_unlock();
-}
-
-
-static void ieee80211_sta_process_delba(struct net_device *dev,
- struct ieee80211_mgmt *mgmt, size_t len)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct sta_info *sta;
- u16 tid, params;
- u16 initiator;
- DECLARE_MAC_BUF(mac);
-
- rcu_read_lock();
-
- sta = sta_info_get(local, mgmt->sa);
- if (!sta) {
- rcu_read_unlock();
- return;
- }
-
- params = le16_to_cpu(mgmt->u.action.u.delba.params);
- 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 %s (%s) tid %d reason code %d\n",
- print_mac(mac, mgmt->sa),
- initiator ? "initiator" : "recipient", tid,
- mgmt->u.action.u.delba.reason_code);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-
- if (initiator == WLAN_BACK_INITIATOR)
- ieee80211_sta_stop_rx_ba_session(dev, sta->addr, tid,
- WLAN_BACK_INITIATOR, 0);
- else { /* WLAN_BACK_RECIPIENT */
- spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
- sta->ampdu_mlme.tid_state_tx[tid] =
- HT_AGG_STATE_OPERATIONAL;
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
- ieee80211_stop_tx_ba_session(&local->hw, sta->addr, tid,
- WLAN_BACK_RECIPIENT);
- }
- rcu_read_unlock();
-}
-
-/*
- * After sending add Block Ack request we activated a timer until
- * add Block Ack response will arrive from the recipient.
- * If this timer expires sta_addba_resp_timer_expired will be executed.
- */
-void sta_addba_resp_timer_expired(unsigned long data)
-{
- /* not an elegant detour, but there is no choice as the timer passes
- * only one argument, and both sta_info and TID are needed, so init
- * flow in sta_info_create gives the TID as data, while the timer_to_id
- * array gives the sta through container_of */
- u16 tid = *(u8 *)data;
- struct sta_info *temp_sta = container_of((void *)data,
- struct sta_info, timer_to_tid[tid]);
-
- struct ieee80211_local *local = temp_sta->local;
- struct ieee80211_hw *hw = &local->hw;
- struct sta_info *sta;
- u8 *state;
-
- rcu_read_lock();
-
- sta = sta_info_get(local, temp_sta->addr);
- if (!sta) {
- rcu_read_unlock();
- return;
- }
-
- state = &sta->ampdu_mlme.tid_state_tx[tid];
- /* check if the TID waits for addBA response */
- spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
- if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
- *state = HT_AGG_STATE_IDLE;
- printk(KERN_DEBUG "timer expired on tid %d but we are not "
- "expecting addBA response there", tid);
- goto timer_expired_exit;
- }
-
- printk(KERN_DEBUG "addBA response timer expired on tid %d\n", tid);
-
- /* go through the state check in stop_BA_session */
- *state = HT_AGG_STATE_OPERATIONAL;
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
- ieee80211_stop_tx_ba_session(hw, temp_sta->addr, tid,
- WLAN_BACK_INITIATOR);
-
-timer_expired_exit:
- rcu_read_unlock();
-}
-
-/*
- * After accepting the AddBA Request we activated a timer,
- * resetting it after each frame that arrives from the originator.
- * if this timer expires ieee80211_sta_stop_rx_ba_session will be executed.
- */
-void sta_rx_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]);
-
- printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid);
- ieee80211_sta_stop_rx_ba_session(sta->sdata->dev, sta->addr,
- (u16)*ptid, WLAN_BACK_TIMER,
- WLAN_REASON_QSTA_TIMEOUT);
-}
-
-void ieee80211_sta_tear_down_BA_sessions(struct net_device *dev, u8 *addr)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- int i;
-
- for (i = 0; i < STA_TID_NUM; i++) {
- ieee80211_stop_tx_ba_session(&local->hw, addr, i,
- WLAN_BACK_INITIATOR);
- ieee80211_sta_stop_rx_ba_session(dev, addr, i,
- WLAN_BACK_RECIPIENT,
- WLAN_REASON_QSTA_LEAVE_QBSS);
- }
-}
-
-static void ieee80211_rx_mgmt_auth(struct net_device *dev,
+static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
u16 auth_alg, auth_transaction, status_code;
DECLARE_MAC_BUF(mac);
- if (ifsta->state != IEEE80211_AUTHENTICATE &&
- sdata->vif.type != IEEE80211_IF_TYPE_IBSS) {
- printk(KERN_DEBUG "%s: authentication frame received from "
- "%s, but not in authenticate state - ignored\n",
- dev->name, print_mac(mac, mgmt->sa));
+ if (ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE &&
+ sdata->vif.type != NL80211_IFTYPE_ADHOC)
return;
- }
- if (len < 24 + 6) {
- printk(KERN_DEBUG "%s: too short (%zd) authentication frame "
- "received from %s - ignored\n",
- dev->name, len, print_mac(mac, mgmt->sa));
+ if (len < 24 + 6)
return;
- }
- if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
- memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) {
- printk(KERN_DEBUG "%s: authentication frame received from "
- "unknown AP (SA=%s BSSID=%s) - "
- "ignored\n", dev->name, print_mac(mac, mgmt->sa),
- print_mac(mac, mgmt->bssid));
+ if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0)
return;
- }
- if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
- memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0) {
- printk(KERN_DEBUG "%s: authentication frame received from "
- "unknown BSSID (SA=%s BSSID=%s) - "
- "ignored\n", dev->name, print_mac(mac, mgmt->sa),
- print_mac(mac, mgmt->bssid));
+ if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0)
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);
- printk(KERN_DEBUG "%s: RX authentication from %s (alg=%d "
- "transaction=%d status=%d)\n",
- dev->name, print_mac(mac, mgmt->sa), auth_alg,
- auth_transaction, status_code);
-
- if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
- /* IEEE 802.11 standard does not require authentication in IBSS
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ /*
+ * 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.
- * TODO: Could implement shared key authentication. */
- if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) {
- printk(KERN_DEBUG "%s: unexpected IBSS authentication "
- "frame (alg=%d transaction=%d)\n",
- dev->name, auth_alg, auth_transaction);
+ */
+ if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1)
return;
- }
- ieee80211_send_auth(dev, ifsta, 2, NULL, 0, 0);
+ ieee80211_send_auth(sdata, ifsta, 2, NULL, 0, 0);
}
if (auth_alg != ifsta->auth_alg ||
- auth_transaction != ifsta->auth_transaction) {
- printk(KERN_DEBUG "%s: unexpected authentication frame "
- "(alg=%d transaction=%d)\n",
- dev->name, auth_alg, auth_transaction);
+ auth_transaction != ifsta->auth_transaction)
return;
- }
if (status_code != WLAN_STATUS_SUCCESS) {
- printk(KERN_DEBUG "%s: AP denied authentication (auth_alg=%d "
- "code=%d)\n", dev->name, ifsta->auth_alg, status_code);
if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) {
u8 algs[3];
const int num_algs = ARRAY_SIZE(algs);
@@ -1795,12 +1095,9 @@ static void ieee80211_rx_mgmt_auth(struct net_device *dev,
algs[pos] == 0xff)
continue;
if (algs[pos] == WLAN_AUTH_SHARED_KEY &&
- !ieee80211_sta_wep_configured(dev))
+ !ieee80211_sta_wep_configured(sdata))
continue;
ifsta->auth_alg = algs[pos];
- printk(KERN_DEBUG "%s: set auth_alg=%d for "
- "next try\n",
- dev->name, ifsta->auth_alg);
break;
}
}
@@ -1810,19 +1107,19 @@ static void ieee80211_rx_mgmt_auth(struct net_device *dev,
switch (ifsta->auth_alg) {
case WLAN_AUTH_OPEN:
case WLAN_AUTH_LEAP:
- ieee80211_auth_completed(dev, ifsta);
+ ieee80211_auth_completed(sdata, ifsta);
break;
case WLAN_AUTH_SHARED_KEY:
if (ifsta->auth_transaction == 4)
- ieee80211_auth_completed(dev, ifsta);
+ ieee80211_auth_completed(sdata, ifsta);
else
- ieee80211_auth_challenge(dev, ifsta, mgmt, len);
+ ieee80211_auth_challenge(sdata, ifsta, mgmt, len);
break;
}
}
-static void ieee80211_rx_mgmt_deauth(struct net_device *dev,
+static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len)
@@ -1830,45 +1127,31 @@ static void ieee80211_rx_mgmt_deauth(struct net_device *dev,
u16 reason_code;
DECLARE_MAC_BUF(mac);
- if (len < 24 + 2) {
- printk(KERN_DEBUG "%s: too short (%zd) deauthentication frame "
- "received from %s - ignored\n",
- dev->name, len, print_mac(mac, mgmt->sa));
+ if (len < 24 + 2)
return;
- }
- if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) {
- printk(KERN_DEBUG "%s: deauthentication frame received from "
- "unknown AP (SA=%s BSSID=%s) - "
- "ignored\n", dev->name, print_mac(mac, mgmt->sa),
- print_mac(mac, mgmt->bssid));
+ if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN))
return;
- }
reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
- printk(KERN_DEBUG "%s: RX deauthentication from %s"
- " (reason=%d)\n",
- dev->name, print_mac(mac, mgmt->sa), reason_code);
-
- if (ifsta->flags & IEEE80211_STA_AUTHENTICATED) {
- printk(KERN_DEBUG "%s: deauthenticated\n", dev->name);
- }
+ if (ifsta->flags & IEEE80211_STA_AUTHENTICATED)
+ printk(KERN_DEBUG "%s: deauthenticated\n", sdata->dev->name);
- if (ifsta->state == IEEE80211_AUTHENTICATE ||
- ifsta->state == IEEE80211_ASSOCIATE ||
- ifsta->state == IEEE80211_ASSOCIATED) {
- ifsta->state = IEEE80211_AUTHENTICATE;
+ if (ifsta->state == IEEE80211_STA_MLME_AUTHENTICATE ||
+ ifsta->state == IEEE80211_STA_MLME_ASSOCIATE ||
+ ifsta->state == IEEE80211_STA_MLME_ASSOCIATED) {
+ ifsta->state = IEEE80211_STA_MLME_DIRECT_PROBE;
mod_timer(&ifsta->timer, jiffies +
IEEE80211_RETRY_AUTH_INTERVAL);
}
- ieee80211_set_disassoc(dev, ifsta, 1);
+ ieee80211_set_disassoc(sdata, ifsta, true, false, 0);
ifsta->flags &= ~IEEE80211_STA_AUTHENTICATED;
}
-static void ieee80211_rx_mgmt_disassoc(struct net_device *dev,
+static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len)
@@ -1876,37 +1159,24 @@ static void ieee80211_rx_mgmt_disassoc(struct net_device *dev,
u16 reason_code;
DECLARE_MAC_BUF(mac);
- if (len < 24 + 2) {
- printk(KERN_DEBUG "%s: too short (%zd) disassociation frame "
- "received from %s - ignored\n",
- dev->name, len, print_mac(mac, mgmt->sa));
+ if (len < 24 + 2)
return;
- }
- if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) {
- printk(KERN_DEBUG "%s: disassociation frame received from "
- "unknown AP (SA=%s BSSID=%s) - "
- "ignored\n", dev->name, print_mac(mac, mgmt->sa),
- print_mac(mac, mgmt->bssid));
+ if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN))
return;
- }
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
- printk(KERN_DEBUG "%s: RX disassociation from %s"
- " (reason=%d)\n",
- dev->name, print_mac(mac, mgmt->sa), reason_code);
-
if (ifsta->flags & IEEE80211_STA_ASSOCIATED)
- printk(KERN_DEBUG "%s: disassociated\n", dev->name);
+ printk(KERN_DEBUG "%s: disassociated\n", sdata->dev->name);
- if (ifsta->state == IEEE80211_ASSOCIATED) {
- ifsta->state = IEEE80211_ASSOCIATE;
+ if (ifsta->state == IEEE80211_STA_MLME_ASSOCIATED) {
+ ifsta->state = IEEE80211_STA_MLME_ASSOCIATE;
mod_timer(&ifsta->timer, jiffies +
IEEE80211_RETRY_AUTH_INTERVAL);
}
- ieee80211_set_disassoc(dev, ifsta, 0);
+ ieee80211_set_disassoc(sdata, ifsta, false, false, 0);
}
@@ -1917,7 +1187,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
int reassoc)
{
struct ieee80211_local *local = sdata->local;
- struct net_device *dev = sdata->dev;
struct ieee80211_supported_band *sband;
struct sta_info *sta;
u64 rates, basic_rates;
@@ -1932,27 +1201,14 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
/* AssocResp and ReassocResp have identical structure, so process both
* of them in this function. */
- if (ifsta->state != IEEE80211_ASSOCIATE) {
- printk(KERN_DEBUG "%s: association frame received from "
- "%s, but not in associate state - ignored\n",
- dev->name, print_mac(mac, mgmt->sa));
+ if (ifsta->state != IEEE80211_STA_MLME_ASSOCIATE)
return;
- }
- if (len < 24 + 6) {
- printk(KERN_DEBUG "%s: too short (%zd) association frame "
- "received from %s - ignored\n",
- dev->name, len, print_mac(mac, mgmt->sa));
+ if (len < 24 + 6)
return;
- }
- if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) {
- printk(KERN_DEBUG "%s: association frame received from "
- "unknown AP (SA=%s BSSID=%s) - "
- "ignored\n", dev->name, print_mac(mac, mgmt->sa),
- print_mac(mac, mgmt->bssid));
+ if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0)
return;
- }
capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
@@ -1960,12 +1216,12 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: RX %sssocResp from %s (capab=0x%x "
"status=%d aid=%d)\n",
- dev->name, reassoc ? "Rea" : "A", print_mac(mac, mgmt->sa),
+ sdata->dev->name, reassoc ? "Rea" : "A", print_mac(mac, mgmt->sa),
capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
if (status_code != WLAN_STATUS_SUCCESS) {
printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
- dev->name, status_code);
+ sdata->dev->name, status_code);
/* if this was a reassociation, ensure we try a "full"
* association next time. This works around some broken APs
* which do not correctly reject reassociation requests. */
@@ -1975,7 +1231,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not "
- "set\n", dev->name, aid);
+ "set\n", sdata->dev->name, aid);
aid &= ~(BIT(15) | BIT(14));
pos = mgmt->u.assoc_resp.variable;
@@ -1983,11 +1239,11 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
if (!elems.supp_rates) {
printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n",
- dev->name);
+ sdata->dev->name);
return;
}
- printk(KERN_DEBUG "%s: associated\n", dev->name);
+ printk(KERN_DEBUG "%s: associated\n", sdata->dev->name);
ifsta->aid = aid;
ifsta->ap_capab = capab_info;
@@ -2002,33 +1258,35 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
/* Add STA entry for the AP */
sta = sta_info_get(local, ifsta->bssid);
if (!sta) {
- struct ieee80211_sta_bss *bss;
+ struct ieee80211_bss *bss;
int err;
sta = sta_info_alloc(sdata, ifsta->bssid, GFP_ATOMIC);
if (!sta) {
printk(KERN_DEBUG "%s: failed to alloc STA entry for"
- " the AP\n", dev->name);
+ " the AP\n", sdata->dev->name);
rcu_read_unlock();
return;
}
- bss = ieee80211_rx_bss_get(dev, ifsta->bssid,
+ bss = ieee80211_rx_bss_get(local, ifsta->bssid,
local->hw.conf.channel->center_freq,
ifsta->ssid, ifsta->ssid_len);
if (bss) {
- sta->last_rssi = bss->rssi;
sta->last_signal = bss->signal;
+ sta->last_qual = bss->qual;
sta->last_noise = bss->noise;
- ieee80211_rx_bss_put(dev, bss);
+ ieee80211_rx_bss_put(local, bss);
}
err = sta_info_insert(sta);
if (err) {
printk(KERN_DEBUG "%s: failed to insert STA entry for"
- " the AP (error %d)\n", dev->name, err);
+ " the AP (error %d)\n", sdata->dev->name, err);
rcu_read_unlock();
return;
}
+ /* update new sta with its last rx activity */
+ sta->last_rx = jiffies;
}
/*
@@ -2041,8 +1299,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
* to between the sta_info_alloc() and sta_info_insert() above.
*/
- sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_AP |
- WLAN_STA_AUTHORIZED;
+ set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_AP |
+ WLAN_STA_AUTHORIZED);
rates = 0;
basic_rates = 0;
@@ -2076,8 +1334,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
}
}
- sta->supp_rates[local->hw.conf.channel->band] = rates;
- sdata->basic_rates = basic_rates;
+ sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
+ sdata->bss_conf.basic_rates = basic_rates;
/* cf. IEEE 802.11 9.2.12 */
if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
@@ -2086,23 +1344,22 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
else
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
- if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param) {
+ if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param &&
+ (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
struct ieee80211_ht_bss_info bss_info;
ieee80211_ht_cap_ie_to_ht_info(
- (struct ieee80211_ht_cap *)
- elems.ht_cap_elem, &sta->ht_info);
+ elems.ht_cap_elem, &sta->sta.ht_info);
ieee80211_ht_addt_info_ie_to_ht_bss_info(
- (struct ieee80211_ht_addt_info *)
elems.ht_info_elem, &bss_info);
- ieee80211_handle_ht(local, 1, &sta->ht_info, &bss_info);
+ ieee80211_handle_ht(local, 1, &sta->sta.ht_info, &bss_info);
}
- rate_control_rate_init(sta, local);
+ rate_control_rate_init(sta);
- if (elems.wmm_param && (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
- sta->flags |= WLAN_STA_WME;
+ if (elems.wmm_param) {
+ set_sta_flags(sta, WLAN_STA_WME);
rcu_read_unlock();
- ieee80211_sta_wmm_params(dev, ifsta, elems.wmm_param,
+ ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
elems.wmm_param_len);
} else
rcu_read_unlock();
@@ -2111,240 +1368,26 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
* ieee80211_set_associated() will tell the driver */
bss_conf->aid = aid;
bss_conf->assoc_capability = capab_info;
- ieee80211_set_associated(dev, ifsta, 1);
+ ieee80211_set_associated(sdata, ifsta);
- ieee80211_associated(dev, ifsta);
+ ieee80211_associated(sdata, ifsta);
}
-/* Caller must hold local->sta_bss_lock */
-static void __ieee80211_rx_bss_hash_add(struct net_device *dev,
- struct ieee80211_sta_bss *bss)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- u8 hash_idx;
-
- if (bss_mesh_cfg(bss))
- hash_idx = mesh_id_hash(bss_mesh_id(bss),
- bss_mesh_id_len(bss));
- else
- hash_idx = STA_HASH(bss->bssid);
-
- bss->hnext = local->sta_bss_hash[hash_idx];
- local->sta_bss_hash[hash_idx] = bss;
-}
-
-
-/* Caller must hold local->sta_bss_lock */
-static void __ieee80211_rx_bss_hash_del(struct net_device *dev,
- struct ieee80211_sta_bss *bss)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sta_bss *b, *prev = NULL;
- b = local->sta_bss_hash[STA_HASH(bss->bssid)];
- while (b) {
- if (b == bss) {
- if (!prev)
- local->sta_bss_hash[STA_HASH(bss->bssid)] =
- bss->hnext;
- else
- prev->hnext = bss->hnext;
- break;
- }
- prev = b;
- b = b->hnext;
- }
-}
-
-
-static struct ieee80211_sta_bss *
-ieee80211_rx_bss_add(struct net_device *dev, u8 *bssid, int freq,
- u8 *ssid, u8 ssid_len)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sta_bss *bss;
-
- bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
- if (!bss)
- return NULL;
- atomic_inc(&bss->users);
- atomic_inc(&bss->users);
- memcpy(bss->bssid, bssid, ETH_ALEN);
- bss->freq = freq;
- if (ssid && ssid_len <= IEEE80211_MAX_SSID_LEN) {
- memcpy(bss->ssid, ssid, ssid_len);
- bss->ssid_len = ssid_len;
- }
-
- spin_lock_bh(&local->sta_bss_lock);
- /* TODO: order by RSSI? */
- list_add_tail(&bss->list, &local->sta_bss_list);
- __ieee80211_rx_bss_hash_add(dev, bss);
- spin_unlock_bh(&local->sta_bss_lock);
- return bss;
-}
-
-static struct ieee80211_sta_bss *
-ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid, int freq,
- u8 *ssid, u8 ssid_len)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sta_bss *bss;
-
- spin_lock_bh(&local->sta_bss_lock);
- bss = local->sta_bss_hash[STA_HASH(bssid)];
- while (bss) {
- if (!bss_mesh_cfg(bss) &&
- !memcmp(bss->bssid, bssid, ETH_ALEN) &&
- bss->freq == freq &&
- bss->ssid_len == ssid_len &&
- (ssid_len == 0 || !memcmp(bss->ssid, ssid, ssid_len))) {
- atomic_inc(&bss->users);
- break;
- }
- bss = bss->hnext;
- }
- spin_unlock_bh(&local->sta_bss_lock);
- return bss;
-}
-
-#ifdef CONFIG_MAC80211_MESH
-static struct ieee80211_sta_bss *
-ieee80211_rx_mesh_bss_get(struct net_device *dev, u8 *mesh_id, int mesh_id_len,
- u8 *mesh_cfg, int freq)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sta_bss *bss;
-
- spin_lock_bh(&local->sta_bss_lock);
- bss = local->sta_bss_hash[mesh_id_hash(mesh_id, mesh_id_len)];
- while (bss) {
- if (bss_mesh_cfg(bss) &&
- !memcmp(bss_mesh_cfg(bss), mesh_cfg, MESH_CFG_CMP_LEN) &&
- bss->freq == freq &&
- mesh_id_len == bss->mesh_id_len &&
- (mesh_id_len == 0 || !memcmp(bss->mesh_id, mesh_id,
- mesh_id_len))) {
- atomic_inc(&bss->users);
- break;
- }
- bss = bss->hnext;
- }
- spin_unlock_bh(&local->sta_bss_lock);
- return bss;
-}
-
-static struct ieee80211_sta_bss *
-ieee80211_rx_mesh_bss_add(struct net_device *dev, u8 *mesh_id, int mesh_id_len,
- u8 *mesh_cfg, int mesh_config_len, int freq)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sta_bss *bss;
-
- if (mesh_config_len != MESH_CFG_LEN)
- return NULL;
-
- bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
- if (!bss)
- return NULL;
-
- bss->mesh_cfg = kmalloc(MESH_CFG_CMP_LEN, GFP_ATOMIC);
- if (!bss->mesh_cfg) {
- kfree(bss);
- return NULL;
- }
-
- if (mesh_id_len && mesh_id_len <= IEEE80211_MAX_MESH_ID_LEN) {
- bss->mesh_id = kmalloc(mesh_id_len, GFP_ATOMIC);
- if (!bss->mesh_id) {
- kfree(bss->mesh_cfg);
- kfree(bss);
- return NULL;
- }
- memcpy(bss->mesh_id, mesh_id, mesh_id_len);
- }
-
- atomic_inc(&bss->users);
- atomic_inc(&bss->users);
- memcpy(bss->mesh_cfg, mesh_cfg, MESH_CFG_CMP_LEN);
- bss->mesh_id_len = mesh_id_len;
- bss->freq = freq;
- spin_lock_bh(&local->sta_bss_lock);
- /* TODO: order by RSSI? */
- list_add_tail(&bss->list, &local->sta_bss_list);
- __ieee80211_rx_bss_hash_add(dev, bss);
- spin_unlock_bh(&local->sta_bss_lock);
- return bss;
-}
-#endif
-
-static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss)
-{
- kfree(bss->wpa_ie);
- kfree(bss->rsn_ie);
- kfree(bss->wmm_ie);
- kfree(bss->ht_ie);
- kfree(bss_mesh_id(bss));
- kfree(bss_mesh_cfg(bss));
- kfree(bss);
-}
-
-
-static void ieee80211_rx_bss_put(struct net_device *dev,
- struct ieee80211_sta_bss *bss)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
- local_bh_disable();
- if (!atomic_dec_and_lock(&bss->users, &local->sta_bss_lock)) {
- local_bh_enable();
- return;
- }
-
- __ieee80211_rx_bss_hash_del(dev, bss);
- list_del(&bss->list);
- spin_unlock_bh(&local->sta_bss_lock);
- ieee80211_rx_bss_free(bss);
-}
-
-
-void ieee80211_rx_bss_list_init(struct net_device *dev)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- spin_lock_init(&local->sta_bss_lock);
- INIT_LIST_HEAD(&local->sta_bss_list);
-}
-
-
-void ieee80211_rx_bss_list_deinit(struct net_device *dev)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sta_bss *bss, *tmp;
-
- list_for_each_entry_safe(bss, tmp, &local->sta_bss_list, list)
- ieee80211_rx_bss_put(dev, bss);
-}
-
-
-static int ieee80211_sta_join_ibss(struct net_device *dev,
+static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta,
- struct ieee80211_sta_bss *bss)
+ struct ieee80211_bss *bss)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = sdata->local;
int res, rates, i, j;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- struct ieee80211_tx_control control;
- struct rate_selection ratesel;
u8 *pos;
- struct ieee80211_sub_if_data *sdata;
struct ieee80211_supported_band *sband;
union iwreq_data wrqu;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
/* Remove possible STA entries from other IBSS networks. */
sta_info_flush_delayed(sdata);
@@ -2353,7 +1396,7 @@ static int ieee80211_sta_join_ibss(struct net_device *dev,
local->ops->reset_tsf(local_to_hw(local));
}
memcpy(ifsta->bssid, bss->bssid, ETH_ALEN);
- res = ieee80211_if_config(dev);
+ res = ieee80211_if_config(sdata, IEEE80211_IFCC_BSSID);
if (res)
return res;
@@ -2362,29 +1405,27 @@ static int ieee80211_sta_join_ibss(struct net_device *dev,
sdata->drop_unencrypted = bss->capability &
WLAN_CAPABILITY_PRIVACY ? 1 : 0;
- res = ieee80211_set_freq(dev, bss->freq);
+ res = ieee80211_set_freq(sdata, bss->freq);
if (res)
return res;
- /* Set beacon template */
+ /* Build IBSS probe response */
skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
- do {
- if (!skb)
- break;
-
+ if (skb) {
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 = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
- IEEE80211_STYPE_BEACON);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_RESP);
memset(mgmt->da, 0xff, ETH_ALEN);
- memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
mgmt->u.beacon.beacon_int =
cpu_to_le16(local->hw.conf.beacon_int);
+ mgmt->u.beacon.timestamp = cpu_to_le64(bss->timestamp);
mgmt->u.beacon.capab_info = cpu_to_le16(bss->capability);
pos = skb_put(skb, 2 + ifsta->ssid_len);
@@ -2422,184 +1463,55 @@ static int ieee80211_sta_join_ibss(struct net_device *dev,
memcpy(pos, &bss->supp_rates[8], rates);
}
- memset(&control, 0, sizeof(control));
- rate_control_get_rate(dev, sband, skb, &ratesel);
- if (!ratesel.rate) {
- printk(KERN_DEBUG "%s: Failed to determine TX rate "
- "for IBSS beacon\n", dev->name);
- break;
- }
- control.vif = &sdata->vif;
- control.tx_rate = ratesel.rate;
- if (sdata->bss_conf.use_short_preamble &&
- ratesel.rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
- control.flags |= IEEE80211_TXCTL_SHORT_PREAMBLE;
- control.antenna_sel_tx = local->hw.conf.antenna_sel_tx;
- control.flags |= IEEE80211_TXCTL_NO_ACK;
- control.retry_limit = 1;
-
- ifsta->probe_resp = skb_copy(skb, GFP_ATOMIC);
- if (ifsta->probe_resp) {
- mgmt = (struct ieee80211_mgmt *)
- ifsta->probe_resp->data;
- mgmt->frame_control =
- IEEE80211_FC(IEEE80211_FTYPE_MGMT,
- IEEE80211_STYPE_PROBE_RESP);
- } else {
- printk(KERN_DEBUG "%s: Could not allocate ProbeResp "
- "template for IBSS\n", dev->name);
- }
+ ifsta->probe_resp = skb;
- if (local->ops->beacon_update &&
- local->ops->beacon_update(local_to_hw(local),
- skb, &control) == 0) {
- printk(KERN_DEBUG "%s: Configured IBSS beacon "
- "template\n", dev->name);
- skb = NULL;
- }
-
- rates = 0;
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- for (i = 0; i < bss->supp_rates_len; i++) {
- int bitrate = (bss->supp_rates[i] & 0x7f) * 5;
- for (j = 0; j < sband->n_bitrates; j++)
- if (sband->bitrates[j].bitrate == bitrate)
- rates |= BIT(j);
- }
- ifsta->supp_rates_bits[local->hw.conf.channel->band] = rates;
-
- ieee80211_sta_def_wmm_params(dev, bss, 1);
- } while (0);
+ ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
+ }
- if (skb) {
- printk(KERN_DEBUG "%s: Failed to configure IBSS beacon "
- "template\n", dev->name);
- dev_kfree_skb(skb);
+ rates = 0;
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+ for (i = 0; i < bss->supp_rates_len; i++) {
+ int bitrate = (bss->supp_rates[i] & 0x7f) * 5;
+ for (j = 0; j < sband->n_bitrates; j++)
+ if (sband->bitrates[j].bitrate == bitrate)
+ rates |= BIT(j);
}
+ ifsta->supp_rates_bits[local->hw.conf.channel->band] = rates;
+
+ ieee80211_sta_def_wmm_params(sdata, bss);
- ifsta->state = IEEE80211_IBSS_JOINED;
+ ifsta->state = IEEE80211_STA_MLME_IBSS_JOINED;
mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
+ ieee80211_led_assoc(local, true);
+
memset(&wrqu, 0, sizeof(wrqu));
memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN);
- wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+ wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL);
return res;
}
-u64 ieee80211_sta_get_rates(struct ieee80211_local *local,
- struct ieee802_11_elems *elems,
- enum ieee80211_band band)
-{
- struct ieee80211_supported_band *sband;
- struct ieee80211_rate *bitrates;
- size_t num_rates;
- u64 supp_rates;
- int i, j;
- sband = local->hw.wiphy->bands[band];
-
- if (!sband) {
- WARN_ON(1);
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- }
-
- 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;
- 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)
- supp_rates |= BIT(j);
- }
- return supp_rates;
-}
-
-
-static void ieee80211_rx_bss_info(struct net_device *dev,
+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 beacon)
+ struct ieee802_11_elems *elems,
+ bool beacon)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee802_11_elems elems;
- size_t baselen;
- int freq, clen;
- struct ieee80211_sta_bss *bss;
+ struct ieee80211_local *local = sdata->local;
+ int freq;
+ struct ieee80211_bss *bss;
struct sta_info *sta;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- u64 beacon_timestamp, rx_timestamp;
struct ieee80211_channel *channel;
+ u64 beacon_timestamp, rx_timestamp;
+ u64 supp_rates = 0;
+ enum ieee80211_band band = rx_status->band;
DECLARE_MAC_BUF(mac);
DECLARE_MAC_BUF(mac2);
- if (!beacon && memcmp(mgmt->da, dev->dev_addr, ETH_ALEN))
- return; /* ignore ProbeResp to foreign address */
-
-#if 0
- printk(KERN_DEBUG "%s: RX %s from %s to %s\n",
- dev->name, beacon ? "Beacon" : "Probe Response",
- print_mac(mac, mgmt->sa), print_mac(mac2, mgmt->da));
-#endif
-
- baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
- if (baselen > len)
- return;
-
- beacon_timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
- ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems);
-
- if (ieee80211_vif_is_mesh(&sdata->vif) && elems.mesh_id &&
- elems.mesh_config && mesh_matches_local(&elems, dev)) {
- u64 rates = ieee80211_sta_get_rates(local, &elems,
- rx_status->band);
-
- mesh_neighbour_update(mgmt->sa, rates, dev,
- mesh_peer_accepts_plinks(&elems, dev));
- }
-
- rcu_read_lock();
-
- if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && elems.supp_rates &&
- memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0 &&
- (sta = sta_info_get(local, mgmt->sa))) {
- u64 prev_rates;
- u64 supp_rates = ieee80211_sta_get_rates(local, &elems,
- rx_status->band);
-
- prev_rates = sta->supp_rates[rx_status->band];
- sta->supp_rates[rx_status->band] &= supp_rates;
- if (sta->supp_rates[rx_status->band] == 0) {
- /* No matching rates - this should not really happen.
- * Make sure that at least one rate is marked
- * supported to avoid issues with TX rate ctrl. */
- sta->supp_rates[rx_status->band] =
- sdata->u.sta.supp_rates_bits[rx_status->band];
- }
- if (sta->supp_rates[rx_status->band] != prev_rates) {
- printk(KERN_DEBUG "%s: updated supp_rates set for "
- "%s based on beacon info (0x%llx & 0x%llx -> "
- "0x%llx)\n",
- dev->name, print_mac(mac, sta->addr),
- (unsigned long long) prev_rates,
- (unsigned long long) supp_rates,
- (unsigned long long) sta->supp_rates[rx_status->band]);
- }
- }
-
- rcu_read_unlock();
-
- if (elems.ds_params && elems.ds_params_len == 1)
- freq = ieee80211_channel_to_frequency(elems.ds_params[0]);
+ if (elems->ds_params && elems->ds_params_len == 1)
+ freq = ieee80211_channel_to_frequency(elems->ds_params[0]);
else
freq = rx_status->freq;
@@ -2608,188 +1520,65 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
return;
-#ifdef CONFIG_MAC80211_MESH
- if (elems.mesh_config)
- bss = ieee80211_rx_mesh_bss_get(dev, elems.mesh_id,
- elems.mesh_id_len, elems.mesh_config, freq);
- else
-#endif
- bss = ieee80211_rx_bss_get(dev, mgmt->bssid, freq,
- elems.ssid, elems.ssid_len);
- if (!bss) {
-#ifdef CONFIG_MAC80211_MESH
- if (elems.mesh_config)
- bss = ieee80211_rx_mesh_bss_add(dev, elems.mesh_id,
- elems.mesh_id_len, elems.mesh_config,
- elems.mesh_config_len, freq);
- else
-#endif
- bss = ieee80211_rx_bss_add(dev, mgmt->bssid, freq,
- elems.ssid, elems.ssid_len);
- if (!bss)
- return;
- } else {
-#if 0
- /* TODO: order by RSSI? */
- spin_lock_bh(&local->sta_bss_lock);
- list_move_tail(&bss->list, &local->sta_bss_list);
- spin_unlock_bh(&local->sta_bss_lock);
-#endif
- }
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC && elems->supp_rates &&
+ memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) {
+ supp_rates = ieee80211_sta_get_rates(local, elems, band);
- /* 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;
- }
+ rcu_read_lock();
- if (elems.ht_cap_elem &&
- (!bss->ht_ie || bss->ht_ie_len != elems.ht_cap_elem_len ||
- memcmp(bss->ht_ie, elems.ht_cap_elem, elems.ht_cap_elem_len))) {
- kfree(bss->ht_ie);
- bss->ht_ie = kmalloc(elems.ht_cap_elem_len + 2, GFP_ATOMIC);
- if (bss->ht_ie) {
- memcpy(bss->ht_ie, elems.ht_cap_elem - 2,
- elems.ht_cap_elem_len + 2);
- bss->ht_ie_len = elems.ht_cap_elem_len + 2;
- } else
- bss->ht_ie_len = 0;
- } else if (!elems.ht_cap_elem && bss->ht_ie) {
- kfree(bss->ht_ie);
- bss->ht_ie = NULL;
- bss->ht_ie_len = 0;
- }
+ sta = sta_info_get(local, mgmt->sa);
+ if (sta) {
+ u64 prev_rates;
- bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int);
- bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info);
-
- bss->supp_rates_len = 0;
- if (elems.supp_rates) {
- clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
- if (clen > elems.supp_rates_len)
- clen = elems.supp_rates_len;
- memcpy(&bss->supp_rates[bss->supp_rates_len], elems.supp_rates,
- clen);
- bss->supp_rates_len += clen;
- }
- if (elems.ext_supp_rates) {
- clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
- if (clen > elems.ext_supp_rates_len)
- clen = elems.ext_supp_rates_len;
- memcpy(&bss->supp_rates[bss->supp_rates_len],
- elems.ext_supp_rates, clen);
- bss->supp_rates_len += clen;
+ 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);
+
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ if (sta->sta.supp_rates[band] != prev_rates)
+ printk(KERN_DEBUG "%s: updated supp_rates set "
+ "for %s based on beacon info (0x%llx | "
+ "0x%llx -> 0x%llx)\n",
+ sdata->dev->name,
+ print_mac(mac, sta->sta.addr),
+ (unsigned long long) prev_rates,
+ (unsigned long long) supp_rates,
+ (unsigned long long) sta->sta.supp_rates[band]);
+#endif
+ } else {
+ ieee80211_ibss_add_sta(sdata, NULL, mgmt->bssid,
+ mgmt->sa, supp_rates);
+ }
+
+ rcu_read_unlock();
}
- bss->band = rx_status->band;
+ bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
+ freq, beacon);
+ if (!bss)
+ return;
- bss->timestamp = beacon_timestamp;
- bss->last_update = jiffies;
- bss->rssi = rx_status->ssi;
- bss->signal = rx_status->signal;
- bss->noise = rx_status->noise;
- if (!beacon && !bss->probe_resp)
- bss->probe_resp = true;
+ /* was just updated in ieee80211_bss_info_update */
+ beacon_timestamp = bss->timestamp;
/*
* In STA mode, the remaining parameters should not be overridden
* by beacons because they're not necessarily accurate there.
*/
- if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
- bss->probe_resp && beacon) {
- ieee80211_rx_bss_put(dev, bss);
+ if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ bss->last_probe_resp && beacon) {
+ ieee80211_rx_bss_put(local, bss);
return;
}
- if (elems.wpa &&
- (!bss->wpa_ie || bss->wpa_ie_len != elems.wpa_len ||
- memcmp(bss->wpa_ie, elems.wpa, elems.wpa_len))) {
- kfree(bss->wpa_ie);
- bss->wpa_ie = kmalloc(elems.wpa_len + 2, GFP_ATOMIC);
- if (bss->wpa_ie) {
- memcpy(bss->wpa_ie, elems.wpa - 2, elems.wpa_len + 2);
- bss->wpa_ie_len = elems.wpa_len + 2;
- } else
- bss->wpa_ie_len = 0;
- } else if (!elems.wpa && bss->wpa_ie) {
- kfree(bss->wpa_ie);
- bss->wpa_ie = NULL;
- bss->wpa_ie_len = 0;
- }
-
- if (elems.rsn &&
- (!bss->rsn_ie || bss->rsn_ie_len != elems.rsn_len ||
- memcmp(bss->rsn_ie, elems.rsn, elems.rsn_len))) {
- kfree(bss->rsn_ie);
- bss->rsn_ie = kmalloc(elems.rsn_len + 2, GFP_ATOMIC);
- if (bss->rsn_ie) {
- memcpy(bss->rsn_ie, elems.rsn - 2, elems.rsn_len + 2);
- bss->rsn_ie_len = elems.rsn_len + 2;
- } else
- bss->rsn_ie_len = 0;
- } else if (!elems.rsn && bss->rsn_ie) {
- kfree(bss->rsn_ie);
- bss->rsn_ie = NULL;
- bss->rsn_ie_len = 0;
- }
-
- /*
- * Cf.
- * http://www.wipo.int/pctdb/en/wo.jsp?wo=2007047181&IA=WO2007047181&DISPLAY=DESC
- *
- * quoting:
- *
- * In particular, "Wi-Fi CERTIFIED for WMM - Support for Multimedia
- * Applications with Quality of Service in Wi-Fi Networks," Wi- Fi
- * Alliance (September 1, 2004) is incorporated by reference herein.
- * The inclusion of the WMM Parameters in probe responses and
- * association responses is mandatory for WMM enabled networks. The
- * inclusion of the WMM Parameters in beacons, however, is optional.
- */
-
- if (elems.wmm_param &&
- (!bss->wmm_ie || bss->wmm_ie_len != elems.wmm_param_len ||
- memcmp(bss->wmm_ie, elems.wmm_param, elems.wmm_param_len))) {
- kfree(bss->wmm_ie);
- bss->wmm_ie = kmalloc(elems.wmm_param_len + 2, GFP_ATOMIC);
- if (bss->wmm_ie) {
- memcpy(bss->wmm_ie, elems.wmm_param - 2,
- elems.wmm_param_len + 2);
- bss->wmm_ie_len = elems.wmm_param_len + 2;
- } else
- bss->wmm_ie_len = 0;
- } else if (elems.wmm_info &&
- (!bss->wmm_ie || bss->wmm_ie_len != elems.wmm_info_len ||
- memcmp(bss->wmm_ie, elems.wmm_info, elems.wmm_info_len))) {
- /* As for certain AP's Fifth bit is not set in WMM IE in
- * beacon frames.So while parsing the beacon frame the
- * wmm_info structure is used instead of wmm_param.
- * wmm_info structure was never used to set bss->wmm_ie.
- * This code fixes this problem by copying the WME
- * information from wmm_info to bss->wmm_ie and enabling
- * n-band association.
- */
- kfree(bss->wmm_ie);
- bss->wmm_ie = kmalloc(elems.wmm_info_len + 2, GFP_ATOMIC);
- if (bss->wmm_ie) {
- memcpy(bss->wmm_ie, elems.wmm_info - 2,
- elems.wmm_info_len + 2);
- bss->wmm_ie_len = elems.wmm_info_len + 2;
- } else
- bss->wmm_ie_len = 0;
- } else if (!elems.wmm_param && !elems.wmm_info && bss->wmm_ie) {
- kfree(bss->wmm_ie);
- bss->wmm_ie = NULL;
- bss->wmm_ie_len = 0;
- }
-
/* check if we need to merge IBSS */
- if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && beacon &&
- !local->sta_sw_scanning && !local->sta_hw_scanning &&
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC && beacon &&
bss->capability & WLAN_CAPABILITY_IBSS &&
bss->freq == local->oper_channel->center_freq &&
- elems.ssid_len == sdata->u.sta.ssid_len &&
- memcmp(elems.ssid, sdata->u.sta.ssid, sdata->u.sta.ssid_len) == 0) {
+ elems->ssid_len == sdata->u.sta.ssid_len &&
+ memcmp(elems->ssid, sdata->u.sta.ssid,
+ sdata->u.sta.ssid_len) == 0) {
if (rx_status->flag & RX_FLAG_TSFT) {
/* in order for correct IBSS merging we need mactime
*
@@ -2806,7 +1595,7 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
* e.g: at 1 MBit that means mactime is 192 usec earlier
* (=24 bytes * 8 usecs/byte) than the beacon timestamp.
*/
- int rate = local->hw.wiphy->bands[rx_status->band]->
+ int rate = local->hw.wiphy->bands[band]->
bitrates[rx_status->rate_idx].bitrate;
rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate);
} else if (local && local->ops && local->ops->get_tsf)
@@ -2826,55 +1615,65 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
jiffies);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
if (beacon_timestamp > rx_timestamp) {
-#ifndef CONFIG_MAC80211_IBSS_DEBUG
- if (net_ratelimit())
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ printk(KERN_DEBUG "%s: beacon TSF higher than "
+ "local TSF - IBSS merge with BSSID %s\n",
+ sdata->dev->name, print_mac(mac, mgmt->bssid));
#endif
- printk(KERN_DEBUG "%s: beacon TSF higher than "
- "local TSF - IBSS merge with BSSID %s\n",
- dev->name, print_mac(mac, mgmt->bssid));
- ieee80211_sta_join_ibss(dev, &sdata->u.sta, bss);
- ieee80211_ibss_add_sta(dev, NULL,
- mgmt->bssid, mgmt->sa);
+ ieee80211_sta_join_ibss(sdata, &sdata->u.sta, bss);
+ ieee80211_ibss_add_sta(sdata, NULL,
+ mgmt->bssid, mgmt->sa,
+ supp_rates);
}
}
- ieee80211_rx_bss_put(dev, bss);
+ ieee80211_rx_bss_put(local, bss);
}
-static void ieee80211_rx_mgmt_probe_resp(struct net_device *dev,
+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)
{
- ieee80211_rx_bss_info(dev, mgmt, len, rx_status, 0);
+ size_t baselen;
+ struct ieee802_11_elems elems;
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+
+ if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN))
+ return; /* ignore ProbeResp to foreign address */
+
+ 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);
+
+ ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
+
+ /* direct probe may be part of the association flow */
+ if (test_and_clear_bit(IEEE80211_STA_REQ_DIRECT_PROBE,
+ &ifsta->request)) {
+ printk(KERN_DEBUG "%s direct probe responded\n",
+ sdata->dev->name);
+ ieee80211_authenticate(sdata, ifsta);
+ }
}
-static void ieee80211_rx_mgmt_beacon(struct net_device *dev,
+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)
{
- struct ieee80211_sub_if_data *sdata;
struct ieee80211_if_sta *ifsta;
size_t baselen;
struct ieee802_11_elems elems;
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_conf *conf = &local->hw.conf;
u32 changed = 0;
- ieee80211_rx_bss_info(dev, mgmt, len, rx_status, 1);
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
- return;
- ifsta = &sdata->u.sta;
-
- if (!(ifsta->flags & IEEE80211_STA_ASSOCIATED) ||
- memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0)
- return;
-
/* Process beacon from the current BSS */
baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
if (baselen > len)
@@ -2882,16 +1681,18 @@ static void ieee80211_rx_mgmt_beacon(struct net_device *dev,
ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems);
- if (elems.wmm_param && (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
- ieee80211_sta_wmm_params(dev, ifsta, elems.wmm_param,
- elems.wmm_param_len);
- }
+ ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true);
- /* Do not send changes to driver if we are scanning. This removes
- * requirement that driver's bss_info_changed function needs to be
- * atomic. */
- if (local->sta_sw_scanning || local->sta_hw_scanning)
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
return;
+ ifsta = &sdata->u.sta;
+
+ if (!(ifsta->flags & IEEE80211_STA_ASSOCIATED) ||
+ memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0)
+ return;
+
+ ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
+ elems.wmm_param_len);
if (elems.erp_info && elems.erp_info_len >= 1)
changed |= ieee80211_handle_erp_ie(sdata, elems.erp_info[0]);
@@ -2906,7 +1707,6 @@ static void ieee80211_rx_mgmt_beacon(struct net_device *dev,
struct ieee80211_ht_bss_info bss_info;
ieee80211_ht_addt_info_ie_to_ht_bss_info(
- (struct ieee80211_ht_addt_info *)
elems.ht_info_elem, &bss_info);
changed |= ieee80211_handle_ht(local, 1, &conf->ht_conf,
&bss_info);
@@ -2916,14 +1716,13 @@ static void ieee80211_rx_mgmt_beacon(struct net_device *dev,
}
-static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
+static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len,
struct ieee80211_rx_status *rx_status)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
int tx_last_beacon;
struct sk_buff *skb;
struct ieee80211_mgmt *resp;
@@ -2934,8 +1733,8 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
DECLARE_MAC_BUF(mac3);
#endif
- if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS ||
- ifsta->state != IEEE80211_IBSS_JOINED ||
+ if (sdata->vif.type != NL80211_IFTYPE_ADHOC ||
+ ifsta->state != IEEE80211_STA_MLME_IBSS_JOINED ||
len < 24 + 2 || !ifsta->probe_resp)
return;
@@ -2947,7 +1746,7 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
#ifdef CONFIG_MAC80211_IBSS_DEBUG
printk(KERN_DEBUG "%s: RX ProbeReq SA=%s DA=%s BSSID="
"%s (tx_last_beacon=%d)\n",
- dev->name, print_mac(mac, mgmt->sa), print_mac(mac2, mgmt->da),
+ sdata->dev->name, print_mac(mac, mgmt->sa), print_mac(mac2, mgmt->da),
print_mac(mac3, mgmt->bssid), tx_last_beacon);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
@@ -2962,11 +1761,11 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
pos = mgmt->u.probe_req.variable;
if (pos[0] != WLAN_EID_SSID ||
pos + 2 + pos[1] > end) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: Invalid SSID IE in ProbeReq "
- "from %s\n",
- dev->name, print_mac(mac, mgmt->sa));
- }
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ printk(KERN_DEBUG "%s: Invalid SSID IE in ProbeReq "
+ "from %s\n",
+ sdata->dev->name, print_mac(mac, mgmt->sa));
+#endif
return;
}
if (pos[1] != 0 &&
@@ -2985,71 +1784,15 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
memcpy(resp->da, mgmt->sa, ETH_ALEN);
#ifdef CONFIG_MAC80211_IBSS_DEBUG
printk(KERN_DEBUG "%s: Sending ProbeResp to %s\n",
- dev->name, print_mac(mac, resp->da));
+ sdata->dev->name, print_mac(mac, resp->da));
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
- ieee80211_sta_tx(dev, skb, 0);
+ ieee80211_tx_skb(sdata, skb, 0);
}
-static void ieee80211_rx_mgmt_action(struct net_device *dev,
- struct ieee80211_if_sta *ifsta,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- if (len < IEEE80211_MIN_ACTION_SIZE)
- return;
-
- switch (mgmt->u.action.category) {
- case WLAN_CATEGORY_BACK:
- switch (mgmt->u.action.u.addba_req.action_code) {
- case WLAN_ACTION_ADDBA_REQ:
- if (len < (IEEE80211_MIN_ACTION_SIZE +
- sizeof(mgmt->u.action.u.addba_req)))
- break;
- ieee80211_sta_process_addba_request(dev, mgmt, len);
- break;
- case WLAN_ACTION_ADDBA_RESP:
- if (len < (IEEE80211_MIN_ACTION_SIZE +
- sizeof(mgmt->u.action.u.addba_resp)))
- break;
- ieee80211_sta_process_addba_resp(dev, mgmt, len);
- break;
- case WLAN_ACTION_DELBA:
- if (len < (IEEE80211_MIN_ACTION_SIZE +
- sizeof(mgmt->u.action.u.delba)))
- break;
- ieee80211_sta_process_delba(dev, mgmt, len);
- break;
- default:
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: Rx unknown A-MPDU action\n",
- dev->name);
- break;
- }
- break;
- case PLINK_CATEGORY:
- if (ieee80211_vif_is_mesh(&sdata->vif))
- mesh_rx_plink_frame(dev, mgmt, len, rx_status);
- break;
- case MESH_PATH_SEL_CATEGORY:
- if (ieee80211_vif_is_mesh(&sdata->vif))
- mesh_rx_path_sel_frame(dev, mgmt, len);
- break;
- default:
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: Rx unknown action frame - "
- "category=%d\n", dev->name, mgmt->u.action.category);
- break;
- }
-}
-
-void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
+void ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
struct ieee80211_rx_status *rx_status)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_if_sta *ifsta;
struct ieee80211_mgmt *mgmt;
u16 fc;
@@ -3057,7 +1800,6 @@ void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
if (skb->len < 24)
goto fail;
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
ifsta = &sdata->u.sta;
mgmt = (struct ieee80211_mgmt *) skb->data;
@@ -3067,7 +1809,6 @@ void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
case IEEE80211_STYPE_PROBE_REQ:
case IEEE80211_STYPE_PROBE_RESP:
case IEEE80211_STYPE_BEACON:
- case IEEE80211_STYPE_ACTION:
memcpy(skb->cb, rx_status, sizeof(*rx_status));
case IEEE80211_STYPE_AUTH:
case IEEE80211_STYPE_ASSOC_RESP:
@@ -3077,28 +1818,20 @@ void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
skb_queue_tail(&ifsta->skb_queue, skb);
queue_work(local->hw.workqueue, &ifsta->work);
return;
- default:
- printk(KERN_DEBUG "%s: received unknown management frame - "
- "stype=%d\n", dev->name,
- (fc & IEEE80211_FCTL_STYPE) >> 4);
- break;
}
fail:
kfree_skb(skb);
}
-
-static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
+static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
struct ieee80211_rx_status *rx_status;
- struct ieee80211_sub_if_data *sdata;
struct ieee80211_if_sta *ifsta;
struct ieee80211_mgmt *mgmt;
u16 fc;
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
ifsta = &sdata->u.sta;
rx_status = (struct ieee80211_rx_status *) skb->cb;
@@ -3107,17 +1840,17 @@ static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
switch (fc & IEEE80211_FCTL_STYPE) {
case IEEE80211_STYPE_PROBE_REQ:
- ieee80211_rx_mgmt_probe_req(dev, ifsta, mgmt, skb->len,
+ ieee80211_rx_mgmt_probe_req(sdata, ifsta, mgmt, skb->len,
rx_status);
break;
case IEEE80211_STYPE_PROBE_RESP:
- ieee80211_rx_mgmt_probe_resp(dev, mgmt, skb->len, rx_status);
+ ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len, rx_status);
break;
case IEEE80211_STYPE_BEACON:
- ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len, rx_status);
+ ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status);
break;
case IEEE80211_STYPE_AUTH:
- ieee80211_rx_mgmt_auth(dev, ifsta, mgmt, skb->len);
+ ieee80211_rx_mgmt_auth(sdata, ifsta, mgmt, skb->len);
break;
case IEEE80211_STYPE_ASSOC_RESP:
ieee80211_rx_mgmt_assoc_resp(sdata, ifsta, mgmt, skb->len, 0);
@@ -3126,13 +1859,10 @@ static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
ieee80211_rx_mgmt_assoc_resp(sdata, ifsta, mgmt, skb->len, 1);
break;
case IEEE80211_STYPE_DEAUTH:
- ieee80211_rx_mgmt_deauth(dev, ifsta, mgmt, skb->len);
+ ieee80211_rx_mgmt_deauth(sdata, ifsta, mgmt, skb->len);
break;
case IEEE80211_STYPE_DISASSOC:
- ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len);
- break;
- case IEEE80211_STYPE_ACTION:
- ieee80211_rx_mgmt_action(dev, ifsta, mgmt, skb->len, rx_status);
+ ieee80211_rx_mgmt_disassoc(sdata, ifsta, mgmt, skb->len);
break;
}
@@ -3140,48 +1870,11 @@ static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
}
-ieee80211_rx_result
-ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb,
- struct ieee80211_rx_status *rx_status)
-{
- struct ieee80211_mgmt *mgmt;
- u16 fc;
-
- if (skb->len < 2)
- return RX_DROP_UNUSABLE;
-
- mgmt = (struct ieee80211_mgmt *) skb->data;
- fc = le16_to_cpu(mgmt->frame_control);
-
- if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL)
- return RX_CONTINUE;
-
- if (skb->len < 24)
- return RX_DROP_MONITOR;
-
- if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
- if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP) {
- ieee80211_rx_mgmt_probe_resp(dev, mgmt,
- skb->len, rx_status);
- dev_kfree_skb(skb);
- return RX_QUEUED;
- } else if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON) {
- ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len,
- rx_status);
- dev_kfree_skb(skb);
- return RX_QUEUED;
- }
- }
- return RX_CONTINUE;
-}
-
-
-static int ieee80211_sta_active_ibss(struct net_device *dev)
+static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = sdata->local;
int active = 0;
struct sta_info *sta;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
rcu_read_lock();
@@ -3200,180 +1893,36 @@ static int ieee80211_sta_active_ibss(struct net_device *dev)
}
-static void ieee80211_sta_expire(struct net_device *dev, unsigned long exp_time)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct sta_info *sta, *tmp;
- LIST_HEAD(tmp_list);
- DECLARE_MAC_BUF(mac);
- unsigned long flags;
-
- spin_lock_irqsave(&local->sta_lock, flags);
- list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
- if (time_after(jiffies, sta->last_rx + exp_time)) {
- printk(KERN_DEBUG "%s: expiring inactive STA %s\n",
- dev->name, print_mac(mac, sta->addr));
- __sta_info_unlink(&sta);
- if (sta)
- list_add(&sta->list, &tmp_list);
- }
- spin_unlock_irqrestore(&local->sta_lock, flags);
-
- list_for_each_entry_safe(sta, tmp, &tmp_list, list)
- sta_info_destroy(sta);
-}
-
-
-static void ieee80211_sta_merge_ibss(struct net_device *dev,
+static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
- ieee80211_sta_expire(dev, IEEE80211_IBSS_INACTIVITY_LIMIT);
- if (ieee80211_sta_active_ibss(dev))
+ ieee80211_sta_expire(sdata, IEEE80211_IBSS_INACTIVITY_LIMIT);
+ if (ieee80211_sta_active_ibss(sdata))
return;
printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other "
- "IBSS networks with same SSID (merge)\n", dev->name);
- ieee80211_sta_req_scan(dev, ifsta->ssid, ifsta->ssid_len);
+ "IBSS networks with same SSID (merge)\n", sdata->dev->name);
+ ieee80211_request_scan(sdata, ifsta->ssid, ifsta->ssid_len);
}
-#ifdef CONFIG_MAC80211_MESH
-static void ieee80211_mesh_housekeeping(struct net_device *dev,
- struct ieee80211_if_sta *ifsta)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- bool free_plinks;
-
- ieee80211_sta_expire(dev, IEEE80211_MESH_PEER_INACTIVITY_LIMIT);
- mesh_path_expire(dev);
-
- free_plinks = mesh_plink_availables(sdata);
- if (free_plinks != sdata->u.sta.accepting_plinks)
- ieee80211_if_config_beacon(dev);
-
- mod_timer(&ifsta->timer, jiffies +
- IEEE80211_MESH_HOUSEKEEPING_INTERVAL);
-}
-
-
-void ieee80211_start_mesh(struct net_device *dev)
-{
- struct ieee80211_if_sta *ifsta;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- ifsta = &sdata->u.sta;
- ifsta->state = IEEE80211_MESH_UP;
- ieee80211_sta_timer((unsigned long)sdata);
-}
-#endif
-
-
-void ieee80211_sta_timer(unsigned long data)
+static void ieee80211_sta_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- struct ieee80211_local *local = wdev_priv(&sdata->wdev);
+ struct ieee80211_local *local = sdata->local;
set_bit(IEEE80211_STA_REQ_RUN, &ifsta->request);
queue_work(local->hw.workqueue, &ifsta->work);
}
-void ieee80211_sta_work(struct work_struct *work)
-{
- struct ieee80211_sub_if_data *sdata =
- container_of(work, struct ieee80211_sub_if_data, u.sta.work);
- struct net_device *dev = sdata->dev;
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_if_sta *ifsta;
- struct sk_buff *skb;
-
- if (!netif_running(dev))
- return;
-
- if (local->sta_sw_scanning || local->sta_hw_scanning)
- return;
-
- if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
- sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
- sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT) {
- printk(KERN_DEBUG "%s: ieee80211_sta_work: non-STA interface "
- "(type=%d)\n", dev->name, sdata->vif.type);
- return;
- }
- ifsta = &sdata->u.sta;
-
- while ((skb = skb_dequeue(&ifsta->skb_queue)))
- ieee80211_sta_rx_queued_mgmt(dev, skb);
-
-#ifdef CONFIG_MAC80211_MESH
- if (ifsta->preq_queue_len &&
- time_after(jiffies,
- ifsta->last_preq + msecs_to_jiffies(ifsta->mshcfg.dot11MeshHWMPpreqMinInterval)))
- mesh_path_start_discovery(dev);
-#endif
-
- if (ifsta->state != IEEE80211_AUTHENTICATE &&
- ifsta->state != IEEE80211_ASSOCIATE &&
- test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) {
- if (ifsta->scan_ssid_len)
- ieee80211_sta_start_scan(dev, ifsta->scan_ssid, ifsta->scan_ssid_len);
- else
- ieee80211_sta_start_scan(dev, NULL, 0);
- return;
- }
-
- if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request)) {
- if (ieee80211_sta_config_auth(dev, ifsta))
- return;
- clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request);
- } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request))
- return;
-
- switch (ifsta->state) {
- case IEEE80211_DISABLED:
- break;
- case IEEE80211_AUTHENTICATE:
- ieee80211_authenticate(dev, ifsta);
- break;
- case IEEE80211_ASSOCIATE:
- ieee80211_associate(dev, ifsta);
- break;
- case IEEE80211_ASSOCIATED:
- ieee80211_associated(dev, ifsta);
- break;
- case IEEE80211_IBSS_SEARCH:
- ieee80211_sta_find_ibss(dev, ifsta);
- break;
- case IEEE80211_IBSS_JOINED:
- ieee80211_sta_merge_ibss(dev, ifsta);
- break;
-#ifdef CONFIG_MAC80211_MESH
- case IEEE80211_MESH_UP:
- ieee80211_mesh_housekeeping(dev, ifsta);
- break;
-#endif
- default:
- printk(KERN_DEBUG "ieee80211_sta_work: Unknown state %d\n",
- ifsta->state);
- break;
- }
-
- if (ieee80211_privacy_mismatch(dev, ifsta)) {
- printk(KERN_DEBUG "%s: privacy configuration mismatch and "
- "mixed-cell disabled - disassociate\n", dev->name);
-
- ieee80211_send_disassoc(dev, ifsta, WLAN_REASON_UNSPECIFIED);
- ieee80211_set_disassoc(dev, ifsta, 0);
- }
-}
-
-
-static void ieee80211_sta_reset_auth(struct net_device *dev,
+static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = sdata->local;
if (local->ops->reset_tsf) {
/* Reset own TSF to allow time synchronization work. */
@@ -3391,33 +1940,17 @@ static void ieee80211_sta_reset_auth(struct net_device *dev,
ifsta->auth_alg = WLAN_AUTH_LEAP;
else
ifsta->auth_alg = WLAN_AUTH_OPEN;
- printk(KERN_DEBUG "%s: Initial auth_alg=%d\n", dev->name,
- ifsta->auth_alg);
ifsta->auth_transaction = -1;
ifsta->flags &= ~IEEE80211_STA_ASSOCIATED;
- ifsta->auth_tries = ifsta->assoc_tries = 0;
- netif_carrier_off(dev);
+ ifsta->assoc_scan_tries = 0;
+ ifsta->direct_probe_tries = 0;
+ ifsta->auth_tries = 0;
+ ifsta->assoc_tries = 0;
+ netif_tx_stop_all_queues(sdata->dev);
+ netif_carrier_off(sdata->dev);
}
-void ieee80211_sta_req_auth(struct net_device *dev,
- struct ieee80211_if_sta *ifsta)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
- return;
-
- if ((ifsta->flags & (IEEE80211_STA_BSSID_SET |
- IEEE80211_STA_AUTO_BSSID_SEL)) &&
- (ifsta->flags & (IEEE80211_STA_SSID_SET |
- IEEE80211_STA_AUTO_SSID_SEL))) {
- set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
- queue_work(local->hw.workqueue, &ifsta->work);
- }
-}
-
static int ieee80211_sta_match_ssid(struct ieee80211_if_sta *ifsta,
const char *ssid, int ssid_len)
{
@@ -3448,81 +1981,11 @@ static int ieee80211_sta_match_ssid(struct ieee80211_if_sta *ifsta,
return 0;
}
-static int ieee80211_sta_config_auth(struct net_device *dev,
+static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_sta_bss *bss, *selected = NULL;
- int top_rssi = 0, freq;
-
- spin_lock_bh(&local->sta_bss_lock);
- freq = local->oper_channel->center_freq;
- list_for_each_entry(bss, &local->sta_bss_list, list) {
- if (!(bss->capability & WLAN_CAPABILITY_ESS))
- continue;
-
- if ((ifsta->flags & (IEEE80211_STA_AUTO_SSID_SEL |
- IEEE80211_STA_AUTO_BSSID_SEL |
- IEEE80211_STA_AUTO_CHANNEL_SEL)) &&
- (!!(bss->capability & WLAN_CAPABILITY_PRIVACY) ^
- !!sdata->default_key))
- continue;
-
- if (!(ifsta->flags & IEEE80211_STA_AUTO_CHANNEL_SEL) &&
- bss->freq != freq)
- continue;
-
- if (!(ifsta->flags & IEEE80211_STA_AUTO_BSSID_SEL) &&
- memcmp(bss->bssid, ifsta->bssid, ETH_ALEN))
- continue;
-
- if (!(ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL) &&
- !ieee80211_sta_match_ssid(ifsta, bss->ssid, bss->ssid_len))
- continue;
-
- if (!selected || top_rssi < bss->rssi) {
- selected = bss;
- top_rssi = bss->rssi;
- }
- }
- if (selected)
- atomic_inc(&selected->users);
- spin_unlock_bh(&local->sta_bss_lock);
-
- if (selected) {
- ieee80211_set_freq(dev, selected->freq);
- if (!(ifsta->flags & IEEE80211_STA_SSID_SET))
- ieee80211_sta_set_ssid(dev, selected->ssid,
- selected->ssid_len);
- ieee80211_sta_set_bssid(dev, selected->bssid);
- ieee80211_sta_def_wmm_params(dev, selected, 0);
- ieee80211_rx_bss_put(dev, selected);
- ifsta->state = IEEE80211_AUTHENTICATE;
- ieee80211_sta_reset_auth(dev, ifsta);
- return 0;
- } else {
- if (ifsta->state != IEEE80211_AUTHENTICATE) {
- if (ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL)
- ieee80211_sta_start_scan(dev, NULL, 0);
- else
- ieee80211_sta_start_scan(dev, ifsta->ssid,
- ifsta->ssid_len);
- ifsta->state = IEEE80211_AUTHENTICATE;
- set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
- } else
- ifsta->state = IEEE80211_DISABLED;
- }
- return -1;
-}
-
-
-static int ieee80211_sta_create_ibss(struct net_device *dev,
- struct ieee80211_if_sta *ifsta)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sta_bss *bss;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_bss *bss;
struct ieee80211_supported_band *sband;
u8 bssid[ETH_ALEN], *pos;
int i;
@@ -3538,15 +2001,15 @@ static int ieee80211_sta_create_ibss(struct net_device *dev,
* random number generator get different BSSID. */
get_random_bytes(bssid, ETH_ALEN);
for (i = 0; i < ETH_ALEN; i++)
- bssid[i] ^= dev->dev_addr[i];
+ bssid[i] ^= sdata->dev->dev_addr[i];
bssid[0] &= ~0x01;
bssid[0] |= 0x02;
#endif
printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %s\n",
- dev->name, print_mac(mac, bssid));
+ sdata->dev->name, print_mac(mac, bssid));
- bss = ieee80211_rx_bss_add(dev, bssid,
+ bss = ieee80211_rx_bss_add(local, bssid,
local->hw.conf.channel->center_freq,
sdata->u.sta.ssid, sdata->u.sta.ssid_len);
if (!bss)
@@ -3556,14 +2019,16 @@ static int ieee80211_sta_create_ibss(struct net_device *dev,
sband = local->hw.wiphy->bands[bss->band];
if (local->hw.conf.beacon_int == 0)
- local->hw.conf.beacon_int = 10000;
+ local->hw.conf.beacon_int = 100;
bss->beacon_int = local->hw.conf.beacon_int;
bss->last_update = jiffies;
bss->capability = WLAN_CAPABILITY_IBSS;
- if (sdata->default_key) {
+
+ if (sdata->default_key)
bss->capability |= WLAN_CAPABILITY_PRIVACY;
- } else
+ else
sdata->drop_unencrypted = 0;
+
bss->supp_rates_len = sband->n_bitrates;
pos = bss->supp_rates;
for (i = 0; i < sband->n_bitrates; i++) {
@@ -3571,17 +2036,17 @@ static int ieee80211_sta_create_ibss(struct net_device *dev,
*pos++ = (u8) (rate / 5);
}
- ret = ieee80211_sta_join_ibss(dev, ifsta, bss);
- ieee80211_rx_bss_put(dev, bss);
+ ret = ieee80211_sta_join_ibss(sdata, ifsta, bss);
+ ieee80211_rx_bss_put(local, bss);
return ret;
}
-static int ieee80211_sta_find_ibss(struct net_device *dev,
+static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sta_bss *bss;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_bss *bss;
int found = 0;
u8 bssid[ETH_ALEN];
int active_ibss;
@@ -3591,13 +2056,13 @@ static int ieee80211_sta_find_ibss(struct net_device *dev,
if (ifsta->ssid_len == 0)
return -EINVAL;
- active_ibss = ieee80211_sta_active_ibss(dev);
+ active_ibss = ieee80211_sta_active_ibss(sdata);
#ifdef CONFIG_MAC80211_IBSS_DEBUG
printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n",
- dev->name, active_ibss);
+ sdata->dev->name, active_ibss);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
- spin_lock_bh(&local->sta_bss_lock);
- list_for_each_entry(bss, &local->sta_bss_list, list) {
+ spin_lock_bh(&local->bss_lock);
+ list_for_each_entry(bss, &local->bss_list, list) {
if (ifsta->ssid_len != bss->ssid_len ||
memcmp(ifsta->ssid, bss->ssid, bss->ssid_len) != 0
|| !(bss->capability & WLAN_CAPABILITY_IBSS))
@@ -3611,7 +2076,7 @@ static int ieee80211_sta_find_ibss(struct net_device *dev,
if (active_ibss || memcmp(bssid, ifsta->bssid, ETH_ALEN) != 0)
break;
}
- spin_unlock_bh(&local->sta_bss_lock);
+ spin_unlock_bh(&local->bss_lock);
#ifdef CONFIG_MAC80211_IBSS_DEBUG
if (found)
@@ -3619,34 +2084,46 @@ static int ieee80211_sta_find_ibss(struct net_device *dev,
"%s\n", print_mac(mac, bssid),
print_mac(mac2, ifsta->bssid));
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
- if (found && memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0 &&
- (bss = ieee80211_rx_bss_get(dev, bssid,
- local->hw.conf.channel->center_freq,
- ifsta->ssid, ifsta->ssid_len))) {
+
+ if (found && memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0) {
int ret;
+ int search_freq;
+
+ if (ifsta->flags & IEEE80211_STA_AUTO_CHANNEL_SEL)
+ search_freq = bss->freq;
+ else
+ search_freq = local->hw.conf.channel->center_freq;
+
+ bss = ieee80211_rx_bss_get(local, bssid, search_freq,
+ ifsta->ssid, ifsta->ssid_len);
+ if (!bss)
+ goto dont_join;
+
printk(KERN_DEBUG "%s: Selected IBSS BSSID %s"
" based on configured SSID\n",
- dev->name, print_mac(mac, bssid));
- ret = ieee80211_sta_join_ibss(dev, ifsta, bss);
- ieee80211_rx_bss_put(dev, bss);
+ sdata->dev->name, print_mac(mac, bssid));
+ ret = ieee80211_sta_join_ibss(sdata, ifsta, bss);
+ ieee80211_rx_bss_put(local, bss);
return ret;
}
+
+dont_join:
#ifdef CONFIG_MAC80211_IBSS_DEBUG
printk(KERN_DEBUG " did not try to join ibss\n");
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
/* Selected IBSS not found in current scan results - try to scan */
- if (ifsta->state == IEEE80211_IBSS_JOINED &&
- !ieee80211_sta_active_ibss(dev)) {
+ if (ifsta->state == IEEE80211_STA_MLME_IBSS_JOINED &&
+ !ieee80211_sta_active_ibss(sdata)) {
mod_timer(&ifsta->timer, jiffies +
IEEE80211_IBSS_MERGE_INTERVAL);
} else if (time_after(jiffies, local->last_scan_completed +
IEEE80211_SCAN_INTERVAL)) {
printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to "
- "join\n", dev->name);
- return ieee80211_sta_req_scan(dev, ifsta->ssid,
+ "join\n", sdata->dev->name);
+ return ieee80211_request_scan(sdata, ifsta->ssid,
ifsta->ssid_len);
- } else if (ifsta->state != IEEE80211_IBSS_JOINED) {
+ } else if (ifsta->state != IEEE80211_STA_MLME_IBSS_JOINED) {
int interval = IEEE80211_SCAN_INTERVAL;
if (time_after(jiffies, ifsta->ibss_join_req +
@@ -3654,10 +2131,10 @@ static int ieee80211_sta_find_ibss(struct net_device *dev,
if ((ifsta->flags & IEEE80211_STA_CREATE_IBSS) &&
(!(local->oper_channel->flags &
IEEE80211_CHAN_NO_IBSS)))
- return ieee80211_sta_create_ibss(dev, ifsta);
+ return ieee80211_sta_create_ibss(sdata, ifsta);
if (ifsta->flags & IEEE80211_STA_CREATE_IBSS) {
printk(KERN_DEBUG "%s: IBSS not allowed on"
- " %d MHz\n", dev->name,
+ " %d MHz\n", sdata->dev->name,
local->hw.conf.channel->center_freq);
}
@@ -3666,7 +2143,7 @@ static int ieee80211_sta_find_ibss(struct net_device *dev,
interval = IEEE80211_SCAN_INTERVAL_SLOW;
}
- ifsta->state = IEEE80211_IBSS_SEARCH;
+ ifsta->state = IEEE80211_STA_MLME_IBSS_SEARCH;
mod_timer(&ifsta->timer, jiffies + interval);
return 0;
}
@@ -3675,583 +2152,346 @@ static int ieee80211_sta_find_ibss(struct net_device *dev,
}
-int ieee80211_sta_set_ssid(struct net_device *dev, char *ssid, size_t len)
+static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_if_sta *ifsta)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_if_sta *ifsta;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_bss *bss, *selected = NULL;
+ int top_rssi = 0, freq;
- if (len > IEEE80211_MAX_SSID_LEN)
- return -EINVAL;
+ spin_lock_bh(&local->bss_lock);
+ freq = local->oper_channel->center_freq;
+ list_for_each_entry(bss, &local->bss_list, list) {
+ if (!(bss->capability & WLAN_CAPABILITY_ESS))
+ continue;
- ifsta = &sdata->u.sta;
+ if ((ifsta->flags & (IEEE80211_STA_AUTO_SSID_SEL |
+ IEEE80211_STA_AUTO_BSSID_SEL |
+ IEEE80211_STA_AUTO_CHANNEL_SEL)) &&
+ (!!(bss->capability & WLAN_CAPABILITY_PRIVACY) ^
+ !!sdata->default_key))
+ continue;
- if (ifsta->ssid_len != len || memcmp(ifsta->ssid, ssid, len) != 0)
- ifsta->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
- memcpy(ifsta->ssid, ssid, len);
- memset(ifsta->ssid + len, 0, IEEE80211_MAX_SSID_LEN - len);
- ifsta->ssid_len = len;
+ if (!(ifsta->flags & IEEE80211_STA_AUTO_CHANNEL_SEL) &&
+ bss->freq != freq)
+ continue;
- if (len)
- ifsta->flags |= IEEE80211_STA_SSID_SET;
- else
- ifsta->flags &= ~IEEE80211_STA_SSID_SET;
- if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS &&
- !(ifsta->flags & IEEE80211_STA_BSSID_SET)) {
- ifsta->ibss_join_req = jiffies;
- ifsta->state = IEEE80211_IBSS_SEARCH;
- return ieee80211_sta_find_ibss(dev, ifsta);
+ if (!(ifsta->flags & IEEE80211_STA_AUTO_BSSID_SEL) &&
+ memcmp(bss->bssid, ifsta->bssid, ETH_ALEN))
+ continue;
+
+ if (!(ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL) &&
+ !ieee80211_sta_match_ssid(ifsta, bss->ssid, bss->ssid_len))
+ continue;
+
+ if (!selected || top_rssi < bss->signal) {
+ selected = bss;
+ top_rssi = bss->signal;
+ }
}
- return 0;
-}
+ if (selected)
+ atomic_inc(&selected->users);
+ spin_unlock_bh(&local->bss_lock);
+ if (selected) {
+ ieee80211_set_freq(sdata, selected->freq);
+ if (!(ifsta->flags & IEEE80211_STA_SSID_SET))
+ ieee80211_sta_set_ssid(sdata, selected->ssid,
+ selected->ssid_len);
+ ieee80211_sta_set_bssid(sdata, selected->bssid);
+ ieee80211_sta_def_wmm_params(sdata, selected);
-int ieee80211_sta_get_ssid(struct net_device *dev, char *ssid, size_t *len)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- memcpy(ssid, ifsta->ssid, ifsta->ssid_len);
- *len = ifsta->ssid_len;
- return 0;
+ /* Send out direct probe if no probe resp was received or
+ * the one we have is outdated
+ */
+ if (!selected->last_probe_resp ||
+ time_after(jiffies, selected->last_probe_resp
+ + IEEE80211_SCAN_RESULT_EXPIRE))
+ ifsta->state = IEEE80211_STA_MLME_DIRECT_PROBE;
+ else
+ ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
+
+ ieee80211_rx_bss_put(local, selected);
+ ieee80211_sta_reset_auth(sdata, ifsta);
+ return 0;
+ } else {
+ if (ifsta->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) {
+ ifsta->assoc_scan_tries++;
+ if (ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL)
+ ieee80211_start_scan(sdata, NULL, 0);
+ else
+ ieee80211_start_scan(sdata, ifsta->ssid,
+ ifsta->ssid_len);
+ ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
+ set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
+ } else
+ ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ }
+ return -1;
}
-int ieee80211_sta_set_bssid(struct net_device *dev, u8 *bssid)
+static void ieee80211_sta_work(struct work_struct *work)
{
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data, u.sta.work);
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_if_sta *ifsta;
- int res;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- ifsta = &sdata->u.sta;
+ struct sk_buff *skb;
- if (memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0) {
- memcpy(ifsta->bssid, bssid, ETH_ALEN);
- res = ieee80211_if_config(dev);
- if (res) {
- printk(KERN_DEBUG "%s: Failed to config new BSSID to "
- "the low-level driver\n", dev->name);
- return res;
- }
- }
+ if (!netif_running(sdata->dev))
+ return;
- if (is_valid_ether_addr(bssid))
- ifsta->flags |= IEEE80211_STA_BSSID_SET;
- else
- ifsta->flags &= ~IEEE80211_STA_BSSID_SET;
+ if (local->sw_scanning || local->hw_scanning)
+ return;
- return 0;
-}
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION &&
+ sdata->vif.type != NL80211_IFTYPE_ADHOC))
+ return;
+ ifsta = &sdata->u.sta;
+ while ((skb = skb_dequeue(&ifsta->skb_queue)))
+ ieee80211_sta_rx_queued_mgmt(sdata, skb);
-static void ieee80211_send_nullfunc(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- int powersave)
-{
- struct sk_buff *skb;
- struct ieee80211_hdr *nullfunc;
- u16 fc;
+ if (ifsta->state != IEEE80211_STA_MLME_DIRECT_PROBE &&
+ ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE &&
+ ifsta->state != IEEE80211_STA_MLME_ASSOCIATE &&
+ test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) {
+ ieee80211_start_scan(sdata, ifsta->scan_ssid,
+ ifsta->scan_ssid_len);
+ return;
+ }
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
- if (!skb) {
- printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
- "frame\n", sdata->dev->name);
+ if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request)) {
+ if (ieee80211_sta_config_auth(sdata, ifsta))
+ return;
+ clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request);
+ } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request))
return;
+
+ switch (ifsta->state) {
+ case IEEE80211_STA_MLME_DISABLED:
+ break;
+ case IEEE80211_STA_MLME_DIRECT_PROBE:
+ ieee80211_direct_probe(sdata, ifsta);
+ break;
+ case IEEE80211_STA_MLME_AUTHENTICATE:
+ ieee80211_authenticate(sdata, ifsta);
+ break;
+ case IEEE80211_STA_MLME_ASSOCIATE:
+ ieee80211_associate(sdata, ifsta);
+ break;
+ case IEEE80211_STA_MLME_ASSOCIATED:
+ ieee80211_associated(sdata, ifsta);
+ break;
+ case IEEE80211_STA_MLME_IBSS_SEARCH:
+ ieee80211_sta_find_ibss(sdata, ifsta);
+ break;
+ case IEEE80211_STA_MLME_IBSS_JOINED:
+ ieee80211_sta_merge_ibss(sdata, ifsta);
+ break;
+ default:
+ WARN_ON(1);
+ break;
}
- skb_reserve(skb, local->hw.extra_tx_headroom);
- nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
- memset(nullfunc, 0, 24);
- fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
- IEEE80211_FCTL_TODS;
- if (powersave)
- fc |= IEEE80211_FCTL_PM;
- nullfunc->frame_control = cpu_to_le16(fc);
- memcpy(nullfunc->addr1, sdata->u.sta.bssid, ETH_ALEN);
- memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
- memcpy(nullfunc->addr3, sdata->u.sta.bssid, ETH_ALEN);
-
- ieee80211_sta_tx(sdata->dev, skb, 0);
-}
+ if (ieee80211_privacy_mismatch(sdata, ifsta)) {
+ printk(KERN_DEBUG "%s: privacy configuration mismatch and "
+ "mixed-cell disabled - disassociate\n", sdata->dev->name);
+ ieee80211_set_disassoc(sdata, ifsta, false, true,
+ WLAN_REASON_UNSPECIFIED);
+ }
+}
static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
{
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
- ieee80211_vif_is_mesh(&sdata->vif))
- ieee80211_sta_timer((unsigned long)sdata);
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ queue_work(sdata->local->hw.workqueue,
+ &sdata->u.sta.work);
}
-void ieee80211_scan_completed(struct ieee80211_hw *hw)
+/* interface setup */
+void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_local *local = hw_to_local(hw);
- struct net_device *dev = local->scan_dev;
- struct ieee80211_sub_if_data *sdata;
- union iwreq_data wrqu;
+ struct ieee80211_if_sta *ifsta;
- local->last_scan_completed = jiffies;
- memset(&wrqu, 0, sizeof(wrqu));
- wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
-
- if (local->sta_hw_scanning) {
- local->sta_hw_scanning = 0;
- if (ieee80211_hw_config(local))
- printk(KERN_DEBUG "%s: failed to restore operational "
- "channel after scan\n", dev->name);
- /* Restart STA timer for HW scan case */
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &local->interfaces, list)
- ieee80211_restart_sta_timer(sdata);
- rcu_read_unlock();
+ ifsta = &sdata->u.sta;
+ INIT_WORK(&ifsta->work, ieee80211_sta_work);
+ setup_timer(&ifsta->timer, ieee80211_sta_timer,
+ (unsigned long) sdata);
+ skb_queue_head_init(&ifsta->skb_queue);
- goto done;
- }
+ ifsta->capab = WLAN_CAPABILITY_ESS;
+ ifsta->auth_algs = IEEE80211_AUTH_ALG_OPEN |
+ IEEE80211_AUTH_ALG_SHARED_KEY;
+ ifsta->flags |= IEEE80211_STA_CREATE_IBSS |
+ IEEE80211_STA_AUTO_BSSID_SEL |
+ IEEE80211_STA_AUTO_CHANNEL_SEL;
+ if (ieee80211_num_regular_queues(&sdata->local->hw) >= 4)
+ ifsta->flags |= IEEE80211_STA_WMM_ENABLED;
+}
- local->sta_sw_scanning = 0;
- if (ieee80211_hw_config(local))
- printk(KERN_DEBUG "%s: failed to restore operational "
- "channel after scan\n", dev->name);
+/*
+ * 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,
+ struct sk_buff *skb, u8 *bssid,
+ u8 *addr, u64 supp_rates)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
+ DECLARE_MAC_BUF(mac);
+ int band = local->hw.conf.channel->band;
+ /* TODO: Could 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 %s\n", sdata->dev->name, print_mac(mac, addr));
+ }
+ return NULL;
+ }
- netif_tx_lock_bh(local->mdev);
- local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC;
- local->ops->configure_filter(local_to_hw(local),
- FIF_BCN_PRBRESP_PROMISC,
- &local->filter_flags,
- local->mdev->mc_count,
- local->mdev->mc_list);
+ if (compare_ether_addr(bssid, sdata->u.sta.bssid))
+ return NULL;
- netif_tx_unlock_bh(local->mdev);
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ printk(KERN_DEBUG "%s: Adding new IBSS station %s (dev=%s)\n",
+ wiphy_name(local->hw.wiphy), print_mac(mac, addr), sdata->dev->name);
+#endif
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
+ if (!sta)
+ return NULL;
- /* No need to wake the master device. */
- if (sdata->dev == local->mdev)
- continue;
+ set_sta_flags(sta, WLAN_STA_AUTHORIZED);
- /* Tell AP we're back */
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA &&
- sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED)
- ieee80211_send_nullfunc(local, sdata, 0);
+ /* make sure mandatory rates are always added */
+ sta->sta.supp_rates[band] = supp_rates |
+ ieee80211_mandatory_rates(local, band);
- ieee80211_restart_sta_timer(sdata);
+ rate_control_rate_init(sta);
- netif_wake_queue(sdata->dev);
- }
- rcu_read_unlock();
+ if (sta_info_insert(sta))
+ return NULL;
-done:
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- if (!(ifsta->flags & IEEE80211_STA_BSSID_SET) ||
- (!ifsta->state == IEEE80211_IBSS_JOINED &&
- !ieee80211_sta_active_ibss(dev)))
- ieee80211_sta_find_ibss(dev, ifsta);
- }
+ return sta;
}
-EXPORT_SYMBOL(ieee80211_scan_completed);
-void ieee80211_sta_scan_work(struct work_struct *work)
+/* configuration hooks */
+void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_if_sta *ifsta)
{
- struct ieee80211_local *local =
- container_of(work, struct ieee80211_local, scan_work.work);
- struct net_device *dev = local->scan_dev;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_supported_band *sband;
- struct ieee80211_channel *chan;
- int skip;
- unsigned long next_delay = 0;
+ struct ieee80211_local *local = sdata->local;
- if (!local->sta_sw_scanning)
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
return;
- switch (local->scan_state) {
- case SCAN_SET_CHANNEL:
- /*
- * Get current scan band. scan_band may be IEEE80211_NUM_BANDS
- * after we successfully scanned the last channel of the last
- * band (and the last band is supported by the hw)
- */
- if (local->scan_band < IEEE80211_NUM_BANDS)
- sband = local->hw.wiphy->bands[local->scan_band];
- else
- sband = NULL;
-
- /*
- * If we are at an unsupported band and have more bands
- * left to scan, advance to the next supported one.
- */
- while (!sband && local->scan_band < IEEE80211_NUM_BANDS - 1) {
- local->scan_band++;
- sband = local->hw.wiphy->bands[local->scan_band];
- local->scan_channel_idx = 0;
- }
-
- /* if no more bands/channels left, complete scan */
- if (!sband || local->scan_channel_idx >= sband->n_channels) {
- ieee80211_scan_completed(local_to_hw(local));
- return;
- }
- skip = 0;
- chan = &sband->channels[local->scan_channel_idx];
-
- if (chan->flags & IEEE80211_CHAN_DISABLED ||
- (sdata->vif.type == IEEE80211_IF_TYPE_IBSS &&
- chan->flags & IEEE80211_CHAN_NO_IBSS))
- skip = 1;
-
- if (!skip) {
- local->scan_channel = chan;
- if (ieee80211_hw_config(local)) {
- printk(KERN_DEBUG "%s: failed to set freq to "
- "%d MHz for scan\n", dev->name,
- chan->center_freq);
- skip = 1;
- }
- }
-
- /* advance state machine to next channel/band */
- local->scan_channel_idx++;
- if (local->scan_channel_idx >= sband->n_channels) {
- /*
- * scan_band may end up == IEEE80211_NUM_BANDS, but
- * we'll catch that case above and complete the scan
- * if that is the case.
- */
- local->scan_band++;
- local->scan_channel_idx = 0;
- }
-
- if (skip)
- break;
+ if ((ifsta->flags & (IEEE80211_STA_BSSID_SET |
+ IEEE80211_STA_AUTO_BSSID_SEL)) &&
+ (ifsta->flags & (IEEE80211_STA_SSID_SET |
+ IEEE80211_STA_AUTO_SSID_SEL))) {
- next_delay = IEEE80211_PROBE_DELAY +
- usecs_to_jiffies(local->hw.channel_change_time);
- local->scan_state = SCAN_SEND_PROBE;
- break;
- case SCAN_SEND_PROBE:
- next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
- local->scan_state = SCAN_SET_CHANNEL;
+ if (ifsta->state == IEEE80211_STA_MLME_ASSOCIATED)
+ ieee80211_set_disassoc(sdata, ifsta, true, true,
+ WLAN_REASON_DEAUTH_LEAVING);
- if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN)
- break;
- ieee80211_send_probe_req(dev, NULL, local->scan_ssid,
- local->scan_ssid_len);
- next_delay = IEEE80211_CHANNEL_TIME;
- break;
+ set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
+ queue_work(local->hw.workqueue, &ifsta->work);
}
-
- if (local->sta_sw_scanning)
- queue_delayed_work(local->hw.workqueue, &local->scan_work,
- next_delay);
}
-
-static int ieee80211_sta_start_scan(struct net_device *dev,
- u8 *ssid, size_t ssid_len)
+int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_if_sta *ifsta;
+ int res;
- if (ssid_len > IEEE80211_MAX_SSID_LEN)
+ if (len > IEEE80211_MAX_SSID_LEN)
return -EINVAL;
- /* MLME-SCAN.request (page 118) page 144 (11.1.3.1)
- * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS
- * BSSID: MACAddress
- * SSID
- * ScanType: ACTIVE, PASSIVE
- * ProbeDelay: delay (in microseconds) to be used prior to transmitting
- * a Probe frame during active scanning
- * ChannelList
- * MinChannelTime (>= ProbeDelay), in TU
- * MaxChannelTime: (>= MinChannelTime), in TU
- */
-
- /* MLME-SCAN.confirm
- * BSSDescriptionSet
- * ResultCode: SUCCESS, INVALID_PARAMETERS
- */
+ ifsta = &sdata->u.sta;
- if (local->sta_sw_scanning || local->sta_hw_scanning) {
- if (local->scan_dev == dev)
- return 0;
- return -EBUSY;
- }
+ if (ifsta->ssid_len != len || memcmp(ifsta->ssid, ssid, len) != 0) {
+ memset(ifsta->ssid, 0, sizeof(ifsta->ssid));
+ memcpy(ifsta->ssid, ssid, len);
+ ifsta->ssid_len = len;
+ ifsta->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
- if (local->ops->hw_scan) {
- int rc = local->ops->hw_scan(local_to_hw(local),
- ssid, ssid_len);
- if (!rc) {
- local->sta_hw_scanning = 1;
- local->scan_dev = dev;
+ res = 0;
+ /*
+ * Hack! MLME code needs to be cleaned up to have different
+ * entry points for configuration and internal selection change
+ */
+ if (netif_running(sdata->dev))
+ res = ieee80211_if_config(sdata, IEEE80211_IFCC_SSID);
+ if (res) {
+ printk(KERN_DEBUG "%s: Failed to config new SSID to "
+ "the low-level driver\n", sdata->dev->name);
+ return res;
}
- return rc;
}
- local->sta_sw_scanning = 1;
-
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-
- /* Don't stop the master interface, otherwise we can't transmit
- * probes! */
- if (sdata->dev == local->mdev)
- continue;
+ if (len)
+ ifsta->flags |= IEEE80211_STA_SSID_SET;
+ else
+ ifsta->flags &= ~IEEE80211_STA_SSID_SET;
- netif_stop_queue(sdata->dev);
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA &&
- (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED))
- ieee80211_send_nullfunc(local, sdata, 1);
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
+ !(ifsta->flags & IEEE80211_STA_BSSID_SET)) {
+ ifsta->ibss_join_req = jiffies;
+ ifsta->state = IEEE80211_STA_MLME_IBSS_SEARCH;
+ return ieee80211_sta_find_ibss(sdata, ifsta);
}
- rcu_read_unlock();
-
- if (ssid) {
- local->scan_ssid_len = ssid_len;
- memcpy(local->scan_ssid, ssid, ssid_len);
- } else
- local->scan_ssid_len = 0;
- local->scan_state = SCAN_SET_CHANNEL;
- local->scan_channel_idx = 0;
- local->scan_band = IEEE80211_BAND_2GHZ;
- local->scan_dev = dev;
-
- netif_tx_lock_bh(local->mdev);
- local->filter_flags |= FIF_BCN_PRBRESP_PROMISC;
- local->ops->configure_filter(local_to_hw(local),
- FIF_BCN_PRBRESP_PROMISC,
- &local->filter_flags,
- local->mdev->mc_count,
- local->mdev->mc_list);
- netif_tx_unlock_bh(local->mdev);
-
- /* TODO: start scan as soon as all nullfunc frames are ACKed */
- queue_delayed_work(local->hw.workqueue, &local->scan_work,
- IEEE80211_CHANNEL_TIME);
return 0;
}
-
-int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len)
+int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
- if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
- return ieee80211_sta_start_scan(dev, ssid, ssid_len);
-
- if (local->sta_sw_scanning || local->sta_hw_scanning) {
- if (local->scan_dev == dev)
- return 0;
- return -EBUSY;
- }
-
- ifsta->scan_ssid_len = ssid_len;
- if (ssid_len)
- memcpy(ifsta->scan_ssid, ssid, ssid_len);
- set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request);
- queue_work(local->hw.workqueue, &ifsta->work);
+ memcpy(ssid, ifsta->ssid, ifsta->ssid_len);
+ *len = ifsta->ssid_len;
return 0;
}
-static char *
-ieee80211_sta_scan_result(struct net_device *dev,
- struct ieee80211_sta_bss *bss,
- char *current_ev, char *end_buf)
+int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct iw_event iwe;
-
- if (time_after(jiffies,
- bss->last_update + IEEE80211_SCAN_RESULT_EXPIRE))
- return current_ev;
-
- memset(&iwe, 0, sizeof(iwe));
- iwe.cmd = SIOCGIWAP;
- iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
- memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
- current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
- IW_EV_ADDR_LEN);
-
- memset(&iwe, 0, sizeof(iwe));
- iwe.cmd = SIOCGIWESSID;
- if (bss_mesh_cfg(bss)) {
- iwe.u.data.length = bss_mesh_id_len(bss);
- iwe.u.data.flags = 1;
- current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
- bss_mesh_id(bss));
- } else {
- iwe.u.data.length = bss->ssid_len;
- iwe.u.data.flags = 1;
- current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
- bss->ssid);
- }
-
- if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)
- || bss_mesh_cfg(bss)) {
- memset(&iwe, 0, sizeof(iwe));
- iwe.cmd = SIOCGIWMODE;
- if (bss_mesh_cfg(bss))
- iwe.u.mode = IW_MODE_MESH;
- else if (bss->capability & WLAN_CAPABILITY_ESS)
- iwe.u.mode = IW_MODE_MASTER;
- else
- iwe.u.mode = IW_MODE_ADHOC;
- current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
- IW_EV_UINT_LEN);
- }
-
- memset(&iwe, 0, sizeof(iwe));
- iwe.cmd = SIOCGIWFREQ;
- iwe.u.freq.m = ieee80211_frequency_to_channel(bss->freq);
- iwe.u.freq.e = 0;
- current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
- IW_EV_FREQ_LEN);
-
- memset(&iwe, 0, sizeof(iwe));
- iwe.cmd = SIOCGIWFREQ;
- iwe.u.freq.m = bss->freq;
- iwe.u.freq.e = 6;
- current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
- IW_EV_FREQ_LEN);
- memset(&iwe, 0, sizeof(iwe));
- iwe.cmd = IWEVQUAL;
- iwe.u.qual.qual = bss->signal;
- iwe.u.qual.level = bss->rssi;
- iwe.u.qual.noise = bss->noise;
- iwe.u.qual.updated = local->wstats_flags;
- current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
- IW_EV_QUAL_LEN);
-
- memset(&iwe, 0, sizeof(iwe));
- iwe.cmd = SIOCGIWENCODE;
- if (bss->capability & WLAN_CAPABILITY_PRIVACY)
- iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
- else
- iwe.u.data.flags = IW_ENCODE_DISABLED;
- iwe.u.data.length = 0;
- current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, "");
-
- if (bss && bss->wpa_ie) {
- memset(&iwe, 0, sizeof(iwe));
- iwe.cmd = IWEVGENIE;
- iwe.u.data.length = bss->wpa_ie_len;
- current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
- bss->wpa_ie);
- }
-
- if (bss && bss->rsn_ie) {
- memset(&iwe, 0, sizeof(iwe));
- iwe.cmd = IWEVGENIE;
- iwe.u.data.length = bss->rsn_ie_len;
- current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
- bss->rsn_ie);
- }
-
- if (bss && bss->supp_rates_len > 0) {
- /* display all supported rates in readable format */
- char *p = current_ev + IW_EV_LCP_LEN;
- int i;
-
- memset(&iwe, 0, sizeof(iwe));
- iwe.cmd = SIOCGIWRATE;
- /* Those two flags are ignored... */
- iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
-
- for (i = 0; i < bss->supp_rates_len; i++) {
- iwe.u.bitrate.value = ((bss->supp_rates[i] &
- 0x7f) * 500000);
- p = iwe_stream_add_value(current_ev, p,
- end_buf, &iwe, IW_EV_PARAM_LEN);
- }
- current_ev = p;
- }
+ struct ieee80211_if_sta *ifsta;
+ int res;
- if (bss) {
- char *buf;
- buf = kmalloc(30, GFP_ATOMIC);
- if (buf) {
- memset(&iwe, 0, sizeof(iwe));
- iwe.cmd = IWEVCUSTOM;
- sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp));
- iwe.u.data.length = strlen(buf);
- current_ev = iwe_stream_add_point(current_ev, end_buf,
- &iwe, buf);
- kfree(buf);
- }
- }
+ ifsta = &sdata->u.sta;
- if (bss_mesh_cfg(bss)) {
- char *buf;
- u8 *cfg = bss_mesh_cfg(bss);
- buf = kmalloc(50, GFP_ATOMIC);
- if (buf) {
- memset(&iwe, 0, sizeof(iwe));
- iwe.cmd = IWEVCUSTOM;
- sprintf(buf, "Mesh network (version %d)", cfg[0]);
- iwe.u.data.length = strlen(buf);
- current_ev = iwe_stream_add_point(current_ev, end_buf,
- &iwe, buf);
- sprintf(buf, "Path Selection Protocol ID: "
- "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3],
- cfg[4]);
- iwe.u.data.length = strlen(buf);
- current_ev = iwe_stream_add_point(current_ev, end_buf,
- &iwe, buf);
- sprintf(buf, "Path Selection Metric ID: "
- "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7],
- cfg[8]);
- iwe.u.data.length = strlen(buf);
- current_ev = iwe_stream_add_point(current_ev, end_buf,
- &iwe, buf);
- sprintf(buf, "Congestion Control Mode ID: "
- "0x%02X%02X%02X%02X", cfg[9], cfg[10],
- cfg[11], cfg[12]);
- iwe.u.data.length = strlen(buf);
- current_ev = iwe_stream_add_point(current_ev, end_buf,
- &iwe, buf);
- sprintf(buf, "Channel Precedence: "
- "0x%02X%02X%02X%02X", cfg[13], cfg[14],
- cfg[15], cfg[16]);
- iwe.u.data.length = strlen(buf);
- current_ev = iwe_stream_add_point(current_ev, end_buf,
- &iwe, buf);
- kfree(buf);
+ if (memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0) {
+ memcpy(ifsta->bssid, bssid, ETH_ALEN);
+ res = 0;
+ /*
+ * Hack! See also ieee80211_sta_set_ssid.
+ */
+ if (netif_running(sdata->dev))
+ res = ieee80211_if_config(sdata, IEEE80211_IFCC_BSSID);
+ if (res) {
+ printk(KERN_DEBUG "%s: Failed to config new BSSID to "
+ "the low-level driver\n", sdata->dev->name);
+ return res;
}
}
- return current_ev;
-}
-
+ if (is_valid_ether_addr(bssid))
+ ifsta->flags |= IEEE80211_STA_BSSID_SET;
+ else
+ ifsta->flags &= ~IEEE80211_STA_BSSID_SET;
-int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- char *current_ev = buf;
- char *end_buf = buf + len;
- struct ieee80211_sta_bss *bss;
-
- spin_lock_bh(&local->sta_bss_lock);
- list_for_each_entry(bss, &local->sta_bss_list, list) {
- if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
- spin_unlock_bh(&local->sta_bss_lock);
- return -E2BIG;
- }
- current_ev = ieee80211_sta_scan_result(dev, bss, current_ev,
- end_buf);
- }
- spin_unlock_bh(&local->sta_bss_lock);
- return current_ev - buf;
+ return 0;
}
-
-int ieee80211_sta_set_extra_ie(struct net_device *dev, char *ie, size_t len)
+int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, char *ie, size_t len)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+
kfree(ifsta->extra_ie);
if (len == 0) {
ifsta->extra_ie = NULL;
@@ -4268,84 +2508,60 @@ int ieee80211_sta_set_extra_ie(struct net_device *dev, char *ie, size_t len)
return 0;
}
-
-struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev,
- struct sk_buff *skb, u8 *bssid,
- u8 *addr)
+int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct sta_info *sta;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- DECLARE_MAC_BUF(mac);
-
- /* TODO: Could 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 %s\n", dev->name, print_mac(mac, addr));
- }
- return NULL;
- }
-
- printk(KERN_DEBUG "%s: Adding new IBSS station %s (dev=%s)\n",
- wiphy_name(local->hw.wiphy), print_mac(mac, addr), dev->name);
-
- sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
- if (!sta)
- return NULL;
-
- sta->flags |= WLAN_STA_AUTHORIZED;
-
- sta->supp_rates[local->hw.conf.channel->band] =
- sdata->u.sta.supp_rates_bits[local->hw.conf.channel->band];
-
- rate_control_rate_init(sta, local);
-
- if (sta_info_insert(sta))
- return NULL;
-
- return sta;
-}
-
-
-int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- printk(KERN_DEBUG "%s: deauthenticate(reason=%d)\n",
- dev->name, reason);
+ printk(KERN_DEBUG "%s: deauthenticating by local choice (reason=%d)\n",
+ sdata->dev->name, reason);
- if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
- sdata->vif.type != IEEE80211_IF_TYPE_IBSS)
+ if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+ sdata->vif.type != NL80211_IFTYPE_ADHOC)
return -EINVAL;
- ieee80211_send_deauth(dev, ifsta, reason);
- ieee80211_set_disassoc(dev, ifsta, 1);
+ ieee80211_set_disassoc(sdata, ifsta, true, true, reason);
return 0;
}
-
-int ieee80211_sta_disassociate(struct net_device *dev, u16 reason)
+int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- printk(KERN_DEBUG "%s: disassociate(reason=%d)\n",
- dev->name, reason);
+ printk(KERN_DEBUG "%s: disassociating by local choice (reason=%d)\n",
+ sdata->dev->name, reason);
- if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
return -EINVAL;
if (!(ifsta->flags & IEEE80211_STA_ASSOCIATED))
return -1;
- ieee80211_send_disassoc(dev, ifsta, reason);
- ieee80211_set_disassoc(dev, ifsta, 0);
+ ieee80211_set_disassoc(sdata, ifsta, false, true, reason);
return 0;
}
+/* scan finished notification */
+void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
+{
+ struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+ struct ieee80211_if_sta *ifsta;
+
+ if (sdata && sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ ifsta = &sdata->u.sta;
+ if (!(ifsta->flags & IEEE80211_STA_BSSID_SET) ||
+ (!(ifsta->state == IEEE80211_STA_MLME_IBSS_JOINED) &&
+ !ieee80211_sta_active_ibss(sdata)))
+ ieee80211_sta_find_ibss(sdata, ifsta);
+ }
+
+ /* Restart STA timers */
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list)
+ ieee80211_restart_sta_timer(sdata);
+ rcu_read_unlock();
+}
+
+/* driver notification call */
void ieee80211_notify_mac(struct ieee80211_hw *hw,
enum ieee80211_notification_types notif_type)
{
@@ -4356,12 +2572,10 @@ void ieee80211_notify_mac(struct ieee80211_hw *hw,
case IEEE80211_NOTIFY_RE_ASSOC:
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ continue;
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
- ieee80211_sta_req_auth(sdata->dev,
- &sdata->u.sta);
- }
-
+ ieee80211_sta_req_auth(sdata, &sdata->u.sta);
}
rcu_read_unlock();
break;
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 841df93807f..5d786720d93 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -12,6 +12,7 @@
#include <linux/rtnetlink.h>
#include "rate.h"
#include "ieee80211_i.h"
+#include "debugfs.h"
struct rate_control_alg {
struct list_head list;
@@ -127,19 +128,46 @@ 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)
+{
+ struct rate_control_ref *ref = file->private_data;
+ int len = strlen(ref->ops->name);
+
+ return simple_read_from_buffer(userbuf, count, ppos,
+ ref->ops->name, len);
+}
+
+static const struct file_operations rcname_ops = {
+ .read = rcname_read,
+ .open = mac80211_open_file_generic,
+};
+#endif
+
struct rate_control_ref *rate_control_alloc(const char *name,
struct ieee80211_local *local)
{
+ struct dentry *debugfsdir = NULL;
struct rate_control_ref *ref;
ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL);
if (!ref)
goto fail_ref;
kref_init(&ref->kref);
+ ref->local = local;
ref->ops = ieee80211_rate_control_ops_get(name);
if (!ref->ops)
goto fail_ops;
- ref->priv = ref->ops->alloc(local);
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ debugfsdir = debugfs_create_dir("rc", local->hw.wiphy->debugfsdir);
+ local->debugfs.rcdir = debugfsdir;
+ local->debugfs.rcname = debugfs_create_file("name", 0400, debugfsdir,
+ ref, &rcname_ops);
+#endif
+
+ ref->priv = ref->ops->alloc(&local->hw, debugfsdir);
if (!ref->priv)
goto fail_priv;
return ref;
@@ -158,42 +186,61 @@ static void rate_control_release(struct kref *kref)
ctrl_ref = container_of(kref, struct rate_control_ref, kref);
ctrl_ref->ops->free(ctrl_ref->priv);
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ debugfs_remove(ctrl_ref->local->debugfs.rcname);
+ ctrl_ref->local->debugfs.rcname = NULL;
+ debugfs_remove(ctrl_ref->local->debugfs.rcdir);
+ ctrl_ref->local->debugfs.rcdir = NULL;
+#endif
+
ieee80211_rate_control_ops_put(ctrl_ref->ops);
kfree(ctrl_ref);
}
-void rate_control_get_rate(struct net_device *dev,
+void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
- struct sk_buff *skb,
+ struct sta_info *sta, struct sk_buff *skb,
struct rate_selection *sel)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct rate_control_ref *ref = local->rate_ctrl;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct sta_info *sta;
+ struct rate_control_ref *ref = sdata->local->rate_ctrl;
+ void *priv_sta = NULL;
+ struct ieee80211_sta *ista = NULL;
int i;
- rcu_read_lock();
- sta = sta_info_get(local, hdr->addr1);
+ sel->rate_idx = -1;
+ sel->nonerp_idx = -1;
+ sel->probe_idx = -1;
+ sel->max_rate_idx = sdata->max_ratectrl_rateidx;
- memset(sel, 0, sizeof(struct rate_selection));
+ if (sta) {
+ ista = &sta->sta;
+ priv_sta = sta->rate_ctrl_priv;
+ }
- ref->ops->get_rate(ref->priv, dev, sband, skb, sel);
+ if (sta && sdata->force_unicast_rateidx > -1)
+ sel->rate_idx = sdata->force_unicast_rateidx;
+ else
+ ref->ops->get_rate(ref->priv, sband, ista, priv_sta, skb, sel);
+
+ if (sdata->max_ratectrl_rateidx > -1 &&
+ sel->rate_idx > sdata->max_ratectrl_rateidx)
+ sel->rate_idx = sdata->max_ratectrl_rateidx;
+
+ BUG_ON(sel->rate_idx < 0);
/* Select a non-ERP backup rate. */
- if (!sel->nonerp) {
+ if (sel->nonerp_idx < 0) {
for (i = 0; i < sband->n_bitrates; i++) {
struct ieee80211_rate *rate = &sband->bitrates[i];
- if (sel->rate->bitrate < rate->bitrate)
+ if (sband->bitrates[sel->rate_idx].bitrate < rate->bitrate)
break;
- if (rate_supported(sta, sband->band, i) &&
+ if (rate_supported(ista, sband->band, i) &&
!(rate->flags & IEEE80211_RATE_ERP_G))
- sel->nonerp = rate;
+ sel->nonerp_idx = i;
}
}
-
- rcu_read_unlock();
}
struct rate_control_ref *rate_control_get(struct rate_control_ref *ref)
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index 5b45f33cb76..d0092f847f8 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -19,78 +19,48 @@
#include "ieee80211_i.h"
#include "sta_info.h"
-/* TODO: kdoc */
-struct rate_selection {
- /* Selected transmission rate */
- struct ieee80211_rate *rate;
- /* Non-ERP rate to use if mac80211 decides it cannot use an ERP rate */
- struct ieee80211_rate *nonerp;
- /* probe with this rate, or NULL for no probing */
- struct ieee80211_rate *probe;
-};
-
-struct rate_control_ops {
- struct module *module;
- const char *name;
- void (*tx_status)(void *priv, struct net_device *dev,
- struct sk_buff *skb,
- struct ieee80211_tx_status *status);
- void (*get_rate)(void *priv, struct net_device *dev,
- struct ieee80211_supported_band *band,
- struct sk_buff *skb,
- struct rate_selection *sel);
- void (*rate_init)(void *priv, void *priv_sta,
- struct ieee80211_local *local, struct sta_info *sta);
- void (*clear)(void *priv);
-
- void *(*alloc)(struct ieee80211_local *local);
- void (*free)(void *priv);
- void *(*alloc_sta)(void *priv, gfp_t gfp);
- void (*free_sta)(void *priv, void *priv_sta);
-
- int (*add_attrs)(void *priv, struct kobject *kobj);
- void (*remove_attrs)(void *priv, struct kobject *kobj);
- void (*add_sta_debugfs)(void *priv, void *priv_sta,
- struct dentry *dir);
- void (*remove_sta_debugfs)(void *priv, void *priv_sta);
-};
-
struct rate_control_ref {
+ struct ieee80211_local *local;
struct rate_control_ops *ops;
void *priv;
struct kref kref;
};
-int ieee80211_rate_control_register(struct rate_control_ops *ops);
-void ieee80211_rate_control_unregister(struct rate_control_ops *ops);
-
/* Get a reference to the rate control algorithm. If `name' is NULL, get the
* first available algorithm. */
struct rate_control_ref *rate_control_alloc(const char *name,
struct ieee80211_local *local);
-void rate_control_get_rate(struct net_device *dev,
+void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
- struct sk_buff *skb,
+ struct sta_info *sta, struct sk_buff *skb,
struct rate_selection *sel);
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 net_device *dev,
- struct sk_buff *skb,
- struct ieee80211_tx_status *status)
+static inline void rate_control_tx_status(struct ieee80211_local *local,
+ struct ieee80211_supported_band *sband,
+ struct sta_info *sta,
+ struct sk_buff *skb)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct rate_control_ref *ref = local->rate_ctrl;
+ struct ieee80211_sta *ista = &sta->sta;
+ void *priv_sta = sta->rate_ctrl_priv;
- ref->ops->tx_status(ref->priv, dev, skb, status);
+ ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb);
}
-static inline void rate_control_rate_init(struct sta_info *sta,
- struct ieee80211_local *local)
+static inline void rate_control_rate_init(struct sta_info *sta)
{
+ struct ieee80211_local *local = sta->sdata->local;
struct rate_control_ref *ref = sta->rate_ctrl;
- ref->ops->rate_init(ref->priv, sta->rate_ctrl_priv, local, sta);
+ struct ieee80211_sta *ista = &sta->sta;
+ void *priv_sta = sta->rate_ctrl_priv;
+ struct ieee80211_supported_band *sband;
+
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+
+ ref->ops->rate_init(ref->priv, sband, ista, priv_sta);
}
@@ -101,15 +71,19 @@ static inline void rate_control_clear(struct ieee80211_local *local)
}
static inline void *rate_control_alloc_sta(struct rate_control_ref *ref,
+ struct ieee80211_sta *sta,
gfp_t gfp)
{
- return ref->ops->alloc_sta(ref->priv, gfp);
+ return ref->ops->alloc_sta(ref->priv, sta, gfp);
}
-static inline void rate_control_free_sta(struct rate_control_ref *ref,
- void *priv)
+static inline void rate_control_free_sta(struct sta_info *sta)
{
- ref->ops->free_sta(ref->priv, priv);
+ struct rate_control_ref *ref = sta->rate_ctrl;
+ struct ieee80211_sta *ista = &sta->sta;
+ void *priv_sta = sta->rate_ctrl_priv;
+
+ ref->ops->free_sta(ref->priv, ista, priv_sta);
}
static inline void rate_control_add_sta_debugfs(struct sta_info *sta)
@@ -131,39 +105,6 @@ static inline void rate_control_remove_sta_debugfs(struct sta_info *sta)
#endif
}
-static inline int rate_supported(struct sta_info *sta,
- enum ieee80211_band band,
- int index)
-{
- return (sta == NULL || sta->supp_rates[band] & BIT(index));
-}
-
-static inline int
-rate_lowest_index(struct ieee80211_local *local,
- struct ieee80211_supported_band *sband,
- struct sta_info *sta)
-{
- int i;
-
- for (i = 0; i < sband->n_bitrates; i++)
- if (rate_supported(sta, sband->band, i))
- return i;
-
- /* warn when we cannot find a rate. */
- WARN_ON(1);
-
- return 0;
-}
-
-static inline struct ieee80211_rate *
-rate_lowest(struct ieee80211_local *local,
- struct ieee80211_supported_band *sband,
- struct sta_info *sta)
-{
- return &sband->bitrates[rate_lowest_index(local, sband, sta)];
-}
-
-
/* functions for rate control related to a device */
int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
const char *name);
@@ -171,9 +112,7 @@ void rate_control_deinitialize(struct ieee80211_local *local);
/* Rate control algorithms */
-#if defined(RC80211_PID_COMPILE) || \
- (defined(CONFIG_MAC80211_RC_PID) && \
- !defined(CONFIG_MAC80211_RC_PID_MODULE))
+#ifdef CONFIG_MAC80211_RC_PID
extern int rc80211_pid_init(void);
extern void rc80211_pid_exit(void);
#else
@@ -186,4 +125,18 @@ 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);
+#else
+static inline int rc80211_minstrel_init(void)
+{
+ return 0;
+}
+static inline void rc80211_minstrel_exit(void)
+{
+}
+#endif
+
+
#endif /* IEEE80211_RATE_H */
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
new file mode 100644
index 00000000000..f6d69dab07a
--- /dev/null
+++ b/net/mac80211/rc80211_minstrel.c
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2008 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
+ * published by the Free Software Foundation.
+ *
+ * Based on minstrel.c:
+ * Copyright (C) 2005-2007 Derek Smithies <derek@indranet.co.nz>
+ * Sponsored by Indranet Technologies Ltd
+ *
+ * Based on sample.c:
+ * Copyright (c) 2005 John Bicket
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/debugfs.h>
+#include <linux/random.h>
+#include <linux/ieee80211.h>
+#include <net/mac80211.h>
+#include "rate.h"
+#include "rc80211_minstrel.h"
+
+#define SAMPLE_COLUMNS 10
+#define SAMPLE_TBL(_mi, _idx, _col) \
+ _mi->sample_table[(_idx * SAMPLE_COLUMNS) + _col]
+
+/* convert mac80211 rate index to local array index */
+static inline int
+rix_to_ndx(struct minstrel_sta_info *mi, int rix)
+{
+ int i = rix;
+ for (i = rix; i >= 0; i--)
+ if (mi->r[i].rix == rix)
+ break;
+ WARN_ON(mi->r[i].rix != rix);
+ return i;
+}
+
+static inline bool
+use_low_rate(struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ u16 fc;
+
+ fc = le16_to_cpu(hdr->frame_control);
+
+ return ((info->flags & IEEE80211_TX_CTL_NO_ACK) ||
+ (fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
+ is_multicast_ether_addr(hdr->addr1));
+}
+
+
+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;
+ u32 usecs;
+ u32 p;
+ int i;
+
+ mi->stats_update = jiffies;
+ for (i = 0; i < mi->n_rates; i++) {
+ struct minstrel_rate *mr = &mi->r[i];
+
+ usecs = mr->perfect_tx_time;
+ 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;
+ 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->last_success = mr->success;
+ mr->last_attempts = mr->attempts;
+ mr->success = 0;
+ mr->attempts = 0;
+
+ /* 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)) {
+ mr->adjusted_retry_count = mr->retry_count >> 1;
+ if (mr->adjusted_retry_count > 2)
+ mr->adjusted_retry_count = 2;
+ } else {
+ mr->adjusted_retry_count = mr->retry_count;
+ }
+ 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;
+ }
+ }
+
+ max_tp = 0;
+ for (i = 0; i < mi->n_rates; i++) {
+ struct minstrel_rate *mr = &mi->r[i];
+
+ if (i == index_max_tp)
+ continue;
+
+ if (max_tp < mr->cur_tp) {
+ index_max_tp2 = i;
+ max_tp = mr->cur_tp;
+ }
+ }
+ mi->max_tp_rate = index_max_tp;
+ mi->max_tp_rate2 = index_max_tp2;
+ mi->max_prob_rate = index_max_prob;
+}
+
+static void
+minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta, void *priv_sta,
+ struct sk_buff *skb)
+{
+ struct minstrel_sta_info *mi = priv_sta;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_tx_altrate *ar = info->status.retries;
+ struct minstrel_priv *mp = priv;
+ int i, ndx, tries;
+ int success = 0;
+
+ if (!info->status.excessive_retries)
+ success = 1;
+
+ if (!mp->has_mrr || (ar[0].rate_idx < 0)) {
+ ndx = rix_to_ndx(mi, info->tx_rate_idx);
+ tries = info->status.retry_count + 1;
+ mi->r[ndx].success += success;
+ mi->r[ndx].attempts += tries;
+ return;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (ar[i].rate_idx < 0)
+ break;
+
+ ndx = rix_to_ndx(mi, ar[i].rate_idx);
+ mi->r[ndx].attempts += ar[i].limit + 1;
+
+ if ((i != 3) && (ar[i + 1].rate_idx < 0))
+ mi->r[ndx].success += success;
+ }
+
+ if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) && (i >= 0))
+ mi->sample_count++;
+
+ if (mi->sample_deferred > 0)
+ mi->sample_deferred--;
+}
+
+
+static inline unsigned int
+minstrel_get_retry_count(struct minstrel_rate *mr,
+ struct ieee80211_tx_info *info)
+{
+ unsigned int retry = mr->adjusted_retry_count;
+
+ if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS)
+ retry = max(2U, min(mr->retry_count_rtscts, retry));
+ else if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)
+ retry = max(2U, min(mr->retry_count_cts, retry));
+ return retry;
+}
+
+
+static int
+minstrel_get_next_sample(struct minstrel_sta_info *mi)
+{
+ unsigned int sample_ndx;
+ sample_ndx = SAMPLE_TBL(mi, mi->sample_idx, mi->sample_column);
+ mi->sample_idx++;
+ if (mi->sample_idx > (mi->n_rates - 2)) {
+ mi->sample_idx = 0;
+ mi->sample_column++;
+ if (mi->sample_column >= SAMPLE_COLUMNS)
+ mi->sample_column = 0;
+ }
+ return sample_ndx;
+}
+
+void
+minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta, void *priv_sta,
+ struct sk_buff *skb, struct rate_selection *sel)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct minstrel_sta_info *mi = priv_sta;
+ struct minstrel_priv *mp = priv;
+ struct ieee80211_tx_altrate *ar = info->control.retries;
+ 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;
+
+ if (!sta || !mi || use_low_rate(skb)) {
+ sel->rate_idx = rate_lowest_index(sband, sta);
+ return;
+ }
+
+ mrr = mp->has_mrr;
+
+ /* mac80211 does not allow mrr for RTS/CTS */
+ if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) ||
+ (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT))
+ mrr = false;
+
+ 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;
+ else
+ sample_rate = mp->lookaround_rate;
+
+ mi->packet_count++;
+ delta = (mi->packet_count * sample_rate / 100) -
+ (mi->sample_count + mi->sample_deferred / 2);
+
+ /* delta > 0: sampling required */
+ if (delta > 0) {
+ 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);
+ }
+
+ sample_ndx = minstrel_get_next_sample(mi);
+ sample = true;
+ sample_slower = mrr && (mi->r[sample_ndx].perfect_tx_time >
+ mi->r[ndx].perfect_tx_time);
+
+ if (!sample_slower) {
+ ndx = sample_ndx;
+ mi->sample_count++;
+ } 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++;
+ }
+ }
+ sel->rate_idx = mi->r[ndx].rix;
+ info->control.retry_limit = minstrel_get_retry_count(&mi->r[ndx], info);
+
+ if (!mrr) {
+ ar[0].rate_idx = mi->lowest_rix;
+ ar[0].limit = mp->max_retry;
+ ar[1].rate_idx = -1;
+ 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 = 0; i < 3; i++) {
+ ar[i].rate_idx = mi->r[mrr_ndx[i]].rix;
+ ar[i].limit = mi->r[mrr_ndx[i]].adjusted_retry_count;
+ }
+}
+
+
+static void
+calc_rate_durations(struct minstrel_sta_info *mi, struct ieee80211_local *local,
+ struct minstrel_rate *d, struct ieee80211_rate *rate)
+{
+ 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);
+}
+
+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);
+
+ 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;
+ }
+ }
+}
+
+static void
+minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta, void *priv_sta)
+{
+ struct minstrel_sta_info *mi = priv_sta;
+ struct minstrel_priv *mp = priv;
+ struct minstrel_rate *mr_ctl;
+ unsigned int i, n = 0;
+ unsigned int t_slot = 9; /* FIXME: get real slot time */
+
+ mi->lowest_rix = rate_lowest_index(sband, sta);
+ mr_ctl = &mi->r[rix_to_ndx(mi, mi->lowest_rix)];
+ mi->sp_ack_dur = mr_ctl->ack_time;
+
+ 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;
+
+ if (!rate_supported(sta, sband->band, i))
+ continue;
+ n++;
+ memset(mr, 0, sizeof(*mr));
+
+ mr->rix = i;
+ mr->bitrate = sband->bitrates[i].bitrate / 5;
+ calc_rate_durations(mi, hw_to_local(mp->hw), mr,
+ &sband->bitrates[i]);
+
+ /* calculate maximum number of retransmissions before
+ * fallback (based on maximum segment size) */
+ mr->retry_count = 1;
+ mr->retry_count_cts = 1;
+ mr->retry_count_rtscts = 1;
+ tx_time = mr->perfect_tx_time + mi->sp_ack_dur;
+ do {
+ /* add one retransmission */
+ tx_time_single = mr->ack_time + mr->perfect_tx_time;
+
+ /* contention window */
+ tx_time_single += t_slot + min(cw, mp->cw_max);
+ cw = (cw + 1) << 1;
+
+ tx_time += tx_time_single;
+ tx_time_cts += tx_time_single + mi->sp_ack_dur;
+ tx_time_rtscts += tx_time_single + 2 * mi->sp_ack_dur;
+ if ((tx_time_cts < mp->segment_size) &&
+ (mr->retry_count_cts < mp->max_retry))
+ mr->retry_count_cts++;
+ if ((tx_time_rtscts < mp->segment_size) &&
+ (mr->retry_count_rtscts < mp->max_retry))
+ mr->retry_count_rtscts++;
+ } while ((tx_time < mp->segment_size) &&
+ (++mr->retry_count < mp->max_retry));
+ mr->adjusted_retry_count = mr->retry_count;
+ }
+
+ for (i = n; i < sband->n_bitrates; i++) {
+ struct minstrel_rate *mr = &mi->r[i];
+ mr->rix = -1;
+ }
+
+ mi->n_rates = n;
+ mi->stats_update = jiffies;
+
+ init_sample_table(mi);
+}
+
+static void *
+minstrel_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp)
+{
+ struct ieee80211_supported_band *sband;
+ struct minstrel_sta_info *mi;
+ struct minstrel_priv *mp = priv;
+ struct ieee80211_hw *hw = mp->hw;
+ int max_rates = 0;
+ int i;
+
+ mi = kzalloc(sizeof(struct minstrel_sta_info), gfp);
+ if (!mi)
+ return NULL;
+
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
+ sband = hw->wiphy->bands[hw->conf.channel->band];
+ if (sband->n_bitrates > max_rates)
+ max_rates = sband->n_bitrates;
+ }
+
+ mi->r = kzalloc(sizeof(struct minstrel_rate) * max_rates, gfp);
+ if (!mi->r)
+ goto error;
+
+ mi->sample_table = kmalloc(SAMPLE_COLUMNS * max_rates, gfp);
+ if (!mi->sample_table)
+ goto error1;
+
+ mi->stats_update = jiffies;
+ return mi;
+
+error1:
+ kfree(mi->r);
+error:
+ kfree(mi);
+ return NULL;
+}
+
+static void
+minstrel_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta)
+{
+ struct minstrel_sta_info *mi = priv_sta;
+
+ kfree(mi->sample_table);
+ kfree(mi->r);
+ kfree(mi);
+}
+
+static void
+minstrel_clear(void *priv)
+{
+}
+
+static void *
+minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
+{
+ struct minstrel_priv *mp;
+
+ mp = kzalloc(sizeof(struct minstrel_priv), GFP_ATOMIC);
+ if (!mp)
+ return NULL;
+
+ /* contention window settings
+ * Just an approximation. Using the per-queue values would complicate
+ * the calculations and is probably unnecessary */
+ mp->cw_min = 15;
+ mp->cw_max = 1023;
+
+ /* number of packets (in %) to use for sampling other rates
+ * sample less often for non-mrr packets, because the overhead
+ * is much higher than with mrr */
+ mp->lookaround_rate = 5;
+ mp->lookaround_rate_mrr = 10;
+
+ /* 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;
+
+ if (hw->max_altrate_tries > 0)
+ mp->max_retry = hw->max_altrate_tries;
+ else
+ /* safe default, does not necessarily have to match hw properties */
+ mp->max_retry = 7;
+
+ if (hw->max_altrates >= 3)
+ mp->has_mrr = true;
+
+ mp->hw = hw;
+ mp->update_interval = 100;
+
+ return mp;
+}
+
+static void
+minstrel_free(void *priv)
+{
+ kfree(priv);
+}
+
+static struct rate_control_ops mac80211_minstrel = {
+ .name = "minstrel",
+ .tx_status = minstrel_tx_status,
+ .get_rate = minstrel_get_rate,
+ .rate_init = minstrel_rate_init,
+ .clear = minstrel_clear,
+ .alloc = minstrel_alloc,
+ .free = minstrel_free,
+ .alloc_sta = minstrel_alloc_sta,
+ .free_sta = minstrel_free_sta,
+#ifdef CONFIG_MAC80211_DEBUGFS
+ .add_sta_debugfs = minstrel_add_sta_debugfs,
+ .remove_sta_debugfs = minstrel_remove_sta_debugfs,
+#endif
+};
+
+int __init
+rc80211_minstrel_init(void)
+{
+ return ieee80211_rate_control_register(&mac80211_minstrel);
+}
+
+void
+rc80211_minstrel_exit(void)
+{
+ ieee80211_rate_control_unregister(&mac80211_minstrel);
+}
+
diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h
new file mode 100644
index 00000000000..9a90a6aee04
--- /dev/null
+++ b/net/mac80211/rc80211_minstrel.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2008 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
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __RC_MINSTREL_H
+#define __RC_MINSTREL_H
+
+struct minstrel_rate {
+ int bitrate;
+ int rix;
+
+ unsigned int perfect_tx_time;
+ unsigned int ack_time;
+
+ unsigned int retry_count;
+ unsigned int retry_count_cts;
+ unsigned int retry_count_rtscts;
+ unsigned int adjusted_retry_count;
+
+ u32 success;
+ u32 attempts;
+ u32 last_attempts;
+ u32 last_success;
+
+ /* parts per thousand */
+ u32 cur_prob;
+ u32 probability;
+
+ /* per-rate throughput */
+ u32 cur_tp;
+ u32 throughput;
+
+ u64 succ_hist;
+ u64 att_hist;
+};
+
+struct minstrel_sta_info {
+ 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;
+ unsigned int packet_count;
+ unsigned int sample_count;
+ int sample_deferred;
+
+ unsigned int sample_idx;
+ unsigned int sample_column;
+
+ int n_rates;
+ struct minstrel_rate *r;
+
+ /* sampling table */
+ u8 *sample_table;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ struct dentry *dbg_stats;
+#endif
+};
+
+struct minstrel_priv {
+ struct ieee80211_hw *hw;
+ bool has_mrr;
+ 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;
+};
+
+void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
+void minstrel_remove_sta_debugfs(void *priv, void *priv_sta);
+
+#endif
diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c
new file mode 100644
index 00000000000..0b024cd6b80
--- /dev/null
+++ b/net/mac80211/rc80211_minstrel_debugfs.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2008 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
+ * published by the Free Software Foundation.
+ *
+ * Based on minstrel.c:
+ * Copyright (C) 2005-2007 Derek Smithies <derek@indranet.co.nz>
+ * Sponsored by Indranet Technologies Ltd
+ *
+ * Based on sample.c:
+ * Copyright (c) 2005 John Bicket
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/debugfs.h>
+#include <linux/ieee80211.h>
+#include <net/mac80211.h>
+#include "rc80211_minstrel.h"
+
+struct minstrel_stats_info {
+ struct minstrel_sta_info *mi;
+ char buf[4096];
+ size_t len;
+};
+
+static int
+minstrel_stats_open(struct inode *inode, struct file *file)
+{
+ struct minstrel_sta_info *mi = inode->i_private;
+ struct minstrel_stats_info *ms;
+ unsigned int i, tp, prob, eprob;
+ char *p;
+
+ ms = kmalloc(sizeof(*ms), GFP_KERNEL);
+ if (!ms)
+ return -ENOMEM;
+
+ file->private_data = ms;
+ p = ms->buf;
+ 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_prob_rate) ? 'P' : ' ';
+ p += sprintf(p, "%3u%s", mr->bitrate / 2,
+ (mr->bitrate & 1 ? ".5" : " "));
+
+ tp = ((mr->cur_tp * 96) / 18000) >> 10;
+ prob = mr->cur_prob / 18;
+ eprob = mr->probability / 18;
+
+ 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,
+ mr->succ_hist,
+ mr->att_hist);
+ }
+ p += sprintf(p, "\nTotal packet count:: ideal %d "
+ "lookaround %d\n\n",
+ mi->packet_count - mi->sample_count,
+ mi->sample_count);
+ ms->len = p - ms->buf;
+
+ return 0;
+}
+
+static int
+minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *o)
+{
+ struct minstrel_stats_info *ms;
+ char *src;
+
+ ms = file->private_data;
+ src = ms->buf;
+
+ len = min(len, ms->len);
+ if (len <= *o)
+ return 0;
+
+ src += *o;
+ len -= *o;
+ *o += len;
+
+ if (copy_to_user(buf, src, len))
+ return -EFAULT;
+
+ return len;
+}
+
+static int
+minstrel_stats_release(struct inode *inode, struct file *file)
+{
+ struct minstrel_stats_info *ms = file->private_data;
+
+ kfree(ms);
+
+ return 0;
+}
+
+static struct file_operations minstrel_stat_fops = {
+ .owner = THIS_MODULE,
+ .open = minstrel_stats_open,
+ .read = minstrel_stats_read,
+ .release = minstrel_stats_release,
+};
+
+void
+minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir)
+{
+ struct minstrel_sta_info *mi = priv_sta;
+
+ mi->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, mi,
+ &minstrel_stat_fops);
+}
+
+void
+minstrel_remove_sta_debugfs(void *priv, void *priv_sta)
+{
+ struct minstrel_sta_info *mi = priv_sta;
+
+ debugfs_remove(mi->dbg_stats);
+}
diff --git a/net/mac80211/rc80211_pid.h b/net/mac80211/rc80211_pid.h
index 4ea7b97d1af..01d64d53f3b 100644
--- a/net/mac80211/rc80211_pid.h
+++ b/net/mac80211/rc80211_pid.h
@@ -61,7 +61,7 @@ enum rc_pid_event_type {
union rc_pid_event_data {
/* RC_PID_EVENT_TX_STATUS */
struct {
- struct ieee80211_tx_status tx_status;
+ struct ieee80211_tx_info tx_status;
};
/* RC_PID_EVENT_TYPE_RATE_CHANGE */
/* RC_PID_EVENT_TYPE_TX_RATE */
@@ -124,7 +124,6 @@ struct rc_pid_events_file_info {
* struct rc_pid_debugfs_entries - tunable parameters
*
* Algorithm parameters, tunable via debugfs.
- * @dir: the debugfs directory for a specific phy
* @target: target percentage for failed frames
* @sampling_period: error sampling interval in milliseconds
* @coeff_p: absolute value of the proportional coefficient
@@ -143,7 +142,6 @@ struct rc_pid_events_file_info {
* ordering of rates)
*/
struct rc_pid_debugfs_entries {
- struct dentry *dir;
struct dentry *target;
struct dentry *sampling_period;
struct dentry *coeff_p;
@@ -156,7 +154,7 @@ struct rc_pid_debugfs_entries {
};
void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf,
- struct ieee80211_tx_status *stat);
+ struct ieee80211_tx_info *stat);
void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf,
int index, int rate);
@@ -180,6 +178,8 @@ struct rc_pid_sta_info {
u32 tx_num_failed;
u32 tx_num_xmit;
+ int txrate_idx;
+
/* Average failed frames percentage error (i.e. actual vs. target
* percentage), scaled by RC_PID_SMOOTHING. This value is computed
* using using an exponential weighted average technique:
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c
index bcd27c1d759..86eb374e3b8 100644
--- a/net/mac80211/rc80211_pid_algo.c
+++ b/net/mac80211/rc80211_pid_algo.c
@@ -68,17 +68,14 @@
* exhibited a worse failed frames behaviour and we'll choose the highest rate
* whose failed frames behaviour is not worse than the one of the original rate
* target. While at it, check that the new rate is valid. */
-static void rate_control_pid_adjust_rate(struct ieee80211_local *local,
- struct sta_info *sta, int adj,
+static void rate_control_pid_adjust_rate(struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta,
+ struct rc_pid_sta_info *spinfo, int adj,
struct rc_pid_rateinfo *rinfo)
{
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_supported_band *sband;
int cur_sorted, new_sorted, probe, tmp, n_bitrates, band;
- int cur = sta->txrate_idx;
+ int cur = spinfo->txrate_idx;
- sdata = sta->sdata;
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
band = sband->band;
n_bitrates = sband->n_bitrates;
@@ -111,7 +108,7 @@ static void rate_control_pid_adjust_rate(struct ieee80211_local *local,
/* Fit the rate found to the nearest supported rate. */
do {
if (rate_supported(sta, band, rinfo[tmp].index)) {
- sta->txrate_idx = rinfo[tmp].index;
+ spinfo->txrate_idx = rinfo[tmp].index;
break;
}
if (adj < 0)
@@ -121,9 +118,9 @@ static void rate_control_pid_adjust_rate(struct ieee80211_local *local,
} while (tmp < n_bitrates && tmp >= 0);
#ifdef CONFIG_MAC80211_DEBUGFS
- rate_control_pid_event_rate_change(
- &((struct rc_pid_sta_info *)sta->rate_ctrl_priv)->events,
- sta->txrate_idx, sband->bitrates[sta->txrate_idx].bitrate);
+ rate_control_pid_event_rate_change(&spinfo->events,
+ spinfo->txrate_idx,
+ sband->bitrates[spinfo->txrate_idx].bitrate);
#endif
}
@@ -145,15 +142,11 @@ static void rate_control_pid_normalize(struct rc_pid_info *pinfo, int l)
}
static void rate_control_pid_sample(struct rc_pid_info *pinfo,
- struct ieee80211_local *local,
- struct sta_info *sta)
+ struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta,
+ struct rc_pid_sta_info *spinfo)
{
-#ifdef CONFIG_MAC80211_MESH
- struct ieee80211_sub_if_data *sdata = sta->sdata;
-#endif
- struct rc_pid_sta_info *spinfo = sta->rate_ctrl_priv;
struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
- struct ieee80211_supported_band *sband;
u32 pf;
s32 err_avg;
u32 err_prop;
@@ -162,9 +155,6 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo,
int adj, i, j, tmp;
unsigned long period;
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- spinfo = sta->rate_ctrl_priv;
-
/* In case nothing happened during the previous control interval, turn
* the sharpening factor on. */
period = (HZ * pinfo->sampling_period + 500) / 1000;
@@ -180,14 +170,15 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo,
if (unlikely(spinfo->tx_num_xmit == 0))
pf = spinfo->last_pf;
else {
+ /* XXX: BAD HACK!!! */
+ struct sta_info *si = container_of(sta, struct sta_info, sta);
+
pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit;
-#ifdef CONFIG_MAC80211_MESH
- if (pf == 100 &&
- sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT)
- mesh_plink_broken(sta);
-#endif
+
+ if (ieee80211_vif_is_mesh(&si->sdata->vif) && pf == 100)
+ mesh_plink_broken(si);
pf <<= RC_PID_ARITH_SHIFT;
- sta->fail_avg = ((pf + (spinfo->last_pf << 3)) / 9)
+ si->fail_avg = ((pf + (spinfo->last_pf << 3)) / 9)
>> RC_PID_ARITH_SHIFT;
}
@@ -195,16 +186,16 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo,
spinfo->tx_num_failed = 0;
/* If we just switched rate, update the rate behaviour info. */
- if (pinfo->oldrate != sta->txrate_idx) {
+ if (pinfo->oldrate != spinfo->txrate_idx) {
i = rinfo[pinfo->oldrate].rev_index;
- j = rinfo[sta->txrate_idx].rev_index;
+ j = rinfo[spinfo->txrate_idx].rev_index;
tmp = (pf - spinfo->last_pf);
tmp = RC_PID_DO_ARITH_RIGHT_SHIFT(tmp, RC_PID_ARITH_SHIFT);
rinfo[j].diff = rinfo[i].diff + tmp;
- pinfo->oldrate = sta->txrate_idx;
+ pinfo->oldrate = spinfo->txrate_idx;
}
rate_control_pid_normalize(pinfo, sband->n_bitrates);
@@ -233,147 +224,105 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo,
/* Change rate. */
if (adj)
- rate_control_pid_adjust_rate(local, sta, adj, rinfo);
+ rate_control_pid_adjust_rate(sband, sta, spinfo, adj, rinfo);
}
-static void rate_control_pid_tx_status(void *priv, struct net_device *dev,
- struct sk_buff *skb,
- struct ieee80211_tx_status *status)
+static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta, void *priv_sta,
+ struct sk_buff *skb)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct ieee80211_sub_if_data *sdata;
struct rc_pid_info *pinfo = priv;
- struct sta_info *sta;
- struct rc_pid_sta_info *spinfo;
+ struct rc_pid_sta_info *spinfo = priv_sta;
unsigned long period;
- struct ieee80211_supported_band *sband;
-
- rcu_read_lock();
-
- sta = sta_info_get(local, hdr->addr1);
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- if (!sta)
- goto unlock;
-
- /* Don't update the state if we're not controlling the rate. */
- sdata = sta->sdata;
- if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
- sta->txrate_idx = sdata->bss->max_ratectrl_rateidx;
- goto unlock;
- }
+ if (!spinfo)
+ return;
/* Ignore all frames that were sent with a different rate than the rate
* we currently advise mac80211 to use. */
- if (status->control.tx_rate != &sband->bitrates[sta->txrate_idx])
- goto unlock;
+ if (info->tx_rate_idx != spinfo->txrate_idx)
+ return;
- spinfo = sta->rate_ctrl_priv;
spinfo->tx_num_xmit++;
#ifdef CONFIG_MAC80211_DEBUGFS
- rate_control_pid_event_tx_status(&spinfo->events, status);
+ rate_control_pid_event_tx_status(&spinfo->events, info);
#endif
/* We count frames that totally failed to be transmitted as two bad
* frames, those that made it out but had some retries as one good and
* one bad frame. */
- if (status->excessive_retries) {
+ if (info->status.excessive_retries) {
spinfo->tx_num_failed += 2;
spinfo->tx_num_xmit++;
- } else if (status->retry_count) {
+ } else if (info->status.retry_count) {
spinfo->tx_num_failed++;
spinfo->tx_num_xmit++;
}
- if (status->excessive_retries) {
- sta->tx_retry_failed++;
- sta->tx_num_consecutive_failures++;
- sta->tx_num_mpdu_fail++;
- } else {
- sta->tx_num_consecutive_failures = 0;
- sta->tx_num_mpdu_ok++;
- }
- sta->tx_retry_count += status->retry_count;
- sta->tx_num_mpdu_fail += status->retry_count;
-
/* Update PID controller state. */
period = (HZ * pinfo->sampling_period + 500) / 1000;
if (!period)
period = 1;
if (time_after(jiffies, spinfo->last_sample + period))
- rate_control_pid_sample(pinfo, local, sta);
-
- unlock:
- rcu_read_unlock();
+ rate_control_pid_sample(pinfo, sband, sta, spinfo);
}
-static void rate_control_pid_get_rate(void *priv, struct net_device *dev,
- struct ieee80211_supported_band *sband,
- struct sk_buff *skb,
- struct rate_selection *sel)
+static void
+rate_control_pid_get_rate(void *priv, struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta, void *priv_sta,
+ struct sk_buff *skb,
+ struct rate_selection *sel)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct ieee80211_sub_if_data *sdata;
- struct sta_info *sta;
+ struct rc_pid_sta_info *spinfo = priv_sta;
int rateidx;
u16 fc;
- rcu_read_lock();
-
- sta = sta_info_get(local, hdr->addr1);
-
/* Send management frames and broadcast/multicast data using lowest
* rate. */
fc = le16_to_cpu(hdr->frame_control);
- if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
- is_multicast_ether_addr(hdr->addr1) || !sta) {
- sel->rate = rate_lowest(local, sband, sta);
- rcu_read_unlock();
+ if (!sta || !spinfo ||
+ (fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
+ is_multicast_ether_addr(hdr->addr1)) {
+ sel->rate_idx = rate_lowest_index(sband, sta);
return;
}
- /* If a forced rate is in effect, select it. */
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->bss && sdata->bss->force_unicast_rateidx > -1)
- sta->txrate_idx = sdata->bss->force_unicast_rateidx;
-
- rateidx = sta->txrate_idx;
+ rateidx = spinfo->txrate_idx;
if (rateidx >= sband->n_bitrates)
rateidx = sband->n_bitrates - 1;
- sta->last_txrate_idx = rateidx;
-
- rcu_read_unlock();
-
- sel->rate = &sband->bitrates[rateidx];
+ sel->rate_idx = rateidx;
#ifdef CONFIG_MAC80211_DEBUGFS
- rate_control_pid_event_tx_rate(
- &((struct rc_pid_sta_info *) sta->rate_ctrl_priv)->events,
+ rate_control_pid_event_tx_rate(&spinfo->events,
rateidx, sband->bitrates[rateidx].bitrate);
#endif
}
-static void rate_control_pid_rate_init(void *priv, void *priv_sta,
- struct ieee80211_local *local,
- struct sta_info *sta)
+static void
+rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta, void *priv_sta)
{
+ struct rc_pid_sta_info *spinfo = priv_sta;
+ struct sta_info *si;
+
/* TODO: This routine should consider using RSSI from previous packets
* as we need to have IEEE 802.1X auth succeed immediately after assoc..
* Until that method is implemented, we will use the lowest supported
* rate as a workaround. */
- struct ieee80211_supported_band *sband;
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- sta->txrate_idx = rate_lowest_index(local, sband, sta);
- sta->fail_avg = 0;
+ spinfo->txrate_idx = rate_lowest_index(sband, sta);
+ /* HACK */
+ si = container_of(sta, struct sta_info, sta);
+ si->fail_avg = 0;
}
-static void *rate_control_pid_alloc(struct ieee80211_local *local)
+static void *rate_control_pid_alloc(struct ieee80211_hw *hw,
+ struct dentry *debugfsdir)
{
struct rc_pid_info *pinfo;
struct rc_pid_rateinfo *rinfo;
@@ -384,7 +333,7 @@ static void *rate_control_pid_alloc(struct ieee80211_local *local)
struct rc_pid_debugfs_entries *de;
#endif
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+ sband = hw->wiphy->bands[hw->conf.channel->band];
pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC);
if (!pinfo)
@@ -439,30 +388,28 @@ static void *rate_control_pid_alloc(struct ieee80211_local *local)
#ifdef CONFIG_MAC80211_DEBUGFS
de = &pinfo->dentries;
- de->dir = debugfs_create_dir("rc80211_pid",
- local->hw.wiphy->debugfsdir);
de->target = debugfs_create_u32("target_pf", S_IRUSR | S_IWUSR,
- de->dir, &pinfo->target);
+ debugfsdir, &pinfo->target);
de->sampling_period = debugfs_create_u32("sampling_period",
- S_IRUSR | S_IWUSR, de->dir,
+ S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->sampling_period);
de->coeff_p = debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR,
- de->dir, &pinfo->coeff_p);
+ debugfsdir, &pinfo->coeff_p);
de->coeff_i = debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR,
- de->dir, &pinfo->coeff_i);
+ debugfsdir, &pinfo->coeff_i);
de->coeff_d = debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR,
- de->dir, &pinfo->coeff_d);
+ debugfsdir, &pinfo->coeff_d);
de->smoothing_shift = debugfs_create_u32("smoothing_shift",
- S_IRUSR | S_IWUSR, de->dir,
+ S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->smoothing_shift);
de->sharpen_factor = debugfs_create_u32("sharpen_factor",
- S_IRUSR | S_IWUSR, de->dir,
+ S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->sharpen_factor);
de->sharpen_duration = debugfs_create_u32("sharpen_duration",
- S_IRUSR | S_IWUSR, de->dir,
+ S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->sharpen_duration);
de->norm_offset = debugfs_create_u32("norm_offset",
- S_IRUSR | S_IWUSR, de->dir,
+ S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->norm_offset);
#endif
@@ -484,7 +431,6 @@ static void rate_control_pid_free(void *priv)
debugfs_remove(de->coeff_p);
debugfs_remove(de->sampling_period);
debugfs_remove(de->target);
- debugfs_remove(de->dir);
#endif
kfree(pinfo->rinfo);
@@ -495,7 +441,8 @@ static void rate_control_pid_clear(void *priv)
{
}
-static void *rate_control_pid_alloc_sta(void *priv, gfp_t gfp)
+static void *rate_control_pid_alloc_sta(void *priv, struct ieee80211_sta *sta,
+ gfp_t gfp)
{
struct rc_pid_sta_info *spinfo;
@@ -513,10 +460,10 @@ static void *rate_control_pid_alloc_sta(void *priv, gfp_t gfp)
return spinfo;
}
-static void rate_control_pid_free_sta(void *priv, void *priv_sta)
+static void rate_control_pid_free_sta(void *priv, struct ieee80211_sta *sta,
+ void *priv_sta)
{
- struct rc_pid_sta_info *spinfo = priv_sta;
- kfree(spinfo);
+ kfree(priv_sta);
}
static struct rate_control_ops mac80211_rcpid = {
@@ -535,11 +482,6 @@ static struct rate_control_ops mac80211_rcpid = {
#endif
};
-MODULE_DESCRIPTION("PID controller based rate control algorithm");
-MODULE_AUTHOR("Stefano Brivio");
-MODULE_AUTHOR("Mattias Nissler");
-MODULE_LICENSE("GPL");
-
int __init rc80211_pid_init(void)
{
return ieee80211_rate_control_register(&mac80211_rcpid);
@@ -549,8 +491,3 @@ void rc80211_pid_exit(void)
{
ieee80211_rate_control_unregister(&mac80211_rcpid);
}
-
-#ifdef CONFIG_MAC80211_RC_PID_MODULE
-module_init(rc80211_pid_init);
-module_exit(rc80211_pid_exit);
-#endif
diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c
index ff5c380f3c1..8121d3bc683 100644
--- a/net/mac80211/rc80211_pid_debugfs.c
+++ b/net/mac80211/rc80211_pid_debugfs.c
@@ -39,11 +39,11 @@ static void rate_control_pid_event(struct rc_pid_event_buffer *buf,
}
void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf,
- struct ieee80211_tx_status *stat)
+ struct ieee80211_tx_info *stat)
{
union rc_pid_event_data evd;
- memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_status));
+ memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_info));
rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_STATUS, &evd);
}
@@ -167,8 +167,8 @@ static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,
switch (ev->type) {
case RC_PID_EVENT_TYPE_TX_STATUS:
p += snprintf(pb + p, length - p, "tx_status %u %u",
- ev->data.tx_status.excessive_retries,
- ev->data.tx_status.retry_count);
+ ev->data.tx_status.status.excessive_retries,
+ ev->data.tx_status.status.retry_count);
break;
case RC_PID_EVENT_TYPE_RATE_CHANGE:
p += snprintf(pb + p, length - p, "rate_change %d %d",
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 0941e5d6a52..cf6b121e1bb 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -61,22 +61,152 @@ static inline int should_drop_frame(struct ieee80211_rx_status *status,
int present_fcs_len,
int radiotap_len)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
return 1;
if (unlikely(skb->len < 16 + present_fcs_len + radiotap_len))
return 1;
- if (((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_FTYPE)) ==
- cpu_to_le16(IEEE80211_FTYPE_CTL)) &&
- ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE)) !=
- cpu_to_le16(IEEE80211_STYPE_PSPOLL)) &&
- ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE)) !=
- cpu_to_le16(IEEE80211_STYPE_BACK_REQ)))
+ if (ieee80211_is_ctl(hdr->frame_control) &&
+ !ieee80211_is_pspoll(hdr->frame_control) &&
+ !ieee80211_is_back_req(hdr->frame_control))
return 1;
return 0;
}
+static int
+ieee80211_rx_radiotap_len(struct ieee80211_local *local,
+ struct ieee80211_rx_status *status)
+{
+ int len;
+
+ /* always present fields */
+ len = sizeof(struct ieee80211_radiotap_header) + 9;
+
+ if (status->flag & RX_FLAG_TSFT)
+ len += 8;
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_DB ||
+ local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
+ len += 1;
+ if (local->hw.flags & IEEE80211_HW_NOISE_DBM)
+ len += 1;
+
+ if (len & 1) /* padding for RX_FLAGS if necessary */
+ len++;
+
+ /* make sure radiotap starts at a naturally aligned address */
+ if (len % 8)
+ len = roundup(len, 8);
+
+ return len;
+}
+
+/**
+ * ieee80211_add_rx_radiotap_header - add radiotap header
+ *
+ * add a radiotap header containing all the fields which the hardware provided.
+ */
+static void
+ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
+ struct sk_buff *skb,
+ struct ieee80211_rx_status *status,
+ struct ieee80211_rate *rate,
+ int rtap_len)
+{
+ struct ieee80211_radiotap_header *rthdr;
+ unsigned char *pos;
+
+ rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
+ memset(rthdr, 0, rtap_len);
+
+ /* radiotap header, set always present flags */
+ rthdr->it_present =
+ cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
+ (1 << IEEE80211_RADIOTAP_RATE) |
+ (1 << IEEE80211_RADIOTAP_CHANNEL) |
+ (1 << IEEE80211_RADIOTAP_ANTENNA) |
+ (1 << IEEE80211_RADIOTAP_RX_FLAGS));
+ rthdr->it_len = cpu_to_le16(rtap_len);
+
+ pos = (unsigned char *)(rthdr+1);
+
+ /* the order of the following fields is important */
+
+ /* IEEE80211_RADIOTAP_TSFT */
+ if (status->flag & RX_FLAG_TSFT) {
+ *(__le64 *)pos = cpu_to_le64(status->mactime);
+ rthdr->it_present |=
+ cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
+ pos += 8;
+ }
+
+ /* IEEE80211_RADIOTAP_FLAGS */
+ if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
+ *pos |= IEEE80211_RADIOTAP_F_FCS;
+ if (status->flag & RX_FLAG_SHORTPRE)
+ *pos |= IEEE80211_RADIOTAP_F_SHORTPRE;
+ pos++;
+
+ /* IEEE80211_RADIOTAP_RATE */
+ *pos = rate->bitrate / 5;
+ pos++;
+
+ /* IEEE80211_RADIOTAP_CHANNEL */
+ *(__le16 *)pos = cpu_to_le16(status->freq);
+ pos += 2;
+ if (status->band == IEEE80211_BAND_5GHZ)
+ *(__le16 *)pos = cpu_to_le16(IEEE80211_CHAN_OFDM |
+ IEEE80211_CHAN_5GHZ);
+ else if (rate->flags & IEEE80211_RATE_ERP_G)
+ *(__le16 *)pos = cpu_to_le16(IEEE80211_CHAN_OFDM |
+ IEEE80211_CHAN_2GHZ);
+ else
+ *(__le16 *)pos = cpu_to_le16(IEEE80211_CHAN_CCK |
+ IEEE80211_CHAN_2GHZ);
+ pos += 2;
+
+ /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
+ *pos = status->signal;
+ rthdr->it_present |=
+ cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+ pos++;
+ }
+
+ /* IEEE80211_RADIOTAP_DBM_ANTNOISE */
+ if (local->hw.flags & IEEE80211_HW_NOISE_DBM) {
+ *pos = status->noise;
+ rthdr->it_present |=
+ cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTNOISE);
+ pos++;
+ }
+
+ /* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */
+
+ /* IEEE80211_RADIOTAP_ANTENNA */
+ *pos = status->antenna;
+ pos++;
+
+ /* IEEE80211_RADIOTAP_DB_ANTSIGNAL */
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_DB) {
+ *pos = status->signal;
+ rthdr->it_present |=
+ cpu_to_le32(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL);
+ 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 - (unsigned char *)rthdr) & 1)
+ pos++;
+ /* FIXME: when radiotap gets a 'bad PLCP' flag use it here */
+ if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
+ *(__le16 *)pos |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADFCS);
+ pos += 2;
+}
+
/*
* This function copies a received frame to all monitor interfaces and
* returns a cleaned-up SKB that no longer includes the FCS nor the
@@ -89,17 +219,6 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
{
struct ieee80211_sub_if_data *sdata;
int needed_headroom = 0;
- struct ieee80211_radiotap_header *rthdr;
- __le64 *rttsft = NULL;
- struct ieee80211_rtap_fixed_data {
- u8 flags;
- u8 rate;
- __le16 chan_freq;
- __le16 chan_flags;
- u8 antsignal;
- u8 padding_for_rxflags;
- __le16 rx_flags;
- } __attribute__ ((packed)) *rtfixed;
struct sk_buff *skb, *skb2;
struct net_device *prev_dev = NULL;
int present_fcs_len = 0;
@@ -116,8 +235,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
if (status->flag & RX_FLAG_RADIOTAP)
rtap_len = ieee80211_get_radiotap_len(origskb->data);
else
- /* room for radiotap header, always present fields and TSFT */
- needed_headroom = sizeof(*rthdr) + sizeof(*rtfixed) + 8;
+ /* 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;
@@ -163,55 +282,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
}
/* if necessary, prepend radiotap information */
- if (!(status->flag & RX_FLAG_RADIOTAP)) {
- rtfixed = (void *) skb_push(skb, sizeof(*rtfixed));
- rtap_len = sizeof(*rthdr) + sizeof(*rtfixed);
- if (status->flag & RX_FLAG_TSFT) {
- rttsft = (void *) skb_push(skb, sizeof(*rttsft));
- rtap_len += 8;
- }
- rthdr = (void *) skb_push(skb, sizeof(*rthdr));
- memset(rthdr, 0, sizeof(*rthdr));
- memset(rtfixed, 0, sizeof(*rtfixed));
- rthdr->it_present =
- cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
- (1 << IEEE80211_RADIOTAP_RATE) |
- (1 << IEEE80211_RADIOTAP_CHANNEL) |
- (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |
- (1 << IEEE80211_RADIOTAP_RX_FLAGS));
- rtfixed->flags = 0;
- if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
- rtfixed->flags |= IEEE80211_RADIOTAP_F_FCS;
-
- if (rttsft) {
- *rttsft = cpu_to_le64(status->mactime);
- rthdr->it_present |=
- cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
- }
-
- /* FIXME: when radiotap gets a 'bad PLCP' flag use it here */
- rtfixed->rx_flags = 0;
- if (status->flag &
- (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
- rtfixed->rx_flags |=
- cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADFCS);
-
- rtfixed->rate = rate->bitrate / 5;
-
- rtfixed->chan_freq = cpu_to_le16(status->freq);
-
- if (status->band == IEEE80211_BAND_5GHZ)
- rtfixed->chan_flags =
- cpu_to_le16(IEEE80211_CHAN_OFDM |
- IEEE80211_CHAN_5GHZ);
- else
- rtfixed->chan_flags =
- cpu_to_le16(IEEE80211_CHAN_DYN |
- IEEE80211_CHAN_2GHZ);
-
- rtfixed->antsignal = status->ssi;
- rthdr->it_len = cpu_to_le16(rtap_len);
- }
+ if (!(status->flag & RX_FLAG_RADIOTAP))
+ ieee80211_add_rx_radiotap_header(local, skb, status, rate,
+ needed_headroom);
skb_reset_mac_header(skb);
skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -222,7 +295,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
if (!netif_running(sdata->dev))
continue;
- if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR)
+ if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
continue;
if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)
@@ -253,33 +326,33 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
{
- u8 *data = rx->skb->data;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
int tid;
/* does the frame have a qos control field? */
- if (WLAN_FC_IS_QOS_DATA(rx->fc)) {
- u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN;
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ u8 *qc = ieee80211_get_qos_ctl(hdr);
/* frame has qos control */
- tid = qc[0] & QOS_CONTROL_TID_MASK;
- if (qc[0] & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
+ tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
+ if (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
rx->flags |= IEEE80211_RX_AMSDU;
else
rx->flags &= ~IEEE80211_RX_AMSDU;
} else {
- if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
- /* Separate TID for management frames */
- tid = NUM_RX_DATA_QUEUES - 1;
- } else {
- /* no qos control present */
- tid = 0; /* 802.1d - Best Effort */
- }
+ /*
+ * IEEE 802.11-2007, 7.1.3.4.1 ("Sequence Number field"):
+ *
+ * Sequence numbers for management frames, QoS data
+ * frames with a broadcast/multicast address in the
+ * Address 1 field, and all non-QoS data frames sent
+ * by QoS STAs are assigned using an additional single
+ * modulo-4096 counter, [...]
+ *
+ * We also use that counter for non-QoS STAs.
+ */
+ tid = NUM_RX_DATA_QUEUES - 1;
}
- I802_DEBUG_INC(rx->local->wme_rx_queue[tid]);
- /* only a debug counter, sta might not be assigned properly yet */
- if (rx->sta)
- I802_DEBUG_INC(rx->sta->wme_rx_queue[tid]);
-
rx->queue = tid;
/* 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. */
@@ -289,9 +362,10 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
static void ieee80211_verify_ip_alignment(struct ieee80211_rx_data *rx)
{
#ifdef CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
int hdrlen;
- if (!WLAN_FC_DATA_PRESENT(rx->fc))
+ if (!ieee80211_is_data_present(hdr->frame_control))
return;
/*
@@ -313,7 +387,7 @@ static void ieee80211_verify_ip_alignment(struct ieee80211_rx_data *rx)
* header and the payload is not supported, the driver is required
* to move the 802.11 header further back in that case.
*/
- hdrlen = ieee80211_get_hdrlen(rx->fc);
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (rx->flags & IEEE80211_RX_AMSDU)
hdrlen += ETH_HLEN;
WARN_ON_ONCE(((unsigned long)(rx->skb->data + hdrlen)) & 3);
@@ -321,62 +395,20 @@ static void ieee80211_verify_ip_alignment(struct ieee80211_rx_data *rx)
}
-static u32 ieee80211_rx_load_stats(struct ieee80211_local *local,
- struct sk_buff *skb,
- struct ieee80211_rx_status *status,
- struct ieee80211_rate *rate)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u32 load = 0, hdrtime;
-
- /* Estimate total channel use caused by this frame */
-
- /* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
- * 1 usec = 1/8 * (1080 / 10) = 13.5 */
-
- if (status->band == IEEE80211_BAND_5GHZ ||
- (status->band == IEEE80211_BAND_5GHZ &&
- rate->flags & IEEE80211_RATE_ERP_G))
- hdrtime = CHAN_UTIL_HDR_SHORT;
- else
- hdrtime = CHAN_UTIL_HDR_LONG;
-
- load = hdrtime;
- if (!is_multicast_ether_addr(hdr->addr1))
- load += hdrtime;
-
- /* TODO: optimise again */
- load += skb->len * CHAN_UTIL_RATE_LCM / rate->bitrate;
-
- /* Divide channel_use by 8 to avoid wrapping around the counter */
- load >>= CHAN_UTIL_SHIFT;
-
- return load;
-}
-
/* rx handlers */
-static ieee80211_rx_result
-ieee80211_rx_h_if_stats(struct ieee80211_rx_data *rx)
-{
- if (rx->sta)
- rx->sta->channel_use_raw += rx->load;
- rx->sdata->channel_use_raw += rx->load;
- return RX_CONTINUE;
-}
-
-static ieee80211_rx_result
+static ieee80211_rx_result debug_noinline
ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx)
{
struct ieee80211_local *local = rx->local;
struct sk_buff *skb = rx->skb;
- if (unlikely(local->sta_hw_scanning))
- return ieee80211_sta_rx_scan(rx->dev, skb, rx->status);
+ if (unlikely(local->hw_scanning))
+ return ieee80211_scan_rx(rx->sdata, skb, rx->status);
- if (unlikely(local->sta_sw_scanning)) {
+ if (unlikely(local->sw_scanning)) {
/* drop all the other packets during a software scan anyway */
- if (ieee80211_sta_rx_scan(rx->dev, skb, rx->status)
+ if (ieee80211_scan_rx(rx->sdata, skb, rx->status)
!= RX_QUEUED)
dev_kfree_skb(skb);
return RX_QUEUED;
@@ -394,14 +426,11 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx)
static ieee80211_rx_result
ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
{
- int hdrlen = ieee80211_get_hdrlen(rx->fc);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
-
-#define msh_h_get(h, l) ((struct ieee80211s_hdr *) ((u8 *)h + l))
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+ unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
- if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) {
- if (!((rx->fc & IEEE80211_FCTL_FROMDS) &&
- (rx->fc & IEEE80211_FCTL_TODS)))
+ if (ieee80211_is_data(hdr->frame_control)) {
+ if (!ieee80211_has_a4(hdr->frame_control))
return RX_DROP_MONITOR;
if (memcmp(hdr->addr4, rx->dev->dev_addr, ETH_ALEN) == 0)
return RX_DROP_MONITOR;
@@ -414,27 +443,30 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
if (!rx->sta || sta_plink_state(rx->sta) != PLINK_ESTAB) {
struct ieee80211_mgmt *mgmt;
- if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT)
+ if (!ieee80211_is_mgmt(hdr->frame_control))
return RX_DROP_MONITOR;
- switch (rx->fc & IEEE80211_FCTL_STYPE) {
- case IEEE80211_STYPE_ACTION:
+ if (ieee80211_is_action(hdr->frame_control)) {
mgmt = (struct ieee80211_mgmt *)hdr;
if (mgmt->u.action.category != PLINK_CATEGORY)
return RX_DROP_MONITOR;
- /* fall through on else */
- case IEEE80211_STYPE_PROBE_REQ:
- case IEEE80211_STYPE_PROBE_RESP:
- case IEEE80211_STYPE_BEACON:
return RX_CONTINUE;
- break;
- default:
- return RX_DROP_MONITOR;
}
- } else if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
- is_multicast_ether_addr(hdr->addr1) &&
- mesh_rmc_check(hdr->addr4, msh_h_get(hdr, hdrlen), rx->dev))
+ if (ieee80211_is_probe_req(hdr->frame_control) ||
+ ieee80211_is_probe_resp(hdr->frame_control) ||
+ ieee80211_is_beacon(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->addr4, msh_h_get(hdr, hdrlen), rx->sdata))
return RX_DROP_MONITOR;
#undef msh_h_get
@@ -442,16 +474,14 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
}
-static ieee80211_rx_result
+static ieee80211_rx_result debug_noinline
ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
{
- struct ieee80211_hdr *hdr;
-
- hdr = (struct ieee80211_hdr *) rx->skb->data;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
/* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) {
- if (unlikely(rx->fc & IEEE80211_FCTL_RETRY &&
+ if (unlikely(ieee80211_has_retry(hdr->frame_control) &&
rx->sta->last_seq_ctrl[rx->queue] ==
hdr->seq_ctrl)) {
if (rx->flags & IEEE80211_RX_RA_MATCH) {
@@ -471,8 +501,8 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
/* Drop disallowed frame classes based on STA auth/assoc state;
* IEEE 802.11, Chap 5.5.
*
- * 80211.o does filtering only based on association state, i.e., it
- * drops Class 3 frames from not associated stations. hostapd sends
+ * mac80211 filters only based on association state, i.e. it drops
+ * Class 3 frames from not associated stations. hostapd sends
* deauth/disassoc frames when needed. In addition, hostapd is
* responsible for filtering on both auth and assoc states.
*/
@@ -480,15 +510,14 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
if (ieee80211_vif_is_mesh(&rx->sdata->vif))
return ieee80211_rx_mesh_check(rx);
- if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
- ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL &&
- (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) &&
- rx->sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
- (!rx->sta || !(rx->sta->flags & WLAN_STA_ASSOC)))) {
- if ((!(rx->fc & IEEE80211_FCTL_FROMDS) &&
- !(rx->fc & IEEE80211_FCTL_TODS) &&
- (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
- || !(rx->flags & IEEE80211_RX_RA_MATCH)) {
+ if (unlikely((ieee80211_is_data(hdr->frame_control) ||
+ ieee80211_is_pspoll(hdr->frame_control)) &&
+ rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ (!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)) ||
+ !(rx->flags & IEEE80211_RX_RA_MATCH)) {
/* Drop IBSS frames and frames for other hosts
* silently. */
return RX_DROP_MONITOR;
@@ -501,10 +530,10 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
}
-static ieee80211_rx_result
+static ieee80211_rx_result debug_noinline
ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
int keyidx;
int hdrlen;
ieee80211_rx_result result = RX_DROP_UNUSABLE;
@@ -536,7 +565,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
* possible.
*/
- if (!(rx->fc & IEEE80211_FCTL_PROTECTED))
+ if (!ieee80211_has_protected(hdr->frame_control))
return RX_CONTINUE;
/*
@@ -565,7 +594,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
(rx->status->flag & RX_FLAG_IV_STRIPPED))
return RX_CONTINUE;
- hdrlen = ieee80211_get_hdrlen(rx->fc);
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (rx->skb->len < 8 + hdrlen)
return RX_DROP_UNUSABLE; /* TODO: count this? */
@@ -592,17 +621,12 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
rx->key->tx_rx_count++;
/* TODO: add threshold stuff again */
} else {
-#ifdef CONFIG_MAC80211_DEBUG
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: RX protected frame,"
- " but have no key\n", rx->dev->name);
-#endif /* CONFIG_MAC80211_DEBUG */
return RX_DROP_MONITOR;
}
/* Check for weak IVs if possible */
if (rx->sta && rx->key->conf.alg == ALG_WEP &&
- ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
+ ieee80211_is_data(hdr->frame_control) &&
(!(rx->status->flag & RX_FLAG_IV_STRIPPED) ||
!(rx->status->flag & RX_FLAG_DECRYPTED)) &&
ieee80211_wep_is_weak_iv(rx->skb, rx->key))
@@ -626,76 +650,68 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
return result;
}
-static void ap_sta_ps_start(struct net_device *dev, struct sta_info *sta)
+static void ap_sta_ps_start(struct sta_info *sta)
{
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
DECLARE_MAC_BUF(mac);
- sdata = sta->sdata;
-
- if (sdata->bss)
- atomic_inc(&sdata->bss->num_sta_ps);
- sta->flags |= WLAN_STA_PS;
- sta->flags &= ~WLAN_STA_PSPOLL;
+ atomic_inc(&sdata->bss->num_sta_ps);
+ set_and_clear_sta_flags(sta, WLAN_STA_PS, WLAN_STA_PSPOLL);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "%s: STA %s aid %d enters power save mode\n",
- dev->name, print_mac(mac, sta->addr), sta->aid);
+ sdata->dev->name, print_mac(mac, sta->sta.addr), sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
}
-static int ap_sta_ps_end(struct net_device *dev, struct sta_info *sta)
+static int ap_sta_ps_end(struct sta_info *sta)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
int sent = 0;
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_tx_packet_data *pkt_data;
+ struct ieee80211_tx_info *info;
DECLARE_MAC_BUF(mac);
- sdata = sta->sdata;
-
- if (sdata->bss)
- atomic_dec(&sdata->bss->num_sta_ps);
+ atomic_dec(&sdata->bss->num_sta_ps);
- sta->flags &= ~(WLAN_STA_PS | WLAN_STA_PSPOLL);
+ clear_sta_flags(sta, WLAN_STA_PS | WLAN_STA_PSPOLL);
if (!skb_queue_empty(&sta->ps_tx_buf))
sta_info_clear_tim_bit(sta);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "%s: STA %s aid %d exits power save mode\n",
- dev->name, print_mac(mac, sta->addr), sta->aid);
+ sdata->dev->name, print_mac(mac, sta->sta.addr), sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
/* Send all buffered frames to the station */
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
- pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
+ info = IEEE80211_SKB_CB(skb);
sent++;
- pkt_data->flags |= IEEE80211_TXPD_REQUEUE;
+ info->flags |= IEEE80211_TX_CTL_REQUEUE;
dev_queue_xmit(skb);
}
while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
- pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
+ info = IEEE80211_SKB_CB(skb);
local->total_ps_buffered--;
sent++;
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "%s: STA %s aid %d send PS frame "
- "since STA not sleeping anymore\n", dev->name,
- print_mac(mac, sta->addr), sta->aid);
+ "since STA not sleeping anymore\n", sdata->dev->name,
+ print_mac(mac, sta->sta.addr), sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
- pkt_data->flags |= IEEE80211_TXPD_REQUEUE;
+ info->flags |= IEEE80211_TX_CTL_REQUEUE;
dev_queue_xmit(skb);
}
return sent;
}
-static ieee80211_rx_result
+static ieee80211_rx_result debug_noinline
ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
{
struct sta_info *sta = rx->sta;
- struct net_device *dev = rx->dev;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
if (!sta)
return RX_CONTINUE;
@@ -703,14 +719,14 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
/* 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 are using different BSSID. */
- if (rx->sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
+ if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) {
u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
- IEEE80211_IF_TYPE_IBSS);
+ NL80211_IFTYPE_ADHOC);
if (compare_ether_addr(bssid, rx->sdata->u.sta.bssid) == 0)
sta->last_rx = jiffies;
} else
if (!is_multicast_ether_addr(hdr->addr1) ||
- rx->sdata->vif.type == IEEE80211_IF_TYPE_STA) {
+ rx->sdata->vif.type == NL80211_IFTYPE_STATION) {
/* Update last_rx only for unicast frames in order to prevent
* the Probe Request frames (the only broadcast frames from a
* STA in infrastructure mode) from keeping a connection alive.
@@ -725,24 +741,26 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
sta->rx_fragments++;
sta->rx_bytes += rx->skb->len;
- sta->last_rssi = rx->status->ssi;
sta->last_signal = rx->status->signal;
+ sta->last_qual = rx->status->qual;
sta->last_noise = rx->status->noise;
- if (!(rx->fc & IEEE80211_FCTL_MOREFRAGS)) {
+ if (!ieee80211_has_morefrags(hdr->frame_control) &&
+ (rx->sdata->vif.type == NL80211_IFTYPE_AP ||
+ rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) {
/* Change STA power saving mode only in the end of a frame
* exchange sequence */
- if ((sta->flags & WLAN_STA_PS) && !(rx->fc & IEEE80211_FCTL_PM))
- rx->sent_ps_buffered += ap_sta_ps_end(dev, sta);
- else if (!(sta->flags & WLAN_STA_PS) &&
- (rx->fc & IEEE80211_FCTL_PM))
- ap_sta_ps_start(dev, sta);
+ if (test_sta_flags(sta, WLAN_STA_PS) &&
+ !ieee80211_has_pm(hdr->frame_control))
+ rx->sent_ps_buffered += ap_sta_ps_end(sta);
+ else if (!test_sta_flags(sta, WLAN_STA_PS) &&
+ ieee80211_has_pm(hdr->frame_control))
+ ap_sta_ps_start(sta);
}
/* Drop data::nullfunc frames silently, since they are used only to
* control station power saving mode. */
- if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
- (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_NULLFUNC) {
+ if (ieee80211_is_nullfunc(hdr->frame_control)) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc);
/* Update counter and free packet here to avoid counting this
* as a dropped packed. */
@@ -768,7 +786,7 @@ ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
sdata->fragment_next = 0;
if (!skb_queue_empty(&entry->skb_list)) {
-#ifdef CONFIG_MAC80211_DEBUG
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
struct ieee80211_hdr *hdr =
(struct ieee80211_hdr *) entry->skb_list.next->data;
DECLARE_MAC_BUF(mac);
@@ -780,7 +798,7 @@ ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
jiffies - entry->first_frag_time, entry->seq,
entry->last_frag, print_mac(mac, hdr->addr1),
print_mac(mac2, hdr->addr2));
-#endif /* CONFIG_MAC80211_DEBUG */
+#endif
__skb_queue_purge(&entry->skb_list);
}
@@ -798,7 +816,7 @@ ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
static inline struct ieee80211_fragment_entry *
ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
- u16 fc, unsigned int frag, unsigned int seq,
+ unsigned int frag, unsigned int seq,
int rx_queue, struct ieee80211_hdr *hdr)
{
struct ieee80211_fragment_entry *entry;
@@ -807,7 +825,6 @@ ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
idx = sdata->fragment_next;
for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) {
struct ieee80211_hdr *f_hdr;
- u16 f_fc;
idx--;
if (idx < 0)
@@ -819,10 +836,13 @@ ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
entry->last_frag + 1 != frag)
continue;
- f_hdr = (struct ieee80211_hdr *) entry->skb_list.next->data;
- f_fc = le16_to_cpu(f_hdr->frame_control);
+ f_hdr = (struct ieee80211_hdr *)entry->skb_list.next->data;
- if ((fc & IEEE80211_FCTL_FTYPE) != (f_fc & IEEE80211_FCTL_FTYPE) ||
+ /*
+ * Check ftype and addresses are equal, else check next fragment
+ */
+ 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)
continue;
@@ -837,21 +857,23 @@ ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
return NULL;
}
-static ieee80211_rx_result
+static ieee80211_rx_result debug_noinline
ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr;
u16 sc;
+ __le16 fc;
unsigned int frag, seq;
struct ieee80211_fragment_entry *entry;
struct sk_buff *skb;
DECLARE_MAC_BUF(mac);
- hdr = (struct ieee80211_hdr *) rx->skb->data;
+ hdr = (struct ieee80211_hdr *)rx->skb->data;
+ fc = hdr->frame_control;
sc = le16_to_cpu(hdr->seq_ctrl);
frag = sc & IEEE80211_SCTL_FRAG;
- if (likely((!(rx->fc & IEEE80211_FCTL_MOREFRAGS) && frag == 0) ||
+ if (likely((!ieee80211_has_morefrags(fc) && frag == 0) ||
(rx->skb)->len < 24 ||
is_multicast_ether_addr(hdr->addr1))) {
/* not fragmented */
@@ -866,7 +888,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
rx->queue, &(rx->skb));
if (rx->key && rx->key->conf.alg == ALG_CCMP &&
- (rx->fc & IEEE80211_FCTL_PROTECTED)) {
+ ieee80211_has_protected(fc)) {
/* Store CCMP PN so that we can verify that the next
* fragment has a sequential PN value. */
entry->ccmp = 1;
@@ -880,8 +902,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
/* This is a fragment for a frame that should already be pending in
* fragment cache. Add this fragment to the end of the pending entry.
*/
- entry = ieee80211_reassemble_find(rx->sdata, rx->fc, frag, seq,
- rx->queue, hdr);
+ entry = ieee80211_reassemble_find(rx->sdata, frag, seq, rx->queue, hdr);
if (!entry) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
return RX_DROP_MONITOR;
@@ -901,26 +922,16 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
break;
}
rpn = rx->key->u.ccmp.rx_pn[rx->queue];
- if (memcmp(pn, rpn, CCMP_PN_LEN) != 0) {
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: defrag: CCMP PN not "
- "sequential A2=%s"
- " PN=%02x%02x%02x%02x%02x%02x "
- "(expected %02x%02x%02x%02x%02x%02x)\n",
- rx->dev->name, print_mac(mac, hdr->addr2),
- rpn[0], rpn[1], rpn[2], rpn[3], rpn[4],
- rpn[5], pn[0], pn[1], pn[2], pn[3],
- pn[4], pn[5]);
+ if (memcmp(pn, rpn, CCMP_PN_LEN))
return RX_DROP_UNUSABLE;
- }
memcpy(entry->last_pn, pn, CCMP_PN_LEN);
}
- skb_pull(rx->skb, ieee80211_get_hdrlen(rx->fc));
+ skb_pull(rx->skb, ieee80211_hdrlen(fc));
__skb_queue_tail(&entry->skb_list, rx->skb);
entry->last_frag = frag;
entry->extra_len += rx->skb->len;
- if (rx->fc & IEEE80211_FCTL_MOREFRAGS) {
+ if (ieee80211_has_morefrags(fc)) {
rx->skb = NULL;
return RX_QUEUED;
}
@@ -953,22 +964,21 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
}
-static ieee80211_rx_result
+static ieee80211_rx_result debug_noinline
ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
struct sk_buff *skb;
int no_pending_pkts;
DECLARE_MAC_BUF(mac);
+ __le16 fc = ((struct ieee80211_hdr *)rx->skb->data)->frame_control;
- if (likely(!rx->sta ||
- (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL ||
- (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PSPOLL ||
+ if (likely(!rx->sta || !ieee80211_is_pspoll(fc) ||
!(rx->flags & IEEE80211_RX_RA_MATCH)))
return RX_CONTINUE;
- if ((sdata->vif.type != IEEE80211_IF_TYPE_AP) &&
- (sdata->vif.type != IEEE80211_IF_TYPE_VLAN))
+ if ((sdata->vif.type != NL80211_IFTYPE_AP) &&
+ (sdata->vif.type != NL80211_IFTYPE_AP_VLAN))
return RX_DROP_UNUSABLE;
skb = skb_dequeue(&rx->sta->tx_filtered);
@@ -988,11 +998,11 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
* Tell TX path to send one frame even though the STA may
* still remain is PS mode after this frame exchange.
*/
- rx->sta->flags |= WLAN_STA_PSPOLL;
+ set_sta_flags(rx->sta, WLAN_STA_PSPOLL);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "STA %s aid %d: PS Poll (entries after %d)\n",
- print_mac(mac, rx->sta->addr), rx->sta->aid,
+ print_mac(mac, rx->sta->sta.addr), rx->sta->sta.aid,
skb_queue_len(&rx->sta->ps_tx_buf));
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
@@ -1016,8 +1026,8 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
* have nothing buffered for it?
*/
printk(KERN_DEBUG "%s: STA %s sent PS Poll even "
- "though there is no buffered frames for it\n",
- rx->dev->name, print_mac(mac, rx->sta->addr));
+ "though there are no buffered frames for it\n",
+ rx->dev->name, print_mac(mac, rx->sta->sta.addr));
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
}
@@ -1028,22 +1038,21 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
return RX_QUEUED;
}
-static ieee80211_rx_result
+static ieee80211_rx_result debug_noinline
ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx)
{
- u16 fc = rx->fc;
u8 *data = rx->skb->data;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) data;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)data;
- if (!WLAN_FC_IS_QOS_DATA(fc))
+ if (!ieee80211_is_data_qos(hdr->frame_control))
return RX_CONTINUE;
/* remove the qos control field, update frame type and meta-data */
- memmove(data + 2, data, ieee80211_get_hdrlen(fc) - 2);
- hdr = (struct ieee80211_hdr *) skb_pull(rx->skb, 2);
+ 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 */
- rx->fc = fc &= ~IEEE80211_STYPE_QOS_DATA;
- hdr->frame_control = cpu_to_le16(fc);
+ hdr->frame_control &= ~cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
return RX_CONTINUE;
}
@@ -1051,20 +1060,15 @@ ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx)
static int
ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
{
- if (unlikely(!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED))) {
-#ifdef CONFIG_MAC80211_DEBUG
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: dropped frame "
- "(unauthorized port)\n", rx->dev->name);
-#endif /* CONFIG_MAC80211_DEBUG */
+ if (unlikely(!rx->sta ||
+ !test_sta_flags(rx->sta, WLAN_STA_AUTHORIZED)))
return -EACCES;
- }
return 0;
}
static int
-ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx)
+ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
{
/*
* Pass through unencrypted frames if the hardware has
@@ -1074,9 +1078,8 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx)
return 0;
/* Drop unencrypted frames if key is set. */
- if (unlikely(!(rx->fc & IEEE80211_FCTL_PROTECTED) &&
- (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
- (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC &&
+ if (unlikely(!ieee80211_has_protected(fc) &&
+ !ieee80211_is_nullfunc(fc) &&
(rx->key || rx->sdata->drop_unencrypted)))
return -EACCES;
@@ -1088,7 +1091,7 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
{
struct net_device *dev = rx->dev;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
- u16 fc, hdrlen, ethertype;
+ u16 hdrlen, ethertype;
u8 *payload;
u8 dst[ETH_ALEN];
u8 src[ETH_ALEN] __aligned(2);
@@ -1099,27 +1102,10 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
DECLARE_MAC_BUF(mac3);
DECLARE_MAC_BUF(mac4);
- fc = rx->fc;
-
- if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
+ if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
return -1;
- hdrlen = ieee80211_get_hdrlen(fc);
-
- if (ieee80211_vif_is_mesh(&sdata->vif)) {
- int meshhdrlen = ieee80211_get_mesh_hdrlen(
- (struct ieee80211s_hdr *) (skb->data + hdrlen));
- /* Copy on cb:
- * - mesh header: to be used for mesh forwarding
- * decision. It will also be used as mesh header template at
- * tx.c:ieee80211_subif_start_xmit() if interface
- * type is mesh and skb->pkt_type == PACKET_OTHERHOST
- * - ta: to be used if a RERR needs to be sent.
- */
- memcpy(skb->cb, skb->data + hdrlen, meshhdrlen);
- memcpy(MESH_PREQ(skb), hdr->addr2, ETH_ALEN);
- hdrlen += meshhdrlen;
- }
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
/* convert IEEE 802.11 header + possible LLC headers into Ethernet
* header
@@ -1130,79 +1116,44 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
* 1 0 BSSID SA DA n/a
* 1 1 RA TA DA SA
*/
-
- switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
- case IEEE80211_FCTL_TODS:
- /* BSSID SA DA */
- memcpy(dst, hdr->addr3, ETH_ALEN);
- memcpy(src, hdr->addr2, ETH_ALEN);
-
- if (unlikely(sdata->vif.type != IEEE80211_IF_TYPE_AP &&
- sdata->vif.type != IEEE80211_IF_TYPE_VLAN)) {
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: dropped ToDS frame "
- "(BSSID=%s SA=%s DA=%s)\n",
- dev->name,
- print_mac(mac, hdr->addr1),
- print_mac(mac2, hdr->addr2),
- print_mac(mac3, hdr->addr3));
+ memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN);
+ memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN);
+
+ switch (hdr->frame_control &
+ cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
+ case __constant_cpu_to_le16(IEEE80211_FCTL_TODS):
+ if (unlikely(sdata->vif.type != NL80211_IFTYPE_AP &&
+ sdata->vif.type != NL80211_IFTYPE_AP_VLAN))
return -1;
- }
break;
- case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
- /* RA TA DA SA */
- memcpy(dst, hdr->addr3, ETH_ALEN);
- memcpy(src, hdr->addr4, ETH_ALEN);
-
- if (unlikely(sdata->vif.type != IEEE80211_IF_TYPE_WDS &&
- sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)) {
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: dropped FromDS&ToDS "
- "frame (RA=%s TA=%s DA=%s SA=%s)\n",
- rx->dev->name,
- print_mac(mac, hdr->addr1),
- print_mac(mac2, hdr->addr2),
- print_mac(mac3, hdr->addr3),
- print_mac(mac4, hdr->addr4));
+ case __constant_cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
+ if (unlikely(sdata->vif.type != NL80211_IFTYPE_WDS &&
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT))
return -1;
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ struct ieee80211s_hdr *meshdr = (struct ieee80211s_hdr *)
+ (skb->data + hdrlen);
+ hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
+ if (meshdr->flags & MESH_FLAGS_AE_A5_A6) {
+ memcpy(dst, meshdr->eaddr1, ETH_ALEN);
+ memcpy(src, meshdr->eaddr2, ETH_ALEN);
+ }
}
break;
- case IEEE80211_FCTL_FROMDS:
- /* DA BSSID SA */
- memcpy(dst, hdr->addr1, ETH_ALEN);
- memcpy(src, hdr->addr3, ETH_ALEN);
-
- if (sdata->vif.type != IEEE80211_IF_TYPE_STA ||
+ case __constant_cpu_to_le16(IEEE80211_FCTL_FROMDS):
+ if (sdata->vif.type != NL80211_IFTYPE_STATION ||
(is_multicast_ether_addr(dst) &&
!compare_ether_addr(src, dev->dev_addr)))
return -1;
break;
- case 0:
- /* DA SA BSSID */
- memcpy(dst, hdr->addr1, ETH_ALEN);
- memcpy(src, hdr->addr2, ETH_ALEN);
-
- if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: dropped IBSS frame "
- "(DA=%s SA=%s BSSID=%s)\n",
- dev->name,
- print_mac(mac, hdr->addr1),
- print_mac(mac2, hdr->addr2),
- print_mac(mac3, hdr->addr3));
- }
+ case __constant_cpu_to_le16(0):
+ if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
return -1;
- }
break;
}
- if (unlikely(skb->len - hdrlen < 8)) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: RX too short data frame "
- "payload\n", dev->name);
- }
+ if (unlikely(skb->len - hdrlen < 8))
return -1;
- }
payload = skb->data + hdrlen;
ethertype = (payload[6] << 8) | payload[7];
@@ -1232,7 +1183,7 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
/*
* requires that rx->skb is a frame with ethernet header
*/
-static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx)
+static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc)
{
static const u8 pae_group_addr[ETH_ALEN] __aligned(2)
= { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x03 };
@@ -1248,7 +1199,7 @@ static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx)
return true;
if (ieee80211_802_1x_port_control(rx) ||
- ieee80211_drop_unencrypted(rx))
+ ieee80211_drop_unencrypted(rx, fc))
return false;
return true;
@@ -1270,8 +1221,9 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
skb = rx->skb;
xmit_skb = NULL;
- if (local->bridge_packets && (sdata->vif.type == IEEE80211_IF_TYPE_AP ||
- sdata->vif.type == IEEE80211_IF_TYPE_VLAN) &&
+ if ((sdata->vif.type == NL80211_IFTYPE_AP ||
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
+ !(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) &&
(rx->flags & IEEE80211_RX_RA_MATCH)) {
if (is_multicast_ether_addr(ehdr->h_dest)) {
/*
@@ -1297,38 +1249,6 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
}
}
- /* Mesh forwarding */
- if (ieee80211_vif_is_mesh(&sdata->vif)) {
- u8 *mesh_ttl = &((struct ieee80211s_hdr *)skb->cb)->ttl;
- (*mesh_ttl)--;
-
- if (is_multicast_ether_addr(skb->data)) {
- if (*mesh_ttl > 0) {
- xmit_skb = skb_copy(skb, GFP_ATOMIC);
- if (xmit_skb)
- xmit_skb->pkt_type = PACKET_OTHERHOST;
- else if (net_ratelimit())
- printk(KERN_DEBUG "%s: failed to clone "
- "multicast frame\n", dev->name);
- } else
- IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.sta,
- dropped_frames_ttl);
- } else if (skb->pkt_type != PACKET_OTHERHOST &&
- compare_ether_addr(dev->dev_addr, skb->data) != 0) {
- if (*mesh_ttl == 0) {
- IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.sta,
- dropped_frames_ttl);
- dev_kfree_skb(skb);
- skb = NULL;
- } else {
- xmit_skb = skb;
- xmit_skb->pkt_type = PACKET_OTHERHOST;
- if (!(dev->flags & IFF_PROMISC))
- skb = NULL;
- }
- }
- }
-
if (skb) {
/* deliver to local stack */
skb->protocol = eth_type_trans(skb, dev);
@@ -1345,25 +1265,26 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
}
}
-static ieee80211_rx_result
+static ieee80211_rx_result debug_noinline
ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
{
struct net_device *dev = rx->dev;
struct ieee80211_local *local = rx->local;
- u16 fc, ethertype;
+ u16 ethertype;
u8 *payload;
struct sk_buff *skb = rx->skb, *frame = NULL;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ __le16 fc = hdr->frame_control;
const struct ethhdr *eth;
int remaining, err;
u8 dst[ETH_ALEN];
u8 src[ETH_ALEN];
DECLARE_MAC_BUF(mac);
- fc = rx->fc;
- if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA))
+ if (unlikely(!ieee80211_is_data(fc)))
return RX_CONTINUE;
- if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
+ if (unlikely(!ieee80211_is_data_present(fc)))
return RX_DROP_MONITOR;
if (!(rx->flags & IEEE80211_RX_AMSDU))
@@ -1394,10 +1315,8 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
padding = ((4 - subframe_len) & 0x3);
/* the last MSDU has no padding */
- if (subframe_len > remaining) {
- printk(KERN_DEBUG "%s: wrong buffer size\n", dev->name);
+ if (subframe_len > remaining)
return RX_DROP_UNUSABLE;
- }
skb_pull(skb, sizeof(struct ethhdr));
/* if last subframe reuse skb */
@@ -1418,8 +1337,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
eth = (struct ethhdr *) skb_pull(skb, ntohs(len) +
padding);
if (!eth) {
- printk(KERN_DEBUG "%s: wrong buffer size\n",
- dev->name);
dev_kfree_skb(frame);
return RX_DROP_UNUSABLE;
}
@@ -1449,7 +1366,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
}
- if (!ieee80211_frame_allowed(rx)) {
+ if (!ieee80211_frame_allowed(rx, fc)) {
if (skb == frame) /* last frame */
return RX_DROP_UNUSABLE;
dev_kfree_skb(frame);
@@ -1462,25 +1379,102 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
return RX_QUEUED;
}
+#ifdef CONFIG_MAC80211_MESH
static ieee80211_rx_result
+ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
+{
+ struct ieee80211_hdr *hdr;
+ struct ieee80211s_hdr *mesh_hdr;
+ unsigned int hdrlen;
+ struct sk_buff *skb = rx->skb, *fwd_skb;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
+
+ if (!ieee80211_is_data(hdr->frame_control))
+ return RX_CONTINUE;
+
+ if (!mesh_hdr->ttl)
+ /* illegal frame */
+ return RX_DROP_MONITOR;
+
+ if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6){
+ struct ieee80211_sub_if_data *sdata;
+ struct mesh_path *mppath;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
+ rcu_read_lock();
+ mppath = mpp_path_lookup(mesh_hdr->eaddr2, sdata);
+ if (!mppath) {
+ mpp_path_add(mesh_hdr->eaddr2, hdr->addr4, sdata);
+ } else {
+ spin_lock_bh(&mppath->state_lock);
+ mppath->exp_time = jiffies;
+ if (compare_ether_addr(mppath->mpp, hdr->addr4) != 0)
+ memcpy(mppath->mpp, hdr->addr4, ETH_ALEN);
+ spin_unlock_bh(&mppath->state_lock);
+ }
+ rcu_read_unlock();
+ }
+
+ if (compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0)
+ return RX_CONTINUE;
+
+ mesh_hdr->ttl--;
+
+ if (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;
+ fwd_skb = skb_copy(skb, GFP_ATOMIC);
+
+ if (!fwd_skb && net_ratelimit())
+ printk(KERN_DEBUG "%s: failed to clone mesh frame\n",
+ rx->dev->name);
+
+ fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data;
+ /*
+ * 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);
+ memcpy(fwd_hdr->addr2, rx->dev->dev_addr, ETH_ALEN);
+ fwd_skb->dev = rx->local->mdev;
+ fwd_skb->iif = rx->dev->ifindex;
+ dev_queue_xmit(fwd_skb);
+ }
+ }
+
+ if (is_multicast_ether_addr(hdr->addr3) ||
+ rx->dev->flags & IFF_PROMISC)
+ return RX_CONTINUE;
+ else
+ return RX_DROP_MONITOR;
+}
+#endif
+
+static ieee80211_rx_result debug_noinline
ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
{
struct net_device *dev = rx->dev;
- u16 fc;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+ __le16 fc = hdr->frame_control;
int err;
- fc = rx->fc;
- if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA))
+ if (unlikely(!ieee80211_is_data(hdr->frame_control)))
return RX_CONTINUE;
- if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
+ if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
return RX_DROP_MONITOR;
err = ieee80211_data_to_8023(rx);
if (unlikely(err))
return RX_DROP_UNUSABLE;
- if (!ieee80211_frame_allowed(rx))
+ if (!ieee80211_frame_allowed(rx, fc))
return RX_DROP_MONITOR;
rx->skb->dev = dev;
@@ -1493,21 +1487,21 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
return RX_QUEUED;
}
-static ieee80211_rx_result
+static ieee80211_rx_result debug_noinline
ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
{
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 ieee80211_bar *bar = (struct ieee80211_bar *)skb->data;
struct tid_ampdu_rx *tid_agg_rx;
u16 start_seq_num;
u16 tid;
- if (likely((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL))
+ if (likely(!ieee80211_is_ctl(bar->frame_control)))
return RX_CONTINUE;
- if ((rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BACK_REQ) {
+ if (ieee80211_is_back_req(bar->frame_control)) {
if (!rx->sta)
return RX_CONTINUE;
tid = le16_to_cpu(bar->control) >> 12;
@@ -1537,23 +1531,98 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
}
-static ieee80211_rx_result
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
+{
+ struct ieee80211_local *local = rx->local;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
+ int len = rx->skb->len;
+
+ if (!ieee80211_is_action(mgmt->frame_control))
+ return RX_CONTINUE;
+
+ if (!rx->sta)
+ return RX_DROP_MONITOR;
+
+ if (!(rx->flags & IEEE80211_RX_RA_MATCH))
+ return RX_DROP_MONITOR;
+
+ /* all categories we currently handle have action_code */
+ if (len < IEEE80211_MIN_ACTION_SIZE + 1)
+ return RX_DROP_MONITOR;
+
+ /*
+ * FIXME: revisit this, I'm sure we should handle most
+ * of these frames in other modes as well!
+ */
+ if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+ sdata->vif.type != NL80211_IFTYPE_ADHOC)
+ return RX_CONTINUE;
+
+ switch (mgmt->u.action.category) {
+ case WLAN_CATEGORY_BACK:
+ switch (mgmt->u.action.u.addba_req.action_code) {
+ case WLAN_ACTION_ADDBA_REQ:
+ if (len < (IEEE80211_MIN_ACTION_SIZE +
+ sizeof(mgmt->u.action.u.addba_req)))
+ return RX_DROP_MONITOR;
+ ieee80211_process_addba_request(local, rx->sta, mgmt, len);
+ break;
+ case WLAN_ACTION_ADDBA_RESP:
+ if (len < (IEEE80211_MIN_ACTION_SIZE +
+ sizeof(mgmt->u.action.u.addba_resp)))
+ return RX_DROP_MONITOR;
+ ieee80211_process_addba_resp(local, rx->sta, mgmt, len);
+ break;
+ case WLAN_ACTION_DELBA:
+ if (len < (IEEE80211_MIN_ACTION_SIZE +
+ sizeof(mgmt->u.action.u.delba)))
+ return RX_DROP_MONITOR;
+ ieee80211_process_delba(sdata, rx->sta, mgmt, len);
+ break;
+ }
+ break;
+ case WLAN_CATEGORY_SPECTRUM_MGMT:
+ if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ)
+ return RX_DROP_MONITOR;
+ switch (mgmt->u.action.u.measurement.action_code) {
+ case WLAN_ACTION_SPCT_MSR_REQ:
+ if (len < (IEEE80211_MIN_ACTION_SIZE +
+ sizeof(mgmt->u.action.u.measurement)))
+ return RX_DROP_MONITOR;
+ ieee80211_process_measurement_req(sdata, mgmt, len);
+ break;
+ }
+ break;
+ default:
+ return RX_CONTINUE;
+ }
+
+ rx->sta->rx_packets++;
+ dev_kfree_skb(rx->skb);
+ return RX_QUEUED;
+}
+
+static ieee80211_rx_result debug_noinline
ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
{
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
if (!(rx->flags & IEEE80211_RX_RA_MATCH))
return RX_DROP_MONITOR;
- sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
- if ((sdata->vif.type == IEEE80211_IF_TYPE_STA ||
- sdata->vif.type == IEEE80211_IF_TYPE_IBSS ||
- sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) &&
- !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
- ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->status);
- else
+ if (ieee80211_vif_is_mesh(&sdata->vif))
+ return ieee80211_mesh_rx_mgmt(sdata, rx->skb, rx->status);
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+ sdata->vif.type != NL80211_IFTYPE_ADHOC)
+ return RX_DROP_MONITOR;
+
+ if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
return RX_DROP_MONITOR;
+ ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status);
return RX_QUEUED;
}
@@ -1561,69 +1630,43 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev,
struct ieee80211_hdr *hdr,
struct ieee80211_rx_data *rx)
{
- int keyidx, hdrlen;
+ int keyidx;
+ unsigned int hdrlen;
DECLARE_MAC_BUF(mac);
DECLARE_MAC_BUF(mac2);
- hdrlen = ieee80211_get_hdrlen_from_skb(rx->skb);
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (rx->skb->len >= hdrlen + 4)
keyidx = rx->skb->data[hdrlen + 3] >> 6;
else
keyidx = -1;
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: TKIP hwaccel reported Michael MIC "
- "failure from %s to %s keyidx=%d\n",
- dev->name, print_mac(mac, hdr->addr2),
- print_mac(mac2, hdr->addr1), keyidx);
-
if (!rx->sta) {
/*
* Some hardware seem to generate incorrect Michael MIC
* reports; ignore them to avoid triggering countermeasures.
*/
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
- "error for unknown address %s\n",
- dev->name, print_mac(mac, hdr->addr2));
goto ignore;
}
- if (!(rx->fc & IEEE80211_FCTL_PROTECTED)) {
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
- "error for a frame with no PROTECTED flag (src "
- "%s)\n", dev->name, print_mac(mac, hdr->addr2));
+ if (!ieee80211_has_protected(hdr->frame_control))
goto ignore;
- }
- if (rx->sdata->vif.type == IEEE80211_IF_TYPE_AP && keyidx) {
+ 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.
*/
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: ignored Michael MIC error for "
- "a frame with non-zero keyidx (%d)"
- " (src %s)\n", dev->name, keyidx,
- print_mac(mac, hdr->addr2));
goto ignore;
}
- if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
- ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
- (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)) {
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: ignored spurious Michael MIC "
- "error for a frame that cannot be encrypted "
- "(fc=0x%04x) (src %s)\n",
- dev->name, rx->fc, print_mac(mac, hdr->addr2));
+ if (!ieee80211_is_data(hdr->frame_control) &&
+ !ieee80211_is_auth(hdr->frame_control))
goto ignore;
- }
- mac80211_ev_michael_mic_failure(rx->dev, keyidx, hdr);
+ mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr);
ignore:
dev_kfree_skb(rx->skb);
rx->skb = NULL;
@@ -1679,7 +1722,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx)
if (!netif_running(sdata->dev))
continue;
- if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR ||
+ if (sdata->vif.type != NL80211_IFTYPE_MONITOR ||
!(sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES))
continue;
@@ -1710,67 +1753,64 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx)
dev_kfree_skb(skb);
}
-typedef ieee80211_rx_result (*ieee80211_rx_handler)(struct ieee80211_rx_data *);
-static ieee80211_rx_handler ieee80211_rx_handlers[] =
-{
- ieee80211_rx_h_if_stats,
- ieee80211_rx_h_passive_scan,
- ieee80211_rx_h_check,
- ieee80211_rx_h_decrypt,
- ieee80211_rx_h_sta_process,
- ieee80211_rx_h_defragment,
- ieee80211_rx_h_ps_poll,
- ieee80211_rx_h_michael_mic_verify,
- /* this must be after decryption - so header is counted in MPDU mic
- * must be before pae and data, so QOS_DATA format frames
- * are not passed to user space by these functions
- */
- ieee80211_rx_h_remove_qos_control,
- ieee80211_rx_h_amsdu,
- ieee80211_rx_h_data,
- ieee80211_rx_h_ctrl,
- ieee80211_rx_h_mgmt,
- NULL
-};
static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata,
struct ieee80211_rx_data *rx,
struct sk_buff *skb)
{
- ieee80211_rx_handler *handler;
ieee80211_rx_result res = RX_DROP_MONITOR;
rx->skb = skb;
rx->sdata = sdata;
rx->dev = sdata->dev;
- for (handler = ieee80211_rx_handlers; *handler != NULL; handler++) {
- res = (*handler)(rx);
+#define CALL_RXH(rxh) \
+ do { \
+ res = rxh(rx); \
+ if (res != RX_CONTINUE) \
+ goto rxh_done; \
+ } while (0);
+
+ CALL_RXH(ieee80211_rx_h_passive_scan)
+ CALL_RXH(ieee80211_rx_h_check)
+ CALL_RXH(ieee80211_rx_h_decrypt)
+ CALL_RXH(ieee80211_rx_h_sta_process)
+ 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(&sdata->vif))
+ CALL_RXH(ieee80211_rx_h_mesh_fwding);
+#endif
+ CALL_RXH(ieee80211_rx_h_data)
+ CALL_RXH(ieee80211_rx_h_ctrl)
+ CALL_RXH(ieee80211_rx_h_action)
+ CALL_RXH(ieee80211_rx_h_mgmt)
- switch (res) {
- case RX_CONTINUE:
- continue;
- case RX_DROP_UNUSABLE:
- case RX_DROP_MONITOR:
- I802_DEBUG_INC(sdata->local->rx_handlers_drop);
- if (rx->sta)
- rx->sta->rx_dropped++;
- break;
- case RX_QUEUED:
- I802_DEBUG_INC(sdata->local->rx_handlers_queued);
- break;
- }
- break;
- }
+#undef CALL_RXH
+ rxh_done:
switch (res) {
- case RX_CONTINUE:
case RX_DROP_MONITOR:
+ I802_DEBUG_INC(sdata->local->rx_handlers_drop);
+ if (rx->sta)
+ rx->sta->rx_dropped++;
+ /* fall through */
+ case RX_CONTINUE:
ieee80211_rx_cooked_monitor(rx);
break;
case RX_DROP_UNUSABLE:
+ I802_DEBUG_INC(sdata->local->rx_handlers_drop);
+ if (rx->sta)
+ rx->sta->rx_dropped++;
dev_kfree_skb(rx->skb);
break;
+ case RX_QUEUED:
+ I802_DEBUG_INC(sdata->local->rx_handlers_queued);
+ break;
}
}
@@ -1783,7 +1823,7 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
int multicast = is_multicast_ether_addr(hdr->addr1);
switch (sdata->vif.type) {
- case IEEE80211_IF_TYPE_STA:
+ case NL80211_IFTYPE_STATION:
if (!bssid)
return 0;
if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
@@ -1798,12 +1838,12 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
rx->flags &= ~IEEE80211_RX_RA_MATCH;
}
break;
- case IEEE80211_IF_TYPE_IBSS:
+ case NL80211_IFTYPE_ADHOC:
if (!bssid)
return 0;
- if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT &&
- (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON)
+ if (ieee80211_is_beacon(hdr->frame_control)) {
return 1;
+ }
else if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
if (!(rx->flags & IEEE80211_RX_IN_SCAN))
return 0;
@@ -1815,10 +1855,11 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
return 0;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
} else if (!rx->sta)
- rx->sta = ieee80211_ibss_add_sta(sdata->dev, rx->skb,
- bssid, hdr->addr2);
+ rx->sta = ieee80211_ibss_add_sta(sdata, rx->skb,
+ bssid, hdr->addr2,
+ BIT(rx->status->rate_idx));
break;
- case IEEE80211_IF_TYPE_MESH_POINT:
+ case NL80211_IFTYPE_MESH_POINT:
if (!multicast &&
compare_ether_addr(sdata->dev->dev_addr,
hdr->addr1) != 0) {
@@ -1828,8 +1869,8 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
rx->flags &= ~IEEE80211_RX_RA_MATCH;
}
break;
- case IEEE80211_IF_TYPE_VLAN:
- case IEEE80211_IF_TYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_AP:
if (!bssid) {
if (compare_ether_addr(sdata->dev->dev_addr,
hdr->addr1))
@@ -1840,23 +1881,18 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
return 0;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
}
- if (sdata->dev == sdata->local->mdev &&
- !(rx->flags & IEEE80211_RX_IN_SCAN))
- /* do not receive anything via
- * master device when not scanning */
- return 0;
break;
- case IEEE80211_IF_TYPE_WDS:
- if (bssid ||
- (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
+ 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;
break;
- case IEEE80211_IF_TYPE_MNTR:
+ case NL80211_IFTYPE_MONITOR:
/* take everything */
break;
- case IEEE80211_IF_TYPE_INVALID:
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case __NL80211_IFTYPE_AFTER_LAST:
/* should never get here */
WARN_ON(1);
break;
@@ -1872,31 +1908,26 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
struct sk_buff *skb,
struct ieee80211_rx_status *status,
- u32 load,
struct ieee80211_rate *rate)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
struct ieee80211_hdr *hdr;
struct ieee80211_rx_data rx;
- u16 type;
int prepares;
struct ieee80211_sub_if_data *prev = NULL;
struct sk_buff *skb_new;
u8 *bssid;
- hdr = (struct ieee80211_hdr *) skb->data;
+ hdr = (struct ieee80211_hdr *)skb->data;
memset(&rx, 0, sizeof(rx));
rx.skb = skb;
rx.local = local;
rx.status = status;
- rx.load = load;
rx.rate = rate;
- rx.fc = le16_to_cpu(hdr->frame_control);
- type = rx.fc & IEEE80211_FCTL_FTYPE;
- if (type == IEEE80211_FTYPE_DATA || type == IEEE80211_FTYPE_MGMT)
+ if (ieee80211_is_data(hdr->frame_control) || ieee80211_is_mgmt(hdr->frame_control))
local->dot11ReceivedFragmentCount++;
rx.sta = sta_info_get(local, hdr->addr2);
@@ -1910,7 +1941,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
return;
}
- if (unlikely(local->sta_sw_scanning || local->sta_hw_scanning))
+ if (unlikely(local->sw_scanning || local->hw_scanning))
rx.flags |= IEEE80211_RX_IN_SCAN;
ieee80211_parse_qos(&rx);
@@ -1922,7 +1953,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
if (!netif_running(sdata->dev))
continue;
- if (sdata->vif.type == IEEE80211_IF_TYPE_MNTR)
+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
continue;
bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type);
@@ -1957,14 +1988,12 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
prev->dev->name);
continue;
}
- rx.fc = le16_to_cpu(hdr->frame_control);
ieee80211_invoke_rx_handlers(prev, &rx, skb_new);
prev = sdata;
}
- if (prev) {
- rx.fc = le16_to_cpu(hdr->frame_control);
+ if (prev)
ieee80211_invoke_rx_handlers(prev, &rx, skb);
- } else
+ else
dev_kfree_skb(skb);
}
@@ -2000,7 +2029,6 @@ u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
struct ieee80211_rx_status status;
u16 head_seq_num, buf_size;
int index;
- u32 pkt_load;
struct ieee80211_supported_band *sband;
struct ieee80211_rate *rate;
@@ -2035,12 +2063,9 @@ u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
sizeof(status));
sband = local->hw.wiphy->bands[status.band];
rate = &sband->bitrates[status.rate_idx];
- pkt_load = ieee80211_rx_load_stats(local,
- tid_agg_rx->reorder_buf[index],
- &status, rate);
__ieee80211_rx_handle_packet(hw,
tid_agg_rx->reorder_buf[index],
- &status, pkt_load, rate);
+ &status, rate);
tid_agg_rx->stored_mpdu_num--;
tid_agg_rx->reorder_buf[index] = NULL;
}
@@ -2082,11 +2107,8 @@ u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
sizeof(status));
sband = local->hw.wiphy->bands[status.band];
rate = &sband->bitrates[status.rate_idx];
- pkt_load = ieee80211_rx_load_stats(local,
- tid_agg_rx->reorder_buf[index],
- &status, rate);
__ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index],
- &status, pkt_load, rate);
+ &status, rate);
tid_agg_rx->stored_mpdu_num--;
tid_agg_rx->reorder_buf[index] = NULL;
tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
@@ -2103,32 +2125,29 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct sta_info *sta;
struct tid_ampdu_rx *tid_agg_rx;
- u16 fc, sc;
+ u16 sc;
u16 mpdu_seq_num;
- u8 ret = 0, *qc;
+ u8 ret = 0;
int tid;
sta = sta_info_get(local, hdr->addr2);
if (!sta)
return ret;
- fc = le16_to_cpu(hdr->frame_control);
-
/* filter the QoS data rx stream according to
* STA/TID and check if this STA/TID is on aggregation */
- if (!WLAN_FC_IS_QOS_DATA(fc))
+ if (!ieee80211_is_data_qos(hdr->frame_control))
goto end_reorder;
- qc = skb->data + ieee80211_get_hdrlen(fc) - QOS_CONTROL_LEN;
- tid = qc[0] & QOS_CONTROL_TID_MASK;
+ tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL)
goto end_reorder;
tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
- /* null data frames are excluded */
- if (unlikely(fc & IEEE80211_STYPE_NULLFUNC))
+ /* qos null data frames are excluded */
+ if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
goto end_reorder;
/* new un-ordered ampdu frame - process it */
@@ -2143,7 +2162,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
/* if this mpdu is fragmented - terminate rx aggregation session */
sc = le16_to_cpu(hdr->seq_ctrl);
if (sc & IEEE80211_SCTL_FRAG) {
- ieee80211_sta_stop_rx_ba_session(sta->sdata->dev, sta->addr,
+ ieee80211_sta_stop_rx_ba_session(sta->sdata, sta->sta.addr,
tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP);
ret = 1;
goto end_reorder;
@@ -2165,7 +2184,6 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ieee80211_rx_status *status)
{
struct ieee80211_local *local = hw_to_local(hw);
- u32 pkt_load;
struct ieee80211_rate *rate = NULL;
struct ieee80211_supported_band *sband;
@@ -2205,11 +2223,8 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
return;
}
- pkt_load = ieee80211_rx_load_stats(local, skb, status, rate);
- local->channel_use_raw += pkt_load;
-
if (!ieee80211_rx_reorder_ampdu(local, skb))
- __ieee80211_rx_handle_packet(hw, skb, status, pkt_load, rate);
+ __ieee80211_rx_handle_packet(hw, skb, status, rate);
rcu_read_unlock();
}
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
new file mode 100644
index 00000000000..416bb41099f
--- /dev/null
+++ b/net/mac80211/scan.c
@@ -0,0 +1,938 @@
+/*
+ * Scanning implementation
+ *
+ * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.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>
+ *
+ * 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.
+ */
+
+/* TODO:
+ * order BSS list by RSSI(?) ("quality of AP")
+ * scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE,
+ * SSID)
+ */
+
+#include <linux/wireless.h>
+#include <linux/if_arp.h>
+#include <net/mac80211.h>
+#include <net/iw_handler.h>
+
+#include "ieee80211_i.h"
+#include "mesh.h"
+
+#define IEEE80211_PROBE_DELAY (HZ / 33)
+#define IEEE80211_CHANNEL_TIME (HZ / 33)
+#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5)
+
+void ieee80211_rx_bss_list_init(struct ieee80211_local *local)
+{
+ spin_lock_init(&local->bss_lock);
+ INIT_LIST_HEAD(&local->bss_list);
+}
+
+void ieee80211_rx_bss_list_deinit(struct ieee80211_local *local)
+{
+ struct ieee80211_bss *bss, *tmp;
+
+ list_for_each_entry_safe(bss, tmp, &local->bss_list, list)
+ ieee80211_rx_bss_put(local, bss);
+}
+
+struct ieee80211_bss *
+ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
+ u8 *ssid, u8 ssid_len)
+{
+ struct ieee80211_bss *bss;
+
+ spin_lock_bh(&local->bss_lock);
+ bss = local->bss_hash[STA_HASH(bssid)];
+ while (bss) {
+ if (!bss_mesh_cfg(bss) &&
+ !memcmp(bss->bssid, bssid, ETH_ALEN) &&
+ bss->freq == freq &&
+ bss->ssid_len == ssid_len &&
+ (ssid_len == 0 || !memcmp(bss->ssid, ssid, ssid_len))) {
+ atomic_inc(&bss->users);
+ break;
+ }
+ bss = bss->hnext;
+ }
+ spin_unlock_bh(&local->bss_lock);
+ return bss;
+}
+
+/* Caller must hold local->bss_lock */
+static void __ieee80211_rx_bss_hash_add(struct ieee80211_local *local,
+ struct ieee80211_bss *bss)
+{
+ u8 hash_idx;
+
+ if (bss_mesh_cfg(bss))
+ hash_idx = mesh_id_hash(bss_mesh_id(bss),
+ bss_mesh_id_len(bss));
+ else
+ hash_idx = STA_HASH(bss->bssid);
+
+ bss->hnext = local->bss_hash[hash_idx];
+ local->bss_hash[hash_idx] = bss;
+}
+
+/* Caller must hold local->bss_lock */
+static void __ieee80211_rx_bss_hash_del(struct ieee80211_local *local,
+ struct ieee80211_bss *bss)
+{
+ struct ieee80211_bss *b, *prev = NULL;
+ b = local->bss_hash[STA_HASH(bss->bssid)];
+ while (b) {
+ if (b == bss) {
+ if (!prev)
+ local->bss_hash[STA_HASH(bss->bssid)] =
+ bss->hnext;
+ else
+ prev->hnext = bss->hnext;
+ break;
+ }
+ prev = b;
+ b = b->hnext;
+ }
+}
+
+struct ieee80211_bss *
+ieee80211_rx_bss_add(struct ieee80211_local *local, u8 *bssid, int freq,
+ u8 *ssid, u8 ssid_len)
+{
+ struct ieee80211_bss *bss;
+
+ bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
+ if (!bss)
+ return NULL;
+ atomic_set(&bss->users, 2);
+ memcpy(bss->bssid, bssid, ETH_ALEN);
+ bss->freq = freq;
+ if (ssid && ssid_len <= IEEE80211_MAX_SSID_LEN) {
+ memcpy(bss->ssid, ssid, ssid_len);
+ bss->ssid_len = ssid_len;
+ }
+
+ spin_lock_bh(&local->bss_lock);
+ /* TODO: order by RSSI? */
+ list_add_tail(&bss->list, &local->bss_list);
+ __ieee80211_rx_bss_hash_add(local, bss);
+ spin_unlock_bh(&local->bss_lock);
+ return bss;
+}
+
+#ifdef CONFIG_MAC80211_MESH
+static struct ieee80211_bss *
+ieee80211_rx_mesh_bss_get(struct ieee80211_local *local, u8 *mesh_id, int mesh_id_len,
+ u8 *mesh_cfg, int freq)
+{
+ struct ieee80211_bss *bss;
+
+ spin_lock_bh(&local->bss_lock);
+ bss = local->bss_hash[mesh_id_hash(mesh_id, mesh_id_len)];
+ while (bss) {
+ if (bss_mesh_cfg(bss) &&
+ !memcmp(bss_mesh_cfg(bss), mesh_cfg, MESH_CFG_CMP_LEN) &&
+ bss->freq == freq &&
+ mesh_id_len == bss->mesh_id_len &&
+ (mesh_id_len == 0 || !memcmp(bss->mesh_id, mesh_id,
+ mesh_id_len))) {
+ atomic_inc(&bss->users);
+ break;
+ }
+ bss = bss->hnext;
+ }
+ spin_unlock_bh(&local->bss_lock);
+ return bss;
+}
+
+static struct ieee80211_bss *
+ieee80211_rx_mesh_bss_add(struct ieee80211_local *local, u8 *mesh_id, int mesh_id_len,
+ u8 *mesh_cfg, int mesh_config_len, int freq)
+{
+ struct ieee80211_bss *bss;
+
+ if (mesh_config_len != MESH_CFG_LEN)
+ return NULL;
+
+ bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
+ if (!bss)
+ return NULL;
+
+ bss->mesh_cfg = kmalloc(MESH_CFG_CMP_LEN, GFP_ATOMIC);
+ if (!bss->mesh_cfg) {
+ kfree(bss);
+ return NULL;
+ }
+
+ if (mesh_id_len && mesh_id_len <= IEEE80211_MAX_MESH_ID_LEN) {
+ bss->mesh_id = kmalloc(mesh_id_len, GFP_ATOMIC);
+ if (!bss->mesh_id) {
+ kfree(bss->mesh_cfg);
+ kfree(bss);
+ return NULL;
+ }
+ memcpy(bss->mesh_id, mesh_id, mesh_id_len);
+ }
+
+ atomic_set(&bss->users, 2);
+ memcpy(bss->mesh_cfg, mesh_cfg, MESH_CFG_CMP_LEN);
+ bss->mesh_id_len = mesh_id_len;
+ bss->freq = freq;
+ spin_lock_bh(&local->bss_lock);
+ /* TODO: order by RSSI? */
+ list_add_tail(&bss->list, &local->bss_list);
+ __ieee80211_rx_bss_hash_add(local, bss);
+ spin_unlock_bh(&local->bss_lock);
+ return bss;
+}
+#endif
+
+static void ieee80211_rx_bss_free(struct ieee80211_bss *bss)
+{
+ kfree(bss->ies);
+ kfree(bss_mesh_id(bss));
+ kfree(bss_mesh_cfg(bss));
+ kfree(bss);
+}
+
+void ieee80211_rx_bss_put(struct ieee80211_local *local,
+ struct ieee80211_bss *bss)
+{
+ local_bh_disable();
+ if (!atomic_dec_and_lock(&bss->users, &local->bss_lock)) {
+ local_bh_enable();
+ return;
+ }
+
+ __ieee80211_rx_bss_hash_del(local, bss);
+ list_del(&bss->list);
+ spin_unlock_bh(&local->bss_lock);
+ ieee80211_rx_bss_free(bss);
+}
+
+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 ieee802_11_elems *elems,
+ int freq, bool beacon)
+{
+ struct ieee80211_bss *bss;
+ int clen;
+
+#ifdef CONFIG_MAC80211_MESH
+ if (elems->mesh_config)
+ bss = ieee80211_rx_mesh_bss_get(local, elems->mesh_id,
+ elems->mesh_id_len, elems->mesh_config, freq);
+ else
+#endif
+ bss = ieee80211_rx_bss_get(local, mgmt->bssid, freq,
+ elems->ssid, elems->ssid_len);
+ if (!bss) {
+#ifdef CONFIG_MAC80211_MESH
+ if (elems->mesh_config)
+ bss = ieee80211_rx_mesh_bss_add(local, elems->mesh_id,
+ elems->mesh_id_len, elems->mesh_config,
+ elems->mesh_config_len, freq);
+ else
+#endif
+ bss = ieee80211_rx_bss_add(local, mgmt->bssid, freq,
+ elems->ssid, elems->ssid_len);
+ if (!bss)
+ return NULL;
+ } else {
+#if 0
+ /* TODO: order by RSSI? */
+ spin_lock_bh(&local->bss_lock);
+ list_move_tail(&bss->list, &local->bss_list);
+ spin_unlock_bh(&local->bss_lock);
+#endif
+ }
+
+ /* 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;
+ }
+
+ bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int);
+ bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info);
+
+ if (elems->tim) {
+ struct ieee80211_tim_ie *tim_ie =
+ (struct ieee80211_tim_ie *)elems->tim;
+ bss->dtim_period = tim_ie->dtim_period;
+ }
+
+ /* set default value for buggy APs */
+ if (!elems->tim || bss->dtim_period == 0)
+ bss->dtim_period = 1;
+
+ bss->supp_rates_len = 0;
+ if (elems->supp_rates) {
+ clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
+ if (clen > elems->supp_rates_len)
+ clen = elems->supp_rates_len;
+ memcpy(&bss->supp_rates[bss->supp_rates_len], elems->supp_rates,
+ clen);
+ bss->supp_rates_len += clen;
+ }
+ if (elems->ext_supp_rates) {
+ clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
+ if (clen > elems->ext_supp_rates_len)
+ clen = elems->ext_supp_rates_len;
+ memcpy(&bss->supp_rates[bss->supp_rates_len],
+ elems->ext_supp_rates, clen);
+ bss->supp_rates_len += clen;
+ }
+
+ bss->band = rx_status->band;
+
+ bss->timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
+ bss->last_update = jiffies;
+ bss->signal = rx_status->signal;
+ bss->noise = rx_status->noise;
+ bss->qual = rx_status->qual;
+ bss->wmm_used = elems->wmm_param || elems->wmm_info;
+
+ if (!beacon)
+ bss->last_probe_resp = jiffies;
+
+ /*
+ * For probe responses, or if we don't have any information yet,
+ * use the IEs from the beacon.
+ */
+ if (!bss->ies || !beacon) {
+ if (bss->ies == NULL || bss->ies_len < elems->total_len) {
+ kfree(bss->ies);
+ bss->ies = kmalloc(elems->total_len, GFP_ATOMIC);
+ }
+ if (bss->ies) {
+ memcpy(bss->ies, elems->ie_start, elems->total_len);
+ bss->ies_len = elems->total_len;
+ } else
+ bss->ies_len = 0;
+ }
+
+ return bss;
+}
+
+ieee80211_rx_result
+ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct ieee80211_mgmt *mgmt;
+ 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 (ieee80211_is_ctl(fc))
+ return RX_CONTINUE;
+
+ if (skb->len < 24)
+ return RX_DROP_MONITOR;
+
+ presp = ieee80211_is_probe_resp(fc);
+ if (presp) {
+ /* ignore ProbeResp to foreign address */
+ if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN))
+ return RX_DROP_MONITOR;
+
+ 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);
+
+ 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(sdata->local->hw.wiphy, freq);
+
+ if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
+ return RX_DROP_MONITOR;
+
+ bss = ieee80211_bss_info_update(sdata->local, rx_status,
+ mgmt, skb->len, &elems,
+ freq, beacon);
+ if (bss)
+ ieee80211_rx_bss_put(sdata->local, bss);
+
+ dev_kfree_skb(skb);
+ return RX_QUEUED;
+}
+
+static void ieee80211_send_nullfunc(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ int powersave)
+{
+ struct sk_buff *skb;
+ struct ieee80211_hdr *nullfunc;
+ __le16 fc;
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
+ "frame\n", sdata->dev->name);
+ return;
+ }
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
+ memset(nullfunc, 0, 24);
+ fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
+ IEEE80211_FCTL_TODS);
+ if (powersave)
+ fc |= cpu_to_le16(IEEE80211_FCTL_PM);
+ nullfunc->frame_control = fc;
+ memcpy(nullfunc->addr1, sdata->u.sta.bssid, ETH_ALEN);
+ memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(nullfunc->addr3, sdata->u.sta.bssid, ETH_ALEN);
+
+ ieee80211_tx_skb(sdata, skb, 0);
+}
+
+void ieee80211_scan_completed(struct ieee80211_hw *hw)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata;
+ union iwreq_data wrqu;
+
+ if (WARN_ON(!local->hw_scanning && !local->sw_scanning))
+ return;
+
+ local->last_scan_completed = jiffies;
+ memset(&wrqu, 0, sizeof(wrqu));
+
+ /*
+ * local->scan_sdata could have been NULLed by the interface
+ * down code in case we were scanning on an interface that is
+ * being taken down.
+ */
+ sdata = local->scan_sdata;
+ if (sdata)
+ wireless_send_event(sdata->dev, SIOCGIWSCAN, &wrqu, NULL);
+
+ if (local->hw_scanning) {
+ local->hw_scanning = false;
+ if (ieee80211_hw_config(local))
+ printk(KERN_DEBUG "%s: failed to restore operational "
+ "channel after scan\n", wiphy_name(local->hw.wiphy));
+
+ goto done;
+ }
+
+ local->sw_scanning = false;
+ if (ieee80211_hw_config(local))
+ printk(KERN_DEBUG "%s: failed to restore operational "
+ "channel after scan\n", wiphy_name(local->hw.wiphy));
+
+
+ netif_tx_lock_bh(local->mdev);
+ netif_addr_lock(local->mdev);
+ local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC;
+ local->ops->configure_filter(local_to_hw(local),
+ FIF_BCN_PRBRESP_PROMISC,
+ &local->filter_flags,
+ local->mdev->mc_count,
+ local->mdev->mc_list);
+
+ netif_addr_unlock(local->mdev);
+ netif_tx_unlock_bh(local->mdev);
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ /* Tell AP we're back */
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
+ ieee80211_send_nullfunc(local, sdata, 0);
+ netif_tx_wake_all_queues(sdata->dev);
+ }
+ } else
+ netif_tx_wake_all_queues(sdata->dev);
+ }
+ rcu_read_unlock();
+
+ done:
+ ieee80211_mlme_notify_scan_completed(local);
+ ieee80211_mesh_notify_scan_completed(local);
+}
+EXPORT_SYMBOL(ieee80211_scan_completed);
+
+
+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_supported_band *sband;
+ struct ieee80211_channel *chan;
+ int skip;
+ unsigned long next_delay = 0;
+
+ /*
+ * Avoid re-scheduling when the sdata is going away.
+ */
+ if (!netif_running(sdata->dev))
+ return;
+
+ switch (local->scan_state) {
+ case SCAN_SET_CHANNEL:
+ /*
+ * Get current scan band. scan_band may be IEEE80211_NUM_BANDS
+ * after we successfully scanned the last channel of the last
+ * band (and the last band is supported by the hw)
+ */
+ if (local->scan_band < IEEE80211_NUM_BANDS)
+ sband = local->hw.wiphy->bands[local->scan_band];
+ else
+ sband = NULL;
+
+ /*
+ * If we are at an unsupported band and have more bands
+ * left to scan, advance to the next supported one.
+ */
+ while (!sband && local->scan_band < IEEE80211_NUM_BANDS - 1) {
+ local->scan_band++;
+ sband = local->hw.wiphy->bands[local->scan_band];
+ local->scan_channel_idx = 0;
+ }
+
+ /* if no more bands/channels left, complete scan */
+ if (!sband || local->scan_channel_idx >= sband->n_channels) {
+ ieee80211_scan_completed(local_to_hw(local));
+ return;
+ }
+ skip = 0;
+ chan = &sband->channels[local->scan_channel_idx];
+
+ if (chan->flags & IEEE80211_CHAN_DISABLED ||
+ (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
+ chan->flags & IEEE80211_CHAN_NO_IBSS))
+ skip = 1;
+
+ if (!skip) {
+ local->scan_channel = chan;
+ if (ieee80211_hw_config(local)) {
+ printk(KERN_DEBUG "%s: failed to set freq to "
+ "%d MHz for scan\n", wiphy_name(local->hw.wiphy),
+ chan->center_freq);
+ skip = 1;
+ }
+ }
+
+ /* advance state machine to next channel/band */
+ local->scan_channel_idx++;
+ if (local->scan_channel_idx >= sband->n_channels) {
+ /*
+ * scan_band may end up == IEEE80211_NUM_BANDS, but
+ * we'll catch that case above and complete the scan
+ * if that is the case.
+ */
+ local->scan_band++;
+ local->scan_channel_idx = 0;
+ }
+
+ if (skip)
+ break;
+
+ next_delay = IEEE80211_PROBE_DELAY +
+ usecs_to_jiffies(local->hw.channel_change_time);
+ local->scan_state = SCAN_SEND_PROBE;
+ break;
+ case SCAN_SEND_PROBE:
+ next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
+ local->scan_state = SCAN_SET_CHANNEL;
+
+ if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+ break;
+ ieee80211_send_probe_req(sdata, NULL, local->scan_ssid,
+ local->scan_ssid_len);
+ next_delay = IEEE80211_CHANNEL_TIME;
+ break;
+ }
+
+ queue_delayed_work(local->hw.workqueue, &local->scan_work,
+ next_delay);
+}
+
+
+int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
+ u8 *ssid, size_t ssid_len)
+{
+ struct ieee80211_local *local = scan_sdata->local;
+ struct ieee80211_sub_if_data *sdata;
+
+ if (ssid_len > IEEE80211_MAX_SSID_LEN)
+ return -EINVAL;
+
+ /* MLME-SCAN.request (page 118) page 144 (11.1.3.1)
+ * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS
+ * BSSID: MACAddress
+ * SSID
+ * ScanType: ACTIVE, PASSIVE
+ * ProbeDelay: delay (in microseconds) to be used prior to transmitting
+ * a Probe frame during active scanning
+ * ChannelList
+ * MinChannelTime (>= ProbeDelay), in TU
+ * MaxChannelTime: (>= MinChannelTime), in TU
+ */
+
+ /* MLME-SCAN.confirm
+ * BSSDescriptionSet
+ * ResultCode: SUCCESS, INVALID_PARAMETERS
+ */
+
+ if (local->sw_scanning || local->hw_scanning) {
+ if (local->scan_sdata == scan_sdata)
+ return 0;
+ return -EBUSY;
+ }
+
+ if (local->ops->hw_scan) {
+ int rc;
+
+ local->hw_scanning = true;
+ rc = local->ops->hw_scan(local_to_hw(local), ssid, ssid_len);
+ if (rc) {
+ local->hw_scanning = false;
+ return rc;
+ }
+ local->scan_sdata = scan_sdata;
+ return 0;
+ }
+
+ local->sw_scanning = true;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
+ netif_tx_stop_all_queues(sdata->dev);
+ ieee80211_send_nullfunc(local, sdata, 1);
+ }
+ } else
+ netif_tx_stop_all_queues(sdata->dev);
+ }
+ rcu_read_unlock();
+
+ if (ssid) {
+ local->scan_ssid_len = ssid_len;
+ memcpy(local->scan_ssid, ssid, ssid_len);
+ } else
+ local->scan_ssid_len = 0;
+ local->scan_state = SCAN_SET_CHANNEL;
+ local->scan_channel_idx = 0;
+ local->scan_band = IEEE80211_BAND_2GHZ;
+ local->scan_sdata = scan_sdata;
+
+ netif_addr_lock_bh(local->mdev);
+ local->filter_flags |= FIF_BCN_PRBRESP_PROMISC;
+ local->ops->configure_filter(local_to_hw(local),
+ FIF_BCN_PRBRESP_PROMISC,
+ &local->filter_flags,
+ local->mdev->mc_count,
+ local->mdev->mc_list);
+ netif_addr_unlock_bh(local->mdev);
+
+ /* TODO: start scan as soon as all nullfunc frames are ACKed */
+ queue_delayed_work(local->hw.workqueue, &local->scan_work,
+ IEEE80211_CHANNEL_TIME);
+
+ return 0;
+}
+
+
+int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
+ u8 *ssid, size_t ssid_len)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_sta *ifsta;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return ieee80211_start_scan(sdata, ssid, ssid_len);
+
+ /*
+ * STA has a state machine that might need to defer scanning
+ * while it's trying to associate/authenticate, therefore we
+ * queue it up to the state machine in that case.
+ */
+
+ if (local->sw_scanning || local->hw_scanning) {
+ if (local->scan_sdata == sdata)
+ return 0;
+ return -EBUSY;
+ }
+
+ ifsta = &sdata->u.sta;
+
+ ifsta->scan_ssid_len = ssid_len;
+ if (ssid_len)
+ memcpy(ifsta->scan_ssid, ssid, ssid_len);
+ set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request);
+ queue_work(local->hw.workqueue, &ifsta->work);
+
+ return 0;
+}
+
+
+static void ieee80211_scan_add_ies(struct iw_request_info *info,
+ struct ieee80211_bss *bss,
+ char **current_ev, char *end_buf)
+{
+ u8 *pos, *end, *next;
+ struct iw_event iwe;
+
+ if (bss == NULL || bss->ies == NULL)
+ return;
+
+ /*
+ * If needed, fragment the IEs buffer (at IE boundaries) into short
+ * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
+ */
+ pos = bss->ies;
+ end = pos + bss->ies_len;
+
+ while (end - pos > IW_GENERIC_IE_MAX) {
+ next = pos + 2 + pos[1];
+ while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
+ next = next + 2 + next[1];
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = next - pos;
+ *current_ev = iwe_stream_add_point(info, *current_ev,
+ end_buf, &iwe, pos);
+
+ pos = next;
+ }
+
+ if (end > pos) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = end - pos;
+ *current_ev = iwe_stream_add_point(info, *current_ev,
+ end_buf, &iwe, pos);
+ }
+}
+
+
+static char *
+ieee80211_scan_result(struct ieee80211_local *local,
+ struct iw_request_info *info,
+ struct ieee80211_bss *bss,
+ char *current_ev, char *end_buf)
+{
+ struct iw_event iwe;
+ char *buf;
+
+ if (time_after(jiffies,
+ bss->last_update + IEEE80211_SCAN_RESULT_EXPIRE))
+ return current_ev;
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
+ IW_EV_ADDR_LEN);
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWESSID;
+ if (bss_mesh_cfg(bss)) {
+ iwe.u.data.length = bss_mesh_id_len(bss);
+ iwe.u.data.flags = 1;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, bss_mesh_id(bss));
+ } else {
+ iwe.u.data.length = bss->ssid_len;
+ iwe.u.data.flags = 1;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, bss->ssid);
+ }
+
+ if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)
+ || bss_mesh_cfg(bss)) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWMODE;
+ if (bss_mesh_cfg(bss))
+ iwe.u.mode = IW_MODE_MESH;
+ else if (bss->capability & WLAN_CAPABILITY_ESS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_UINT_LEN);
+ }
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = ieee80211_frequency_to_channel(bss->freq);
+ iwe.u.freq.e = 0;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
+ IW_EV_FREQ_LEN);
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = bss->freq;
+ iwe.u.freq.e = 6;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
+ IW_EV_FREQ_LEN);
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.qual = bss->qual;
+ iwe.u.qual.level = bss->signal;
+ iwe.u.qual.noise = bss->noise;
+ iwe.u.qual.updated = local->wstats_flags;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
+ IW_EV_QUAL_LEN);
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWENCODE;
+ if (bss->capability & WLAN_CAPABILITY_PRIVACY)
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, "");
+
+ ieee80211_scan_add_ies(info, bss, &current_ev, end_buf);
+
+ if (bss->supp_rates_len > 0) {
+ /* display all supported rates in readable format */
+ char *p = current_ev + iwe_stream_lcp_len(info);
+ int i;
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = SIOCGIWRATE;
+ /* Those two flags are ignored... */
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+
+ for (i = 0; i < bss->supp_rates_len; i++) {
+ iwe.u.bitrate.value = ((bss->supp_rates[i] &
+ 0x7f) * 500000);
+ p = iwe_stream_add_value(info, current_ev, p,
+ end_buf, &iwe, IW_EV_PARAM_LEN);
+ }
+ current_ev = p;
+ }
+
+ buf = kmalloc(30, GFP_ATOMIC);
+ if (buf) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp));
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, buf);
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ sprintf(buf, " Last beacon: %dms ago",
+ jiffies_to_msecs(jiffies - bss->last_update));
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point(info, current_ev,
+ end_buf, &iwe, buf);
+ kfree(buf);
+ }
+
+ if (bss_mesh_cfg(bss)) {
+ u8 *cfg = bss_mesh_cfg(bss);
+ buf = kmalloc(50, GFP_ATOMIC);
+ if (buf) {
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ sprintf(buf, "Mesh network (version %d)", cfg[0]);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point(info, current_ev,
+ end_buf,
+ &iwe, buf);
+ sprintf(buf, "Path Selection Protocol ID: "
+ "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3],
+ cfg[4]);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point(info, current_ev,
+ end_buf,
+ &iwe, buf);
+ sprintf(buf, "Path Selection Metric ID: "
+ "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7],
+ cfg[8]);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point(info, current_ev,
+ end_buf,
+ &iwe, buf);
+ sprintf(buf, "Congestion Control Mode ID: "
+ "0x%02X%02X%02X%02X", cfg[9], cfg[10],
+ cfg[11], cfg[12]);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point(info, current_ev,
+ end_buf,
+ &iwe, buf);
+ sprintf(buf, "Channel Precedence: "
+ "0x%02X%02X%02X%02X", cfg[13], cfg[14],
+ cfg[15], cfg[16]);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point(info, current_ev,
+ end_buf,
+ &iwe, buf);
+ kfree(buf);
+ }
+ }
+
+ return current_ev;
+}
+
+
+int ieee80211_scan_results(struct ieee80211_local *local,
+ struct iw_request_info *info,
+ char *buf, size_t len)
+{
+ char *current_ev = buf;
+ char *end_buf = buf + len;
+ struct ieee80211_bss *bss;
+
+ spin_lock_bh(&local->bss_lock);
+ list_for_each_entry(bss, &local->bss_list, list) {
+ if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
+ spin_unlock_bh(&local->bss_lock);
+ return -E2BIG;
+ }
+ current_ev = ieee80211_scan_result(local, info, bss,
+ current_ev, end_buf);
+ }
+ spin_unlock_bh(&local->bss_lock);
+ return current_ev - buf;
+}
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
new file mode 100644
index 00000000000..f72bad636d8
--- /dev/null
+++ b/net/mac80211/spectmgmt.c
@@ -0,0 +1,86 @@
+/*
+ * spectrum management
+ *
+ * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright 2002-2005, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2007-2008, Intel Corporation
+ * Copyright 2008, 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/ieee80211.h>
+#include <net/wireless.h>
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+#include "sta_info.h"
+#include "wme.h"
+
+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,
+ u8 dialog_token)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *msr_report;
+
+ 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->dev->name);
+ return;
+ }
+
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+ msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24);
+ memset(msr_report, 0, 24);
+ memcpy(msr_report->da, da, ETH_ALEN);
+ memcpy(msr_report->sa, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(msr_report->bssid, bssid, ETH_ALEN);
+ msr_report->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION);
+
+ skb_put(skb, 1 + sizeof(msr_report->u.action.u.measurement));
+ msr_report->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
+ msr_report->u.action.u.measurement.action_code =
+ WLAN_ACTION_SPCT_MSR_RPRT;
+ msr_report->u.action.u.measurement.dialog_token = dialog_token;
+
+ msr_report->u.action.u.measurement.element_id = WLAN_EID_MEASURE_REPORT;
+ msr_report->u.action.u.measurement.length =
+ sizeof(struct ieee80211_msrment_ie);
+
+ memset(&msr_report->u.action.u.measurement.msr_elem, 0,
+ sizeof(struct ieee80211_msrment_ie));
+ msr_report->u.action.u.measurement.msr_elem.token = request_ie->token;
+ msr_report->u.action.u.measurement.msr_elem.mode |=
+ IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED;
+ msr_report->u.action.u.measurement.msr_elem.type = request_ie->type;
+
+ ieee80211_tx_skb(sdata, skb, 0);
+}
+
+void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ /*
+ * Ignoring measurement request is spec violation.
+ * Mandatory measurements must be reported optional
+ * measurements might be refused or reported incapable
+ * For now just refuse
+ * TODO: Answer basic measurement as unmeasured
+ */
+ ieee80211_send_refuse_measurement_request(sdata,
+ &mgmt->u.action.u.measurement.msr_elem,
+ mgmt->sa, mgmt->bssid,
+ mgmt->u.action.u.measurement.dialog_token);
+}
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 7d4fe4a5292..7fef8ea1f5e 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -73,11 +73,11 @@ static int sta_info_hash_del(struct ieee80211_local *local,
{
struct sta_info *s;
- s = local->sta_hash[STA_HASH(sta->addr)];
+ s = local->sta_hash[STA_HASH(sta->sta.addr)];
if (!s)
return -ENOENT;
if (s == sta) {
- rcu_assign_pointer(local->sta_hash[STA_HASH(sta->addr)],
+ rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)],
s->hnext);
return 0;
}
@@ -93,26 +93,19 @@ static int sta_info_hash_del(struct ieee80211_local *local,
}
/* protected by RCU */
-static struct sta_info *__sta_info_find(struct ieee80211_local *local,
- u8 *addr)
+struct sta_info *sta_info_get(struct ieee80211_local *local, const u8 *addr)
{
struct sta_info *sta;
sta = rcu_dereference(local->sta_hash[STA_HASH(addr)]);
while (sta) {
- if (compare_ether_addr(sta->addr, addr) == 0)
+ if (compare_ether_addr(sta->sta.addr, addr) == 0)
break;
sta = rcu_dereference(sta->hnext);
}
return sta;
}
-struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
-{
- return __sta_info_find(local, addr);
-}
-EXPORT_SYMBOL(sta_info_get);
-
struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
struct net_device *dev)
{
@@ -135,6 +128,7 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
/**
* __sta_info_free - internal STA free helper
*
+ * @local: pointer to the global information
* @sta: STA info to free
*
* This function must undo everything done by sta_info_alloc()
@@ -145,12 +139,12 @@ static void __sta_info_free(struct ieee80211_local *local,
{
DECLARE_MAC_BUF(mbuf);
- rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv);
+ rate_control_free_sta(sta);
rate_control_put(sta->rate_ctrl);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Destroyed STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->addr));
+ wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->sta.addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
kfree(sta);
@@ -202,14 +196,12 @@ void sta_info_destroy(struct sta_info *sta)
dev_kfree_skb_any(skb);
for (i = 0; i < STA_TID_NUM; i++) {
- spin_lock_bh(&sta->ampdu_mlme.ampdu_rx);
+ spin_lock_bh(&sta->lock);
if (sta->ampdu_mlme.tid_rx[i])
del_timer_sync(&sta->ampdu_mlme.tid_rx[i]->session_timer);
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx);
- spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
if (sta->ampdu_mlme.tid_tx[i])
del_timer_sync(&sta->ampdu_mlme.tid_tx[i]->addba_resp_timer);
- spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+ spin_unlock_bh(&sta->lock);
}
__sta_info_free(local, sta);
@@ -220,8 +212,8 @@ void sta_info_destroy(struct sta_info *sta)
static void sta_info_hash_add(struct ieee80211_local *local,
struct sta_info *sta)
{
- sta->hnext = local->sta_hash[STA_HASH(sta->addr)];
- rcu_assign_pointer(local->sta_hash[STA_HASH(sta->addr)], sta);
+ sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)];
+ rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
}
struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
@@ -232,32 +224,33 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
int i;
DECLARE_MAC_BUF(mbuf);
- sta = kzalloc(sizeof(*sta), gfp);
+ sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);
if (!sta)
return NULL;
- memcpy(sta->addr, addr, ETH_ALEN);
+ spin_lock_init(&sta->lock);
+ spin_lock_init(&sta->flaglock);
+
+ memcpy(sta->sta.addr, addr, ETH_ALEN);
sta->local = local;
sta->sdata = sdata;
sta->rate_ctrl = rate_control_get(local->rate_ctrl);
sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl,
- gfp);
+ &sta->sta, gfp);
if (!sta->rate_ctrl_priv) {
rate_control_put(sta->rate_ctrl);
kfree(sta);
return NULL;
}
- spin_lock_init(&sta->ampdu_mlme.ampdu_rx);
- spin_lock_init(&sta->ampdu_mlme.ampdu_tx);
for (i = 0; i < STA_TID_NUM; i++) {
/* timer_to_tid must be initialized with identity mapping to
* enable session_timer's data differentiation. refer to
* sta_rx_agg_session_timer_expired for useage */
sta->timer_to_tid[i] = i;
/* tid to tx queue: initialize according to HW (0 is valid) */
- sta->tid_to_tx_q[i] = local->hw.queues;
+ sta->tid_to_tx_q[i] = ieee80211_num_queues(&local->hw);
/* rx */
sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE;
sta->ampdu_mlme.tid_rx[i] = NULL;
@@ -271,12 +264,11 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Allocated STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->addr));
+ wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->sta.addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
#ifdef CONFIG_MAC80211_MESH
sta->plink_state = PLINK_LISTEN;
- spin_lock_init(&sta->plink_lock);
init_timer(&sta->plink_timer);
#endif
@@ -301,15 +293,15 @@ int sta_info_insert(struct sta_info *sta)
goto out_free;
}
- if (WARN_ON(compare_ether_addr(sta->addr, sdata->dev->dev_addr) == 0 ||
- is_multicast_ether_addr(sta->addr))) {
+ if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->dev->dev_addr) == 0 ||
+ is_multicast_ether_addr(sta->sta.addr))) {
err = -EINVAL;
goto out_free;
}
spin_lock_irqsave(&local->sta_lock, flags);
/* check if STA exists already */
- if (__sta_info_find(local, sta->addr)) {
+ if (sta_info_get(local, sta->sta.addr)) {
spin_unlock_irqrestore(&local->sta_lock, flags);
err = -EEXIST;
goto out_free;
@@ -320,16 +312,18 @@ int sta_info_insert(struct sta_info *sta)
/* notify driver */
if (local->ops->sta_notify) {
- if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
- sdata = sdata->u.vlan.ap;
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata = container_of(sdata->bss,
+ struct ieee80211_sub_if_data,
+ u.ap);
local->ops->sta_notify(local_to_hw(local), &sdata->vif,
- STA_NOTIFY_ADD, sta->addr);
+ STA_NOTIFY_ADD, &sta->sta);
}
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Inserted STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mac, sta->addr));
+ wiphy_name(local->hw.wiphy), print_mac(mac, sta->sta.addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
spin_unlock_irqrestore(&local->sta_lock, flags);
@@ -376,11 +370,14 @@ static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid)
static void __sta_info_set_tim_bit(struct ieee80211_if_ap *bss,
struct sta_info *sta)
{
- if (bss)
- __bss_tim_set(bss, sta->aid);
+ BUG_ON(!bss);
+
+ __bss_tim_set(bss, sta->sta.aid);
+
if (sta->local->ops->set_tim) {
sta->local->tim_in_locked_section = true;
- sta->local->ops->set_tim(local_to_hw(sta->local), sta->aid, 1);
+ sta->local->ops->set_tim(local_to_hw(sta->local),
+ &sta->sta, true);
sta->local->tim_in_locked_section = false;
}
}
@@ -389,6 +386,8 @@ void sta_info_set_tim_bit(struct sta_info *sta)
{
unsigned long flags;
+ BUG_ON(!sta->sdata->bss);
+
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);
@@ -397,11 +396,14 @@ void sta_info_set_tim_bit(struct sta_info *sta)
static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss,
struct sta_info *sta)
{
- if (bss)
- __bss_tim_clear(bss, sta->aid);
+ BUG_ON(!bss);
+
+ __bss_tim_clear(bss, sta->sta.aid);
+
if (sta->local->ops->set_tim) {
sta->local->tim_in_locked_section = true;
- sta->local->ops->set_tim(local_to_hw(sta->local), sta->aid, 0);
+ sta->local->ops->set_tim(local_to_hw(sta->local),
+ &sta->sta, false);
sta->local->tim_in_locked_section = false;
}
}
@@ -410,12 +412,14 @@ void sta_info_clear_tim_bit(struct sta_info *sta)
{
unsigned long flags;
+ BUG_ON(!sta->sdata->bss);
+
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);
}
-void __sta_info_unlink(struct sta_info **sta)
+static void __sta_info_unlink(struct sta_info **sta)
{
struct ieee80211_local *local = (*sta)->local;
struct ieee80211_sub_if_data *sdata = (*sta)->sdata;
@@ -437,21 +441,23 @@ void __sta_info_unlink(struct sta_info **sta)
list_del(&(*sta)->list);
- if ((*sta)->flags & WLAN_STA_PS) {
- (*sta)->flags &= ~WLAN_STA_PS;
- if (sdata->bss)
- atomic_dec(&sdata->bss->num_sta_ps);
+ if (test_and_clear_sta_flags(*sta, WLAN_STA_PS)) {
+ BUG_ON(!sdata->bss);
+
+ atomic_dec(&sdata->bss->num_sta_ps);
__sta_info_clear_tim_bit(sdata->bss, *sta);
}
local->num_sta--;
if (local->ops->sta_notify) {
- if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
- sdata = sdata->u.vlan.ap;
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata = container_of(sdata->bss,
+ struct ieee80211_sub_if_data,
+ u.ap);
local->ops->sta_notify(local_to_hw(local), &sdata->vif,
- STA_NOTIFY_REMOVE, (*sta)->addr);
+ STA_NOTIFY_REMOVE, &(*sta)->sta);
}
if (ieee80211_vif_is_mesh(&sdata->vif)) {
@@ -463,7 +469,7 @@ void __sta_info_unlink(struct sta_info **sta)
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Removed STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mbuf, (*sta)->addr));
+ wiphy_name(local->hw.wiphy), print_mac(mbuf, (*sta)->sta.addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
/*
@@ -515,20 +521,20 @@ static inline int sta_info_buffer_expired(struct ieee80211_local *local,
struct sta_info *sta,
struct sk_buff *skb)
{
- struct ieee80211_tx_packet_data *pkt_data;
+ struct ieee80211_tx_info *info;
int timeout;
if (!skb)
return 0;
- pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
+ info = IEEE80211_SKB_CB(skb);
/* Timeout: (2 * listen_interval * beacon_int * 1024 / 1000000) sec */
timeout = (sta->listen_interval * local->hw.conf.beacon_int * 32 /
15625) * HZ;
if (timeout < STA_TX_BUFFER_EXPIRE)
timeout = STA_TX_BUFFER_EXPIRE;
- return time_after(jiffies, pkt_data->jiffies + timeout);
+ return time_after(jiffies, info->control.jiffies + timeout);
}
@@ -557,8 +563,10 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
sdata = sta->sdata;
local->total_ps_buffered--;
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "Buffered frame expired (STA "
- "%s)\n", print_mac(mac, sta->addr));
+ "%s)\n", print_mac(mac, sta->sta.addr));
+#endif
dev_kfree_skb(skb);
if (skb_queue_empty(&sta->ps_tx_buf))
@@ -627,7 +635,12 @@ static void sta_info_debugfs_add_work(struct work_struct *work)
spin_lock_irqsave(&local->sta_lock, flags);
list_for_each_entry(tmp, &local->sta_list, list) {
- if (!tmp->debugfs.dir) {
+ /*
+ * debugfs.add_has_run will be set by
+ * ieee80211_sta_debugfs_add regardless
+ * of what else it does.
+ */
+ if (!tmp->debugfs.add_has_run) {
sta = tmp;
__sta_info_pin(sta);
break;
@@ -789,3 +802,40 @@ void sta_info_flush_delayed(struct ieee80211_sub_if_data *sdata)
schedule_work(&local->sta_flush_work);
spin_unlock_irqrestore(&local->sta_lock, flags);
}
+
+void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
+ unsigned long exp_time)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta, *tmp;
+ LIST_HEAD(tmp_list);
+ DECLARE_MAC_BUF(mac);
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->sta_lock, flags);
+ list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
+ if (time_after(jiffies, sta->last_rx + exp_time)) {
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ printk(KERN_DEBUG "%s: expiring inactive STA %s\n",
+ sdata->dev->name, print_mac(mac, sta->sta.addr));
+#endif
+ __sta_info_unlink(&sta);
+ if (sta)
+ list_add(&sta->list, &tmp_list);
+ }
+ spin_unlock_irqrestore(&local->sta_lock, flags);
+
+ list_for_each_entry_safe(sta, tmp, &tmp_list, list)
+ sta_info_destroy(sta);
+}
+
+struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw,
+ const u8 *addr)
+{
+ struct sta_info *sta = sta_info_get(hw_to_local(hw), addr);
+
+ if (!sta)
+ return NULL;
+ return &sta->sta;
+}
+EXPORT_SYMBOL(ieee80211_find_sta);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index f8c95bc9659..168a39a298b 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -32,7 +32,7 @@
* @WLAN_STA_WDS: Station is one of our WDS peers.
* @WLAN_STA_PSPOLL: Station has just PS-polled us.
* @WLAN_STA_CLEAR_PS_FILT: Clear PS filter in hardware (using the
- * IEEE80211_TXCTL_CLEAR_PS_FILT control flag) when the next
+ * IEEE80211_TX_CTL_CLEAR_PS_FILT control flag) when the next
* frame to this station is transmitted.
*/
enum ieee80211_sta_info_flags {
@@ -129,23 +129,19 @@ enum plink_state {
*
* @tid_state_rx: TID's state in Rx session state machine.
* @tid_rx: aggregation info for Rx per TID
- * @ampdu_rx: for locking sections in aggregation Rx flow
* @tid_state_tx: TID's state in Tx session state machine.
* @tid_tx: aggregation info for Tx per TID
* @addba_req_num: number of times addBA request has been sent.
- * @ampdu_tx: for locking sectionsi in aggregation Tx flow
* @dialog_token_allocator: dialog token enumerator for each new session;
*/
struct sta_ampdu_mlme {
/* rx */
u8 tid_state_rx[STA_TID_NUM];
struct tid_ampdu_rx *tid_rx[STA_TID_NUM];
- spinlock_t ampdu_rx;
/* tx */
u8 tid_state_tx[STA_TID_NUM];
struct tid_ampdu_tx *tid_tx[STA_TID_NUM];
u8 addba_req_num[STA_TID_NUM];
- spinlock_t ampdu_tx;
u8 dialog_token_allocator;
};
@@ -164,9 +160,18 @@ struct sta_ampdu_mlme {
* @list: global linked list entry
* @hnext: hash table linked list pointer
* @local: pointer to the global information
+ * @sdata: TBD
+ * @key: TBD
+ * @rate_ctrl: TBD
+ * @rate_ctrl_priv: TBD
+ * @lock: used for locking all fields that require locking, see comments
+ * in the header file.
+ * @flaglock: spinlock for flags accesses
* @addr: MAC address of this STA
* @aid: STA's unique AID (1..2007, 0 = not assigned yet),
* only used in AP (and IBSS?) mode
+ * @listen_interval: TBD
+ * @pin_status: TBD
* @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
@@ -175,8 +180,37 @@ struct sta_ampdu_mlme {
* power saving state
* @rx_packets: Number of MSDUs received from this STA
* @rx_bytes: Number of bytes received from this STA
- * @supp_rates: Bitmap of supported rates (per band)
- * @ht_info: HT capabilities of this STA
+ * @wep_weak_iv_count: TBD
+ * @last_rx: TBD
+ * @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
+ * @last_qual: qual of last received frame from this STA
+ * @last_noise: noise of last received frame from this STA
+ * @last_seq_ctrl: last received seq/frag number from this STA (per RX queue)
+ * @tx_filtered_count: TBD
+ * @tx_retry_failed: TBD
+ * @tx_retry_count: TBD
+ * @fail_avg: moving percentage of failed MSDUs
+ * @tx_packets: number of RX/TX MSDUs
+ * @tx_bytes: TBD
+ * @tx_fragments: number of transmitted MPDUs
+ * @last_txrate_idx: Index of the last used transmit rate
+ * @tid_seq: TBD
+ * @ampdu_mlme: TBD
+ * @timer_to_tid: identity mapping to ID timers
+ * @tid_to_tx_q: map tid to tx queue
+ * @llid: Local link ID
+ * @plid: Peer link ID
+ * @reason: Cancel reason on PLINK_HOLDING state
+ * @plink_retries: Retries in establishment
+ * @ignore_plink_timer: TBD
+ * @plink_state plink_state: TBD
+ * @plink_timeout: TBD
+ * @plink_timer: TBD
+ * @debugfs: debug filesystem info
+ * @sta: station information we share with the driver
*/
struct sta_info {
/* General information, mostly static */
@@ -187,10 +221,9 @@ struct sta_info {
struct ieee80211_key *key;
struct rate_control_ref *rate_ctrl;
void *rate_ctrl_priv;
- struct ieee80211_ht_info ht_info;
- u64 supp_rates[IEEE80211_NUM_BANDS];
- u8 addr[ETH_ALEN];
- u16 aid;
+ spinlock_t lock;
+ spinlock_t flaglock;
+
u16 listen_interval;
/*
@@ -199,7 +232,10 @@ struct sta_info {
*/
u8 pin_status;
- /* frequently updated information, needs locking? */
+ /*
+ * frequently updated, locked with own spinlock (flaglock),
+ * use the accessors defined below
+ */
u32 flags;
/*
@@ -213,66 +249,47 @@ struct sta_info {
unsigned long rx_packets, rx_bytes;
unsigned long wep_weak_iv_count;
unsigned long last_rx;
- unsigned long num_duplicates; /* number of duplicate frames received
- * from this STA */
- unsigned long rx_fragments; /* number of received MPDUs */
- unsigned long rx_dropped; /* number of dropped MPDUs from this STA */
- int last_rssi; /* RSSI of last received frame from this STA */
- int last_signal; /* signal of last received frame from this STA */
- int last_noise; /* noise of last received frame from this STA */
- /* last received seq/frag number from this STA (per RX queue) */
+ unsigned long num_duplicates;
+ unsigned long rx_fragments;
+ unsigned long rx_dropped;
+ int last_signal;
+ int last_qual;
+ int last_noise;
__le16 last_seq_ctrl[NUM_RX_DATA_QUEUES];
-#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
- unsigned int wme_rx_queue[NUM_RX_DATA_QUEUES];
-#endif
/* Updated from TX status path only, no locking requirements */
unsigned long tx_filtered_count;
unsigned long tx_retry_failed, tx_retry_count;
- /* TODO: update in generic code not rate control? */
- u32 tx_num_consecutive_failures;
- u32 tx_num_mpdu_ok;
- u32 tx_num_mpdu_fail;
/* moving percentage of failed MSDUs */
unsigned int fail_avg;
/* Updated from TX path only, no locking requirements */
- unsigned long tx_packets; /* number of RX/TX MSDUs */
+ unsigned long tx_packets;
unsigned long tx_bytes;
- unsigned long tx_fragments; /* number of transmitted MPDUs */
- int txrate_idx;
- int last_txrate_idx;
-#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
- unsigned int wme_tx_queue[NUM_RX_DATA_QUEUES];
-#endif
-
- /* Debug counters, no locking doesn't matter */
- int channel_use;
- int channel_use_raw;
+ unsigned long tx_fragments;
+ unsigned int last_txrate_idx;
+ u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
/*
- * Aggregation information, comes with own locking.
+ * Aggregation information, locked with lock.
*/
struct sta_ampdu_mlme ampdu_mlme;
- u8 timer_to_tid[STA_TID_NUM]; /* identity mapping to ID timers */
- u8 tid_to_tx_q[STA_TID_NUM]; /* map tid to tx queue */
+ u8 timer_to_tid[STA_TID_NUM];
+ u8 tid_to_tx_q[STA_TID_NUM];
#ifdef CONFIG_MAC80211_MESH
/*
* Mesh peer link attributes
* TODO: move to a sub-structure that is referenced with pointer?
*/
- __le16 llid; /* Local link ID */
- __le16 plid; /* Peer link ID */
- __le16 reason; /* Cancel reason on PLINK_HOLDING state */
- u8 plink_retries; /* Retries in establishment */
+ __le16 llid;
+ __le16 plid;
+ __le16 reason;
+ u8 plink_retries;
bool ignore_plink_timer;
enum plink_state plink_state;
u32 plink_timeout;
struct timer_list plink_timer;
- spinlock_t plink_lock; /* For peer_state reads / updates and other
- updates in the structure. Ensures robust
- transitions for the peerlink FSM */
#endif
#ifdef CONFIG_MAC80211_DEBUGFS
@@ -282,13 +299,13 @@ struct sta_info {
struct dentry *num_ps_buf_frames;
struct dentry *inactive_ms;
struct dentry *last_seq_ctrl;
-#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
- struct dentry *wme_rx_queue;
- struct dentry *wme_tx_queue;
-#endif
struct dentry *agg_status;
+ bool add_has_run;
} debugfs;
#endif
+
+ /* keep last! */
+ struct ieee80211_sta sta;
};
static inline enum plink_state sta_plink_state(struct sta_info *sta)
@@ -299,6 +316,73 @@ static inline enum plink_state sta_plink_state(struct sta_info *sta)
return PLINK_LISTEN;
}
+static inline void set_sta_flags(struct sta_info *sta, const u32 flags)
+{
+ unsigned long irqfl;
+
+ spin_lock_irqsave(&sta->flaglock, irqfl);
+ sta->flags |= flags;
+ spin_unlock_irqrestore(&sta->flaglock, irqfl);
+}
+
+static inline void clear_sta_flags(struct sta_info *sta, const u32 flags)
+{
+ unsigned long irqfl;
+
+ spin_lock_irqsave(&sta->flaglock, irqfl);
+ sta->flags &= ~flags;
+ spin_unlock_irqrestore(&sta->flaglock, irqfl);
+}
+
+static inline void set_and_clear_sta_flags(struct sta_info *sta,
+ const u32 set, const u32 clear)
+{
+ unsigned long irqfl;
+
+ spin_lock_irqsave(&sta->flaglock, irqfl);
+ sta->flags |= set;
+ sta->flags &= ~clear;
+ spin_unlock_irqrestore(&sta->flaglock, irqfl);
+}
+
+static inline u32 test_sta_flags(struct sta_info *sta, const u32 flags)
+{
+ u32 ret;
+ unsigned long irqfl;
+
+ spin_lock_irqsave(&sta->flaglock, irqfl);
+ ret = sta->flags & flags;
+ spin_unlock_irqrestore(&sta->flaglock, irqfl);
+
+ return ret;
+}
+
+static inline u32 test_and_clear_sta_flags(struct sta_info *sta,
+ const u32 flags)
+{
+ u32 ret;
+ unsigned long irqfl;
+
+ spin_lock_irqsave(&sta->flaglock, irqfl);
+ ret = sta->flags & flags;
+ sta->flags &= ~flags;
+ spin_unlock_irqrestore(&sta->flaglock, irqfl);
+
+ return ret;
+}
+
+static inline u32 get_sta_flags(struct sta_info *sta)
+{
+ u32 ret;
+ unsigned long irqfl;
+
+ spin_lock_irqsave(&sta->flaglock, irqfl);
+ ret = sta->flags;
+ spin_unlock_irqrestore(&sta->flaglock, irqfl);
+
+ return ret;
+}
+
/* Maximum number of concurrently registered stations */
#define MAX_STA_COUNT 2007
@@ -321,7 +405,7 @@ static inline enum plink_state sta_plink_state(struct sta_info *sta)
/*
* Get a STA info, must have be under RCU read lock.
*/
-struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr);
+struct sta_info *sta_info_get(struct ieee80211_local *local, const u8 *addr);
/*
* Get STA info by index, BROKEN!
*/
@@ -347,7 +431,6 @@ int sta_info_insert(struct sta_info *sta);
* has already unlinked it.
*/
void sta_info_unlink(struct sta_info **sta);
-void __sta_info_unlink(struct sta_info **sta);
void sta_info_destroy(struct sta_info *sta);
void sta_info_set_tim_bit(struct sta_info *sta);
@@ -359,5 +442,7 @@ void sta_info_stop(struct ieee80211_local *local);
int sta_info_flush(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata);
void sta_info_flush_delayed(struct ieee80211_sub_if_data *sdata);
+void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
+ unsigned long exp_time);
#endif /* STA_INFO_H */
diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c
index 09093da24af..34b32bc8f60 100644
--- a/net/mac80211/tkip.c
+++ b/net/mac80211/tkip.c
@@ -6,25 +6,23 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-
#include <linux/kernel.h>
+#include <linux/bitops.h>
#include <linux/types.h>
#include <linux/netdevice.h>
+#include <asm/unaligned.h>
#include <net/mac80211.h>
#include "key.h"
#include "tkip.h"
#include "wep.h"
-
-/* TKIP key mixing functions */
-
-
#define PHASE1_LOOP_COUNT 8
-
-/* 2-byte by 2-byte subset of the full AES S-box table; second part of this
- * table is identical to first part but byte-swapped */
+/*
+ * 2-byte by 2-byte subset of the full AES S-box table; second part of this
+ * table is identical to first part but byte-swapped
+ */
static const u16 tkip_sbox[256] =
{
0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
@@ -61,84 +59,54 @@ static const u16 tkip_sbox[256] =
0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
};
-
-static inline u16 Mk16(u8 x, u8 y)
+static u16 tkipS(u16 val)
{
- return ((u16) x << 8) | (u16) y;
+ return tkip_sbox[val & 0xff] ^ swab16(tkip_sbox[val >> 8]);
}
-
-static inline u8 Hi8(u16 v)
-{
- return v >> 8;
-}
-
-
-static inline u8 Lo8(u16 v)
-{
- return v & 0xff;
-}
-
-
-static inline u16 Hi16(u32 v)
-{
- return v >> 16;
-}
-
-
-static inline u16 Lo16(u32 v)
-{
- return v & 0xffff;
-}
-
-
-static inline u16 RotR1(u16 v)
-{
- return (v >> 1) | ((v & 0x0001) << 15);
-}
-
-
-static inline u16 tkip_S(u16 val)
+static u8 *write_tkip_iv(u8 *pos, u16 iv16)
{
- u16 a = tkip_sbox[Hi8(val)];
-
- return tkip_sbox[Lo8(val)] ^ Hi8(a) ^ (Lo8(a) << 8);
+ *pos++ = iv16 >> 8;
+ *pos++ = ((iv16 >> 8) | 0x20) & 0x7f;
+ *pos++ = iv16 & 0xFF;
+ return pos;
}
-
-
-/* P1K := Phase1(TA, TK, TSC)
+/*
+ * P1K := Phase1(TA, TK, TSC)
* TA = transmitter address (48 bits)
* TK = dot11DefaultKeyValue or dot11KeyMappingValue (128 bits)
* TSC = TKIP sequence counter (48 bits, only 32 msb bits used)
* P1K: 80 bits
*/
-static void tkip_mixing_phase1(const u8 *ta, const u8 *tk, u32 tsc_IV32,
- u16 *p1k)
+static void tkip_mixing_phase1(const u8 *tk, struct tkip_ctx *ctx,
+ const u8 *ta, u32 tsc_IV32)
{
int i, j;
+ u16 *p1k = ctx->p1k;
- p1k[0] = Lo16(tsc_IV32);
- p1k[1] = Hi16(tsc_IV32);
- p1k[2] = Mk16(ta[1], ta[0]);
- p1k[3] = Mk16(ta[3], ta[2]);
- p1k[4] = Mk16(ta[5], ta[4]);
+ p1k[0] = tsc_IV32 & 0xFFFF;
+ p1k[1] = tsc_IV32 >> 16;
+ p1k[2] = get_unaligned_le16(ta + 0);
+ p1k[3] = get_unaligned_le16(ta + 2);
+ p1k[4] = get_unaligned_le16(ta + 4);
for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
j = 2 * (i & 1);
- p1k[0] += tkip_S(p1k[4] ^ Mk16(tk[ 1 + j], tk[ 0 + j]));
- p1k[1] += tkip_S(p1k[0] ^ Mk16(tk[ 5 + j], tk[ 4 + j]));
- p1k[2] += tkip_S(p1k[1] ^ Mk16(tk[ 9 + j], tk[ 8 + j]));
- p1k[3] += tkip_S(p1k[2] ^ Mk16(tk[13 + j], tk[12 + j]));
- p1k[4] += tkip_S(p1k[3] ^ Mk16(tk[ 1 + j], tk[ 0 + j])) + i;
+ p1k[0] += tkipS(p1k[4] ^ get_unaligned_le16(tk + 0 + j));
+ p1k[1] += tkipS(p1k[0] ^ get_unaligned_le16(tk + 4 + j));
+ p1k[2] += tkipS(p1k[1] ^ get_unaligned_le16(tk + 8 + j));
+ p1k[3] += tkipS(p1k[2] ^ get_unaligned_le16(tk + 12 + j));
+ p1k[4] += tkipS(p1k[3] ^ get_unaligned_le16(tk + 0 + j)) + i;
}
+ ctx->initialized = 1;
}
-
-static void tkip_mixing_phase2(const u16 *p1k, const u8 *tk, u16 tsc_IV16,
- u8 *rc4key)
+static void tkip_mixing_phase2(const u8 *tk, struct tkip_ctx *ctx,
+ u16 tsc_IV16, u8 *rc4key)
{
u16 ppk[6];
+ const u16 *p1k = ctx->p1k;
int i;
ppk[0] = p1k[0];
@@ -148,70 +116,35 @@ static void tkip_mixing_phase2(const u16 *p1k, const u8 *tk, u16 tsc_IV16,
ppk[4] = p1k[4];
ppk[5] = p1k[4] + tsc_IV16;
- ppk[0] += tkip_S(ppk[5] ^ Mk16(tk[ 1], tk[ 0]));
- ppk[1] += tkip_S(ppk[0] ^ Mk16(tk[ 3], tk[ 2]));
- ppk[2] += tkip_S(ppk[1] ^ Mk16(tk[ 5], tk[ 4]));
- ppk[3] += tkip_S(ppk[2] ^ Mk16(tk[ 7], tk[ 6]));
- ppk[4] += tkip_S(ppk[3] ^ Mk16(tk[ 9], tk[ 8]));
- ppk[5] += tkip_S(ppk[4] ^ Mk16(tk[11], tk[10]));
- ppk[0] += RotR1(ppk[5] ^ Mk16(tk[13], tk[12]));
- ppk[1] += RotR1(ppk[0] ^ Mk16(tk[15], tk[14]));
- ppk[2] += RotR1(ppk[1]);
- ppk[3] += RotR1(ppk[2]);
- ppk[4] += RotR1(ppk[3]);
- ppk[5] += RotR1(ppk[4]);
-
- rc4key[0] = Hi8(tsc_IV16);
- rc4key[1] = (Hi8(tsc_IV16) | 0x20) & 0x7f;
- rc4key[2] = Lo8(tsc_IV16);
- rc4key[3] = Lo8((ppk[5] ^ Mk16(tk[1], tk[0])) >> 1);
-
- for (i = 0; i < 6; i++) {
- rc4key[4 + 2 * i] = Lo8(ppk[i]);
- rc4key[5 + 2 * i] = Hi8(ppk[i]);
- }
+ ppk[0] += tkipS(ppk[5] ^ get_unaligned_le16(tk + 0));
+ ppk[1] += tkipS(ppk[0] ^ get_unaligned_le16(tk + 2));
+ ppk[2] += tkipS(ppk[1] ^ get_unaligned_le16(tk + 4));
+ ppk[3] += tkipS(ppk[2] ^ get_unaligned_le16(tk + 6));
+ ppk[4] += tkipS(ppk[3] ^ get_unaligned_le16(tk + 8));
+ ppk[5] += tkipS(ppk[4] ^ get_unaligned_le16(tk + 10));
+ ppk[0] += ror16(ppk[5] ^ get_unaligned_le16(tk + 12), 1);
+ ppk[1] += ror16(ppk[0] ^ get_unaligned_le16(tk + 14), 1);
+ ppk[2] += ror16(ppk[1], 1);
+ ppk[3] += ror16(ppk[2], 1);
+ ppk[4] += ror16(ppk[3], 1);
+ ppk[5] += ror16(ppk[4], 1);
+
+ rc4key = write_tkip_iv(rc4key, tsc_IV16);
+ *rc4key++ = ((ppk[5] ^ get_unaligned_le16(tk)) >> 1) & 0xFF;
+
+ for (i = 0; i < 6; i++)
+ put_unaligned_le16(ppk[i], rc4key + 2 * i);
}
-
/* 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,
- u8 iv0, u8 iv1, u8 iv2)
+u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, u16 iv16)
{
- *pos++ = iv0;
- *pos++ = iv1;
- *pos++ = iv2;
+ pos = write_tkip_iv(pos, iv16);
*pos++ = (key->conf.keyidx << 6) | (1 << 5) /* Ext IV */;
- *pos++ = key->u.tkip.iv32 & 0xff;
- *pos++ = (key->u.tkip.iv32 >> 8) & 0xff;
- *pos++ = (key->u.tkip.iv32 >> 16) & 0xff;
- *pos++ = (key->u.tkip.iv32 >> 24) & 0xff;
- return pos;
-}
-
-
-void ieee80211_tkip_gen_phase1key(struct ieee80211_key *key, u8 *ta,
- u16 *phase1key)
-{
- tkip_mixing_phase1(ta, &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY],
- key->u.tkip.iv32, phase1key);
-}
-
-void ieee80211_tkip_gen_rc4key(struct ieee80211_key *key, u8 *ta,
- u8 *rc4key)
-{
- /* Calculate per-packet key */
- if (key->u.tkip.iv16 == 0 || !key->u.tkip.tx_initialized) {
- /* IV16 wrapped around - perform TKIP phase 1 */
- tkip_mixing_phase1(ta, &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY],
- key->u.tkip.iv32, key->u.tkip.p1k);
- key->u.tkip.tx_initialized = 1;
- }
-
- tkip_mixing_phase2(key->u.tkip.p1k,
- &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY],
- key->u.tkip.iv16, rc4key);
+ put_unaligned_le32(key->u.tkip.tx.iv32, pos);
+ return pos + 4;
}
void ieee80211_get_tkip_key(struct ieee80211_key_conf *keyconf,
@@ -220,48 +153,44 @@ void ieee80211_get_tkip_key(struct ieee80211_key_conf *keyconf,
{
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 = (u8 *) hdr;
- u16 fc = le16_to_cpu(hdr->frame_control);
- int hdr_len = ieee80211_get_hdrlen(fc);
- u8 *ta = hdr->addr2;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ u8 *data;
+ const u8 *tk;
+ struct tkip_ctx *ctx;
u16 iv16;
u32 iv32;
- iv16 = data[hdr_len] << 8;
- iv16 += data[hdr_len + 2];
- iv32 = data[hdr_len + 4] | (data[hdr_len + 5] << 8) |
- (data[hdr_len + 6] << 16) | (data[hdr_len + 7] << 24);
+ data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
+ iv16 = data[2] | (data[0] << 8);
+ iv32 = get_unaligned_le32(&data[4]);
+
+ tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];
+ ctx = &key->u.tkip.tx;
-#ifdef CONFIG_TKIP_DEBUG
+#ifdef CONFIG_MAC80211_TKIP_DEBUG
printk(KERN_DEBUG "TKIP encrypt: iv16 = 0x%04x, iv32 = 0x%08x\n",
iv16, iv32);
- if (iv32 != key->u.tkip.iv32) {
+ if (iv32 != ctx->iv32) {
printk(KERN_DEBUG "skb: iv32 = 0x%08x key: iv32 = 0x%08x\n",
- iv32, key->u.tkip.iv32);
+ iv32, ctx->iv32);
printk(KERN_DEBUG "Wrap around of iv16 in the middle of a "
"fragmented packet\n");
}
-#endif /* CONFIG_TKIP_DEBUG */
+#endif
/* 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 || !key->u.tkip.tx_initialized) {
- /* IV16 wrapped around - perform TKIP phase 1 */
- tkip_mixing_phase1(ta, &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY],
- iv32, key->u.tkip.p1k);
- key->u.tkip.tx_initialized = 1;
- }
+ if (iv16 == 0 || !ctx->initialized)
+ tkip_mixing_phase1(tk, ctx, hdr->addr2, iv32);
if (type == IEEE80211_TKIP_P1_KEY) {
- memcpy(outkey, key->u.tkip.p1k, sizeof(u16) * 5);
+ memcpy(outkey, ctx->p1k, sizeof(u16) * 5);
return;
}
- tkip_mixing_phase2(key->u.tkip.p1k,
- &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY], iv16, outkey);
+ tkip_mixing_phase2(tk, ctx, iv16, outkey);
}
EXPORT_SYMBOL(ieee80211_get_tkip_key);
@@ -275,13 +204,19 @@ void ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm,
u8 *pos, size_t payload_len, u8 *ta)
{
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->initialized)
+ tkip_mixing_phase1(tk, ctx, ta, ctx->iv32);
+
+ tkip_mixing_phase2(tk, ctx, ctx->iv16, rc4key);
- ieee80211_tkip_gen_rc4key(key, ta, rc4key);
- pos = ieee80211_tkip_add_iv(pos, key, rc4key[0], rc4key[1], rc4key[2]);
+ pos = ieee80211_tkip_add_iv(pos, key, key->u.tkip.tx.iv16);
ieee80211_wep_encrypt_data(tfm, rc4key, 16, pos, 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
@@ -296,15 +231,16 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
u32 iv16;
u8 rc4key[16], keyid, *pos = payload;
int res;
+ const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];
if (payload_len < 12)
return -1;
iv16 = (pos[0] << 8) | pos[2];
keyid = pos[3];
- iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
+ iv32 = get_unaligned_le32(pos + 4);
pos += 8;
-#ifdef CONFIG_TKIP_DEBUG
+#ifdef CONFIG_MAC80211_TKIP_DEBUG
{
int i;
printk(KERN_DEBUG "TKIP decrypt: data(len=%zd)", payload_len);
@@ -314,7 +250,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
printk(KERN_DEBUG "TKIP decrypt: iv16=%04x iv32=%08x\n",
iv16, iv32);
}
-#endif /* CONFIG_TKIP_DEBUG */
+#endif
if (!(keyid & (1 << 5)))
return TKIP_DECRYPT_NO_EXT_IV;
@@ -322,69 +258,65 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
if ((keyid >> 6) != key->conf.keyidx)
return TKIP_DECRYPT_INVALID_KEYIDX;
- if (key->u.tkip.rx_initialized[queue] &&
- (iv32 < key->u.tkip.iv32_rx[queue] ||
- (iv32 == key->u.tkip.iv32_rx[queue] &&
- iv16 <= key->u.tkip.iv16_rx[queue]))) {
-#ifdef CONFIG_TKIP_DEBUG
+ if (key->u.tkip.rx[queue].initialized &&
+ (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
DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG "TKIP replay detected for RX frame from "
"%s (RX IV (%04x,%02x) <= prev. IV (%04x,%02x)\n",
print_mac(mac, ta),
- iv32, iv16, key->u.tkip.iv32_rx[queue],
- key->u.tkip.iv16_rx[queue]);
-#endif /* CONFIG_TKIP_DEBUG */
+ iv32, iv16, key->u.tkip.rx[queue].iv32,
+ key->u.tkip.rx[queue].iv16);
+#endif
return TKIP_DECRYPT_REPLAY;
}
if (only_iv) {
res = TKIP_DECRYPT_OK;
- key->u.tkip.rx_initialized[queue] = 1;
+ key->u.tkip.rx[queue].initialized = 1;
goto done;
}
- if (!key->u.tkip.rx_initialized[queue] ||
- key->u.tkip.iv32_rx[queue] != iv32) {
- key->u.tkip.rx_initialized[queue] = 1;
+ if (!key->u.tkip.rx[queue].initialized ||
+ key->u.tkip.rx[queue].iv32 != iv32) {
/* IV16 wrapped around - perform TKIP phase 1 */
- tkip_mixing_phase1(ta, &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY],
- iv32, key->u.tkip.p1k_rx[queue]);
-#ifdef CONFIG_TKIP_DEBUG
+ 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;
DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=%s"
" TK=", print_mac(mac, ta));
for (i = 0; i < 16; i++)
printk("%02x ",
- key->conf.key[
- ALG_TKIP_TEMP_ENCR_KEY + i]);
+ 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.p1k_rx[queue][i]);
+ printk("%04x ", key->u.tkip.rx[queue].p1k[i]);
printk("\n");
}
-#endif /* CONFIG_TKIP_DEBUG */
+#endif
if (key->local->ops->update_tkip_key &&
key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
u8 bcast[ETH_ALEN] =
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
- u8 *sta_addr = key->sta->addr;
+ u8 *sta_addr = key->sta->sta.addr;
if (is_multicast_ether_addr(ra))
sta_addr = bcast;
key->local->ops->update_tkip_key(
local_to_hw(key->local), &key->conf,
- sta_addr, iv32, key->u.tkip.p1k_rx[queue]);
+ sta_addr, iv32, key->u.tkip.rx[queue].p1k);
}
}
- tkip_mixing_phase2(key->u.tkip.p1k_rx[queue],
- &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY],
- iv16, rc4key);
-#ifdef CONFIG_TKIP_DEBUG
+ 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=");
@@ -392,7 +324,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
printk("%02x ", rc4key[i]);
printk("\n");
}
-#endif /* CONFIG_TKIP_DEBUG */
+#endif
res = ieee80211_wep_decrypt_data(tfm, rc4key, 16, pos, payload_len - 12);
done:
@@ -409,5 +341,3 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
return res;
}
-
-
diff --git a/net/mac80211/tkip.h b/net/mac80211/tkip.h
index b7c2ee763d9..d4714383f5f 100644
--- a/net/mac80211/tkip.h
+++ b/net/mac80211/tkip.h
@@ -13,12 +13,8 @@
#include <linux/crypto.h>
#include "key.h"
-u8 * ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key,
- u8 iv0, u8 iv1, u8 iv2);
-void ieee80211_tkip_gen_phase1key(struct ieee80211_key *key, u8 *ta,
- u16 *phase1key);
-void ieee80211_tkip_gen_rc4key(struct ieee80211_key *key, u8 *ta,
- u8 *rc4key);
+u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, u16 iv16);
+
void ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm,
struct ieee80211_key *key,
u8 *pos, size_t payload_len, u8 *ta);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index c80d5899f27..1460537faf3 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -38,64 +38,17 @@
/* misc utils */
-static inline void ieee80211_include_sequence(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_hdr *hdr)
-{
- /* Set the sequence number for this frame. */
- hdr->seq_ctrl = cpu_to_le16(sdata->sequence);
-
- /* Increase the sequence number. */
- sdata->sequence = (sdata->sequence + 0x10) & IEEE80211_SCTL_SEQ;
-}
-
-#ifdef CONFIG_MAC80211_LOWTX_FRAME_DUMP
-static void ieee80211_dump_frame(const char *ifname, const char *title,
- const struct sk_buff *skb)
-{
- const struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u16 fc;
- int hdrlen;
- DECLARE_MAC_BUF(mac);
-
- printk(KERN_DEBUG "%s: %s (len=%d)", ifname, title, skb->len);
- if (skb->len < 4) {
- printk("\n");
- return;
- }
-
- fc = le16_to_cpu(hdr->frame_control);
- hdrlen = ieee80211_get_hdrlen(fc);
- if (hdrlen > skb->len)
- hdrlen = skb->len;
- if (hdrlen >= 4)
- printk(" FC=0x%04x DUR=0x%04x",
- fc, le16_to_cpu(hdr->duration_id));
- if (hdrlen >= 10)
- printk(" A1=%s", print_mac(mac, hdr->addr1));
- if (hdrlen >= 16)
- printk(" A2=%s", print_mac(mac, hdr->addr2));
- if (hdrlen >= 24)
- printk(" A3=%s", print_mac(mac, hdr->addr3));
- if (hdrlen >= 30)
- printk(" A4=%s", print_mac(mac, hdr->addr4));
- printk("\n");
-}
-#else /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
-static inline void ieee80211_dump_frame(const char *ifname, const char *title,
- struct sk_buff *skb)
-{
-}
-#endif /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
-
-static u16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
- int next_frag_len)
+static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
+ int next_frag_len)
{
int rate, mrate, erp, dur, i;
- struct ieee80211_rate *txrate = tx->rate;
+ struct ieee80211_rate *txrate;
struct ieee80211_local *local = tx->local;
struct ieee80211_supported_band *sband;
+ struct ieee80211_hdr *hdr;
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+ sband = local->hw.wiphy->bands[tx->channel->band];
+ txrate = &sband->bitrates[tx->rate_idx];
erp = 0;
if (tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
@@ -118,10 +71,10 @@ static u16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
* at the highest possible rate belonging to the PHY rates in the
* BSSBasicRateSet
*/
-
- if ((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) {
+ hdr = (struct ieee80211_hdr *)tx->skb->data;
+ if (ieee80211_is_ctl(hdr->frame_control)) {
/* TODO: These control frames are not currently sent by
- * 80211.o, but should they be implemented, this function
+ * mac80211, but should they be implemented, this function
* needs to be updated to support duration field calculation.
*
* RTS: time needed to transmit pending data/mgmt frame plus
@@ -139,7 +92,7 @@ static u16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
/* data/mgmt */
if (0 /* FIX: data/mgmt during CFP */)
- return 32768;
+ return cpu_to_le16(32768);
if (group_addr) /* Group address as the destination - no ACK */
return 0;
@@ -163,7 +116,7 @@ static u16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
if (r->bitrate > txrate->bitrate)
break;
- if (tx->sdata->basic_rates & BIT(i))
+ if (tx->sdata->bss_conf.basic_rates & BIT(i))
rate = r->bitrate;
switch (sband->band) {
@@ -209,59 +162,44 @@ static u16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
tx->sdata->bss_conf.use_short_preamble);
}
- return dur;
+ return cpu_to_le16(dur);
}
-static inline int __ieee80211_queue_stopped(const struct ieee80211_local *local,
- int queue)
+static int inline is_ieee80211_device(struct ieee80211_local *local,
+ struct net_device *dev)
{
- return test_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
-}
-
-static inline int __ieee80211_queue_pending(const struct ieee80211_local *local,
- int queue)
-{
- return test_bit(IEEE80211_LINK_STATE_PENDING, &local->state[queue]);
-}
-
-static int inline is_ieee80211_device(struct net_device *dev,
- struct net_device *master)
-{
- return (wdev_priv(dev->ieee80211_ptr) ==
- wdev_priv(master->ieee80211_ptr));
+ return local == wdev_priv(dev->ieee80211_ptr);
}
/* tx handlers */
-static ieee80211_tx_result
+static ieee80211_tx_result debug_noinline
ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
{
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- struct sk_buff *skb = tx->skb;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
u32 sta_flags;
- if (unlikely(tx->flags & IEEE80211_TX_INJECTED))
+ if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED))
return TX_CONTINUE;
- if (unlikely(tx->local->sta_sw_scanning) &&
- ((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
- (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
+ if (unlikely(tx->local->sw_scanning) &&
+ !ieee80211_is_probe_req(hdr->frame_control))
return TX_DROP;
- if (tx->sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT)
+ if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
return TX_CONTINUE;
if (tx->flags & IEEE80211_TX_PS_BUFFERED)
return TX_CONTINUE;
- sta_flags = tx->sta ? tx->sta->flags : 0;
+ sta_flags = tx->sta ? get_sta_flags(tx->sta) : 0;
if (likely(tx->flags & IEEE80211_TX_UNICAST)) {
if (unlikely(!(sta_flags & WLAN_STA_ASSOC) &&
- tx->sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
- (tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) {
+ tx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ ieee80211_is_data(hdr->frame_control))) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG "%s: dropped data frame to not "
@@ -272,9 +210,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
return TX_DROP;
}
} else {
- if (unlikely((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
+ if (unlikely(ieee80211_is_data(hdr->frame_control) &&
tx->local->num_sta == 0 &&
- tx->sdata->vif.type != IEEE80211_IF_TYPE_IBSS)) {
+ tx->sdata->vif.type != NL80211_IFTYPE_ADHOC)) {
/*
* No associated STAs - no need to send multicast
* frames.
@@ -287,17 +225,6 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
return TX_CONTINUE;
}
-static ieee80211_tx_result
-ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
-
- if (ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)) >= 24)
- ieee80211_include_sequence(tx->sdata, hdr);
-
- return TX_CONTINUE;
-}
-
/* This function is called whenever the AP is about to exceed the maximum limit
* of buffered frames for power saving STAs. This situation should not really
* happen often during normal operation, so dropping the oldest buffered packet
@@ -316,8 +243,7 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
struct ieee80211_if_ap *ap;
- if (sdata->dev == local->mdev ||
- sdata->vif.type != IEEE80211_IF_TYPE_AP)
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
continue;
ap = &sdata->u.ap;
skb = skb_dequeue(&ap->ps_bc_buf);
@@ -340,13 +266,18 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
rcu_read_unlock();
local->total_ps_buffered = total;
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n",
wiphy_name(local->hw.wiphy), purged);
+#endif
}
static ieee80211_tx_result
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;
+
/*
* broadcast/multicast frame
*
@@ -355,8 +286,12 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
* This is done either by the hardware or us.
*/
- /* not AP/IBSS or ordered frame */
- if (!tx->sdata->bss || (tx->fc & IEEE80211_FCTL_ORDER))
+ /* powersaving STAs only in AP/VLAN mode */
+ if (!tx->sdata->bss)
+ return TX_CONTINUE;
+
+ /* no buffering for ordered frames */
+ if (ieee80211_has_order(hdr->frame_control))
return TX_CONTINUE;
/* no stations in PS mode */
@@ -369,11 +304,13 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
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->dev->name);
}
+#endif
dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf));
} else
tx->local->total_ps_buffered++;
@@ -382,7 +319,7 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
}
/* buffered in hardware */
- tx->control->flags |= IEEE80211_TXCTL_SEND_AFTER_DTIM;
+ info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM;
return TX_CONTINUE;
}
@@ -391,31 +328,35 @@ static ieee80211_tx_result
ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
{
struct sta_info *sta = tx->sta;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
+ u32 staflags;
DECLARE_MAC_BUF(mac);
- if (unlikely(!sta ||
- ((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT &&
- (tx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP)))
+ if (unlikely(!sta || ieee80211_is_probe_resp(hdr->frame_control)))
return TX_CONTINUE;
- if (unlikely((sta->flags & WLAN_STA_PS) &&
- !(sta->flags & WLAN_STA_PSPOLL))) {
- struct ieee80211_tx_packet_data *pkt_data;
+ staflags = get_sta_flags(sta);
+
+ if (unlikely((staflags & WLAN_STA_PS) &&
+ !(staflags & WLAN_STA_PSPOLL))) {
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "STA %s aid %d: PS buffer (entries "
"before %d)\n",
- print_mac(mac, sta->addr), sta->aid,
+ print_mac(mac, sta->sta.addr), sta->sta.aid,
skb_queue_len(&sta->ps_tx_buf));
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
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 %s TX "
"buffer full - dropping oldest frame\n",
- tx->dev->name, print_mac(mac, sta->addr));
+ tx->dev->name, print_mac(mac, sta->sta.addr));
}
+#endif
dev_kfree_skb(old);
} else
tx->local->total_ps_buffered++;
@@ -424,24 +365,23 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
if (skb_queue_empty(&sta->ps_tx_buf))
sta_info_set_tim_bit(sta);
- pkt_data = (struct ieee80211_tx_packet_data *)tx->skb->cb;
- pkt_data->jiffies = jiffies;
+ info->control.jiffies = jiffies;
skb_queue_tail(&sta->ps_tx_buf, tx->skb);
return TX_QUEUED;
}
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- else if (unlikely(sta->flags & WLAN_STA_PS)) {
+ else if (unlikely(test_sta_flags(sta, WLAN_STA_PS))) {
printk(KERN_DEBUG "%s: STA %s in PS mode, but pspoll "
"set -> send frame\n", tx->dev->name,
- print_mac(mac, sta->addr));
+ print_mac(mac, sta->sta.addr));
}
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
- sta->flags &= ~WLAN_STA_PSPOLL;
+ clear_sta_flags(sta, WLAN_STA_PSPOLL);
return TX_CONTINUE;
}
-static ieee80211_tx_result
+static ieee80211_tx_result debug_noinline
ieee80211_tx_h_ps_buf(struct ieee80211_tx_data *tx)
{
if (unlikely(tx->flags & IEEE80211_TX_PS_BUFFERED))
@@ -453,58 +393,246 @@ ieee80211_tx_h_ps_buf(struct ieee80211_tx_data *tx)
return ieee80211_tx_h_multicast_ps_buf(tx);
}
-static ieee80211_tx_result
+static ieee80211_tx_result debug_noinline
ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
{
struct ieee80211_key *key;
- u16 fc = tx->fc;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
- if (unlikely(tx->control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT))
+ if (unlikely(tx->skb->do_not_encrypt))
tx->key = NULL;
else if (tx->sta && (key = rcu_dereference(tx->sta->key)))
tx->key = key;
else if ((key = rcu_dereference(tx->sdata->default_key)))
tx->key = key;
else if (tx->sdata->drop_unencrypted &&
- !(tx->control->flags & IEEE80211_TXCTL_EAPOL_FRAME) &&
- !(tx->flags & IEEE80211_TX_INJECTED)) {
+ (tx->skb->protocol != cpu_to_be16(ETH_P_PAE)) &&
+ !(info->flags & IEEE80211_TX_CTL_INJECTED)) {
I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
return TX_DROP;
} else
tx->key = NULL;
if (tx->key) {
- u16 ftype, stype;
-
tx->key->tx_rx_count++;
/* TODO: add threshold stuff again */
switch (tx->key->conf.alg) {
case ALG_WEP:
- ftype = fc & IEEE80211_FCTL_FTYPE;
- stype = fc & IEEE80211_FCTL_STYPE;
-
- if (ftype == IEEE80211_FTYPE_MGMT &&
- stype == IEEE80211_STYPE_AUTH)
+ if (ieee80211_is_auth(hdr->frame_control))
break;
case ALG_TKIP:
case ALG_CCMP:
- if (!WLAN_FC_DATA_PRESENT(fc))
+ if (!ieee80211_is_data_present(hdr->frame_control))
tx->key = NULL;
break;
}
}
if (!tx->key || !(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
- tx->control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
+ tx->skb->do_not_encrypt = 1;
return TX_CONTINUE;
}
-static ieee80211_tx_result
+static ieee80211_tx_result debug_noinline
+ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
+{
+ struct rate_selection rsel;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+
+ sband = tx->local->hw.wiphy->bands[tx->channel->band];
+
+ if (likely(tx->rate_idx < 0)) {
+ rate_control_get_rate(tx->sdata, sband, tx->sta,
+ tx->skb, &rsel);
+ if (tx->sta)
+ tx->sta->last_txrate_idx = rsel.rate_idx;
+ tx->rate_idx = rsel.rate_idx;
+ if (unlikely(rsel.probe_idx >= 0)) {
+ info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
+ tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
+ info->control.retries[0].rate_idx = tx->rate_idx;
+ info->control.retries[0].limit = tx->local->hw.max_altrate_tries;
+ tx->rate_idx = rsel.probe_idx;
+ } else if (info->control.retries[0].limit == 0)
+ info->control.retries[0].rate_idx = -1;
+
+ if (unlikely(tx->rate_idx < 0))
+ return TX_DROP;
+ } else
+ info->control.retries[0].rate_idx = -1;
+
+ if (tx->sdata->bss_conf.use_cts_prot &&
+ (tx->flags & IEEE80211_TX_FRAGMENTED) && (rsel.nonerp_idx >= 0)) {
+ tx->last_frag_rate_idx = tx->rate_idx;
+ if (rsel.probe_idx >= 0)
+ tx->flags &= ~IEEE80211_TX_PROBE_LAST_FRAG;
+ else
+ tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
+ tx->rate_idx = rsel.nonerp_idx;
+ info->tx_rate_idx = rsel.nonerp_idx;
+ info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
+ } else {
+ tx->last_frag_rate_idx = tx->rate_idx;
+ info->tx_rate_idx = tx->rate_idx;
+ }
+ info->tx_rate_idx = tx->rate_idx;
+
+ return TX_CONTINUE;
+}
+
+static ieee80211_tx_result debug_noinline
+ieee80211_tx_h_misc(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);
+ struct ieee80211_supported_band *sband;
+
+ sband = tx->local->hw.wiphy->bands[tx->channel->band];
+
+ if (tx->sta)
+ info->control.sta = &tx->sta->sta;
+
+ if (!info->control.retry_limit) {
+ if (!is_multicast_ether_addr(hdr->addr1)) {
+ int len = min_t(int, tx->skb->len + FCS_LEN,
+ tx->local->fragmentation_threshold);
+ if (len > tx->local->rts_threshold
+ && tx->local->rts_threshold <
+ IEEE80211_MAX_RTS_THRESHOLD) {
+ info->flags |= IEEE80211_TX_CTL_USE_RTS_CTS;
+ info->flags |=
+ IEEE80211_TX_CTL_LONG_RETRY_LIMIT;
+ info->control.retry_limit =
+ tx->local->long_retry_limit;
+ } else {
+ info->control.retry_limit =
+ tx->local->short_retry_limit;
+ }
+ } else {
+ info->control.retry_limit = 1;
+ }
+ }
+
+ if (tx->flags & IEEE80211_TX_FRAGMENTED) {
+ /* Do not use multiple retry rates when sending fragmented
+ * frames.
+ * TODO: The last fragment could still use multiple retry
+ * rates. */
+ info->control.retries[0].rate_idx = -1;
+ }
+
+ /* Use CTS protection for unicast frames sent using extended rates if
+ * there are associated non-ERP stations and RTS/CTS is not configured
+ * for the frame. */
+ if ((tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) &&
+ (sband->bitrates[tx->rate_idx].flags & IEEE80211_RATE_ERP_G) &&
+ (tx->flags & IEEE80211_TX_UNICAST) &&
+ tx->sdata->bss_conf.use_cts_prot &&
+ !(info->flags & IEEE80211_TX_CTL_USE_RTS_CTS))
+ info->flags |= IEEE80211_TX_CTL_USE_CTS_PROTECT;
+
+ /* Transmit data frames using short preambles if the driver supports
+ * short preambles at the selected rate and short preambles are
+ * available on the network at the current point in time. */
+ if (ieee80211_is_data(hdr->frame_control) &&
+ (sband->bitrates[tx->rate_idx].flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
+ tx->sdata->bss_conf.use_short_preamble &&
+ (!tx->sta || test_sta_flags(tx->sta, WLAN_STA_SHORT_PREAMBLE))) {
+ info->flags |= IEEE80211_TX_CTL_SHORT_PREAMBLE;
+ }
+
+ if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) ||
+ (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)) {
+ struct ieee80211_rate *rate;
+ s8 baserate = -1;
+ int idx;
+
+ /* Do not use multiple retry rates when using RTS/CTS */
+ info->control.retries[0].rate_idx = -1;
+
+ /* Use min(data rate, max base rate) as CTS/RTS rate */
+ rate = &sband->bitrates[tx->rate_idx];
+
+ for (idx = 0; idx < sband->n_bitrates; idx++) {
+ if (sband->bitrates[idx].bitrate > rate->bitrate)
+ continue;
+ if (tx->sdata->bss_conf.basic_rates & BIT(idx) &&
+ (baserate < 0 ||
+ (sband->bitrates[baserate].bitrate
+ < sband->bitrates[idx].bitrate)))
+ baserate = idx;
+ }
+
+ if (baserate >= 0)
+ info->control.rts_cts_rate_idx = baserate;
+ else
+ info->control.rts_cts_rate_idx = 0;
+ }
+
+ if (tx->sta)
+ info->control.sta = &tx->sta->sta;
+
+ return TX_CONTINUE;
+}
+
+static ieee80211_tx_result debug_noinline
+ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
+ u16 *seq;
+ u8 *qc;
+ int tid;
+
+ /*
+ * Packet injection may want to control the sequence
+ * number, if we have no matching interface then we
+ * neither assign one ourselves nor ask the driver to.
+ */
+ if (unlikely(!info->control.vif))
+ return TX_CONTINUE;
+
+ if (unlikely(ieee80211_is_ctl(hdr->frame_control)))
+ return TX_CONTINUE;
+
+ if (ieee80211_hdrlen(hdr->frame_control) < 24)
+ return TX_CONTINUE;
+
+ if (!ieee80211_is_data_qos(hdr->frame_control)) {
+ info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
+ return TX_CONTINUE;
+ }
+
+ /*
+ * This should be true for injected/management frames only, for
+ * management frames we have set the IEEE80211_TX_CTL_ASSIGN_SEQ
+ * above since they are not QoS-data frames.
+ */
+ if (!tx->sta)
+ return TX_CONTINUE;
+
+ /* include per-STA, per-TID sequence counter */
+
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
+ seq = &tx->sta->tid_seq[tid];
+
+ hdr->seq_ctrl = cpu_to_le16(*seq);
+
+ /* Increase the sequence number. */
+ *seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ;
+
+ return TX_CONTINUE;
+}
+
+static ieee80211_tx_result debug_noinline
ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
size_t hdrlen, per_fragm, num_fragm, payload_len, left;
struct sk_buff **frags, *first, *frag;
int i;
@@ -515,9 +643,19 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
if (!(tx->flags & IEEE80211_TX_FRAGMENTED))
return TX_CONTINUE;
+ /*
+ * Warn when submitting a fragmented A-MPDU frame and drop it.
+ * This scenario is handled in __ieee80211_tx_prepare but extra
+ * caution taken here as fragmented ampdu may cause Tx stop.
+ */
+ if (WARN_ON(tx->flags & IEEE80211_TX_CTL_AMPDU ||
+ skb_get_queue_mapping(tx->skb) >=
+ ieee80211_num_regular_queues(&tx->local->hw)))
+ return TX_DROP;
+
first = tx->skb;
- hdrlen = ieee80211_get_hdrlen(tx->fc);
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
payload_len = first->len - hdrlen;
per_fragm = frag_threshold - hdrlen - FCS_LEN;
num_fragm = DIV_ROUND_UP(payload_len, per_fragm);
@@ -558,6 +696,9 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG));
copylen = left > per_fragm ? per_fragm : left;
memcpy(skb_put(frag, copylen), pos, copylen);
+ memcpy(frag->cb, first->cb, sizeof(frag->cb));
+ skb_copy_queue_mapping(frag, first);
+ frag->do_not_encrypt = first->do_not_encrypt;
pos += copylen;
left -= copylen;
@@ -570,7 +711,6 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
return TX_CONTINUE;
fail:
- printk(KERN_DEBUG "%s: failed to fragment frame\n", tx->dev->name);
if (frags) {
for (i = 0; i < num_fragm - 1; i++)
if (frags[i])
@@ -581,7 +721,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
return TX_DROP;
}
-static ieee80211_tx_result
+static ieee80211_tx_result debug_noinline
ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
{
if (!tx->key)
@@ -601,236 +741,57 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
return TX_DROP;
}
-static ieee80211_tx_result
-ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
+static ieee80211_tx_result debug_noinline
+ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx)
{
- struct rate_selection rsel;
- struct ieee80211_supported_band *sband;
-
- sband = tx->local->hw.wiphy->bands[tx->local->hw.conf.channel->band];
-
- if (likely(!tx->rate)) {
- rate_control_get_rate(tx->dev, sband, tx->skb, &rsel);
- tx->rate = rsel.rate;
- if (unlikely(rsel.probe)) {
- tx->control->flags |=
- IEEE80211_TXCTL_RATE_CTRL_PROBE;
- tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
- tx->control->alt_retry_rate = tx->rate;
- tx->rate = rsel.probe;
- } else
- tx->control->alt_retry_rate = NULL;
-
- if (!tx->rate)
- return TX_DROP;
- } else
- tx->control->alt_retry_rate = NULL;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
+ int next_len, i;
+ int group_addr = is_multicast_ether_addr(hdr->addr1);
- if (tx->sdata->bss_conf.use_cts_prot &&
- (tx->flags & IEEE80211_TX_FRAGMENTED) && rsel.nonerp) {
- tx->last_frag_rate = tx->rate;
- if (rsel.probe)
- tx->flags &= ~IEEE80211_TX_PROBE_LAST_FRAG;
- else
- tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
- tx->rate = rsel.nonerp;
- tx->control->tx_rate = rsel.nonerp;
- tx->control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
- } else {
- tx->last_frag_rate = tx->rate;
- tx->control->tx_rate = tx->rate;
+ if (!(tx->flags & IEEE80211_TX_FRAGMENTED)) {
+ hdr->duration_id = ieee80211_duration(tx, group_addr, 0);
+ return TX_CONTINUE;
}
- tx->control->tx_rate = tx->rate;
-
- return TX_CONTINUE;
-}
-static ieee80211_tx_result
-ieee80211_tx_h_misc(struct ieee80211_tx_data *tx)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
- u16 fc = le16_to_cpu(hdr->frame_control);
- u16 dur;
- struct ieee80211_tx_control *control = tx->control;
+ hdr->duration_id = ieee80211_duration(tx, group_addr,
+ tx->extra_frag[0]->len);
- if (!control->retry_limit) {
- if (!is_multicast_ether_addr(hdr->addr1)) {
- if (tx->skb->len + FCS_LEN > tx->local->rts_threshold
- && tx->local->rts_threshold <
- IEEE80211_MAX_RTS_THRESHOLD) {
- control->flags |=
- IEEE80211_TXCTL_USE_RTS_CTS;
- control->flags |=
- IEEE80211_TXCTL_LONG_RETRY_LIMIT;
- control->retry_limit =
- tx->local->long_retry_limit;
- } else {
- control->retry_limit =
- tx->local->short_retry_limit;
- }
+ for (i = 0; i < tx->num_extra_frag; i++) {
+ if (i + 1 < tx->num_extra_frag) {
+ next_len = tx->extra_frag[i + 1]->len;
} else {
- control->retry_limit = 1;
- }
- }
-
- if (tx->flags & IEEE80211_TX_FRAGMENTED) {
- /* Do not use multiple retry rates when sending fragmented
- * frames.
- * TODO: The last fragment could still use multiple retry
- * rates. */
- control->alt_retry_rate = NULL;
- }
-
- /* Use CTS protection for unicast frames sent using extended rates if
- * there are associated non-ERP stations and RTS/CTS is not configured
- * for the frame. */
- if ((tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) &&
- (tx->rate->flags & IEEE80211_RATE_ERP_G) &&
- (tx->flags & IEEE80211_TX_UNICAST) &&
- tx->sdata->bss_conf.use_cts_prot &&
- !(control->flags & IEEE80211_TXCTL_USE_RTS_CTS))
- control->flags |= IEEE80211_TXCTL_USE_CTS_PROTECT;
-
- /* Transmit data frames using short preambles if the driver supports
- * short preambles at the selected rate and short preambles are
- * available on the network at the current point in time. */
- if (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
- (tx->rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
- tx->sdata->bss_conf.use_short_preamble &&
- (!tx->sta || (tx->sta->flags & WLAN_STA_SHORT_PREAMBLE))) {
- tx->control->flags |= IEEE80211_TXCTL_SHORT_PREAMBLE;
- }
-
- /* Setup duration field for the first fragment of the frame. Duration
- * for remaining fragments will be updated when they are being sent
- * to low-level driver in ieee80211_tx(). */
- dur = ieee80211_duration(tx, is_multicast_ether_addr(hdr->addr1),
- (tx->flags & IEEE80211_TX_FRAGMENTED) ?
- tx->extra_frag[0]->len : 0);
- hdr->duration_id = cpu_to_le16(dur);
-
- if ((control->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
- (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)) {
- struct ieee80211_supported_band *sband;
- struct ieee80211_rate *rate, *baserate;
- int idx;
-
- sband = tx->local->hw.wiphy->bands[
- tx->local->hw.conf.channel->band];
-
- /* Do not use multiple retry rates when using RTS/CTS */
- control->alt_retry_rate = NULL;
-
- /* Use min(data rate, max base rate) as CTS/RTS rate */
- rate = tx->rate;
- baserate = NULL;
-
- for (idx = 0; idx < sband->n_bitrates; idx++) {
- if (sband->bitrates[idx].bitrate > rate->bitrate)
- continue;
- if (tx->sdata->basic_rates & BIT(idx) &&
- (!baserate ||
- (baserate->bitrate < sband->bitrates[idx].bitrate)))
- baserate = &sband->bitrates[idx];
+ next_len = 0;
+ tx->rate_idx = tx->last_frag_rate_idx;
}
- if (baserate)
- control->rts_cts_rate = baserate;
- else
- control->rts_cts_rate = &sband->bitrates[0];
- }
-
- if (tx->sta) {
- control->aid = tx->sta->aid;
- tx->sta->tx_packets++;
- tx->sta->tx_fragments++;
- tx->sta->tx_bytes += tx->skb->len;
- if (tx->extra_frag) {
- int i;
- tx->sta->tx_fragments += tx->num_extra_frag;
- for (i = 0; i < tx->num_extra_frag; i++) {
- tx->sta->tx_bytes +=
- tx->extra_frag[i]->len;
- }
- }
+ hdr = (struct ieee80211_hdr *)tx->extra_frag[i]->data;
+ hdr->duration_id = ieee80211_duration(tx, 0, next_len);
}
return TX_CONTINUE;
}
-static ieee80211_tx_result
-ieee80211_tx_h_load_stats(struct ieee80211_tx_data *tx)
+static ieee80211_tx_result debug_noinline
+ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
{
- struct ieee80211_local *local = tx->local;
- struct sk_buff *skb = tx->skb;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u32 load = 0, hdrtime;
- struct ieee80211_rate *rate = tx->rate;
-
- /* TODO: this could be part of tx_status handling, so that the number
- * of retries would be known; TX rate should in that case be stored
- * somewhere with the packet */
-
- /* Estimate total channel use caused by this frame */
-
- /* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
- * 1 usec = 1/8 * (1080 / 10) = 13.5 */
-
- if (tx->channel->band == IEEE80211_BAND_5GHZ ||
- (tx->channel->band == IEEE80211_BAND_2GHZ &&
- rate->flags & IEEE80211_RATE_ERP_G))
- hdrtime = CHAN_UTIL_HDR_SHORT;
- else
- hdrtime = CHAN_UTIL_HDR_LONG;
-
- load = hdrtime;
- if (!is_multicast_ether_addr(hdr->addr1))
- load += hdrtime;
-
- if (tx->control->flags & IEEE80211_TXCTL_USE_RTS_CTS)
- load += 2 * hdrtime;
- else if (tx->control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
- load += hdrtime;
+ int i;
- /* TODO: optimise again */
- load += skb->len * CHAN_UTIL_RATE_LCM / rate->bitrate;
+ if (!tx->sta)
+ return TX_CONTINUE;
+ tx->sta->tx_packets++;
+ tx->sta->tx_fragments++;
+ tx->sta->tx_bytes += tx->skb->len;
if (tx->extra_frag) {
- int i;
- for (i = 0; i < tx->num_extra_frag; i++) {
- load += 2 * hdrtime;
- load += tx->extra_frag[i]->len *
- tx->rate->bitrate;
- }
+ tx->sta->tx_fragments += tx->num_extra_frag;
+ for (i = 0; i < tx->num_extra_frag; i++)
+ tx->sta->tx_bytes += tx->extra_frag[i]->len;
}
- /* Divide channel_use by 8 to avoid wrapping around the counter */
- load >>= CHAN_UTIL_SHIFT;
- local->channel_use_raw += load;
- if (tx->sta)
- tx->sta->channel_use_raw += load;
- tx->sdata->channel_use_raw += load;
-
return TX_CONTINUE;
}
-typedef ieee80211_tx_result (*ieee80211_tx_handler)(struct ieee80211_tx_data *);
-static ieee80211_tx_handler ieee80211_tx_handlers[] =
-{
- ieee80211_tx_h_check_assoc,
- ieee80211_tx_h_sequence,
- ieee80211_tx_h_ps_buf,
- ieee80211_tx_h_select_key,
- ieee80211_tx_h_michael_mic_add,
- ieee80211_tx_h_fragment,
- ieee80211_tx_h_encrypt,
- ieee80211_tx_h_rate_ctrl,
- ieee80211_tx_h_misc,
- ieee80211_tx_h_load_stats,
- NULL
-};
-
/* actual transmit path */
/*
@@ -854,12 +815,11 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
(struct ieee80211_radiotap_header *) skb->data;
struct ieee80211_supported_band *sband;
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
- struct ieee80211_tx_control *control = tx->control;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- sband = tx->local->hw.wiphy->bands[tx->local->hw.conf.channel->band];
+ sband = tx->local->hw.wiphy->bands[tx->channel->band];
- control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
- tx->flags |= IEEE80211_TX_INJECTED;
+ skb->do_not_encrypt = 1;
tx->flags &= ~IEEE80211_TX_FRAGMENTED;
/*
@@ -896,7 +856,7 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
r = &sband->bitrates[i];
if (r->bitrate == target_rate) {
- tx->rate = r;
+ tx->rate_idx = i;
break;
}
}
@@ -907,7 +867,7 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
* radiotap uses 0 for 1st ant, mac80211 is 1 for
* 1st ant
*/
- control->antenna_sel_tx = (*iterator.this_arg) + 1;
+ info->antenna_sel_tx = (*iterator.this_arg) + 1;
break;
#if 0
@@ -931,8 +891,7 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
skb_trim(skb, skb->len - FCS_LEN);
}
if (*iterator.this_arg & IEEE80211_RADIOTAP_F_WEP)
- control->flags &=
- ~IEEE80211_TXCTL_DO_NOT_ENCRYPT;
+ tx->skb->do_not_encrypt = 0;
if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG)
tx->flags |= IEEE80211_TX_FRAGMENTED;
break;
@@ -967,12 +926,12 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
static ieee80211_tx_result
__ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
struct sk_buff *skb,
- struct net_device *dev,
- struct ieee80211_tx_control *control)
+ struct net_device *dev)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_hdr *hdr;
struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int hdrlen;
@@ -981,7 +940,9 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
tx->dev = dev; /* use original interface */
tx->local = local;
tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- tx->control = control;
+ tx->channel = local->hw.conf.channel;
+ tx->rate_idx = -1;
+ tx->last_frag_rate_idx = -1;
/*
* Set this flag (used below to indicate "automatic fragmentation"),
* it will be cleared/left by radiotap as desired.
@@ -990,7 +951,7 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
/* process and remove the injection radiotap header */
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (unlikely(sdata->vif.type == IEEE80211_IF_TYPE_MNTR)) {
+ if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) {
if (__ieee80211_parse_tx_radiotap(tx, skb) == TX_DROP)
return TX_DROP;
@@ -1004,38 +965,36 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
hdr = (struct ieee80211_hdr *) skb->data;
tx->sta = sta_info_get(local, hdr->addr1);
- tx->fc = le16_to_cpu(hdr->frame_control);
if (is_multicast_ether_addr(hdr->addr1)) {
tx->flags &= ~IEEE80211_TX_UNICAST;
- control->flags |= IEEE80211_TXCTL_NO_ACK;
+ info->flags |= IEEE80211_TX_CTL_NO_ACK;
} else {
tx->flags |= IEEE80211_TX_UNICAST;
- control->flags &= ~IEEE80211_TXCTL_NO_ACK;
+ info->flags &= ~IEEE80211_TX_CTL_NO_ACK;
}
if (tx->flags & IEEE80211_TX_FRAGMENTED) {
if ((tx->flags & IEEE80211_TX_UNICAST) &&
skb->len + FCS_LEN > local->fragmentation_threshold &&
- !local->ops->set_frag_threshold)
+ !local->ops->set_frag_threshold &&
+ !(info->flags & IEEE80211_TX_CTL_AMPDU))
tx->flags |= IEEE80211_TX_FRAGMENTED;
else
tx->flags &= ~IEEE80211_TX_FRAGMENTED;
}
if (!tx->sta)
- control->flags |= IEEE80211_TXCTL_CLEAR_PS_FILT;
- else if (tx->sta->flags & WLAN_STA_CLEAR_PS_FILT) {
- control->flags |= IEEE80211_TXCTL_CLEAR_PS_FILT;
- tx->sta->flags &= ~WLAN_STA_CLEAR_PS_FILT;
- }
+ info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
+ else if (test_and_clear_sta_flags(tx->sta, WLAN_STA_CLEAR_PS_FILT))
+ info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
- hdrlen = ieee80211_get_hdrlen(tx->fc);
+ 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];
}
- control->flags |= IEEE80211_TXCTL_FIRST_FRAGMENT;
+ info->flags |= IEEE80211_TX_CTL_FIRST_FRAGMENT;
return TX_CONTINUE;
}
@@ -1043,24 +1002,21 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
/*
* NB: @tx is uninitialised when passed in here
*/
-static int ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
- struct sk_buff *skb,
- struct net_device *mdev,
- struct ieee80211_tx_control *control)
+static int ieee80211_tx_prepare(struct ieee80211_local *local,
+ struct ieee80211_tx_data *tx,
+ struct sk_buff *skb)
{
- struct ieee80211_tx_packet_data *pkt_data;
struct net_device *dev;
- pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
- dev = dev_get_by_index(&init_net, pkt_data->ifindex);
- if (unlikely(dev && !is_ieee80211_device(dev, mdev))) {
+ dev = dev_get_by_index(&init_net, skb->iif);
+ if (unlikely(dev && !is_ieee80211_device(local, dev))) {
dev_put(dev);
dev = NULL;
}
if (unlikely(!dev))
return -ENODEV;
/* initialises tx with control */
- __ieee80211_tx_prepare(tx, skb, dev, control);
+ __ieee80211_tx_prepare(tx, skb, dev);
dev_put(dev);
return 0;
}
@@ -1068,50 +1024,45 @@ static int ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
struct ieee80211_tx_data *tx)
{
- struct ieee80211_tx_control *control = tx->control;
+ struct ieee80211_tx_info *info;
int ret, i;
- if (!ieee80211_qdisc_installed(local->mdev) &&
- __ieee80211_queue_stopped(local, 0)) {
- netif_stop_queue(local->mdev);
- return IEEE80211_TX_AGAIN;
- }
if (skb) {
- ieee80211_dump_frame(wiphy_name(local->hw.wiphy),
- "TX to low-level driver", skb);
- ret = local->ops->tx(local_to_hw(local), skb, control);
+ if (netif_subqueue_stopped(local->mdev, skb))
+ return IEEE80211_TX_AGAIN;
+ info = IEEE80211_SKB_CB(skb);
+
+ ret = local->ops->tx(local_to_hw(local), skb);
if (ret)
return IEEE80211_TX_AGAIN;
local->mdev->trans_start = jiffies;
ieee80211_led_tx(local, 1);
}
if (tx->extra_frag) {
- control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
- IEEE80211_TXCTL_USE_CTS_PROTECT |
- IEEE80211_TXCTL_CLEAR_PS_FILT |
- IEEE80211_TXCTL_FIRST_FRAGMENT);
for (i = 0; i < tx->num_extra_frag; i++) {
if (!tx->extra_frag[i])
continue;
- if (__ieee80211_queue_stopped(local, control->queue))
+ info = IEEE80211_SKB_CB(tx->extra_frag[i]);
+ info->flags &= ~(IEEE80211_TX_CTL_USE_RTS_CTS |
+ IEEE80211_TX_CTL_USE_CTS_PROTECT |
+ IEEE80211_TX_CTL_CLEAR_PS_FILT |
+ IEEE80211_TX_CTL_FIRST_FRAGMENT);
+ if (netif_subqueue_stopped(local->mdev,
+ tx->extra_frag[i]))
return IEEE80211_TX_FRAG_AGAIN;
if (i == tx->num_extra_frag) {
- control->tx_rate = tx->last_frag_rate;
+ info->tx_rate_idx = tx->last_frag_rate_idx;
if (tx->flags & IEEE80211_TX_PROBE_LAST_FRAG)
- control->flags |=
- IEEE80211_TXCTL_RATE_CTRL_PROBE;
+ info->flags |=
+ IEEE80211_TX_CTL_RATE_CTRL_PROBE;
else
- control->flags &=
- ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
+ info->flags &=
+ ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
}
- ieee80211_dump_frame(wiphy_name(local->hw.wiphy),
- "TX to low-level driver",
- tx->extra_frag[i]);
ret = local->ops->tx(local_to_hw(local),
- tx->extra_frag[i],
- control);
+ tx->extra_frag[i]);
if (ret)
return IEEE80211_TX_FRAG_AGAIN;
local->mdev->trans_start = jiffies;
@@ -1124,17 +1075,65 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
return IEEE80211_TX_OK;
}
-static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
- struct ieee80211_tx_control *control)
+/*
+ * Invoke TX handlers, return 0 on success and non-zero if the
+ * frame was dropped or queued.
+ */
+static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
+{
+ struct sk_buff *skb = tx->skb;
+ ieee80211_tx_result res = TX_DROP;
+ int i;
+
+#define CALL_TXH(txh) \
+ res = txh(tx); \
+ if (res != TX_CONTINUE) \
+ goto txh_done;
+
+ CALL_TXH(ieee80211_tx_h_check_assoc)
+ CALL_TXH(ieee80211_tx_h_ps_buf)
+ CALL_TXH(ieee80211_tx_h_select_key)
+ CALL_TXH(ieee80211_tx_h_michael_mic_add)
+ CALL_TXH(ieee80211_tx_h_rate_ctrl)
+ CALL_TXH(ieee80211_tx_h_misc)
+ CALL_TXH(ieee80211_tx_h_sequence)
+ CALL_TXH(ieee80211_tx_h_fragment)
+ /* handlers after fragment must be aware of tx info fragmentation! */
+ CALL_TXH(ieee80211_tx_h_encrypt)
+ CALL_TXH(ieee80211_tx_h_calculate_duration)
+ CALL_TXH(ieee80211_tx_h_stats)
+#undef CALL_TXH
+
+ txh_done:
+ if (unlikely(res == TX_DROP)) {
+ I802_DEBUG_INC(tx->local->tx_handlers_drop);
+ dev_kfree_skb(skb);
+ for (i = 0; i < tx->num_extra_frag; i++)
+ if (tx->extra_frag[i])
+ dev_kfree_skb(tx->extra_frag[i]);
+ kfree(tx->extra_frag);
+ return -1;
+ } else if (unlikely(res == TX_QUEUED)) {
+ I802_DEBUG_INC(tx->local->tx_handlers_queued);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta;
- ieee80211_tx_handler *handler;
struct ieee80211_tx_data tx;
- ieee80211_tx_result res = TX_DROP, res_prepare;
- int ret, i, retries = 0;
+ ieee80211_tx_result res_prepare;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ int ret, i;
+ u16 queue;
- WARN_ON(__ieee80211_queue_pending(local, control->queue));
+ queue = skb_get_queue_mapping(skb);
+
+ WARN_ON(test_bit(queue, local->queues_pending));
if (unlikely(skb->len < 10)) {
dev_kfree_skb(skb);
@@ -1144,7 +1143,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
rcu_read_lock();
/* initialises tx */
- res_prepare = __ieee80211_tx_prepare(&tx, skb, dev, control);
+ res_prepare = __ieee80211_tx_prepare(&tx, skb, dev);
if (res_prepare == TX_DROP) {
dev_kfree_skb(skb);
@@ -1154,86 +1153,54 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
sta = tx.sta;
tx.channel = local->hw.conf.channel;
+ info->band = tx.channel->band;
- for (handler = ieee80211_tx_handlers; *handler != NULL;
- handler++) {
- res = (*handler)(&tx);
- if (res != TX_CONTINUE)
- break;
- }
-
- skb = tx.skb; /* handlers are allowed to change skb */
-
- if (unlikely(res == TX_DROP)) {
- I802_DEBUG_INC(local->tx_handlers_drop);
- goto drop;
- }
-
- if (unlikely(res == TX_QUEUED)) {
- I802_DEBUG_INC(local->tx_handlers_queued);
- rcu_read_unlock();
- return 0;
- }
-
- if (tx.extra_frag) {
- for (i = 0; i < tx.num_extra_frag; i++) {
- int next_len, dur;
- struct ieee80211_hdr *hdr =
- (struct ieee80211_hdr *)
- tx.extra_frag[i]->data;
-
- if (i + 1 < tx.num_extra_frag) {
- next_len = tx.extra_frag[i + 1]->len;
- } else {
- next_len = 0;
- tx.rate = tx.last_frag_rate;
- }
- dur = ieee80211_duration(&tx, 0, next_len);
- hdr->duration_id = cpu_to_le16(dur);
- }
- }
+ if (invoke_tx_handlers(&tx))
+ goto out;
retry:
ret = __ieee80211_tx(local, skb, &tx);
if (ret) {
- struct ieee80211_tx_stored_packet *store =
- &local->pending_packet[control->queue];
+ struct ieee80211_tx_stored_packet *store;
+
+ /*
+ * 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(queue >= ieee80211_num_regular_queues(&local->hw)))
+ goto drop;
+
+ store = &local->pending_packet[queue];
if (ret == IEEE80211_TX_FRAG_AGAIN)
skb = NULL;
- set_bit(IEEE80211_LINK_STATE_PENDING,
- &local->state[control->queue]);
+
+ set_bit(queue, local->queues_pending);
smp_mb();
- /* When the driver gets out of buffers during sending of
- * fragments and calls ieee80211_stop_queue, there is
- * a small window between IEEE80211_LINK_STATE_XOFF and
- * IEEE80211_LINK_STATE_PENDING flags are set. If a buffer
+ /*
+ * When the driver gets out of buffers during sending of
+ * fragments and calls ieee80211_stop_queue, the netif
+ * subqueue is stopped. There is, however, a small window
+ * in which the PENDING bit is not yet set. If a buffer
* gets available in that window (i.e. driver calls
* ieee80211_wake_queue), we would end up with ieee80211_tx
- * called with IEEE80211_LINK_STATE_PENDING. Prevent this by
+ * called with the PENDING bit still set. Prevent this by
* continuing transmitting here when that situation is
- * possible to have happened. */
- if (!__ieee80211_queue_stopped(local, control->queue)) {
- clear_bit(IEEE80211_LINK_STATE_PENDING,
- &local->state[control->queue]);
- retries++;
- /*
- * Driver bug, it's rejecting packets but
- * not stopping queues.
- */
- if (WARN_ON_ONCE(retries > 5))
- goto drop;
+ * possible to have happened.
+ */
+ if (!__netif_subqueue_stopped(local->mdev, queue)) {
+ clear_bit(queue, local->queues_pending);
goto retry;
}
- memcpy(&store->control, control,
- sizeof(struct ieee80211_tx_control));
store->skb = skb;
store->extra_frag = tx.extra_frag;
store->num_extra_frag = tx.num_extra_frag;
- store->last_frag_rate = tx.last_frag_rate;
+ store->last_frag_rate_idx = tx.last_frag_rate_idx;
store->last_frag_rate_ctrl_probe =
!!(tx.flags & IEEE80211_TX_PROBE_LAST_FRAG);
}
+ out:
rcu_read_unlock();
return 0;
@@ -1250,25 +1217,65 @@ retry:
/* device xmit handlers */
-int ieee80211_master_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
+static int ieee80211_skb_resize(struct ieee80211_local *local,
+ struct sk_buff *skb,
+ int head_need, bool may_encrypt)
+{
+ 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) {
+ 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))
+ I802_DEBUG_INC(local->tx_expand_skb_head_cloned);
+ else
+ I802_DEBUG_INC(local->tx_expand_skb_head);
+
+ if (pskb_expand_head(skb, head_need, tail_need, GFP_ATOMIC)) {
+ printk(KERN_DEBUG "%s: failed to reallocate TX buffer\n",
+ wiphy_name(local->hw.wiphy));
+ return -ENOMEM;
+ }
+
+ /* update truesize too */
+ skb->truesize += head_need + tail_need;
+
+ return 0;
+}
+
+int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct ieee80211_tx_control control;
- struct ieee80211_tx_packet_data *pkt_data;
+ struct ieee80211_master_priv *mpriv = netdev_priv(dev);
+ struct ieee80211_local *local = mpriv->local;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct net_device *odev = NULL;
struct ieee80211_sub_if_data *osdata;
int headroom;
+ bool may_encrypt;
+ enum {
+ NOT_MONITOR,
+ FOUND_SDATA,
+ UNKNOWN_ADDRESS,
+ } monitor_iface = NOT_MONITOR;
int ret;
- /*
- * copy control out of the skb so other people can use skb->cb
- */
- pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
- memset(&control, 0, sizeof(struct ieee80211_tx_control));
-
- if (pkt_data->ifindex)
- odev = dev_get_by_index(&init_net, pkt_data->ifindex);
- if (unlikely(odev && !is_ieee80211_device(odev, dev))) {
+ if (skb->iif)
+ odev = dev_get_by_index(&init_net, skb->iif);
+ if (unlikely(odev && !is_ieee80211_device(local, odev))) {
dev_put(odev);
odev = NULL;
}
@@ -1280,32 +1287,88 @@ int ieee80211_master_start_xmit(struct sk_buff *skb,
dev_kfree_skb(skb);
return 0;
}
+
+ memset(info, 0, sizeof(*info));
+
+ info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+
osdata = IEEE80211_DEV_TO_SUB_IF(odev);
- headroom = osdata->local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM;
- if (skb_headroom(skb) < headroom) {
- if (pskb_expand_head(skb, headroom, 0, GFP_ATOMIC)) {
- dev_kfree_skb(skb);
- dev_put(odev);
- return 0;
+ if (ieee80211_vif_is_mesh(&osdata->vif) &&
+ ieee80211_is_data(hdr->frame_control)) {
+ if (is_multicast_ether_addr(hdr->addr3))
+ memcpy(hdr->addr1, hdr->addr3, ETH_ALEN);
+ else
+ if (mesh_nexthop_lookup(skb, osdata))
+ return 0;
+ if (memcmp(odev->dev_addr, hdr->addr4, ETH_ALEN) != 0)
+ IEEE80211_IFSTA_MESH_CTR_INC(&osdata->u.mesh,
+ fwded_frames);
+ } else if (unlikely(osdata->vif.type == NL80211_IFTYPE_MONITOR)) {
+ struct ieee80211_sub_if_data *sdata;
+ int hdrlen;
+ u16 len_rthdr;
+
+ info->flags |= IEEE80211_TX_CTL_INJECTED;
+ monitor_iface = UNKNOWN_ADDRESS;
+
+ 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.
+ */
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces,
+ list) {
+ if (!netif_running(sdata->dev))
+ continue;
+ if (compare_ether_addr(sdata->dev->dev_addr,
+ hdr->addr2)) {
+ dev_hold(sdata->dev);
+ dev_put(odev);
+ osdata = sdata;
+ odev = osdata->dev;
+ skb->iif = sdata->dev->ifindex;
+ monitor_iface = FOUND_SDATA;
+ break;
+ }
+ }
+ rcu_read_unlock();
}
}
- control.vif = &osdata->vif;
- control.type = osdata->vif.type;
- if (pkt_data->flags & IEEE80211_TXPD_REQ_TX_STATUS)
- control.flags |= IEEE80211_TXCTL_REQ_TX_STATUS;
- if (pkt_data->flags & IEEE80211_TXPD_DO_NOT_ENCRYPT)
- control.flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
- if (pkt_data->flags & IEEE80211_TXPD_REQUEUE)
- control.flags |= IEEE80211_TXCTL_REQUEUE;
- if (pkt_data->flags & IEEE80211_TXPD_EAPOL_FRAME)
- control.flags |= IEEE80211_TXCTL_EAPOL_FRAME;
- if (pkt_data->flags & IEEE80211_TXPD_AMPDU)
- control.flags |= IEEE80211_TXCTL_AMPDU;
- control.queue = pkt_data->queue;
-
- ret = ieee80211_tx(odev, skb, &control);
+ may_encrypt = !skb->do_not_encrypt;
+
+ headroom = osdata->local->tx_headroom;
+ if (may_encrypt)
+ headroom += IEEE80211_ENCRYPT_HEADROOM;
+ headroom -= skb_headroom(skb);
+ headroom = max_t(int, 0, headroom);
+
+ if (ieee80211_skb_resize(osdata->local, skb, headroom, may_encrypt)) {
+ dev_kfree_skb(skb);
+ dev_put(odev);
+ return 0;
+ }
+
+ if (osdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ osdata = container_of(osdata->bss,
+ struct ieee80211_sub_if_data,
+ u.ap);
+ if (likely(monitor_iface != UNKNOWN_ADDRESS))
+ info->control.vif = &osdata->vif;
+ ret = ieee80211_tx(odev, skb);
dev_put(odev);
return ret;
@@ -1315,7 +1378,6 @@ int ieee80211_monitor_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_tx_packet_data *pkt_data;
struct ieee80211_radiotap_header *prthdr =
(struct ieee80211_radiotap_header *)skb->data;
u16 len_rthdr;
@@ -1337,12 +1399,12 @@ int ieee80211_monitor_start_xmit(struct sk_buff *skb,
skb->dev = local->mdev;
- pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
- memset(pkt_data, 0, sizeof(*pkt_data));
/* needed because we set skb device to master */
- pkt_data->ifindex = dev->ifindex;
+ skb->iif = dev->ifindex;
- pkt_data->flags |= IEEE80211_TXPD_DO_NOT_ENCRYPT;
+ /* sometimes we do encrypt injected frames, will be fixed
+ * up in radiotap parser if not wanted */
+ skb->do_not_encrypt = 0;
/*
* fix up the pointers accounting for the radiotap
@@ -1385,11 +1447,11 @@ fail:
int ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_tx_packet_data *pkt_data;
- 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 ret = 1, head_need;
- u16 ethertype, hdrlen, meshhdrlen = 0, fc;
+ u16 ethertype, hdrlen, meshhdrlen = 0;
+ __le16 fc;
struct ieee80211_hdr hdr;
struct ieee80211s_hdr mesh_hdr;
const u8 *encaps_data;
@@ -1398,10 +1460,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
struct sta_info *sta;
u32 sta_flags = 0;
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (unlikely(skb->len < ETH_HLEN)) {
- printk(KERN_DEBUG "%s: short skb (len=%d)\n",
- dev->name, skb->len);
ret = 0;
goto fail;
}
@@ -1412,20 +1471,20 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
/* convert Ethernet header to proper 802.11 header (based on
* operation mode) */
ethertype = (skb->data[12] << 8) | skb->data[13];
- fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
+ fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
switch (sdata->vif.type) {
- case IEEE80211_IF_TYPE_AP:
- case IEEE80211_IF_TYPE_VLAN:
- fc |= IEEE80211_FCTL_FROMDS;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
/* DA BSSID SA */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
hdrlen = 24;
break;
- case IEEE80211_IF_TYPE_WDS:
- fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
+ case NL80211_IFTYPE_WDS:
+ fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
/* RA TA DA SA */
memcpy(hdr.addr1, sdata->u.wds.remote_addr, ETH_ALEN);
memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
@@ -1434,45 +1493,64 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
hdrlen = 30;
break;
#ifdef CONFIG_MAC80211_MESH
- case IEEE80211_IF_TYPE_MESH_POINT:
- fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
- /* RA TA DA SA */
- if (is_multicast_ether_addr(skb->data))
- memcpy(hdr.addr1, skb->data, ETH_ALEN);
- else if (mesh_nexthop_lookup(hdr.addr1, skb, dev))
- return 0;
- memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
- memcpy(hdr.addr3, skb->data, ETH_ALEN);
- memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
- if (skb->pkt_type == PACKET_OTHERHOST) {
- /* Forwarded frame, keep mesh ttl and seqnum */
- struct ieee80211s_hdr *prev_meshhdr;
- prev_meshhdr = ((struct ieee80211s_hdr *)skb->cb);
- meshhdrlen = ieee80211_get_mesh_hdrlen(prev_meshhdr);
- memcpy(&mesh_hdr, prev_meshhdr, meshhdrlen);
- sdata->u.sta.mshstats.fwded_frames++;
+ case NL80211_IFTYPE_MESH_POINT:
+ fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
+ if (!sdata->u.mesh.mshcfg.dot11MeshTTL) {
+ /* Do not send frames with mesh_ttl == 0 */
+ sdata->u.mesh.mshstats.dropped_frames_ttl++;
+ ret = 0;
+ goto fail;
+ }
+ memset(&mesh_hdr, 0, sizeof(mesh_hdr));
+
+ if (compare_ether_addr(dev->dev_addr,
+ skb->data + ETH_ALEN) == 0) {
+ /* RA TA DA SA */
+ memset(hdr.addr1, 0, ETH_ALEN);
+ memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
+ memcpy(hdr.addr3, skb->data, ETH_ALEN);
+ memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+ meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata);
} else {
- if (!sdata->u.sta.mshcfg.dot11MeshTTL) {
- /* Do not send frames with mesh_ttl == 0 */
- sdata->u.sta.mshstats.dropped_frames_ttl++;
- ret = 0;
- goto fail;
+ /* packet from other interface */
+ struct mesh_path *mppath;
+
+ memset(hdr.addr1, 0, ETH_ALEN);
+ memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
+ memcpy(hdr.addr4, dev->dev_addr, ETH_ALEN);
+
+ if (is_multicast_ether_addr(skb->data))
+ memcpy(hdr.addr3, skb->data, ETH_ALEN);
+ else {
+ rcu_read_lock();
+ mppath = mpp_path_lookup(skb->data, sdata);
+ if (mppath)
+ memcpy(hdr.addr3, mppath->mpp, ETH_ALEN);
+ else
+ memset(hdr.addr3, 0xff, ETH_ALEN);
+ rcu_read_unlock();
}
- meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
- sdata);
+
+ mesh_hdr.flags |= MESH_FLAGS_AE_A5_A6;
+ mesh_hdr.ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
+ put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &mesh_hdr.seqnum);
+ memcpy(mesh_hdr.eaddr1, skb->data, ETH_ALEN);
+ memcpy(mesh_hdr.eaddr2, skb->data + ETH_ALEN, ETH_ALEN);
+ sdata->u.mesh.mesh_seqnum++;
+ meshhdrlen = 18;
}
hdrlen = 30;
break;
#endif
- case IEEE80211_IF_TYPE_STA:
- fc |= IEEE80211_FCTL_TODS;
+ case NL80211_IFTYPE_STATION:
+ fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
/* BSSID SA DA */
memcpy(hdr.addr1, sdata->u.sta.bssid, ETH_ALEN);
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
memcpy(hdr.addr3, skb->data, ETH_ALEN);
hdrlen = 24;
break;
- case IEEE80211_IF_TYPE_IBSS:
+ case NL80211_IFTYPE_ADHOC:
/* DA SA BSSID */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
@@ -1493,13 +1571,14 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
rcu_read_lock();
sta = sta_info_get(local, hdr.addr1);
if (sta)
- sta_flags = sta->flags;
+ sta_flags = get_sta_flags(sta);
rcu_read_unlock();
}
- /* receiver is QoS enabled, use a QoS type frame */
- if (sta_flags & WLAN_STA_WME) {
- fc |= IEEE80211_STYPE_QOS_DATA;
+ /* receiver and we are QoS enabled, use a QoS type frame */
+ if (sta_flags & WLAN_STA_WME &&
+ ieee80211_num_regular_queues(&local->hw) >= 4) {
+ fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
hdrlen += 2;
}
@@ -1507,7 +1586,8 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
* Drop unicast frames to unauthorised stations unless they are
* EAPOL frames from the local station.
*/
- if (unlikely(!is_multicast_ether_addr(hdr.addr1) &&
+ if (!ieee80211_vif_is_mesh(&sdata->vif) &&
+ unlikely(!is_multicast_ether_addr(hdr.addr1) &&
!(sta_flags & WLAN_STA_AUTHORIZED) &&
!(ethertype == ETH_P_PAE &&
compare_ether_addr(dev->dev_addr,
@@ -1527,7 +1607,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
goto fail;
}
- hdr.frame_control = cpu_to_le16(fc);
+ hdr.frame_control = fc;
hdr.duration_id = 0;
hdr.seq_ctrl = 0;
@@ -1549,45 +1629,26 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
nh_pos -= skip_header_bytes;
h_pos -= skip_header_bytes;
- /* TODO: implement support for fragments so that there is no need to
- * reallocate and copy payload; it might be enough to support one
- * extra fragment that would be copied in the beginning of the frame
- * data.. anyway, it would be nice to include this into skb structure
- * somehow
+ head_need = hdrlen + encaps_len + meshhdrlen - skb_headroom(skb);
+
+ /*
+ * So we need to modify the skb header and hence need a copy of
+ * that. The head_need variable above doesn't, so far, include
+ * the needed header space that we don't need right away. If we
+ * can, then we don't reallocate right now but only after the
+ * frame arrives at the master device (if it does...)
*
- * There are few options for this:
- * use skb->cb as an extra space for 802.11 header
- * allocate new buffer if not enough headroom
- * make sure that there is enough headroom in every skb by increasing
- * build in headroom in __dev_alloc_skb() (linux/skbuff.h) and
- * alloc_skb() (net/core/skbuff.c)
+ * If we cannot, however, then we will reallocate to include all
+ * the ever needed space. Also, if we need to reallocate it anyway,
+ * make it big enough for everything we may ever need.
*/
- head_need = hdrlen + encaps_len + meshhdrlen + local->tx_headroom;
- head_need -= skb_headroom(skb);
-
- /* We are going to modify skb data, so make a copy of it if happens to
- * be cloned. This could happen, e.g., with Linux bridge code passing
- * us broadcast frames. */
if (head_need > 0 || skb_cloned(skb)) {
-#if 0
- printk(KERN_DEBUG "%s: need to reallocate buffer for %d bytes "
- "of headroom\n", dev->name, head_need);
-#endif
-
- if (skb_cloned(skb))
- I802_DEBUG_INC(local->tx_expand_skb_head_cloned);
- else
- I802_DEBUG_INC(local->tx_expand_skb_head);
- /* Since we have to reallocate the buffer, make sure that there
- * is enough room for possible WEP IV/ICV and TKIP (8 bytes
- * before payload and 12 after). */
- if (pskb_expand_head(skb, (head_need > 0 ? head_need + 8 : 8),
- 12, GFP_ATOMIC)) {
- printk(KERN_DEBUG "%s: failed to reallocate TX buffer"
- "\n", dev->name);
+ head_need += IEEE80211_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 (encaps_data) {
@@ -1602,7 +1663,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
h_pos += meshhdrlen;
}
- if (fc & IEEE80211_STYPE_QOS_DATA) {
+ if (ieee80211_is_data_qos(fc)) {
__le16 *qos_control;
qos_control = (__le16*) skb_push(skb, 2);
@@ -1618,11 +1679,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
nh_pos += hdrlen;
h_pos += hdrlen;
- pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
- memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
- pkt_data->ifindex = dev->ifindex;
- if (ethertype == ETH_P_PAE)
- pkt_data->flags |= IEEE80211_TXPD_EAPOL_FRAME;
+ skb->iif = dev->ifindex;
skb->dev = local->mdev;
dev->stats.tx_packets++;
@@ -1647,46 +1704,60 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
return ret;
}
-/* helper functions for pending packets for when queues are stopped */
+/*
+ * ieee80211_clear_tx_pending may not be called in a context where
+ * it is possible that it packets could come in again.
+ */
void ieee80211_clear_tx_pending(struct ieee80211_local *local)
{
int i, j;
struct ieee80211_tx_stored_packet *store;
- for (i = 0; i < local->hw.queues; i++) {
- if (!__ieee80211_queue_pending(local, i))
+ for (i = 0; i < ieee80211_num_regular_queues(&local->hw); i++) {
+ if (!test_bit(i, local->queues_pending))
continue;
store = &local->pending_packet[i];
kfree_skb(store->skb);
for (j = 0; j < store->num_extra_frag; j++)
kfree_skb(store->extra_frag[j]);
kfree(store->extra_frag);
- clear_bit(IEEE80211_LINK_STATE_PENDING, &local->state[i]);
+ clear_bit(i, local->queues_pending);
}
}
+/*
+ * Transmit all pending packets. Called from tasklet, locks master device
+ * TX lock so that no new packets can come in.
+ */
void ieee80211_tx_pending(unsigned long data)
{
struct ieee80211_local *local = (struct ieee80211_local *)data;
struct net_device *dev = local->mdev;
struct ieee80211_tx_stored_packet *store;
struct ieee80211_tx_data tx;
- int i, ret, reschedule = 0;
+ int i, ret;
netif_tx_lock_bh(dev);
- for (i = 0; i < local->hw.queues; i++) {
- if (__ieee80211_queue_stopped(local, i))
+ for (i = 0; i < ieee80211_num_regular_queues(&local->hw); i++) {
+ /* Check that this queue is ok */
+ if (__netif_subqueue_stopped(local->mdev, i) &&
+ !test_bit(i, local->queues_pending_run))
continue;
- if (!__ieee80211_queue_pending(local, i)) {
- reschedule = 1;
+
+ if (!test_bit(i, local->queues_pending)) {
+ clear_bit(i, local->queues_pending_run);
+ ieee80211_wake_queue(&local->hw, i);
continue;
}
+
+ clear_bit(i, local->queues_pending_run);
+ netif_start_subqueue(local->mdev, i);
+
store = &local->pending_packet[i];
- tx.control = &store->control;
tx.extra_frag = store->extra_frag;
tx.num_extra_frag = store->num_extra_frag;
- tx.last_frag_rate = store->last_frag_rate;
+ tx.last_frag_rate_idx = store->last_frag_rate_idx;
tx.flags = 0;
if (store->last_frag_rate_ctrl_probe)
tx.flags |= IEEE80211_TX_PROBE_LAST_FRAG;
@@ -1695,19 +1766,11 @@ void ieee80211_tx_pending(unsigned long data)
if (ret == IEEE80211_TX_FRAG_AGAIN)
store->skb = NULL;
} else {
- clear_bit(IEEE80211_LINK_STATE_PENDING,
- &local->state[i]);
- reschedule = 1;
+ clear_bit(i, local->queues_pending);
+ ieee80211_wake_queue(&local->hw, i);
}
}
netif_tx_unlock_bh(dev);
- if (reschedule) {
- if (!ieee80211_qdisc_installed(dev)) {
- if (!__ieee80211_queue_stopped(local, 0))
- netif_wake_queue(dev);
- } else
- netif_schedule(dev);
- }
}
/* functions for drivers to get certain frames */
@@ -1776,30 +1839,28 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
}
struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_tx_control *control)
+ struct ieee80211_vif *vif)
{
struct ieee80211_local *local = hw_to_local(hw);
- struct sk_buff *skb;
+ struct sk_buff *skb = NULL;
+ struct ieee80211_tx_info *info;
struct net_device *bdev;
struct ieee80211_sub_if_data *sdata = NULL;
struct ieee80211_if_ap *ap = NULL;
+ struct ieee80211_if_sta *ifsta = NULL;
struct rate_selection rsel;
struct beacon_data *beacon;
struct ieee80211_supported_band *sband;
- struct ieee80211_mgmt *mgmt;
- int *num_beacons;
- bool err = true;
- u8 *pos;
+ enum ieee80211_band band = local->hw.conf.channel->band;
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+ sband = local->hw.wiphy->bands[band];
rcu_read_lock();
sdata = vif_to_sdata(vif);
bdev = sdata->dev;
- if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
ap = &sdata->u.ap;
beacon = rcu_dereference(ap->beacon);
if (ap && beacon) {
@@ -1817,9 +1878,6 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
memcpy(skb_put(skb, beacon->head_len), beacon->head,
beacon->head_len);
- ieee80211_include_sequence(sdata,
- (struct ieee80211_hdr *)skb->data);
-
/*
* Not very nice, but we want to allow the driver to call
* ieee80211_beacon_get() as a response to the set_tim()
@@ -1840,12 +1898,27 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
if (beacon->tail)
memcpy(skb_put(skb, beacon->tail_len),
beacon->tail, beacon->tail_len);
+ } else
+ goto out;
+ } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ struct ieee80211_hdr *hdr;
+ ifsta = &sdata->u.sta;
- num_beacons = &ap->num_beacons;
+ if (!ifsta->probe_resp)
+ goto out;
+
+ skb = skb_copy(ifsta->probe_resp, GFP_ATOMIC);
+ if (!skb)
+ goto out;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_BEACON);
- err = false;
- }
} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ struct ieee80211_mgmt *mgmt;
+ u8 *pos;
+
/* headroom, head length, tail length and maximum TIM length */
skb = dev_alloc_skb(local->tx_headroom + 400);
if (!skb)
@@ -1855,8 +1928,8 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
mgmt = (struct ieee80211_mgmt *)
skb_put(skb, 24 + sizeof(mgmt->u.beacon));
memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
- mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
- IEEE80211_STYPE_BEACON);
+ mgmt->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON);
memset(mgmt->da, 0xff, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
/* BSSID is left zeroed, wildcard value */
@@ -1868,48 +1941,43 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
*pos++ = WLAN_EID_SSID;
*pos++ = 0x0;
- mesh_mgmt_ies_add(skb, sdata->dev);
+ mesh_mgmt_ies_add(skb, sdata);
+ } else {
+ WARN_ON(1);
+ goto out;
+ }
- num_beacons = &sdata->u.sta.num_beacons;
+ info = IEEE80211_SKB_CB(skb);
- err = false;
- }
+ skb->do_not_encrypt = 1;
- if (err) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (net_ratelimit())
- printk(KERN_DEBUG "no beacon data avail for %s\n",
- bdev->name);
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+ info->band = band;
+ rate_control_get_rate(sdata, sband, NULL, skb, &rsel);
+
+ if (unlikely(rsel.rate_idx < 0)) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: ieee80211_beacon_get: "
+ "no rate found\n",
+ wiphy_name(local->hw.wiphy));
+ }
+ dev_kfree_skb_any(skb);
skb = NULL;
goto out;
}
- if (control) {
- rate_control_get_rate(local->mdev, sband, skb, &rsel);
- if (!rsel.rate) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: ieee80211_beacon_get: "
- "no rate found\n",
- wiphy_name(local->hw.wiphy));
- }
- dev_kfree_skb(skb);
- skb = NULL;
- goto out;
- }
+ info->control.vif = vif;
+ info->tx_rate_idx = rsel.rate_idx;
+
+ info->flags |= IEEE80211_TX_CTL_NO_ACK;
+ info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
+ info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
+ if (sdata->bss_conf.use_short_preamble &&
+ sband->bitrates[rsel.rate_idx].flags & IEEE80211_RATE_SHORT_PREAMBLE)
+ info->flags |= IEEE80211_TX_CTL_SHORT_PREAMBLE;
+
+ info->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
+ info->control.retry_limit = 1;
- control->vif = vif;
- control->tx_rate = rsel.rate;
- if (sdata->bss_conf.use_short_preamble &&
- rsel.rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
- control->flags |= IEEE80211_TXCTL_SHORT_PREAMBLE;
- control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
- control->flags |= IEEE80211_TXCTL_NO_ACK;
- control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
- control->retry_limit = 1;
- control->flags |= IEEE80211_TXCTL_CLEAR_PS_FILT;
- }
- (*num_beacons)++;
out:
rcu_read_unlock();
return skb;
@@ -1918,14 +1986,13 @@ EXPORT_SYMBOL(ieee80211_beacon_get);
void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
const void *frame, size_t frame_len,
- const struct ieee80211_tx_control *frame_txctl,
+ const struct ieee80211_tx_info *frame_txctl,
struct ieee80211_rts *rts)
{
const struct ieee80211_hdr *hdr = frame;
- u16 fctl;
- fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS;
- rts->frame_control = cpu_to_le16(fctl);
+ rts->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS);
rts->duration = ieee80211_rts_duration(hw, vif, frame_len,
frame_txctl);
memcpy(rts->ra, hdr->addr1, sizeof(rts->ra));
@@ -1935,14 +2002,13 @@ EXPORT_SYMBOL(ieee80211_rts_get);
void ieee80211_ctstoself_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
const void *frame, size_t frame_len,
- const struct ieee80211_tx_control *frame_txctl,
+ const struct ieee80211_tx_info *frame_txctl,
struct ieee80211_cts *cts)
{
const struct ieee80211_hdr *hdr = frame;
- u16 fctl;
- fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS;
- cts->frame_control = cpu_to_le16(fctl);
+ cts->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS);
cts->duration = ieee80211_ctstoself_duration(hw, vif,
frame_len, frame_txctl);
memcpy(cts->ra, hdr->addr1, sizeof(cts->ra));
@@ -1951,23 +2017,21 @@ EXPORT_SYMBOL(ieee80211_ctstoself_get);
struct sk_buff *
ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_tx_control *control)
+ struct ieee80211_vif *vif)
{
struct ieee80211_local *local = hw_to_local(hw);
- struct sk_buff *skb;
+ struct sk_buff *skb = NULL;
struct sta_info *sta;
- ieee80211_tx_handler *handler;
struct ieee80211_tx_data tx;
- ieee80211_tx_result res = TX_DROP;
struct net_device *bdev;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_if_ap *bss = NULL;
struct beacon_data *beacon;
+ struct ieee80211_tx_info *info;
sdata = vif_to_sdata(vif);
bdev = sdata->dev;
-
+ bss = &sdata->u.ap;
if (!bss)
return NULL;
@@ -1975,19 +2039,16 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
rcu_read_lock();
beacon = rcu_dereference(bss->beacon);
- if (sdata->vif.type != IEEE80211_IF_TYPE_AP || !beacon ||
- !beacon->head) {
- rcu_read_unlock();
- return NULL;
- }
+ if (sdata->vif.type != NL80211_IFTYPE_AP || !beacon || !beacon->head)
+ goto out;
if (bss->dtim_count != 0)
- return NULL; /* send buffered bc/mc only after DTIM beacon */
- memset(control, 0, sizeof(*control));
+ goto out; /* send buffered bc/mc only after DTIM beacon */
+
while (1) {
skb = skb_dequeue(&bss->ps_bc_buf);
if (!skb)
- return NULL;
+ goto out;
local->total_ps_buffered--;
if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) {
@@ -2000,30 +2061,21 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
}
- if (!ieee80211_tx_prepare(&tx, skb, local->mdev, control))
+ if (!ieee80211_tx_prepare(local, &tx, skb))
break;
dev_kfree_skb_any(skb);
}
+
+ 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;
- for (handler = ieee80211_tx_handlers; *handler != NULL; handler++) {
- res = (*handler)(&tx);
- if (res == TX_DROP || res == TX_QUEUED)
- break;
- }
- skb = tx.skb; /* handlers are allowed to change skb */
-
- if (res == TX_DROP) {
- I802_DEBUG_INC(local->tx_handlers_drop);
- dev_kfree_skb(skb);
+ if (invoke_tx_handlers(&tx))
skb = NULL;
- } else if (res == TX_QUEUED) {
- I802_DEBUG_INC(local->tx_handlers_queued);
- skb = NULL;
- }
-
+ out:
rcu_read_unlock();
return skb;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 4e97b266f90..cee4884b9d0 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -43,76 +43,67 @@ const unsigned char bridge_tunnel_header[] __aligned(2) =
u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
- enum ieee80211_if_types type)
+ enum nl80211_iftype type)
{
- u16 fc;
+ __le16 fc = hdr->frame_control;
/* drop ACK/CTS frames and incorrect hdr len (ctrl) */
if (len < 16)
return NULL;
- fc = le16_to_cpu(hdr->frame_control);
-
- switch (fc & IEEE80211_FCTL_FTYPE) {
- case IEEE80211_FTYPE_DATA:
+ if (ieee80211_is_data(fc)) {
if (len < 24) /* drop incorrect hdr len (data) */
return NULL;
- switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
- case IEEE80211_FCTL_TODS:
- return hdr->addr1;
- case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
+
+ if (ieee80211_has_a4(fc))
return NULL;
- case IEEE80211_FCTL_FROMDS:
+ if (ieee80211_has_tods(fc))
+ return hdr->addr1;
+ if (ieee80211_has_fromds(fc))
return hdr->addr2;
- case 0:
- return hdr->addr3;
- }
- break;
- case IEEE80211_FTYPE_MGMT:
+
+ return hdr->addr3;
+ }
+
+ if (ieee80211_is_mgmt(fc)) {
if (len < 24) /* drop incorrect hdr len (mgmt) */
return NULL;
return hdr->addr3;
- case IEEE80211_FTYPE_CTL:
- if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)
+ }
+
+ if (ieee80211_is_ctl(fc)) {
+ if(ieee80211_is_pspoll(fc))
return hdr->addr1;
- else if ((fc & IEEE80211_FCTL_STYPE) ==
- IEEE80211_STYPE_BACK_REQ) {
+
+ if (ieee80211_is_back_req(fc)) {
switch (type) {
- case IEEE80211_IF_TYPE_STA:
+ case NL80211_IFTYPE_STATION:
return hdr->addr2;
- case IEEE80211_IF_TYPE_AP:
- case IEEE80211_IF_TYPE_VLAN:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
return hdr->addr1;
default:
- return NULL;
+ break; /* fall through to the return */
}
}
- else
- return NULL;
}
return NULL;
}
-int ieee80211_get_hdrlen(u16 fc)
+unsigned int ieee80211_hdrlen(__le16 fc)
{
- int hdrlen = 24;
+ unsigned int hdrlen = 24;
+
+ if (ieee80211_is_data(fc)) {
+ if (ieee80211_has_a4(fc))
+ hdrlen = 30;
+ if (ieee80211_is_data_qos(fc))
+ hdrlen += IEEE80211_QOS_CTL_LEN;
+ goto out;
+ }
- switch (fc & IEEE80211_FCTL_FTYPE) {
- case IEEE80211_FTYPE_DATA:
- if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
- hdrlen = 30; /* Addr4 */
- /*
- * The QoS Control field is two bytes and its presence is
- * indicated by the IEEE80211_STYPE_QOS_DATA bit. Add 2 to
- * hdrlen if that bit is set.
- * This works by masking out the bit and shifting it to
- * bit position 1 so the result has the value 0 or 2.
- */
- hdrlen += (fc & IEEE80211_STYPE_QOS_DATA)
- >> (ilog2(IEEE80211_STYPE_QOS_DATA)-1);
- break;
- case IEEE80211_FTYPE_CTL:
+ if (ieee80211_is_ctl(fc)) {
/*
* ACK and CTS are 10 bytes, all others 16. To see how
* to get this condition consider
@@ -122,25 +113,24 @@ int ieee80211_get_hdrlen(u16 fc)
* bits that matter: ^^^ (0x00E0)
* value of those: 0b0000000011000000 (0x00C0)
*/
- if ((fc & 0xE0) == 0xC0)
+ if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0))
hdrlen = 10;
else
hdrlen = 16;
- break;
}
-
+out:
return hdrlen;
}
-EXPORT_SYMBOL(ieee80211_get_hdrlen);
+EXPORT_SYMBOL(ieee80211_hdrlen);
-int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
+unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
{
- const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) skb->data;
- int hdrlen;
+ const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *)skb->data;
+ unsigned int hdrlen;
if (unlikely(skb->len < 10))
return 0;
- hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control));
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (unlikely(hdrlen > skb->len))
return 0;
return hdrlen;
@@ -241,16 +231,21 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
struct ieee80211_rate *rate)
{
struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_sub_if_data *sdata;
u16 dur;
int erp;
+ bool short_preamble = false;
erp = 0;
- if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
- erp = rate->flags & IEEE80211_RATE_ERP_G;
+ if (vif) {
+ sdata = vif_to_sdata(vif);
+ short_preamble = sdata->bss_conf.use_short_preamble;
+ if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
+ erp = rate->flags & IEEE80211_RATE_ERP_G;
+ }
dur = ieee80211_frame_duration(local, frame_len, rate->bitrate, erp,
- sdata->bss_conf.use_short_preamble);
+ short_preamble);
return cpu_to_le16(dur);
}
@@ -258,22 +253,29 @@ EXPORT_SYMBOL(ieee80211_generic_frame_duration);
__le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, size_t frame_len,
- const struct ieee80211_tx_control *frame_txctl)
+ const struct ieee80211_tx_info *frame_txctl)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_rate *rate;
- struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_sub_if_data *sdata;
bool short_preamble;
int erp;
u16 dur;
+ struct ieee80211_supported_band *sband;
- short_preamble = sdata->bss_conf.use_short_preamble;
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- rate = frame_txctl->rts_cts_rate;
+ short_preamble = false;
+
+ rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
erp = 0;
- if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
- erp = rate->flags & IEEE80211_RATE_ERP_G;
+ if (vif) {
+ sdata = vif_to_sdata(vif);
+ short_preamble = sdata->bss_conf.use_short_preamble;
+ if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
+ erp = rate->flags & IEEE80211_RATE_ERP_G;
+ }
/* CTS duration */
dur = ieee80211_frame_duration(local, 10, rate->bitrate,
@@ -292,26 +294,33 @@ EXPORT_SYMBOL(ieee80211_rts_duration);
__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
size_t frame_len,
- const struct ieee80211_tx_control *frame_txctl)
+ const struct ieee80211_tx_info *frame_txctl)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_rate *rate;
- struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_sub_if_data *sdata;
bool short_preamble;
int erp;
u16 dur;
+ struct ieee80211_supported_band *sband;
+
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- short_preamble = sdata->bss_conf.use_short_preamble;
+ short_preamble = false;
- rate = frame_txctl->rts_cts_rate;
+ rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx];
erp = 0;
- if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
- erp = rate->flags & IEEE80211_RATE_ERP_G;
+ if (vif) {
+ sdata = vif_to_sdata(vif);
+ short_preamble = sdata->bss_conf.use_short_preamble;
+ if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
+ erp = rate->flags & IEEE80211_RATE_ERP_G;
+ }
/* Data frame duration */
dur = ieee80211_frame_duration(local, frame_len, rate->bitrate,
erp, short_preamble);
- if (!(frame_txctl->flags & IEEE80211_TXCTL_NO_ACK)) {
+ if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) {
/* ACK duration */
dur += ieee80211_frame_duration(local, 10, rate->bitrate,
erp, short_preamble);
@@ -325,17 +334,11 @@ void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
{
struct ieee80211_local *local = hw_to_local(hw);
- if (test_and_clear_bit(IEEE80211_LINK_STATE_XOFF,
- &local->state[queue])) {
- if (test_bit(IEEE80211_LINK_STATE_PENDING,
- &local->state[queue]))
- tasklet_schedule(&local->tx_pending_tasklet);
- else
- if (!ieee80211_qdisc_installed(local->mdev)) {
- if (queue == 0)
- netif_wake_queue(local->mdev);
- } else
- __netif_schedule(local->mdev);
+ if (test_bit(queue, local->queues_pending)) {
+ set_bit(queue, local->queues_pending_run);
+ tasklet_schedule(&local->tx_pending_tasklet);
+ } else {
+ netif_wake_subqueue(local->mdev, queue);
}
}
EXPORT_SYMBOL(ieee80211_wake_queue);
@@ -344,38 +347,31 @@ void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
{
struct ieee80211_local *local = hw_to_local(hw);
- if (!ieee80211_qdisc_installed(local->mdev) && queue == 0)
- netif_stop_queue(local->mdev);
- set_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
+ netif_stop_subqueue(local->mdev, queue);
}
EXPORT_SYMBOL(ieee80211_stop_queue);
-void ieee80211_start_queues(struct ieee80211_hw *hw)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- int i;
-
- for (i = 0; i < local->hw.queues; i++)
- clear_bit(IEEE80211_LINK_STATE_XOFF, &local->state[i]);
- if (!ieee80211_qdisc_installed(local->mdev))
- netif_start_queue(local->mdev);
-}
-EXPORT_SYMBOL(ieee80211_start_queues);
-
void ieee80211_stop_queues(struct ieee80211_hw *hw)
{
int i;
- for (i = 0; i < hw->queues; i++)
+ for (i = 0; i < ieee80211_num_queues(hw); i++)
ieee80211_stop_queue(hw, i);
}
EXPORT_SYMBOL(ieee80211_stop_queues);
+int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ return __netif_subqueue_stopped(local->mdev, queue);
+}
+EXPORT_SYMBOL(ieee80211_queue_stopped);
+
void ieee80211_wake_queues(struct ieee80211_hw *hw)
{
int i;
- for (i = 0; i < hw->queues; i++)
+ for (i = 0; i < hw->queues + hw->ampdu_queues; i++)
ieee80211_wake_queue(hw, i);
}
EXPORT_SYMBOL(ieee80211_wake_queues);
@@ -393,19 +389,18 @@ void ieee80211_iterate_active_interfaces(
list_for_each_entry(sdata, &local->interfaces, list) {
switch (sdata->vif.type) {
- case IEEE80211_IF_TYPE_INVALID:
- case IEEE80211_IF_TYPE_MNTR:
- case IEEE80211_IF_TYPE_VLAN:
+ case __NL80211_IFTYPE_AFTER_LAST:
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_AP_VLAN:
continue;
- case IEEE80211_IF_TYPE_AP:
- case IEEE80211_IF_TYPE_STA:
- case IEEE80211_IF_TYPE_IBSS:
- case IEEE80211_IF_TYPE_WDS:
- case IEEE80211_IF_TYPE_MESH_POINT:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_MESH_POINT:
break;
}
- if (sdata->dev == local->mdev)
- continue;
if (netif_running(sdata->dev))
iterator(data, sdata->dev->dev_addr,
&sdata->vif);
@@ -428,19 +423,18 @@ void ieee80211_iterate_active_interfaces_atomic(
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
switch (sdata->vif.type) {
- case IEEE80211_IF_TYPE_INVALID:
- case IEEE80211_IF_TYPE_MNTR:
- case IEEE80211_IF_TYPE_VLAN:
+ case __NL80211_IFTYPE_AFTER_LAST:
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_AP_VLAN:
continue;
- case IEEE80211_IF_TYPE_AP:
- case IEEE80211_IF_TYPE_STA:
- case IEEE80211_IF_TYPE_IBSS:
- case IEEE80211_IF_TYPE_WDS:
- case IEEE80211_IF_TYPE_MESH_POINT:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_MESH_POINT:
break;
}
- if (sdata->dev == local->mdev)
- continue;
if (netif_running(sdata->dev))
iterator(data, sdata->dev->dev_addr,
&sdata->vif);
@@ -449,3 +443,243 @@ void ieee80211_iterate_active_interfaces_atomic(
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
+
+void ieee802_11_parse_elems(u8 *start, size_t len,
+ struct ieee802_11_elems *elems)
+{
+ size_t left = len;
+ u8 *pos = start;
+
+ memset(elems, 0, sizeof(*elems));
+ elems->ie_start = start;
+ elems->total_len = len;
+
+ while (left >= 2) {
+ u8 id, elen;
+
+ id = *pos++;
+ elen = *pos++;
+ left -= 2;
+
+ if (elen > left)
+ return;
+
+ switch (id) {
+ case WLAN_EID_SSID:
+ elems->ssid = pos;
+ elems->ssid_len = elen;
+ break;
+ case WLAN_EID_SUPP_RATES:
+ 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;
+ break;
+ case WLAN_EID_TIM:
+ elems->tim = pos;
+ elems->tim_len = elen;
+ break;
+ case WLAN_EID_IBSS_PARAMS:
+ elems->ibss_params = pos;
+ elems->ibss_params_len = elen;
+ break;
+ case WLAN_EID_CHALLENGE:
+ elems->challenge = pos;
+ elems->challenge_len = elen;
+ break;
+ case WLAN_EID_WPA:
+ if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
+ pos[2] == 0xf2) {
+ /* Microsoft OUI (00:50:F2) */
+ if (pos[3] == 1) {
+ /* OUI Type 1 - WPA IE */
+ elems->wpa = pos;
+ elems->wpa_len = elen;
+ } else if (elen >= 5 && pos[3] == 2) {
+ if (pos[4] == 0) {
+ elems->wmm_info = pos;
+ elems->wmm_info_len = elen;
+ } else if (pos[4] == 1) {
+ elems->wmm_param = pos;
+ elems->wmm_param_len = elen;
+ }
+ }
+ }
+ break;
+ case WLAN_EID_RSN:
+ elems->rsn = pos;
+ elems->rsn_len = elen;
+ break;
+ case WLAN_EID_ERP_INFO:
+ elems->erp_info = pos;
+ elems->erp_info_len = elen;
+ break;
+ case WLAN_EID_EXT_SUPP_RATES:
+ elems->ext_supp_rates = pos;
+ elems->ext_supp_rates_len = elen;
+ break;
+ case WLAN_EID_HT_CAPABILITY:
+ if (elen >= sizeof(struct ieee80211_ht_cap))
+ elems->ht_cap_elem = (void *)pos;
+ break;
+ case WLAN_EID_HT_EXTRA_INFO:
+ if (elen >= sizeof(struct ieee80211_ht_addt_info))
+ elems->ht_info_elem = (void *)pos;
+ break;
+ case WLAN_EID_MESH_ID:
+ elems->mesh_id = pos;
+ elems->mesh_id_len = elen;
+ break;
+ case WLAN_EID_MESH_CONFIG:
+ elems->mesh_config = pos;
+ elems->mesh_config_len = elen;
+ break;
+ case WLAN_EID_PEER_LINK:
+ elems->peer_link = pos;
+ elems->peer_link_len = elen;
+ break;
+ case WLAN_EID_PREQ:
+ elems->preq = pos;
+ elems->preq_len = elen;
+ break;
+ case WLAN_EID_PREP:
+ elems->prep = pos;
+ elems->prep_len = elen;
+ break;
+ case WLAN_EID_PERR:
+ elems->perr = pos;
+ elems->perr_len = elen;
+ break;
+ case WLAN_EID_CHANNEL_SWITCH:
+ elems->ch_switch_elem = pos;
+ elems->ch_switch_elem_len = elen;
+ break;
+ case WLAN_EID_QUIET:
+ if (!elems->quiet_elem) {
+ elems->quiet_elem = pos;
+ elems->quiet_elem_len = elen;
+ }
+ 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:
+ elems->pwr_constr_elem = pos;
+ elems->pwr_constr_elem_len = elen;
+ break;
+ default:
+ break;
+ }
+
+ left -= elen;
+ pos += elen;
+ }
+}
+
+void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_tx_queue_params qparam;
+ int i;
+
+ if (!local->ops->conf_tx)
+ return;
+
+ memset(&qparam, 0, sizeof(qparam));
+
+ qparam.aifs = 2;
+
+ if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
+ !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE))
+ qparam.cw_min = 31;
+ else
+ qparam.cw_min = 15;
+
+ qparam.cw_max = 1023;
+ qparam.txop = 0;
+
+ for (i = 0; i < local_to_hw(local)->queues; i++)
+ local->ops->conf_tx(local_to_hw(local), i, &qparam);
+}
+
+void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+ int encrypt)
+{
+ skb->dev = sdata->local->mdev;
+ skb_set_mac_header(skb, 0);
+ skb_set_network_header(skb, 0);
+ skb_set_transport_header(skb, 0);
+
+ skb->iif = sdata->dev->ifindex;
+ skb->do_not_encrypt = !encrypt;
+
+ dev_queue_xmit(skb);
+}
+
+int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freqMHz)
+{
+ int ret = -EINVAL;
+ struct ieee80211_channel *chan;
+ struct ieee80211_local *local = sdata->local;
+
+ chan = ieee80211_get_channel(local->hw.wiphy, freqMHz);
+
+ if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
+ chan->flags & IEEE80211_CHAN_NO_IBSS) {
+ printk(KERN_DEBUG "%s: IBSS not allowed on frequency "
+ "%d MHz\n", sdata->dev->name, chan->center_freq);
+ return ret;
+ }
+ local->oper_channel = chan;
+
+ if (local->sw_scanning || local->hw_scanning)
+ ret = 0;
+ else
+ ret = ieee80211_hw_config(local);
+
+ rate_control_clear(local);
+ }
+
+ return ret;
+}
+
+u64 ieee80211_mandatory_rates(struct ieee80211_local *local,
+ enum ieee80211_band band)
+{
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *bitrates;
+ u64 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;
+}
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index affcecd78c1..f0e2d3ecb5c 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -31,13 +31,13 @@ int ieee80211_wep_init(struct ieee80211_local *local)
local->wep_tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0,
CRYPTO_ALG_ASYNC);
if (IS_ERR(local->wep_tx_tfm))
- return -ENOMEM;
+ return PTR_ERR(local->wep_tx_tfm);
local->wep_rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0,
CRYPTO_ALG_ASYNC);
if (IS_ERR(local->wep_rx_tfm)) {
crypto_free_blkcipher(local->wep_tx_tfm);
- return -ENOMEM;
+ return PTR_ERR(local->wep_rx_tfm);
}
return 0;
@@ -84,24 +84,17 @@ static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local,
struct sk_buff *skb,
struct ieee80211_key *key)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u16 fc;
- int hdrlen;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ unsigned int hdrlen;
u8 *newhdr;
- fc = le16_to_cpu(hdr->frame_control);
- fc |= IEEE80211_FCTL_PROTECTED;
- hdr->frame_control = cpu_to_le16(fc);
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
- if ((skb_headroom(skb) < WEP_IV_LEN ||
- skb_tailroom(skb) < WEP_ICV_LEN)) {
- I802_DEBUG_INC(local->tx_expand_skb_head);
- if (unlikely(pskb_expand_head(skb, WEP_IV_LEN, WEP_ICV_LEN,
- GFP_ATOMIC)))
- return NULL;
- }
+ if (WARN_ON(skb_tailroom(skb) < WEP_ICV_LEN ||
+ skb_headroom(skb) < WEP_IV_LEN))
+ return NULL;
- hdrlen = ieee80211_get_hdrlen(fc);
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
newhdr = skb_push(skb, WEP_IV_LEN);
memmove(newhdr, newhdr + WEP_IV_LEN, hdrlen);
ieee80211_wep_get_iv(local, key, newhdr + hdrlen);
@@ -113,12 +106,10 @@ static void ieee80211_wep_remove_iv(struct ieee80211_local *local,
struct sk_buff *skb,
struct ieee80211_key *key)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u16 fc;
- int hdrlen;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ unsigned int hdrlen;
- fc = le16_to_cpu(hdr->frame_control);
- hdrlen = ieee80211_get_hdrlen(fc);
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen);
skb_pull(skb, WEP_IV_LEN);
}
@@ -228,22 +219,19 @@ int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
u32 klen;
u8 *rc4key;
u8 keyidx;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u16 fc;
- int hdrlen;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ unsigned int hdrlen;
size_t len;
int ret = 0;
- fc = le16_to_cpu(hdr->frame_control);
- if (!(fc & IEEE80211_FCTL_PROTECTED))
+ if (!ieee80211_has_protected(hdr->frame_control))
return -1;
- hdrlen = ieee80211_get_hdrlen(fc);
-
- if (skb->len < 8 + hdrlen)
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ if (skb->len < hdrlen + WEP_IV_LEN + WEP_ICV_LEN)
return -1;
- len = skb->len - hdrlen - 8;
+ len = skb->len - hdrlen - WEP_IV_LEN - WEP_ICV_LEN;
keyidx = skb->data[hdrlen + 3] >> 6;
@@ -264,11 +252,8 @@ int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
if (ieee80211_wep_decrypt_data(local->wep_rx_tfm, rc4key, klen,
skb->data + hdrlen + WEP_IV_LEN,
- len)) {
- if (net_ratelimit())
- printk(KERN_DEBUG "WEP decrypt failed (ICV)\n");
+ len))
ret = -1;
- }
kfree(rc4key);
@@ -285,17 +270,15 @@ int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u16 fc;
- int hdrlen;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ unsigned int hdrlen;
u8 *ivpos;
u32 iv;
- fc = le16_to_cpu(hdr->frame_control);
- if (!(fc & IEEE80211_FCTL_PROTECTED))
+ if (!ieee80211_has_protected(hdr->frame_control))
return NULL;
- hdrlen = ieee80211_get_hdrlen(fc);
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
ivpos = skb->data + hdrlen;
iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2];
@@ -308,24 +291,19 @@ u8 * 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)
{
- if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
- ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
- (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH))
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+
+ if (!ieee80211_is_data(hdr->frame_control) &&
+ !ieee80211_is_auth(hdr->frame_control))
return RX_CONTINUE;
if (!(rx->status->flag & RX_FLAG_DECRYPTED)) {
- if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) {
-#ifdef CONFIG_MAC80211_DEBUG
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: RX WEP frame, decrypt "
- "failed\n", rx->dev->name);
-#endif /* CONFIG_MAC80211_DEBUG */
+ if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key))
return RX_DROP_UNUSABLE;
- }
} else if (!(rx->status->flag & RX_FLAG_IV_STRIPPED)) {
ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
/* remove ICV */
- skb_trim(rx->skb, rx->skb->len - 4);
+ skb_trim(rx->skb, rx->skb->len - WEP_ICV_LEN);
}
return RX_CONTINUE;
@@ -333,11 +311,13 @@ 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);
+
if (!(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
if (ieee80211_wep_encrypt(tx->local, skb, tx->key))
return -1;
} else {
- tx->control->key_idx = tx->key->conf.hw_key_idx;
+ info->control.hw_key = &tx->key->conf;
if (tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) {
if (!ieee80211_wep_add_iv(tx->local, skb, tx->key))
return -1;
@@ -349,8 +329,6 @@ static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
ieee80211_tx_result
ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
{
- tx->control->iv_len = WEP_IV_LEN;
- tx->control->icv_len = WEP_ICV_LEN;
ieee80211_tx_set_protected(tx);
if (wep_encrypt_skb(tx, tx->skb) < 0) {
diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h
index 363779c5065..e587172115b 100644
--- a/net/mac80211/wep.h
+++ b/net/mac80211/wep.h
@@ -26,7 +26,7 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb,
struct ieee80211_key *key);
int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
struct ieee80211_key *key);
-u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);
+u8 *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/wext.c b/net/mac80211/wext.c
index e8404212ad5..742f811ca41 100644
--- a/net/mac80211/wext.c
+++ b/net/mac80211/wext.c
@@ -27,22 +27,19 @@
#include "aes_ccm.h"
-static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
+static int ieee80211_set_encryption(struct ieee80211_sub_if_data *sdata, u8 *sta_addr,
int idx, int alg, int remove,
int set_tx_key, const u8 *_key,
size_t key_len)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
struct ieee80211_key *key;
- struct ieee80211_sub_if_data *sdata;
int err;
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
if (idx < 0 || idx >= NUM_DEFAULT_KEYS) {
printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d\n",
- dev->name, idx);
+ sdata->dev->name, idx);
return -EINVAL;
}
@@ -125,13 +122,13 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev,
if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
return -EOPNOTSUPP;
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
- sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
- int ret = ieee80211_sta_set_extra_ie(dev, extra, data->length);
+ if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length);
if (ret)
return ret;
sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
- ieee80211_sta_req_auth(dev, &sdata->u.sta);
+ ieee80211_sta_req_auth(sdata, &sdata->u.sta);
return 0;
}
@@ -142,7 +139,39 @@ static int ieee80211_ioctl_giwname(struct net_device *dev,
struct iw_request_info *info,
char *name, char *extra)
{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_supported_band *sband;
+ u8 is_ht = 0, is_a = 0, is_b = 0, is_g = 0;
+
+
+ sband = local->hw.wiphy->bands[IEEE80211_BAND_5GHZ];
+ if (sband) {
+ is_a = 1;
+ is_ht |= sband->ht_info.ht_supported;
+ }
+
+ sband = local->hw.wiphy->bands[IEEE80211_BAND_2GHZ];
+ if (sband) {
+ int i;
+ /* Check for mandatory rates */
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (sband->bitrates[i].bitrate == 10)
+ is_b = 1;
+ if (sband->bitrates[i].bitrate == 60)
+ is_g = 1;
+ }
+ is_ht |= sband->ht_info.ht_supported;
+ }
+
strcpy(name, "IEEE 802.11");
+ if (is_a)
+ strcat(name, "a");
+ if (is_b)
+ strcat(name, "b");
+ if (is_g)
+ strcat(name, "g");
+ if (is_ht)
+ strcat(name, "n");
return 0;
}
@@ -176,14 +205,26 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev,
range->num_encoding_sizes = 2;
range->max_encoding_tokens = NUM_DEFAULT_KEYS;
- range->max_qual.qual = local->hw.max_signal;
- range->max_qual.level = local->hw.max_rssi;
- range->max_qual.noise = local->hw.max_noise;
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC ||
+ local->hw.flags & IEEE80211_HW_SIGNAL_DB)
+ range->max_qual.level = local->hw.max_signal;
+ else if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
+ range->max_qual.level = -110;
+ else
+ range->max_qual.level = 0;
+
+ if (local->hw.flags & IEEE80211_HW_NOISE_DBM)
+ range->max_qual.noise = -110;
+ else
+ range->max_qual.noise = 0;
+
+ range->max_qual.qual = 100;
range->max_qual.updated = local->wstats_flags;
- range->avg_qual.qual = local->hw.max_signal/2;
- range->avg_qual.level = 0;
- range->avg_qual.noise = 0;
+ range->avg_qual.qual = 50;
+ /* not always true but better than nothing */
+ range->avg_qual.level = range->max_qual.level / 2;
+ range->avg_qual.noise = range->max_qual.noise / 2;
range->avg_qual.updated = local->wstats_flags;
range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
@@ -232,35 +273,27 @@ static int ieee80211_ioctl_siwmode(struct net_device *dev,
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
int type;
- if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
return -EOPNOTSUPP;
switch (*mode) {
case IW_MODE_INFRA:
- type = IEEE80211_IF_TYPE_STA;
+ type = NL80211_IFTYPE_STATION;
break;
case IW_MODE_ADHOC:
- type = IEEE80211_IF_TYPE_IBSS;
+ type = NL80211_IFTYPE_ADHOC;
break;
case IW_MODE_REPEAT:
- type = IEEE80211_IF_TYPE_WDS;
+ type = NL80211_IFTYPE_WDS;
break;
case IW_MODE_MONITOR:
- type = IEEE80211_IF_TYPE_MNTR;
+ type = NL80211_IFTYPE_MONITOR;
break;
default:
return -EINVAL;
}
- if (type == sdata->vif.type)
- return 0;
- if (netif_running(dev))
- return -EBUSY;
-
- ieee80211_if_reinit(dev);
- ieee80211_if_set_type(dev, type);
-
- return 0;
+ return ieee80211_if_change_type(sdata, type);
}
@@ -272,22 +305,22 @@ static int ieee80211_ioctl_giwmode(struct net_device *dev,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
switch (sdata->vif.type) {
- case IEEE80211_IF_TYPE_AP:
+ case NL80211_IFTYPE_AP:
*mode = IW_MODE_MASTER;
break;
- case IEEE80211_IF_TYPE_STA:
+ case NL80211_IFTYPE_STATION:
*mode = IW_MODE_INFRA;
break;
- case IEEE80211_IF_TYPE_IBSS:
+ case NL80211_IFTYPE_ADHOC:
*mode = IW_MODE_ADHOC;
break;
- case IEEE80211_IF_TYPE_MNTR:
+ case NL80211_IFTYPE_MONITOR:
*mode = IW_MODE_MONITOR;
break;
- case IEEE80211_IF_TYPE_WDS:
+ case NL80211_IFTYPE_WDS:
*mode = IW_MODE_REPEAT;
break;
- case IEEE80211_IF_TYPE_VLAN:
+ case NL80211_IFTYPE_AP_VLAN:
*mode = IW_MODE_SECOND; /* FIXME */
break;
default:
@@ -297,60 +330,31 @@ static int ieee80211_ioctl_giwmode(struct net_device *dev,
return 0;
}
-int ieee80211_set_freq(struct net_device *dev, int freqMHz)
-{
- int ret = -EINVAL;
- struct ieee80211_channel *chan;
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- chan = ieee80211_get_channel(local->hw.wiphy, freqMHz);
-
- if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
- if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS &&
- chan->flags & IEEE80211_CHAN_NO_IBSS) {
- printk(KERN_DEBUG "%s: IBSS not allowed on frequency "
- "%d MHz\n", dev->name, chan->center_freq);
- return ret;
- }
- local->oper_channel = chan;
-
- if (local->sta_sw_scanning || local->sta_hw_scanning)
- ret = 0;
- else
- ret = ieee80211_hw_config(local);
-
- rate_control_clear(local);
- }
-
- return ret;
-}
-
static int ieee80211_ioctl_siwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *freq, char *extra)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA)
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL;
/* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */
if (freq->e == 0) {
if (freq->m < 0) {
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA)
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
sdata->u.sta.flags |=
IEEE80211_STA_AUTO_CHANNEL_SEL;
return 0;
} else
- return ieee80211_set_freq(dev,
+ return ieee80211_set_freq(sdata,
ieee80211_channel_to_frequency(freq->m));
} else {
int i, div = 1000000;
for (i = 0; i < freq->e; i++)
div /= 10;
if (div > 0)
- return ieee80211_set_freq(dev, freq->m / div);
+ return ieee80211_set_freq(sdata, freq->m / div);
else
return -EINVAL;
}
@@ -382,8 +386,8 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
len--;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
- sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
+ if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC) {
int ret;
if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
if (len > IEEE80211_MAX_SSID_LEN)
@@ -396,19 +400,19 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
else
sdata->u.sta.flags |= IEEE80211_STA_AUTO_SSID_SEL;
- ret = ieee80211_sta_set_ssid(dev, ssid, len);
+ ret = ieee80211_sta_set_ssid(sdata, ssid, len);
if (ret)
return ret;
- ieee80211_sta_req_auth(dev, &sdata->u.sta);
+ ieee80211_sta_req_auth(sdata, &sdata->u.sta);
return 0;
}
- if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
memcpy(sdata->u.ap.ssid, ssid, len);
memset(sdata->u.ap.ssid + len, 0,
IEEE80211_MAX_SSID_LEN - len);
sdata->u.ap.ssid_len = len;
- return ieee80211_if_config(dev);
+ return ieee80211_if_config(sdata, IEEE80211_IFCC_SSID);
}
return -EOPNOTSUPP;
}
@@ -422,9 +426,9 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev,
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
- sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
- int res = ieee80211_sta_get_ssid(dev, ssid, &len);
+ if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ int res = ieee80211_sta_get_ssid(sdata, ssid, &len);
if (res == 0) {
data->length = len;
data->flags = 1;
@@ -433,7 +437,7 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev,
return res;
}
- if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
len = sdata->u.ap.ssid_len;
if (len > IW_ESSID_MAX_SIZE)
len = IW_ESSID_MAX_SIZE;
@@ -453,8 +457,8 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
- sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
+ if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC) {
int ret;
if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
memcpy(sdata->u.sta.bssid, (u8 *) &ap_addr->sa_data,
@@ -468,12 +472,12 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
sdata->u.sta.flags |= IEEE80211_STA_AUTO_BSSID_SEL;
else
sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
- ret = ieee80211_sta_set_bssid(dev, (u8 *) &ap_addr->sa_data);
+ ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data);
if (ret)
return ret;
- ieee80211_sta_req_auth(dev, &sdata->u.sta);
+ ieee80211_sta_req_auth(sdata, &sdata->u.sta);
return 0;
- } else if (sdata->vif.type == IEEE80211_IF_TYPE_WDS) {
+ } else if (sdata->vif.type == NL80211_IFTYPE_WDS) {
/*
* If it is necessary to update the WDS peer address
* while the interface is running, then we need to do
@@ -501,10 +505,10 @@ static int ieee80211_ioctl_giwap(struct net_device *dev,
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
- sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
- if (sdata->u.sta.state == IEEE80211_ASSOCIATED ||
- sdata->u.sta.state == IEEE80211_IBSS_JOINED) {
+ if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ if (sdata->u.sta.state == IEEE80211_STA_MLME_ASSOCIATED ||
+ sdata->u.sta.state == IEEE80211_STA_MLME_IBSS_JOINED) {
ap_addr->sa_family = ARPHRD_ETHER;
memcpy(&ap_addr->sa_data, sdata->u.sta.bssid, ETH_ALEN);
return 0;
@@ -512,7 +516,7 @@ static int ieee80211_ioctl_giwap(struct net_device *dev,
memset(&ap_addr->sa_data, 0, ETH_ALEN);
return 0;
}
- } else if (sdata->vif.type == IEEE80211_IF_TYPE_WDS) {
+ } else if (sdata->vif.type == NL80211_IFTYPE_WDS) {
ap_addr->sa_family = ARPHRD_ETHER;
memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN);
return 0;
@@ -534,10 +538,10 @@ static int ieee80211_ioctl_siwscan(struct net_device *dev,
if (!netif_running(dev))
return -ENETDOWN;
- if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
- sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
- sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT &&
- sdata->vif.type != IEEE80211_IF_TYPE_AP)
+ if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+ sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
+ sdata->vif.type != NL80211_IFTYPE_AP)
return -EOPNOTSUPP;
/* if SSID was specified explicitly then use that */
@@ -548,7 +552,7 @@ static int ieee80211_ioctl_siwscan(struct net_device *dev,
ssid_len = req->essid_len;
}
- return ieee80211_sta_req_scan(dev, ssid, ssid_len);
+ return ieee80211_request_scan(sdata, ssid, ssid_len);
}
@@ -558,11 +562,14 @@ static int ieee80211_ioctl_giwscan(struct net_device *dev,
{
int res;
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_sub_if_data *sdata;
- if (local->sta_sw_scanning || local->sta_hw_scanning)
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (local->sw_scanning || local->hw_scanning)
return -EAGAIN;
- res = ieee80211_sta_scan_results(dev, extra, data->length);
+ res = ieee80211_scan_results(local, info, extra, data->length);
if (res >= 0) {
data->length = res;
return 0;
@@ -583,16 +590,14 @@ static int ieee80211_ioctl_siwrate(struct net_device *dev,
struct ieee80211_supported_band *sband;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (!sdata->bss)
- return -ENODEV;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
/* target_rate = -1, rate->fixed = 0 means auto only, so use all rates
* target_rate = X, rate->fixed = 1 means only rate X
* target_rate = X, rate->fixed = 0 means all rates <= X */
- sdata->bss->max_ratectrl_rateidx = -1;
- sdata->bss->force_unicast_rateidx = -1;
+ sdata->max_ratectrl_rateidx = -1;
+ sdata->force_unicast_rateidx = -1;
if (rate->value < 0)
return 0;
@@ -601,9 +606,9 @@ static int ieee80211_ioctl_siwrate(struct net_device *dev,
int this_rate = brate->bitrate;
if (target_rate == this_rate) {
- sdata->bss->max_ratectrl_rateidx = i;
+ sdata->max_ratectrl_rateidx = i;
if (rate->fixed)
- sdata->bss->force_unicast_rateidx = i;
+ sdata->force_unicast_rateidx = i;
err = 0;
break;
}
@@ -622,7 +627,7 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
return -EOPNOTSUPP;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
@@ -631,8 +636,8 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev,
sta = sta_info_get(local, sdata->u.sta.bssid);
- if (sta && sta->txrate_idx < sband->n_bitrates)
- rate->value = sband->bitrates[sta->txrate_idx].bitrate;
+ if (sta && sta->last_txrate_idx < sband->n_bitrates)
+ rate->value = sband->bitrates[sta->last_txrate_idx].bitrate;
else
rate->value = 0;
@@ -716,6 +721,9 @@ static int ieee80211_ioctl_siwrts(struct net_device *dev,
if (rts->disabled)
local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
+ else if (!rts->fixed)
+ /* if the rts value is not fixed, then take default */
+ local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
else if (rts->value < 0 || rts->value > IEEE80211_MAX_RTS_THRESHOLD)
return -EINVAL;
else
@@ -753,6 +761,8 @@ static int ieee80211_ioctl_siwfrag(struct net_device *dev,
if (frag->disabled)
local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
+ else if (!frag->fixed)
+ local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
else if (frag->value < 256 ||
frag->value > IEEE80211_MAX_FRAG_THRESHOLD)
return -EINVAL;
@@ -765,7 +775,7 @@ static int ieee80211_ioctl_siwfrag(struct net_device *dev,
* configure it here */
if (local->ops->set_frag_threshold)
- local->ops->set_frag_threshold(
+ return local->ops->set_frag_threshold(
local_to_hw(local),
local->fragmentation_threshold);
@@ -848,17 +858,17 @@ static int ieee80211_ioctl_siwmlme(struct net_device *dev,
struct iw_mlme *mlme = (struct iw_mlme *) extra;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
- sdata->vif.type != IEEE80211_IF_TYPE_IBSS)
+ if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+ sdata->vif.type != NL80211_IFTYPE_ADHOC)
return -EINVAL;
switch (mlme->cmd) {
case IW_MLME_DEAUTH:
/* TODO: mlme->addr.sa_data */
- return ieee80211_sta_deauthenticate(dev, mlme->reason_code);
+ return ieee80211_sta_deauthenticate(sdata, mlme->reason_code);
case IW_MLME_DISASSOC:
/* TODO: mlme->addr.sa_data */
- return ieee80211_sta_disassociate(dev, mlme->reason_code);
+ return ieee80211_sta_disassociate(sdata, mlme->reason_code);
default:
return -EOPNOTSUPP;
}
@@ -899,7 +909,7 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
}
return ieee80211_set_encryption(
- dev, bcaddr,
+ sdata, bcaddr,
idx, alg, remove,
!sdata->default_key,
keybuf, erq->length);
@@ -944,6 +954,58 @@ static int ieee80211_ioctl_giwencode(struct net_device *dev,
erq->length = sdata->keys[idx]->conf.keylen;
erq->flags |= IW_ENCODE_ENABLED;
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ switch (ifsta->auth_alg) {
+ case WLAN_AUTH_OPEN:
+ case WLAN_AUTH_LEAP:
+ erq->flags |= IW_ENCODE_OPEN;
+ break;
+ case WLAN_AUTH_SHARED_KEY:
+ erq->flags |= IW_ENCODE_RESTRICTED;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int ieee80211_ioctl_siwpower(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *wrq,
+ char *extra)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_conf *conf = &local->hw.conf;
+
+ if (wrq->disabled) {
+ conf->flags &= ~IEEE80211_CONF_PS;
+ return ieee80211_hw_config(local);
+ }
+
+ switch (wrq->flags & IW_POWER_MODE) {
+ case IW_POWER_ON: /* If not specified */
+ case IW_POWER_MODE: /* If set all mask */
+ case IW_POWER_ALL_R: /* If explicitely state all */
+ conf->flags |= IEEE80211_CONF_PS;
+ break;
+ default: /* Otherwise we don't support it */
+ return -EINVAL;
+ }
+
+ return ieee80211_hw_config(local);
+}
+
+static int ieee80211_ioctl_giwpower(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_conf *conf = &local->hw.conf;
+
+ wrqu->power.disabled = !(conf->flags & IEEE80211_CONF_PS);
+
return 0;
}
@@ -966,7 +1028,7 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev,
sdata->drop_unencrypted = !!data->value;
break;
case IW_AUTH_PRIVACY_INVOKED:
- if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
ret = -EINVAL;
else {
sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
@@ -981,8 +1043,8 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev,
}
break;
case IW_AUTH_80211_AUTH_ALG:
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
- sdata->vif.type == IEEE80211_IF_TYPE_IBSS)
+ if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC)
sdata->u.sta.auth_algs = data->value;
else
ret = -EOPNOTSUPP;
@@ -1004,8 +1066,8 @@ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev
rcu_read_lock();
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
- sdata->vif.type == IEEE80211_IF_TYPE_IBSS)
+ if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC)
sta = sta_info_get(local, sdata->u.sta.bssid);
if (!sta) {
wstats->discard.fragment = 0;
@@ -1015,8 +1077,8 @@ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev
wstats->qual.noise = 0;
wstats->qual.updated = IW_QUAL_ALL_INVALID;
} else {
- wstats->qual.level = sta->last_rssi;
- wstats->qual.qual = sta->last_signal;
+ wstats->qual.level = sta->last_signal;
+ wstats->qual.qual = sta->last_qual;
wstats->qual.noise = sta->last_noise;
wstats->qual.updated = local->wstats_flags;
}
@@ -1035,8 +1097,8 @@ static int ieee80211_ioctl_giwauth(struct net_device *dev,
switch (data->flags & IW_AUTH_INDEX) {
case IW_AUTH_80211_AUTH_ALG:
- if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
- sdata->vif.type == IEEE80211_IF_TYPE_IBSS)
+ if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC)
data->value = sdata->u.sta.auth_algs;
else
ret = -EOPNOTSUPP;
@@ -1093,7 +1155,7 @@ static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
} else
idx--;
- return ieee80211_set_encryption(dev, ext->addr.sa_data, idx, alg,
+ return ieee80211_set_encryption(sdata, ext->addr.sa_data, idx, alg,
remove,
ext->ext_flags &
IW_ENCODE_EXT_SET_TX_KEY,
@@ -1149,8 +1211,8 @@ static const iw_handler ieee80211_handler[] =
(iw_handler) ieee80211_ioctl_giwretry, /* SIOCGIWRETRY */
(iw_handler) ieee80211_ioctl_siwencode, /* SIOCSIWENCODE */
(iw_handler) ieee80211_ioctl_giwencode, /* SIOCGIWENCODE */
- (iw_handler) NULL, /* SIOCSIWPOWER */
- (iw_handler) NULL, /* SIOCGIWPOWER */
+ (iw_handler) ieee80211_ioctl_siwpower, /* SIOCSIWPOWER */
+ (iw_handler) ieee80211_ioctl_giwpower, /* SIOCGIWPOWER */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) ieee80211_ioctl_siwgenie, /* SIOCSIWGENIE */
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 5d09e8698b5..139b5f267b3 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -18,61 +18,40 @@
#include "ieee80211_i.h"
#include "wme.h"
-/* maximum number of hardware queues we support. */
-#define TC_80211_MAX_QUEUES 16
-
+/* Default mapping in classifier to work with default
+ * queue setup.
+ */
const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 };
-struct ieee80211_sched_data
-{
- unsigned long qdisc_pool[BITS_TO_LONGS(TC_80211_MAX_QUEUES)];
- struct tcf_proto *filter_list;
- struct Qdisc *queues[TC_80211_MAX_QUEUES];
- struct sk_buff_head requeued[TC_80211_MAX_QUEUES];
-};
-
static const char llc_ip_hdr[8] = {0xAA, 0xAA, 0x3, 0, 0, 0, 0x08, 0};
-/* given a data frame determine the 802.1p/1d tag to use */
-static inline unsigned classify_1d(struct sk_buff *skb, struct Qdisc *qd)
+/* Given a data frame determine the 802.1p/1d tag to use. */
+static unsigned int classify_1d(struct sk_buff *skb)
{
- struct iphdr *ip;
- int dscp;
- int offset;
-
- struct ieee80211_sched_data *q = qdisc_priv(qd);
- struct tcf_result res = { -1, 0 };
-
- /* if there is a user set filter list, call out to that */
- if (q->filter_list) {
- tc_classify(skb, q->filter_list, &res);
- if (res.class != -1)
- return res.class;
- }
+ unsigned int dscp;
/* skb->priority values from 256->263 are magic values to
- * directly indicate a specific 802.1d priority.
- * This is used to allow 802.1d priority to be passed directly in
- * from VLAN tags, etc. */
+ * directly indicate a specific 802.1d priority. This is used
+ * to allow 802.1d priority to be passed directly in from VLAN
+ * tags, etc.
+ */
if (skb->priority >= 256 && skb->priority <= 263)
return skb->priority - 256;
- /* check there is a valid IP header present */
- offset = ieee80211_get_hdrlen_from_skb(skb);
- if (skb->len < offset + sizeof(llc_ip_hdr) + sizeof(*ip) ||
- memcmp(skb->data + offset, llc_ip_hdr, sizeof(llc_ip_hdr)))
- return 0;
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ dscp = ip_hdr(skb)->tos & 0xfc;
+ break;
- ip = (struct iphdr *) (skb->data + offset + sizeof(llc_ip_hdr));
-
- dscp = ip->tos & 0xfc;
- if (dscp & 0x1c)
+ default:
return 0;
+ }
+
return dscp >> 5;
}
-static inline int wme_downgrade_ac(struct sk_buff *skb)
+static int wme_downgrade_ac(struct sk_buff *skb)
{
switch (skb->priority) {
case 6:
@@ -93,43 +72,37 @@ static inline int wme_downgrade_ac(struct sk_buff *skb)
}
-/* positive return value indicates which queue to use
- * negative return value indicates to drop the frame */
-static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd)
+/* Indicate which queue to use. */
+static u16 classify80211(struct ieee80211_local *local, struct sk_buff *skb)
{
- struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- unsigned short fc = le16_to_cpu(hdr->frame_control);
- int qos;
- /* see if frame is data or non data frame */
- if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)) {
+ if (!ieee80211_is_data(hdr->frame_control)) {
/* management frames go on AC_VO queue, but are sent
* without QoS control fields */
- return IEEE80211_TX_QUEUE_DATA0;
+ return 0;
}
if (0 /* injected */) {
/* use AC from radiotap */
}
- /* is this a QoS frame? */
- qos = fc & IEEE80211_STYPE_QOS_DATA;
-
- if (!qos) {
+ if (!ieee80211_is_data_qos(hdr->frame_control)) {
skb->priority = 0; /* required for correct WPA/11i MIC */
return ieee802_1d_to_ac[skb->priority];
}
/* use the data classifier to determine what 802.1d tag the
* data frame has */
- skb->priority = classify_1d(skb, qd);
+ skb->priority = classify_1d(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)) {
- /* No AC with lower priority has acm=0, drop packet. */
- return -1;
+ /* The old code would drop the packet in this
+ * case.
+ */
+ return 0;
}
}
@@ -137,55 +110,53 @@ static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd)
return ieee802_1d_to_ac[skb->priority];
}
-
-static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd)
+u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
{
- struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr);
- struct ieee80211_sched_data *q = qdisc_priv(qd);
- struct ieee80211_tx_packet_data *pkt_data =
- (struct ieee80211_tx_packet_data *) skb->cb;
+ struct ieee80211_master_priv *mpriv = netdev_priv(dev);
+ struct ieee80211_local *local = mpriv->local;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- unsigned short fc = le16_to_cpu(hdr->frame_control);
- struct Qdisc *qdisc;
- int err, queue;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct sta_info *sta;
+ u16 queue;
u8 tid;
- if (pkt_data->flags & IEEE80211_TXPD_REQUEUE) {
- queue = pkt_data->queue;
+ queue = classify80211(local, skb);
+ if (unlikely(queue >= local->hw.queues))
+ queue = local->hw.queues - 1;
+
+ if (info->flags & IEEE80211_TX_CTL_REQUEUE) {
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
- tid = skb->priority & QOS_CONTROL_TAG1D_MASK;
+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
if (sta) {
+ struct ieee80211_hw *hw = &local->hw;
int ampdu_queue = sta->tid_to_tx_q[tid];
- if ((ampdu_queue < local->hw.queues) &&
- test_bit(ampdu_queue, q->qdisc_pool)) {
+
+ if ((ampdu_queue < ieee80211_num_queues(hw)) &&
+ test_bit(ampdu_queue, local->queue_pool)) {
queue = ampdu_queue;
- pkt_data->flags |= IEEE80211_TXPD_AMPDU;
+ info->flags |= IEEE80211_TX_CTL_AMPDU;
} else {
- pkt_data->flags &= ~IEEE80211_TXPD_AMPDU;
+ info->flags &= ~IEEE80211_TX_CTL_AMPDU;
}
}
rcu_read_unlock();
- skb_queue_tail(&q->requeued[queue], skb);
- qd->q.qlen++;
- return 0;
- }
- queue = classify80211(skb, qd);
+ return queue;
+ }
- /* now we know the 1d priority, fill in the QoS header if there is one
+ /* Now we know the 1d priority, fill in the QoS header if
+ * there is one.
*/
- if (WLAN_FC_IS_QOS_DATA(fc)) {
- u8 *p = skb->data + ieee80211_get_hdrlen(fc) - 2;
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ u8 *p = ieee80211_get_qos_ctl(hdr);
u8 ack_policy = 0;
- tid = skb->priority & QOS_CONTROL_TAG1D_MASK;
+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
if (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;
- p++;
+ *p++ = ack_policy | tid;
*p = 0;
rcu_read_lock();
@@ -193,475 +164,40 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd)
sta = sta_info_get(local, hdr->addr1);
if (sta) {
int ampdu_queue = sta->tid_to_tx_q[tid];
- if ((ampdu_queue < local->hw.queues) &&
- test_bit(ampdu_queue, q->qdisc_pool)) {
+ struct ieee80211_hw *hw = &local->hw;
+
+ if ((ampdu_queue < ieee80211_num_queues(hw)) &&
+ test_bit(ampdu_queue, local->queue_pool)) {
queue = ampdu_queue;
- pkt_data->flags |= IEEE80211_TXPD_AMPDU;
+ info->flags |= IEEE80211_TX_CTL_AMPDU;
} else {
- pkt_data->flags &= ~IEEE80211_TXPD_AMPDU;
+ info->flags &= ~IEEE80211_TX_CTL_AMPDU;
}
}
rcu_read_unlock();
}
- if (unlikely(queue >= local->hw.queues)) {
-#if 0
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s - queue=%d (hw does not "
- "support) -> %d\n",
- __func__, queue, local->hw.queues - 1);
- }
-#endif
- queue = local->hw.queues - 1;
- }
-
- if (unlikely(queue < 0)) {
- kfree_skb(skb);
- err = NET_XMIT_DROP;
- } else {
- tid = skb->priority & QOS_CONTROL_TAG1D_MASK;
- pkt_data->queue = (unsigned int) queue;
- qdisc = q->queues[queue];
- err = qdisc->enqueue(skb, qdisc);
- if (err == NET_XMIT_SUCCESS) {
- qd->q.qlen++;
- qd->bstats.bytes += skb->len;
- qd->bstats.packets++;
- return NET_XMIT_SUCCESS;
- }
- }
- qd->qstats.drops++;
- return err;
-}
-
-
-/* TODO: clean up the cases where master_hard_start_xmit
- * returns non 0 - it shouldn't ever do that. Once done we
- * can remove this function */
-static int wme_qdiscop_requeue(struct sk_buff *skb, struct Qdisc* qd)
-{
- struct ieee80211_sched_data *q = qdisc_priv(qd);
- struct ieee80211_tx_packet_data *pkt_data =
- (struct ieee80211_tx_packet_data *) skb->cb;
- struct Qdisc *qdisc;
- int err;
-
- /* we recorded which queue to use earlier! */
- qdisc = q->queues[pkt_data->queue];
-
- if ((err = qdisc->ops->requeue(skb, qdisc)) == 0) {
- qd->q.qlen++;
- return 0;
- }
- qd->qstats.drops++;
- return err;
-}
-
-
-static struct sk_buff *wme_qdiscop_dequeue(struct Qdisc* qd)
-{
- struct ieee80211_sched_data *q = qdisc_priv(qd);
- struct net_device *dev = qd->dev;
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_hw *hw = &local->hw;
- struct sk_buff *skb;
- struct Qdisc *qdisc;
- int queue;
-
- /* check all the h/w queues in numeric/priority order */
- for (queue = 0; queue < hw->queues; queue++) {
- /* see if there is room in this hardware queue */
- if ((test_bit(IEEE80211_LINK_STATE_XOFF,
- &local->state[queue])) ||
- (test_bit(IEEE80211_LINK_STATE_PENDING,
- &local->state[queue])) ||
- (!test_bit(queue, q->qdisc_pool)))
- continue;
-
- /* there is space - try and get a frame */
- skb = skb_dequeue(&q->requeued[queue]);
- if (skb) {
- qd->q.qlen--;
- return skb;
- }
-
- qdisc = q->queues[queue];
- skb = qdisc->dequeue(qdisc);
- if (skb) {
- qd->q.qlen--;
- return skb;
- }
- }
- /* returning a NULL here when all the h/w queues are full means we
- * never need to call netif_stop_queue in the driver */
- return NULL;
-}
-
-
-static void wme_qdiscop_reset(struct Qdisc* qd)
-{
- struct ieee80211_sched_data *q = qdisc_priv(qd);
- struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr);
- struct ieee80211_hw *hw = &local->hw;
- int queue;
-
- /* QUESTION: should we have some hardware flush functionality here? */
-
- for (queue = 0; queue < hw->queues; queue++) {
- skb_queue_purge(&q->requeued[queue]);
- qdisc_reset(q->queues[queue]);
- }
- qd->q.qlen = 0;
-}
-
-
-static void wme_qdiscop_destroy(struct Qdisc* qd)
-{
- struct ieee80211_sched_data *q = qdisc_priv(qd);
- struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr);
- struct ieee80211_hw *hw = &local->hw;
- int queue;
-
- tcf_destroy_chain(&q->filter_list);
-
- for (queue=0; queue < hw->queues; queue++) {
- skb_queue_purge(&q->requeued[queue]);
- qdisc_destroy(q->queues[queue]);
- q->queues[queue] = &noop_qdisc;
- }
-}
-
-
-/* called whenever parameters are updated on existing qdisc */
-static int wme_qdiscop_tune(struct Qdisc *qd, struct nlattr *opt)
-{
-/* struct ieee80211_sched_data *q = qdisc_priv(qd);
-*/
- /* check our options block is the right size */
- /* copy any options to our local structure */
-/* Ignore options block for now - always use static mapping
- struct tc_ieee80211_qopt *qopt = nla_data(opt);
-
- if (opt->nla_len < nla_attr_size(sizeof(*qopt)))
- return -EINVAL;
- memcpy(q->tag2queue, qopt->tag2queue, sizeof(qopt->tag2queue));
-*/
- return 0;
-}
-
-
-/* called during initial creation of qdisc on device */
-static int wme_qdiscop_init(struct Qdisc *qd, struct nlattr *opt)
-{
- struct ieee80211_sched_data *q = qdisc_priv(qd);
- struct net_device *dev = qd->dev;
- struct ieee80211_local *local;
- int queues;
- int err = 0, i;
-
- /* check that device is a mac80211 device */
- if (!dev->ieee80211_ptr ||
- dev->ieee80211_ptr->wiphy->privid != mac80211_wiphy_privid)
- return -EINVAL;
-
- /* check this device is an ieee80211 master type device */
- if (dev->type != ARPHRD_IEEE80211)
- return -EINVAL;
-
- /* check that there is no qdisc currently attached to device
- * this ensures that we will be the root qdisc. (I can't find a better
- * way to test this explicitly) */
- if (dev->qdisc_sleeping != &noop_qdisc)
- return -EINVAL;
-
- if (qd->flags & TCQ_F_INGRESS)
- return -EINVAL;
-
- local = wdev_priv(dev->ieee80211_ptr);
- queues = local->hw.queues;
-
- /* if options were passed in, set them */
- if (opt) {
- err = wme_qdiscop_tune(qd, opt);
- }
-
- /* create child queues */
- for (i = 0; i < queues; i++) {
- skb_queue_head_init(&q->requeued[i]);
- q->queues[i] = qdisc_create_dflt(qd->dev, &pfifo_qdisc_ops,
- qd->handle);
- if (!q->queues[i]) {
- q->queues[i] = &noop_qdisc;
- printk(KERN_ERR "%s child qdisc %i creation failed\n",
- dev->name, i);
- }
- }
-
- /* reserve all legacy QoS queues */
- for (i = 0; i < min(IEEE80211_TX_QUEUE_DATA4, queues); i++)
- set_bit(i, q->qdisc_pool);
-
- return err;
-}
-
-static int wme_qdiscop_dump(struct Qdisc *qd, struct sk_buff *skb)
-{
-/* struct ieee80211_sched_data *q = qdisc_priv(qd);
- unsigned char *p = skb->tail;
- struct tc_ieee80211_qopt opt;
-
- memcpy(&opt.tag2queue, q->tag2queue, TC_80211_MAX_TAG + 1);
- NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
-*/ return skb->len;
-/*
-nla_put_failure:
- skb_trim(skb, p - skb->data);*/
- return -1;
-}
-
-
-static int wme_classop_graft(struct Qdisc *qd, unsigned long arg,
- struct Qdisc *new, struct Qdisc **old)
-{
- struct ieee80211_sched_data *q = qdisc_priv(qd);
- struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr);
- struct ieee80211_hw *hw = &local->hw;
- unsigned long queue = arg - 1;
-
- if (queue >= hw->queues)
- return -EINVAL;
-
- if (!new)
- new = &noop_qdisc;
-
- sch_tree_lock(qd);
- *old = q->queues[queue];
- q->queues[queue] = new;
- qdisc_reset(*old);
- sch_tree_unlock(qd);
-
- return 0;
-}
-
-
-static struct Qdisc *
-wme_classop_leaf(struct Qdisc *qd, unsigned long arg)
-{
- struct ieee80211_sched_data *q = qdisc_priv(qd);
- struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr);
- struct ieee80211_hw *hw = &local->hw;
- unsigned long queue = arg - 1;
-
- if (queue >= hw->queues)
- return NULL;
-
- return q->queues[queue];
-}
-
-
-static unsigned long wme_classop_get(struct Qdisc *qd, u32 classid)
-{
- struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr);
- struct ieee80211_hw *hw = &local->hw;
- unsigned long queue = TC_H_MIN(classid);
-
- if (queue - 1 >= hw->queues)
- return 0;
-
return queue;
}
-
-static unsigned long wme_classop_bind(struct Qdisc *qd, unsigned long parent,
- u32 classid)
-{
- return wme_classop_get(qd, classid);
-}
-
-
-static void wme_classop_put(struct Qdisc *q, unsigned long cl)
-{
-}
-
-
-static int wme_classop_change(struct Qdisc *qd, u32 handle, u32 parent,
- struct nlattr **tca, unsigned long *arg)
-{
- unsigned long cl = *arg;
- struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr);
- struct ieee80211_hw *hw = &local->hw;
-
- if (cl - 1 > hw->queues)
- return -ENOENT;
-
- /* TODO: put code to program hardware queue parameters here,
- * to allow programming from tc command line */
-
- return 0;
-}
-
-
-/* we don't support deleting hardware queues
- * when we add WMM-SA support - TSPECs may be deleted here */
-static int wme_classop_delete(struct Qdisc *qd, unsigned long cl)
-{
- struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr);
- struct ieee80211_hw *hw = &local->hw;
-
- if (cl - 1 > hw->queues)
- return -ENOENT;
- return 0;
-}
-
-
-static int wme_classop_dump_class(struct Qdisc *qd, unsigned long cl,
- struct sk_buff *skb, struct tcmsg *tcm)
-{
- struct ieee80211_sched_data *q = qdisc_priv(qd);
- struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr);
- struct ieee80211_hw *hw = &local->hw;
-
- if (cl - 1 > hw->queues)
- return -ENOENT;
- tcm->tcm_handle = TC_H_MIN(cl);
- tcm->tcm_parent = qd->handle;
- tcm->tcm_info = q->queues[cl-1]->handle; /* do we need this? */
- return 0;
-}
-
-
-static void wme_classop_walk(struct Qdisc *qd, struct qdisc_walker *arg)
-{
- struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr);
- struct ieee80211_hw *hw = &local->hw;
- int queue;
-
- if (arg->stop)
- return;
-
- for (queue = 0; queue < hw->queues; queue++) {
- if (arg->count < arg->skip) {
- arg->count++;
- continue;
- }
- /* we should return classids for our internal queues here
- * as well as the external ones */
- if (arg->fn(qd, queue+1, arg) < 0) {
- arg->stop = 1;
- break;
- }
- arg->count++;
- }
-}
-
-
-static struct tcf_proto ** wme_classop_find_tcf(struct Qdisc *qd,
- unsigned long cl)
-{
- struct ieee80211_sched_data *q = qdisc_priv(qd);
-
- if (cl)
- return NULL;
-
- return &q->filter_list;
-}
-
-
-/* this qdisc is classful (i.e. has classes, some of which may have leaf qdiscs attached)
- * - these are the operations on the classes */
-static const struct Qdisc_class_ops class_ops =
-{
- .graft = wme_classop_graft,
- .leaf = wme_classop_leaf,
-
- .get = wme_classop_get,
- .put = wme_classop_put,
- .change = wme_classop_change,
- .delete = wme_classop_delete,
- .walk = wme_classop_walk,
-
- .tcf_chain = wme_classop_find_tcf,
- .bind_tcf = wme_classop_bind,
- .unbind_tcf = wme_classop_put,
-
- .dump = wme_classop_dump_class,
-};
-
-
-/* queueing discipline operations */
-static struct Qdisc_ops wme_qdisc_ops __read_mostly =
-{
- .next = NULL,
- .cl_ops = &class_ops,
- .id = "ieee80211",
- .priv_size = sizeof(struct ieee80211_sched_data),
-
- .enqueue = wme_qdiscop_enqueue,
- .dequeue = wme_qdiscop_dequeue,
- .requeue = wme_qdiscop_requeue,
- .drop = NULL, /* drop not needed since we are always the root qdisc */
-
- .init = wme_qdiscop_init,
- .reset = wme_qdiscop_reset,
- .destroy = wme_qdiscop_destroy,
- .change = wme_qdiscop_tune,
-
- .dump = wme_qdiscop_dump,
-};
-
-
-void ieee80211_install_qdisc(struct net_device *dev)
-{
- struct Qdisc *qdisc;
-
- qdisc = qdisc_create_dflt(dev, &wme_qdisc_ops, TC_H_ROOT);
- if (!qdisc) {
- printk(KERN_ERR "%s: qdisc installation failed\n", dev->name);
- return;
- }
-
- /* same handle as would be allocated by qdisc_alloc_handle() */
- qdisc->handle = 0x80010000;
-
- qdisc_lock_tree(dev);
- list_add_tail(&qdisc->list, &dev->qdisc_list);
- dev->qdisc_sleeping = qdisc;
- qdisc_unlock_tree(dev);
-}
-
-
-int ieee80211_qdisc_installed(struct net_device *dev)
-{
- return dev->qdisc_sleeping->ops == &wme_qdisc_ops;
-}
-
-
-int ieee80211_wme_register(void)
-{
- return register_qdisc(&wme_qdisc_ops);
-}
-
-
-void ieee80211_wme_unregister(void)
-{
- unregister_qdisc(&wme_qdisc_ops);
-}
-
int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
- struct sta_info *sta, u16 tid)
+ struct sta_info *sta, u16 tid)
{
int i;
- struct ieee80211_sched_data *q =
- qdisc_priv(local->mdev->qdisc_sleeping);
- DECLARE_MAC_BUF(mac);
+
+ /* XXX: currently broken due to cb/requeue use */
+ return -EPERM;
/* prepare the filter and save it for the SW queue
- * matching the recieved HW queue */
+ * matching the received HW queue */
+
+ if (!local->hw.ampdu_queues)
+ return -EPERM;
/* try to get a Qdisc from the pool */
- for (i = IEEE80211_TX_QUEUE_BEACON; i < local->hw.queues; i++)
- if (!test_and_set_bit(i, q->qdisc_pool)) {
+ for (i = local->hw.queues; i < ieee80211_num_queues(&local->hw); i++)
+ if (!test_and_set_bit(i, local->queue_pool)) {
ieee80211_stop_queue(local_to_hw(local), i);
sta->tid_to_tx_q[tid] = i;
@@ -670,11 +206,13 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
* on the previous queue
* since HT is strict in order */
#ifdef CONFIG_MAC80211_HT_DEBUG
- if (net_ratelimit())
+ if (net_ratelimit()) {
+ DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG "allocated aggregation queue"
" %d tid %d addr %s pool=0x%lX\n",
- i, tid, print_mac(mac, sta->addr),
- q->qdisc_pool[0]);
+ i, tid, print_mac(mac, sta->sta.addr),
+ local->queue_pool[0]);
+ }
#endif /* CONFIG_MAC80211_HT_DEBUG */
return 0;
}
@@ -683,44 +221,81 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
}
/**
- * the caller needs to hold local->mdev->queue_lock
+ * the caller needs to hold netdev_get_tx_queue(local->mdev, X)->lock
*/
void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local,
struct sta_info *sta, u16 tid,
u8 requeue)
{
- struct ieee80211_sched_data *q =
- qdisc_priv(local->mdev->qdisc_sleeping);
int agg_queue = sta->tid_to_tx_q[tid];
+ struct ieee80211_hw *hw = &local->hw;
/* return the qdisc to the pool */
- clear_bit(agg_queue, q->qdisc_pool);
- sta->tid_to_tx_q[tid] = local->hw.queues;
+ clear_bit(agg_queue, local->queue_pool);
+ sta->tid_to_tx_q[tid] = ieee80211_num_queues(hw);
- if (requeue)
+ if (requeue) {
ieee80211_requeue(local, agg_queue);
- else
- q->queues[agg_queue]->ops->reset(q->queues[agg_queue]);
+ } else {
+ struct netdev_queue *txq;
+ spinlock_t *root_lock;
+ struct Qdisc *q;
+
+ txq = netdev_get_tx_queue(local->mdev, agg_queue);
+ q = rcu_dereference(txq->qdisc);
+ root_lock = qdisc_lock(q);
+
+ spin_lock_bh(root_lock);
+ qdisc_reset(q);
+ spin_unlock_bh(root_lock);
+ }
}
void ieee80211_requeue(struct ieee80211_local *local, int queue)
{
- struct Qdisc *root_qd = local->mdev->qdisc_sleeping;
- struct ieee80211_sched_data *q = qdisc_priv(root_qd);
- struct Qdisc *qdisc = q->queues[queue];
- struct sk_buff *skb = NULL;
+ struct netdev_queue *txq = netdev_get_tx_queue(local->mdev, queue);
+ struct sk_buff_head list;
+ spinlock_t *root_lock;
+ struct Qdisc *qdisc;
u32 len;
+ rcu_read_lock_bh();
+
+ qdisc = rcu_dereference(txq->qdisc);
if (!qdisc || !qdisc->dequeue)
- return;
+ goto out_unlock;
+
+ skb_queue_head_init(&list);
- printk(KERN_DEBUG "requeue: qlen = %d\n", qdisc->q.qlen);
+ root_lock = qdisc_root_lock(qdisc);
+ spin_lock(root_lock);
for (len = qdisc->q.qlen; len > 0; len--) {
- skb = qdisc->dequeue(qdisc);
- root_qd->q.qlen--;
- /* packet will be classified again and */
- /* skb->packet_data->queue will be overridden if needed */
+ struct sk_buff *skb = qdisc->dequeue(qdisc);
+
if (skb)
- wme_qdiscop_enqueue(skb, root_qd);
+ __skb_queue_tail(&list, skb);
+ }
+ spin_unlock(root_lock);
+
+ for (len = list.qlen; len > 0; len--) {
+ struct sk_buff *skb = __skb_dequeue(&list);
+ u16 new_queue;
+
+ BUG_ON(!skb);
+ new_queue = ieee80211_select_queue(local->mdev, skb);
+ skb_set_queue_mapping(skb, new_queue);
+
+ txq = netdev_get_tx_queue(local->mdev, new_queue);
+
+
+ qdisc = rcu_dereference(txq->qdisc);
+ root_lock = qdisc_root_lock(qdisc);
+
+ spin_lock(root_lock);
+ qdisc_enqueue_root(skb, qdisc);
+ spin_unlock(root_lock);
}
+
+out_unlock:
+ rcu_read_unlock_bh();
}
diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h
index fcc6b05508c..bc62f28a4d3 100644
--- a/net/mac80211/wme.h
+++ b/net/mac80211/wme.h
@@ -1,5 +1,4 @@
/*
- * IEEE 802.11 driver (80211.o) - QoS datatypes
* Copyright 2004, Instant802 Networks, Inc.
* Copyright 2005, Devicescape Software, Inc.
*
@@ -14,62 +13,19 @@
#include <linux/netdevice.h>
#include "ieee80211_i.h"
-#define QOS_CONTROL_LEN 2
-
#define QOS_CONTROL_ACK_POLICY_NORMAL 0
#define QOS_CONTROL_ACK_POLICY_NOACK 1
-#define QOS_CONTROL_TID_MASK 0x0f
#define QOS_CONTROL_ACK_POLICY_SHIFT 5
-#define QOS_CONTROL_TAG1D_MASK 0x07
-
extern const int ieee802_1d_to_ac[8];
-static inline int WLAN_FC_IS_QOS_DATA(u16 fc)
-{
- return (fc & 0x8C) == 0x88;
-}
-
-#ifdef CONFIG_NET_SCHED
-void ieee80211_install_qdisc(struct net_device *dev);
-int ieee80211_qdisc_installed(struct net_device *dev);
+u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb);
int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
struct sta_info *sta, u16 tid);
void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local,
struct sta_info *sta, u16 tid,
u8 requeue);
void ieee80211_requeue(struct ieee80211_local *local, int queue);
-int ieee80211_wme_register(void);
-void ieee80211_wme_unregister(void);
-#else
-static inline void ieee80211_install_qdisc(struct net_device *dev)
-{
-}
-static inline int ieee80211_qdisc_installed(struct net_device *dev)
-{
- return 0;
-}
-static inline int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
- struct sta_info *sta, u16 tid)
-{
- return -EAGAIN;
-}
-static inline void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local,
- struct sta_info *sta, u16 tid,
- u8 requeue)
-{
-}
-static inline void ieee80211_requeue(struct ieee80211_local *local, int queue)
-{
-}
-static inline int ieee80211_wme_register(void)
-{
- return 0;
-}
-static inline void ieee80211_wme_unregister(void)
-{
-}
-#endif /* CONFIG_NET_SCHED */
#endif /* _WME_H */
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 45709ada8fe..6db649480e8 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -11,6 +11,8 @@
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/compiler.h>
+#include <linux/ieee80211.h>
+#include <asm/unaligned.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
@@ -19,76 +21,30 @@
#include "aes_ccm.h"
#include "wpa.h"
-static int ieee80211_get_hdr_info(const struct sk_buff *skb, u8 **sa, u8 **da,
- u8 *qos_tid, u8 **data, size_t *data_len)
-{
- struct ieee80211_hdr *hdr;
- size_t hdrlen;
- u16 fc;
- int a4_included;
- u8 *pos;
-
- hdr = (struct ieee80211_hdr *) skb->data;
- fc = le16_to_cpu(hdr->frame_control);
-
- hdrlen = 24;
- if ((fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) ==
- (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
- hdrlen += ETH_ALEN;
- *sa = hdr->addr4;
- *da = hdr->addr3;
- } else if (fc & IEEE80211_FCTL_FROMDS) {
- *sa = hdr->addr3;
- *da = hdr->addr1;
- } else if (fc & IEEE80211_FCTL_TODS) {
- *sa = hdr->addr2;
- *da = hdr->addr3;
- } else {
- *sa = hdr->addr2;
- *da = hdr->addr1;
- }
-
- if (fc & 0x80)
- hdrlen += 2;
-
- *data = skb->data + hdrlen;
- *data_len = skb->len - hdrlen;
-
- a4_included = (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
- (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS);
- if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
- fc & IEEE80211_STYPE_QOS_DATA) {
- pos = (u8 *) &hdr->addr4;
- if (a4_included)
- pos += 6;
- *qos_tid = pos[0] & 0x0f;
- *qos_tid |= 0x80; /* qos_included flag */
- } else
- *qos_tid = 0;
-
- return skb->len < hdrlen ? -1 : 0;
-}
-
-
ieee80211_tx_result
ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
{
- u8 *data, *sa, *da, *key, *mic, qos_tid;
+ u8 *data, *key, *mic, key_offset;
size_t data_len;
- u16 fc;
+ unsigned int hdrlen;
+ struct ieee80211_hdr *hdr;
struct sk_buff *skb = tx->skb;
int authenticator;
int wpa_test = 0;
+ int tail;
- fc = tx->fc;
-
+ hdr = (struct ieee80211_hdr *)skb->data;
if (!tx->key || tx->key->conf.alg != ALG_TKIP || skb->len < 24 ||
- !WLAN_FC_DATA_PRESENT(fc))
+ !ieee80211_is_data_present(hdr->frame_control))
return TX_CONTINUE;
- if (ieee80211_get_hdr_info(skb, &sa, &da, &qos_tid, &data, &data_len))
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ if (skb->len < hdrlen)
return TX_DROP;
+ data = skb->data + hdrlen;
+ data_len = skb->len - hdrlen;
+
if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
!(tx->flags & IEEE80211_TX_FRAGMENTED) &&
!(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) &&
@@ -98,26 +54,27 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
return TX_CONTINUE;
}
- if (skb_tailroom(skb) < MICHAEL_MIC_LEN) {
- I802_DEBUG_INC(tx->local->tx_expand_skb_head);
- if (unlikely(pskb_expand_head(skb, TKIP_IV_LEN,
- MICHAEL_MIC_LEN + TKIP_ICV_LEN,
- GFP_ATOMIC))) {
- printk(KERN_DEBUG "%s: failed to allocate more memory "
- "for Michael MIC\n", tx->dev->name);
- return TX_DROP;
- }
- }
+ tail = MICHAEL_MIC_LEN;
+ if (!(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
+ tail += TKIP_ICV_LEN;
+
+ if (WARN_ON(skb_tailroom(skb) < tail ||
+ skb_headroom(skb) < TKIP_IV_LEN))
+ return TX_DROP;
#if 0
authenticator = fc & IEEE80211_FCTL_FROMDS; /* FIX */
#else
authenticator = 1;
#endif
- key = &tx->key->conf.key[authenticator ? ALG_TKIP_TEMP_AUTH_TX_MIC_KEY :
- ALG_TKIP_TEMP_AUTH_RX_MIC_KEY];
+ /* At this point we know we're using ALG_TKIP. To get the MIC key
+ * we now will rely on the offset from the ieee80211_key_conf::key */
+ key_offset = authenticator ?
+ NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY :
+ NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
+ key = &tx->key->conf.key[key_offset];
mic = skb_put(skb, MICHAEL_MIC_LEN);
- michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic);
+ michael_mic(key, hdr, data, data_len, mic);
return TX_CONTINUE;
}
@@ -126,48 +83,51 @@ 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, *sa, *da, *key = NULL, qos_tid;
+ u8 *data, *key = NULL, key_offset;
size_t data_len;
- u16 fc;
+ unsigned int hdrlen;
+ struct ieee80211_hdr *hdr;
u8 mic[MICHAEL_MIC_LEN];
struct sk_buff *skb = rx->skb;
int authenticator = 1, wpa_test = 0;
DECLARE_MAC_BUF(mac);
- fc = rx->fc;
-
/*
* No way to verify the MIC if the hardware stripped it
*/
if (rx->status->flag & RX_FLAG_MMIC_STRIPPED)
return RX_CONTINUE;
+ hdr = (struct ieee80211_hdr *)skb->data;
if (!rx->key || rx->key->conf.alg != ALG_TKIP ||
- !(rx->fc & IEEE80211_FCTL_PROTECTED) || !WLAN_FC_DATA_PRESENT(fc))
+ !ieee80211_has_protected(hdr->frame_control) ||
+ !ieee80211_is_data_present(hdr->frame_control))
return RX_CONTINUE;
- if (ieee80211_get_hdr_info(skb, &sa, &da, &qos_tid, &data, &data_len)
- || data_len < MICHAEL_MIC_LEN)
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ if (skb->len < hdrlen + MICHAEL_MIC_LEN)
return RX_DROP_UNUSABLE;
- data_len -= MICHAEL_MIC_LEN;
+ 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 = &rx->key->conf.key[authenticator ? ALG_TKIP_TEMP_AUTH_RX_MIC_KEY :
- ALG_TKIP_TEMP_AUTH_TX_MIC_KEY];
- michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic);
+ /* At this point we know we're using ALG_TKIP. To get the MIC key
+ * we now will rely on the offset from the ieee80211_key_conf::key */
+ key_offset = authenticator ?
+ NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY :
+ NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
+ key = &rx->key->conf.key[key_offset];
+ michael_mic(key, hdr, data, data_len, mic);
if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0 || wpa_test) {
if (!(rx->flags & IEEE80211_RX_RA_MATCH))
return RX_DROP_UNUSABLE;
- printk(KERN_DEBUG "%s: invalid Michael MIC in data frame from "
- "%s\n", rx->dev->name, print_mac(mac, sa));
-
- mac80211_ev_michael_mic_failure(rx->dev, rx->key->conf.keyidx,
+ mac80211_ev_michael_mic_failure(rx->sdata, rx->key->conf.keyidx,
(void *) skb->data);
return RX_DROP_UNUSABLE;
}
@@ -176,59 +136,55 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
/* update IV in key information to be able to detect replays */
- rx->key->u.tkip.iv32_rx[rx->queue] = rx->tkip_iv32;
- rx->key->u.tkip.iv16_rx[rx->queue] = rx->tkip_iv16;
+ rx->key->u.tkip.rx[rx->queue].iv32 = rx->tkip_iv32;
+ rx->key->u.tkip.rx[rx->queue].iv16 = rx->tkip_iv16;
return RX_CONTINUE;
}
-static int tkip_encrypt_skb(struct ieee80211_tx_data *tx,
- struct sk_buff *skb, int test)
+static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_key *key = tx->key;
- int hdrlen, len, tailneed;
- u16 fc;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ unsigned int hdrlen;
+ int len, tail;
u8 *pos;
- fc = le16_to_cpu(hdr->frame_control);
- hdrlen = ieee80211_get_hdrlen(fc);
+ if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
+ !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
+ /* hwaccel - with no need for preallocated room for IV/ICV */
+ info->control.hw_key = &tx->key->conf;
+ return 0;
+ }
+
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
len = skb->len - hdrlen;
if (tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)
- tailneed = 0;
+ tail = 0;
else
- tailneed = TKIP_ICV_LEN;
-
- if ((skb_headroom(skb) < TKIP_IV_LEN ||
- skb_tailroom(skb) < tailneed)) {
- I802_DEBUG_INC(tx->local->tx_expand_skb_head);
- if (unlikely(pskb_expand_head(skb, TKIP_IV_LEN, tailneed,
- GFP_ATOMIC)))
- return -1;
- }
+ tail = TKIP_ICV_LEN;
+
+ if (WARN_ON(skb_tailroom(skb) < tail ||
+ skb_headroom(skb) < TKIP_IV_LEN))
+ return -1;
pos = skb_push(skb, TKIP_IV_LEN);
memmove(pos, pos + TKIP_IV_LEN, hdrlen);
pos += hdrlen;
/* Increase IV for the frame */
- key->u.tkip.iv16++;
- if (key->u.tkip.iv16 == 0)
- key->u.tkip.iv32++;
+ key->u.tkip.tx.iv16++;
+ if (key->u.tkip.tx.iv16 == 0)
+ key->u.tkip.tx.iv32++;
if (tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
- hdr = (struct ieee80211_hdr *)skb->data;
-
/* hwaccel - with preallocated room for IV */
- ieee80211_tkip_add_iv(pos, key,
- (u8) (key->u.tkip.iv16 >> 8),
- (u8) (((key->u.tkip.iv16 >> 8) | 0x20) &
- 0x7f),
- (u8) key->u.tkip.iv16);
+ ieee80211_tkip_add_iv(pos, key, key->u.tkip.tx.iv16);
- tx->control->key_idx = tx->key->conf.hw_key_idx;
+ info->control.hw_key = &tx->key->conf;
return 0;
}
@@ -246,28 +202,16 @@ ieee80211_tx_result
ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb = tx->skb;
- int wpa_test = 0, test = 0;
- tx->control->icv_len = TKIP_ICV_LEN;
- tx->control->iv_len = TKIP_IV_LEN;
ieee80211_tx_set_protected(tx);
- if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
- !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
- !wpa_test) {
- /* hwaccel - with no need for preallocated room for IV/ICV */
- tx->control->key_idx = tx->key->conf.hw_key_idx;
- return TX_CONTINUE;
- }
-
- if (tkip_encrypt_skb(tx, skb, test) < 0)
+ if (tkip_encrypt_skb(tx, skb) < 0)
return TX_DROP;
if (tx->extra_frag) {
int i;
for (i = 0; i < tx->num_extra_frag; i++) {
- if (tkip_encrypt_skb(tx, tx->extra_frag[i], test)
- < 0)
+ if (tkip_encrypt_skb(tx, tx->extra_frag[i]) < 0)
return TX_DROP;
}
}
@@ -280,16 +224,14 @@ ieee80211_rx_result
ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
- u16 fc;
int hdrlen, res, hwaccel = 0, wpa_test = 0;
struct ieee80211_key *key = rx->key;
struct sk_buff *skb = rx->skb;
DECLARE_MAC_BUF(mac);
- fc = le16_to_cpu(hdr->frame_control);
- hdrlen = ieee80211_get_hdrlen(fc);
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
- if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
+ if (!ieee80211_is_data(hdr->frame_control))
return RX_CONTINUE;
if (!rx->sta || skb->len - hdrlen < 12)
@@ -311,19 +253,12 @@ 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->addr,
+ skb->len - hdrlen, rx->sta->sta.addr,
hdr->addr1, hwaccel, rx->queue,
&rx->tkip_iv32,
&rx->tkip_iv16);
- if (res != TKIP_DECRYPT_OK || wpa_test) {
-#ifdef CONFIG_MAC80211_DEBUG
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: TKIP decrypt failed for RX "
- "frame from %s (res=%d)\n", rx->dev->name,
- print_mac(mac, rx->sta->addr), res);
-#endif /* CONFIG_MAC80211_DEBUG */
+ if (res != TKIP_DECRYPT_OK || wpa_test)
return RX_DROP_UNUSABLE;
- }
/* Trim ICV */
skb_trim(skb, skb->len - TKIP_ICV_LEN);
@@ -336,70 +271,68 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
}
-static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad,
+static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
int encrypted)
{
- u16 fc;
- int a4_included, qos_included;
- u8 qos_tid, *fc_pos, *data, *sa, *da;
- int len_a;
- size_t data_len;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ __le16 mask_fc;
+ int a4_included;
+ u8 qos_tid;
+ u8 *b_0, *aad;
+ u16 data_len, len_a;
+ unsigned int hdrlen;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- fc_pos = (u8 *) &hdr->frame_control;
- fc = fc_pos[0] ^ (fc_pos[1] << 8);
- a4_included = (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
- (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS);
-
- ieee80211_get_hdr_info(skb, &sa, &da, &qos_tid, &data, &data_len);
- data_len -= CCMP_HDR_LEN + (encrypted ? CCMP_MIC_LEN : 0);
- if (qos_tid & 0x80) {
- qos_included = 1;
- qos_tid &= 0x0f;
- } else
- qos_included = 0;
- /* First block, b_0 */
+ b_0 = scratch + 3 * AES_BLOCK_LEN;
+ aad = scratch + 4 * AES_BLOCK_LEN;
+ /*
+ * Mask FC: zero subtype b4 b5 b6
+ * Retry, PwrMgt, MoreData; set Protected
+ */
+ mask_fc = hdr->frame_control;
+ mask_fc &= ~cpu_to_le16(0x0070 | IEEE80211_FCTL_RETRY |
+ IEEE80211_FCTL_PM | IEEE80211_FCTL_MOREDATA);
+ mask_fc |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ len_a = hdrlen - 2;
+ a4_included = ieee80211_has_a4(hdr->frame_control);
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ qos_tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+ else
+ qos_tid = 0;
+
+ data_len = skb->len - hdrlen - CCMP_HDR_LEN;
+ if (encrypted)
+ data_len -= CCMP_MIC_LEN;
+
+ /* First block, b_0 */
b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */
/* Nonce: QoS Priority | A2 | PN */
b_0[1] = qos_tid;
- memcpy(&b_0[2], hdr->addr2, 6);
+ memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
memcpy(&b_0[8], pn, CCMP_PN_LEN);
/* l(m) */
- b_0[14] = (data_len >> 8) & 0xff;
- b_0[15] = data_len & 0xff;
-
+ put_unaligned_be16(data_len, &b_0[14]);
/* AAD (extra authenticate-only data) / masked 802.11 header
* FC | A1 | A2 | A3 | SC | [A4] | [QC] */
-
- len_a = a4_included ? 28 : 22;
- if (qos_included)
- len_a += 2;
-
- aad[0] = 0; /* (len_a >> 8) & 0xff; */
- aad[1] = len_a & 0xff;
- /* Mask FC: zero subtype b4 b5 b6 */
- aad[2] = fc_pos[0] & ~(BIT(4) | BIT(5) | BIT(6));
- /* Retry, PwrMgt, MoreData; set Protected */
- aad[3] = (fc_pos[1] & ~(BIT(3) | BIT(4) | BIT(5))) | BIT(6);
- memcpy(&aad[4], &hdr->addr1, 18);
+ put_unaligned_be16(len_a, &aad[0]);
+ put_unaligned(mask_fc, (__le16 *)&aad[2]);
+ memcpy(&aad[4], &hdr->addr1, 3 * ETH_ALEN);
/* Mask Seq#, leave Frag# */
aad[22] = *((u8 *) &hdr->seq_ctrl) & 0x0f;
aad[23] = 0;
+
if (a4_included) {
- memcpy(&aad[24], hdr->addr4, 6);
- aad[30] = 0;
+ memcpy(&aad[24], hdr->addr4, ETH_ALEN);
+ aad[30] = qos_tid;
aad[31] = 0;
- } else
- memset(&aad[24], 0, 8);
- if (qos_included) {
- u8 *dpos = &aad[a4_included ? 30 : 24];
-
- /* Mask QoS Control field */
- dpos[0] = qos_tid;
- dpos[1] = 0;
+ } else {
+ memset(&aad[24], 0, ETH_ALEN + IEEE80211_QOS_CTL_LEN);
+ aad[24] = qos_tid;
}
}
@@ -429,36 +362,34 @@ static inline int ccmp_hdr2pn(u8 *pn, u8 *hdr)
}
-static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx,
- struct sk_buff *skb, int test)
+static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_key *key = tx->key;
- int hdrlen, len, tailneed;
- u16 fc;
- u8 *pos, *pn, *b_0, *aad, *scratch;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ int hdrlen, len, tail;
+ u8 *pos, *pn;
int i;
- scratch = key->u.ccmp.tx_crypto_buf;
- b_0 = scratch + 3 * AES_BLOCK_LEN;
- aad = scratch + 4 * AES_BLOCK_LEN;
+ if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
+ !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
+ /* hwaccel - with no need for preallocated room for CCMP "
+ * header or MIC fields */
+ info->control.hw_key = &tx->key->conf;
+ return 0;
+ }
- fc = le16_to_cpu(hdr->frame_control);
- hdrlen = ieee80211_get_hdrlen(fc);
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
len = skb->len - hdrlen;
if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)
- tailneed = 0;
+ tail = 0;
else
- tailneed = CCMP_MIC_LEN;
-
- if ((skb_headroom(skb) < CCMP_HDR_LEN ||
- skb_tailroom(skb) < tailneed)) {
- I802_DEBUG_INC(tx->local->tx_expand_skb_head);
- if (unlikely(pskb_expand_head(skb, CCMP_HDR_LEN, tailneed,
- GFP_ATOMIC)))
- return -1;
- }
+ tail = CCMP_MIC_LEN;
+
+ if (WARN_ON(skb_tailroom(skb) < tail ||
+ skb_headroom(skb) < CCMP_HDR_LEN))
+ return -1;
pos = skb_push(skb, CCMP_HDR_LEN);
memmove(pos, pos + CCMP_HDR_LEN, hdrlen);
@@ -478,13 +409,13 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx,
if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
/* hwaccel - with preallocated room for CCMP header */
- tx->control->key_idx = key->conf.hw_key_idx;
+ info->control.hw_key = &tx->key->conf;
return 0;
}
pos += CCMP_HDR_LEN;
- ccmp_special_blocks(skb, pn, b_0, aad, 0);
- ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, b_0, aad, pos, 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));
return 0;
@@ -495,28 +426,16 @@ ieee80211_tx_result
ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb = tx->skb;
- int test = 0;
- tx->control->icv_len = CCMP_MIC_LEN;
- tx->control->iv_len = CCMP_HDR_LEN;
ieee80211_tx_set_protected(tx);
- if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
- !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
- /* hwaccel - with no need for preallocated room for CCMP "
- * header or MIC fields */
- tx->control->key_idx = tx->key->conf.hw_key_idx;
- return TX_CONTINUE;
- }
-
- if (ccmp_encrypt_skb(tx, skb, test) < 0)
+ if (ccmp_encrypt_skb(tx, skb) < 0)
return TX_DROP;
if (tx->extra_frag) {
int i;
for (i = 0; i < tx->num_extra_frag; i++) {
- if (ccmp_encrypt_skb(tx, tx->extra_frag[i], test)
- < 0)
+ if (ccmp_encrypt_skb(tx, tx->extra_frag[i]) < 0)
return TX_DROP;
}
}
@@ -528,8 +447,7 @@ ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
ieee80211_rx_result
ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
- u16 fc;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
int hdrlen;
struct ieee80211_key *key = rx->key;
struct sk_buff *skb = rx->skb;
@@ -537,10 +455,9 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
int data_len;
DECLARE_MAC_BUF(mac);
- fc = le16_to_cpu(hdr->frame_control);
- hdrlen = ieee80211_get_hdrlen(fc);
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
- if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)
+ if (!ieee80211_is_data(hdr->frame_control))
return RX_CONTINUE;
data_len = skb->len - hdrlen - CCMP_HDR_LEN - CCMP_MIC_LEN;
@@ -554,41 +471,19 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
(void) ccmp_hdr2pn(pn, skb->data + hdrlen);
if (memcmp(pn, key->u.ccmp.rx_pn[rx->queue], CCMP_PN_LEN) <= 0) {
-#ifdef CONFIG_MAC80211_DEBUG
- u8 *ppn = key->u.ccmp.rx_pn[rx->queue];
-
- printk(KERN_DEBUG "%s: CCMP replay detected for RX frame from "
- "%s (RX PN %02x%02x%02x%02x%02x%02x <= prev. PN "
- "%02x%02x%02x%02x%02x%02x)\n", rx->dev->name,
- print_mac(mac, rx->sta->addr),
- pn[0], pn[1], pn[2], pn[3], pn[4], pn[5],
- ppn[0], ppn[1], ppn[2], ppn[3], ppn[4], ppn[5]);
-#endif /* CONFIG_MAC80211_DEBUG */
key->u.ccmp.replays++;
return RX_DROP_UNUSABLE;
}
if (!(rx->status->flag & RX_FLAG_DECRYPTED)) {
/* hardware didn't decrypt/verify MIC */
- u8 *scratch, *b_0, *aad;
-
- scratch = key->u.ccmp.rx_crypto_buf;
- b_0 = scratch + 3 * AES_BLOCK_LEN;
- aad = scratch + 4 * AES_BLOCK_LEN;
-
- ccmp_special_blocks(skb, pn, b_0, aad, 1);
+ ccmp_special_blocks(skb, pn, key->u.ccmp.rx_crypto_buf, 1);
if (ieee80211_aes_ccm_decrypt(
- key->u.ccmp.tfm, scratch, b_0, aad,
+ 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)) {
-#ifdef CONFIG_MAC80211_DEBUG
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: CCMP decrypt failed "
- "for RX frame from %s\n", rx->dev->name,
- print_mac(mac, rx->sta->addr));
-#endif /* CONFIG_MAC80211_DEBUG */
return RX_DROP_UNUSABLE;
}
}