2007-05-06 02:45:53 +08:00
|
|
|
/*
|
|
|
|
* BSS client mode implementation
|
2009-01-08 19:31:59 +08:00
|
|
|
* Copyright 2003-2008, Jouni Malinen <j@w1.fi>
|
2007-05-06 02:45:53 +08:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2007-05-09 09:40:27 +08:00
|
|
|
#include <linux/delay.h>
|
2007-05-06 02:45:53 +08:00
|
|
|
#include <linux/if_ether.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <linux/etherdevice.h>
|
2008-02-25 23:27:46 +08:00
|
|
|
#include <linux/rtnetlink.h>
|
2009-04-16 19:17:25 +08:00
|
|
|
#include <linux/pm_qos_params.h>
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 19:17:26 +08:00
|
|
|
#include <linux/crc32.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2007-05-06 02:45:53 +08:00
|
|
|
#include <net/mac80211.h>
|
2008-09-11 06:01:49 +08:00
|
|
|
#include <asm/unaligned.h>
|
2008-09-08 23:44:22 +08:00
|
|
|
|
2007-05-06 02:45:53 +08:00
|
|
|
#include "ieee80211_i.h"
|
2009-04-24 00:52:52 +08:00
|
|
|
#include "driver-ops.h"
|
2008-04-09 03:14:40 +08:00
|
|
|
#include "rate.h"
|
|
|
|
#include "led.h"
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-07-31 23:54:12 +08:00
|
|
|
#define IEEE80211_MAX_PROBE_TRIES 5
|
2009-07-10 21:29:03 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* beacon loss detection timeout
|
|
|
|
* XXX: should depend on beacon interval
|
|
|
|
*/
|
|
|
|
#define IEEE80211_BEACON_LOSS_TIME (2 * HZ)
|
|
|
|
/*
|
|
|
|
* Time the connection can be idle before we probe
|
|
|
|
* it to see if we can still talk to the AP.
|
|
|
|
*/
|
2009-07-31 23:54:23 +08:00
|
|
|
#define IEEE80211_CONNECTION_IDLE_TIME (30 * HZ)
|
2009-07-10 21:29:03 +08:00
|
|
|
/*
|
|
|
|
* Time we wait for a probe response after sending
|
|
|
|
* a probe request because of beacon loss or for
|
|
|
|
* checking the connection still works.
|
|
|
|
*/
|
2009-07-31 23:54:23 +08:00
|
|
|
#define IEEE80211_PROBE_WAIT (HZ / 2)
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2010-03-30 14:28:30 +08:00
|
|
|
/*
|
|
|
|
* Weight given to the latest Beacon frame when calculating average signal
|
|
|
|
* strength for Beacon frames received in the current BSS. This must be
|
|
|
|
* between 1 and 15.
|
|
|
|
*/
|
|
|
|
#define IEEE80211_SIGNAL_AVE_WEIGHT 3
|
|
|
|
|
2009-05-17 17:40:42 +08:00
|
|
|
#define TMR_RUNNING_TIMER 0
|
|
|
|
#define TMR_RUNNING_CHANSW 1
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
/*
|
|
|
|
* All cfg80211 functions have to be called outside a locked
|
|
|
|
* section so that they can acquire a lock themselves... This
|
|
|
|
* is much simpler than queuing up things in cfg80211, but we
|
|
|
|
* do need some indirection for that here.
|
|
|
|
*/
|
|
|
|
enum rx_mgmt_action {
|
|
|
|
/* no action required */
|
|
|
|
RX_MGMT_NONE,
|
|
|
|
|
|
|
|
/* caller must call cfg80211_send_rx_auth() */
|
|
|
|
RX_MGMT_CFG80211_AUTH,
|
|
|
|
|
|
|
|
/* caller must call cfg80211_send_rx_assoc() */
|
|
|
|
RX_MGMT_CFG80211_ASSOC,
|
|
|
|
|
|
|
|
/* caller must call cfg80211_send_deauth() */
|
|
|
|
RX_MGMT_CFG80211_DEAUTH,
|
|
|
|
|
|
|
|
/* caller must call cfg80211_send_disassoc() */
|
|
|
|
RX_MGMT_CFG80211_DISASSOC,
|
|
|
|
|
2009-12-02 19:43:43 +08:00
|
|
|
/* caller must tell cfg80211 about internal error */
|
|
|
|
RX_MGMT_CFG80211_ASSOC_ERROR,
|
2009-07-07 09:45:17 +08:00
|
|
|
};
|
|
|
|
|
2008-09-08 23:44:27 +08:00
|
|
|
/* utils */
|
2009-07-07 09:45:17 +08:00
|
|
|
static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd)
|
|
|
|
{
|
|
|
|
WARN_ON(!mutex_is_locked(&ifmgd->mtx));
|
|
|
|
}
|
|
|
|
|
2009-07-10 08:39:48 +08:00
|
|
|
/*
|
|
|
|
* We can have multiple work items (and connection probing)
|
|
|
|
* scheduling this timer, but we need to take care to only
|
|
|
|
* reschedule it when it should fire _earlier_ than it was
|
|
|
|
* asked for before, or if it's not pending right now. This
|
|
|
|
* function ensures that. Note that it then is required to
|
|
|
|
* run this function for all timeouts after the first one
|
|
|
|
* has happened -- the work that runs from this timer will
|
|
|
|
* do that.
|
|
|
|
*/
|
|
|
|
static void run_again(struct ieee80211_if_managed *ifmgd,
|
|
|
|
unsigned long timeout)
|
|
|
|
{
|
|
|
|
ASSERT_MGD_MTX(ifmgd);
|
|
|
|
|
|
|
|
if (!timer_pending(&ifmgd->timer) ||
|
|
|
|
time_before(timeout, ifmgd->timer.expires))
|
|
|
|
mod_timer(&ifmgd->timer, timeout);
|
|
|
|
}
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
static void mod_beacon_timer(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
if (sdata->local->hw.flags & IEEE80211_HW_BEACON_FILTER)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mod_timer(&sdata->u.mgd.bcn_mon_timer,
|
|
|
|
round_jiffies_up(jiffies + IEEE80211_BEACON_LOSS_TIME));
|
|
|
|
}
|
|
|
|
|
2008-09-08 23:44:27 +08:00
|
|
|
static int ecw2cw(int ecw)
|
2008-09-08 23:44:22 +08:00
|
|
|
{
|
2008-09-08 23:44:27 +08:00
|
|
|
return (1 << ecw) - 1;
|
2008-09-08 23:44:22 +08:00
|
|
|
}
|
|
|
|
|
2009-03-30 19:23:35 +08:00
|
|
|
/*
|
|
|
|
* ieee80211_enable_ht should be called only after the operating band
|
|
|
|
* has been determined as ht configuration depends on the hw's
|
|
|
|
* HT abilities for a specific band.
|
|
|
|
*/
|
|
|
|
static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_ht_info *hti,
|
2009-07-07 09:45:17 +08:00
|
|
|
const u8 *bssid, u16 ap_ht_cap_flags)
|
2009-03-30 19:23:35 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
|
struct sta_info *sta;
|
|
|
|
u32 changed = 0;
|
2009-05-09 02:47:39 +08:00
|
|
|
u16 ht_opmode;
|
2010-05-05 21:28:27 +08:00
|
|
|
bool enable_ht = true;
|
|
|
|
enum nl80211_channel_type prev_chantype;
|
2009-03-30 19:23:35 +08:00
|
|
|
enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
|
|
|
|
|
|
|
|
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
|
|
|
|
|
2010-05-05 21:28:27 +08:00
|
|
|
prev_chantype = sdata->vif.bss_conf.channel_type;
|
|
|
|
|
2009-03-30 19:23:35 +08:00
|
|
|
/* HT is not supported */
|
|
|
|
if (!sband->ht_cap.ht_supported)
|
|
|
|
enable_ht = false;
|
|
|
|
|
|
|
|
/* check that channel matches the right operating channel */
|
|
|
|
if (local->hw.conf.channel->center_freq !=
|
|
|
|
ieee80211_channel_to_frequency(hti->control_chan))
|
|
|
|
enable_ht = false;
|
|
|
|
|
|
|
|
if (enable_ht) {
|
|
|
|
channel_type = NL80211_CHAN_HT20;
|
|
|
|
|
|
|
|
if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
|
|
|
|
(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
|
|
|
|
(hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
|
|
|
|
switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
|
|
|
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
2009-05-02 12:37:19 +08:00
|
|
|
if (!(local->hw.conf.channel->flags &
|
|
|
|
IEEE80211_CHAN_NO_HT40PLUS))
|
|
|
|
channel_type = NL80211_CHAN_HT40PLUS;
|
2009-03-30 19:23:35 +08:00
|
|
|
break;
|
|
|
|
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
2009-05-02 12:37:19 +08:00
|
|
|
if (!(local->hw.conf.channel->flags &
|
|
|
|
IEEE80211_CHAN_NO_HT40MINUS))
|
|
|
|
channel_type = NL80211_CHAN_HT40MINUS;
|
2009-03-30 19:23:35 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-20 01:46:31 +08:00
|
|
|
if (local->tmp_channel)
|
|
|
|
local->tmp_channel_type = channel_type;
|
2009-03-30 19:23:35 +08:00
|
|
|
|
2010-05-05 21:28:27 +08:00
|
|
|
if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
|
|
|
|
/* can only fail due to HT40+/- mismatch */
|
|
|
|
channel_type = NL80211_CHAN_HT20;
|
|
|
|
WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type));
|
|
|
|
}
|
2009-03-30 19:23:35 +08:00
|
|
|
|
2010-05-05 21:28:27 +08:00
|
|
|
/* channel_type change automatically detected */
|
|
|
|
ieee80211_hw_config(local, 0);
|
2009-03-30 19:23:35 +08:00
|
|
|
|
2010-05-05 21:28:27 +08:00
|
|
|
if (prev_chantype != channel_type) {
|
2009-03-30 19:23:35 +08:00
|
|
|
rcu_read_lock();
|
2009-11-26 00:46:18 +08:00
|
|
|
sta = sta_info_get(sdata, bssid);
|
2009-03-30 19:23:35 +08:00
|
|
|
if (sta)
|
|
|
|
rate_control_rate_update(local, sband, sta,
|
2010-03-01 17:12:57 +08:00
|
|
|
IEEE80211_RC_HT_CHANGED,
|
2010-05-05 21:28:27 +08:00
|
|
|
channel_type);
|
2009-03-30 19:23:35 +08:00
|
|
|
rcu_read_unlock();
|
2010-05-05 21:28:27 +08:00
|
|
|
}
|
2009-03-30 19:23:35 +08:00
|
|
|
|
2009-05-09 02:47:39 +08:00
|
|
|
ht_opmode = le16_to_cpu(hti->operation_mode);
|
2009-03-30 19:23:35 +08:00
|
|
|
|
|
|
|
/* if bss configuration changed store the new one */
|
2010-05-05 21:28:27 +08:00
|
|
|
if (sdata->ht_opmode_valid != enable_ht ||
|
|
|
|
sdata->vif.bss_conf.ht_operation_mode != ht_opmode ||
|
|
|
|
prev_chantype != channel_type) {
|
2009-03-30 19:23:35 +08:00
|
|
|
changed |= BSS_CHANGED_HT;
|
2009-05-09 02:47:39 +08:00
|
|
|
sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
|
2010-05-05 21:28:27 +08:00
|
|
|
sdata->ht_opmode_valid = enable_ht;
|
2009-03-30 19:23:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2008-09-11 06:01:52 +08:00
|
|
|
/* frame sending functions */
|
|
|
|
|
2008-09-09 16:58:25 +08:00
|
|
|
static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
|
2009-07-07 09:56:11 +08:00
|
|
|
const u8 *bssid, u16 stype, u16 reason,
|
2010-04-04 14:37:19 +08:00
|
|
|
void *cookie, bool send_frame)
|
2008-09-09 16:57:09 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
2009-02-15 19:44:28 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2008-09-09 16:57:09 +08:00
|
|
|
struct sk_buff *skb;
|
|
|
|
struct ieee80211_mgmt *mgmt;
|
2009-01-13 22:03:29 +08:00
|
|
|
|
2009-03-21 03:21:16 +08:00
|
|
|
skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
|
2008-09-09 16:57:09 +08:00
|
|
|
if (!skb) {
|
2008-09-09 16:58:25 +08:00
|
|
|
printk(KERN_DEBUG "%s: failed to allocate buffer for "
|
2009-11-26 00:46:19 +08:00
|
|
|
"deauth/disassoc frame\n", sdata->name);
|
2008-09-09 16:57:09 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
skb_reserve(skb, local->hw.extra_tx_headroom);
|
|
|
|
|
|
|
|
mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
|
|
|
|
memset(mgmt, 0, 24);
|
2009-07-07 09:45:17 +08:00
|
|
|
memcpy(mgmt->da, bssid, ETH_ALEN);
|
2009-11-26 00:46:19 +08:00
|
|
|
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
2009-07-07 09:45:17 +08:00
|
|
|
memcpy(mgmt->bssid, bssid, ETH_ALEN);
|
2008-09-09 16:58:25 +08:00
|
|
|
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
|
2008-09-09 16:57:09 +08:00
|
|
|
skb_put(skb, 2);
|
2008-09-09 16:58:25 +08:00
|
|
|
/* u.deauth.reason_code == u.disassoc.reason_code */
|
2008-09-09 16:57:09 +08:00
|
|
|
mgmt->u.deauth.reason_code = cpu_to_le16(reason);
|
|
|
|
|
2009-03-28 02:53:56 +08:00
|
|
|
if (stype == IEEE80211_STYPE_DEAUTH)
|
2009-10-13 19:28:13 +08:00
|
|
|
if (cookie)
|
|
|
|
__cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
|
|
|
|
else
|
|
|
|
cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
|
2009-03-28 02:53:56 +08:00
|
|
|
else
|
2009-10-13 19:28:13 +08:00
|
|
|
if (cookie)
|
|
|
|
__cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
|
|
|
|
else
|
|
|
|
cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
|
2009-11-19 01:42:05 +08:00
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
|
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
2010-04-04 14:37:19 +08:00
|
|
|
|
|
|
|
if (send_frame)
|
|
|
|
ieee80211_tx_skb(sdata, skb);
|
|
|
|
else
|
|
|
|
kfree_skb(skb);
|
2008-09-09 16:57:09 +08:00
|
|
|
}
|
|
|
|
|
2009-02-10 23:09:31 +08:00
|
|
|
void ieee80211_send_pspoll(struct ieee80211_local *local,
|
|
|
|
struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_pspoll *pspoll;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
2010-01-06 02:16:26 +08:00
|
|
|
skb = ieee80211_pspoll_get(&local->hw, &sdata->vif);
|
|
|
|
if (!skb)
|
2009-02-10 23:09:31 +08:00
|
|
|
return;
|
|
|
|
|
2010-01-06 02:16:26 +08:00
|
|
|
pspoll = (struct ieee80211_pspoll *) skb->data;
|
|
|
|
pspoll->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
|
2009-02-10 23:09:31 +08:00
|
|
|
|
2009-11-19 01:42:05 +08:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
|
|
|
ieee80211_tx_skb(sdata, skb);
|
2009-02-10 23:09:31 +08:00
|
|
|
}
|
|
|
|
|
2009-04-16 19:17:24 +08:00
|
|
|
void ieee80211_send_nullfunc(struct ieee80211_local *local,
|
|
|
|
struct ieee80211_sub_if_data *sdata,
|
|
|
|
int powersave)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
2010-01-06 02:16:26 +08:00
|
|
|
struct ieee80211_hdr_3addr *nullfunc;
|
2009-04-16 19:17:24 +08:00
|
|
|
|
2010-01-06 02:16:26 +08:00
|
|
|
skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif);
|
|
|
|
if (!skb)
|
2009-04-16 19:17:24 +08:00
|
|
|
return;
|
|
|
|
|
2010-01-06 02:16:26 +08:00
|
|
|
nullfunc = (struct ieee80211_hdr_3addr *) skb->data;
|
2009-04-16 19:17:24 +08:00
|
|
|
if (powersave)
|
2010-01-06 02:16:26 +08:00
|
|
|
nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
|
2009-04-16 19:17:24 +08:00
|
|
|
|
2009-11-19 01:42:05 +08:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
|
|
|
ieee80211_tx_skb(sdata, skb);
|
2009-04-16 19:17:24 +08:00
|
|
|
}
|
|
|
|
|
2010-01-09 01:06:26 +08:00
|
|
|
static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
|
|
|
|
struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct ieee80211_hdr *nullfunc;
|
|
|
|
__le16 fc;
|
|
|
|
|
|
|
|
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
|
|
|
|
return;
|
|
|
|
|
|
|
|
skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30);
|
|
|
|
if (!skb) {
|
|
|
|
printk(KERN_DEBUG "%s: failed to allocate buffer for 4addr "
|
|
|
|
"nullfunc frame\n", sdata->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
skb_reserve(skb, local->hw.extra_tx_headroom);
|
|
|
|
|
|
|
|
nullfunc = (struct ieee80211_hdr *) skb_put(skb, 30);
|
|
|
|
memset(nullfunc, 0, 30);
|
|
|
|
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
|
|
|
|
IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
|
|
|
|
nullfunc->frame_control = fc;
|
|
|
|
memcpy(nullfunc->addr1, sdata->u.mgd.bssid, ETH_ALEN);
|
|
|
|
memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
|
|
|
|
memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN);
|
|
|
|
memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN);
|
|
|
|
|
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
|
|
|
ieee80211_tx_skb(sdata, skb);
|
|
|
|
}
|
|
|
|
|
2009-05-15 17:52:31 +08:00
|
|
|
/* spectrum management related things */
|
|
|
|
static void ieee80211_chswitch_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
|
2009-12-23 20:15:31 +08:00
|
|
|
if (!ieee80211_sdata_running(sdata))
|
2009-05-15 17:52:31 +08:00
|
|
|
return;
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
|
if (!ifmgd->associated)
|
|
|
|
goto out;
|
2009-05-15 17:52:31 +08:00
|
|
|
|
|
|
|
sdata->local->oper_channel = sdata->local->csa_channel;
|
2010-05-11 22:20:57 +08:00
|
|
|
if (!sdata->local->ops->channel_switch) {
|
|
|
|
/* call "hw_config" only if doing sw channel switch */
|
|
|
|
ieee80211_hw_config(sdata->local,
|
|
|
|
IEEE80211_CONF_CHANGE_CHANNEL);
|
|
|
|
}
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2009-05-15 17:52:31 +08:00
|
|
|
/* XXX: shouldn't really modify cfg80211-owned data! */
|
2009-12-23 20:15:39 +08:00
|
|
|
ifmgd->associated->channel = sdata->local->oper_channel;
|
2009-05-15 17:52:31 +08:00
|
|
|
|
|
|
|
ieee80211_wake_queues_by_reason(&sdata->local->hw,
|
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
2009-07-07 09:45:17 +08:00
|
|
|
out:
|
|
|
|
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
|
|
|
|
mutex_unlock(&ifmgd->mtx);
|
2009-05-15 17:52:31 +08:00
|
|
|
}
|
|
|
|
|
2010-05-11 22:20:57 +08:00
|
|
|
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata;
|
|
|
|
struct ieee80211_if_managed *ifmgd;
|
|
|
|
|
|
|
|
sdata = vif_to_sdata(vif);
|
|
|
|
ifmgd = &sdata->u.mgd;
|
|
|
|
|
|
|
|
trace_api_chswitch_done(sdata, success);
|
|
|
|
if (!success) {
|
|
|
|
/*
|
|
|
|
* If the channel switch was not successful, stay
|
|
|
|
* around on the old channel. We currently lack
|
|
|
|
* good handling of this situation, possibly we
|
|
|
|
* should just drop the association.
|
|
|
|
*/
|
|
|
|
sdata->local->csa_channel = sdata->local->oper_channel;
|
|
|
|
}
|
|
|
|
|
|
|
|
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_chswitch_done);
|
|
|
|
|
2009-05-15 17:52:31 +08:00
|
|
|
static void ieee80211_chswitch_timer(unsigned long data)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
(struct ieee80211_sub_if_data *) data;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
|
2009-05-17 17:40:42 +08:00
|
|
|
if (sdata->local->quiescing) {
|
|
|
|
set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-30 08:08:07 +08:00
|
|
|
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
|
2009-05-15 17:52:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_channel_sw_ie *sw_elem,
|
2010-05-11 22:20:57 +08:00
|
|
|
struct ieee80211_bss *bss,
|
|
|
|
u64 timestamp)
|
2009-05-15 17:52:31 +08:00
|
|
|
{
|
2009-12-23 20:15:39 +08:00
|
|
|
struct cfg80211_bss *cbss =
|
|
|
|
container_of((void *)bss, struct cfg80211_bss, priv);
|
2009-05-15 17:52:31 +08:00
|
|
|
struct ieee80211_channel *new_ch;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num);
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
ASSERT_MGD_MTX(ifmgd);
|
|
|
|
|
|
|
|
if (!ifmgd->associated)
|
2009-05-15 17:52:31 +08:00
|
|
|
return;
|
|
|
|
|
2009-07-23 18:14:04 +08:00
|
|
|
if (sdata->local->scanning)
|
2009-05-15 17:52:31 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* Disregard subsequent beacons if we are already running a timer
|
|
|
|
processing a CSA */
|
|
|
|
|
|
|
|
if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
|
|
|
|
if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sdata->local->csa_channel = new_ch;
|
|
|
|
|
2010-05-11 22:20:57 +08:00
|
|
|
if (sdata->local->ops->channel_switch) {
|
|
|
|
/* use driver's channel switch callback */
|
|
|
|
struct ieee80211_channel_switch ch_switch;
|
|
|
|
memset(&ch_switch, 0, sizeof(ch_switch));
|
|
|
|
ch_switch.timestamp = timestamp;
|
|
|
|
if (sw_elem->mode) {
|
|
|
|
ch_switch.block_tx = true;
|
|
|
|
ieee80211_stop_queues_by_reason(&sdata->local->hw,
|
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
|
|
}
|
|
|
|
ch_switch.channel = new_ch;
|
|
|
|
ch_switch.count = sw_elem->count;
|
|
|
|
ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
|
|
|
|
drv_channel_switch(sdata->local, &ch_switch);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* channel switch handled in software */
|
2009-05-15 17:52:31 +08:00
|
|
|
if (sw_elem->count <= 1) {
|
2009-07-30 08:08:07 +08:00
|
|
|
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
|
2009-05-15 17:52:31 +08:00
|
|
|
} else {
|
2010-05-06 11:34:02 +08:00
|
|
|
if (sw_elem->mode)
|
|
|
|
ieee80211_stop_queues_by_reason(&sdata->local->hw,
|
2009-05-15 17:52:31 +08:00
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
|
|
ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
|
|
|
|
mod_timer(&ifmgd->chswitch_timer,
|
|
|
|
jiffies +
|
|
|
|
msecs_to_jiffies(sw_elem->count *
|
2009-12-23 20:15:39 +08:00
|
|
|
cbss->beacon_interval));
|
2009-05-15 17:52:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
|
|
|
u16 capab_info, u8 *pwr_constr_elem,
|
|
|
|
u8 pwr_constr_elem_len)
|
|
|
|
{
|
|
|
|
struct ieee80211_conf *conf = &sdata->local->hw.conf;
|
|
|
|
|
|
|
|
if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Power constraint IE length should be 1 octet */
|
|
|
|
if (pwr_constr_elem_len != 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((*pwr_constr_elem <= conf->channel->max_power) &&
|
|
|
|
(*pwr_constr_elem != sdata->local->power_constr_level)) {
|
|
|
|
sdata->local->power_constr_level = *pwr_constr_elem;
|
|
|
|
ieee80211_hw_config(sdata->local, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
mac80211: Add interface for driver to temporarily disable dynamic ps
This mechanism introduced in this patch applies (at least) for hardware
designs using a single shared antenna for both WLAN and BT. In these designs,
the antenna must be toggled between WLAN and BT.
In those hardware, managing WLAN co-existence with Bluetooth requires WLAN
full power save whenever there is Bluetooth activity in order for WLAN to be
able to periodically relinquish the antenna to be used for BT. This is because
BT can only access the shared antenna when WLAN is idle or asleep.
Some hardware, for instance the wl1271, are able to indicate to the host
whenever there is BT traffic. In essence, the hardware will send an indication
to the host whenever there is, for example, SCO traffic or A2DP traffic, and
will send another indication when the traffic is over.
The hardware gets information of Bluetooth traffic via hardware co-existence
control lines - these lines are used to negotiate the shared antenna
ownership. The hardware will give the antenna to BT whenever WLAN is sleeping.
This patch adds the interface to mac80211 to facilitate temporarily disabling
of dynamic power save as per request of the WLAN driver. This interface will
immediately force WLAN to full powersave, hence allowing BT coexistence as
described above.
In these kind of shared antenna desings, when WLAN powersave is fully disabled,
Bluetooth will not work simultaneously with WLAN at all. This patch does not
address that problem. This interface will not change PSM state, so if PSM is
disabled it will remain so. Solving this problem requires knowledge about BT
state, and is best done in user-space.
Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-06-21 13:59:39 +08:00
|
|
|
void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_conf *conf = &local->hw.conf;
|
|
|
|
|
|
|
|
WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION ||
|
|
|
|
!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS) ||
|
|
|
|
(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS));
|
|
|
|
|
|
|
|
local->disable_dynamic_ps = false;
|
|
|
|
conf->dynamic_ps_timeout = local->dynamic_ps_user_timeout;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_enable_dyn_ps);
|
|
|
|
|
|
|
|
void ieee80211_disable_dyn_ps(struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_conf *conf = &local->hw.conf;
|
|
|
|
|
|
|
|
WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION ||
|
|
|
|
!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS) ||
|
|
|
|
(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS));
|
|
|
|
|
|
|
|
local->disable_dynamic_ps = true;
|
|
|
|
conf->dynamic_ps_timeout = 0;
|
|
|
|
del_timer_sync(&local->dynamic_ps_timer);
|
|
|
|
ieee80211_queue_work(&local->hw,
|
|
|
|
&local->dynamic_ps_enable_work);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_disable_dyn_ps);
|
|
|
|
|
2009-04-16 19:17:24 +08:00
|
|
|
/* powersave */
|
|
|
|
static void ieee80211_enable_ps(struct ieee80211_local *local,
|
|
|
|
struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_conf *conf = &local->hw.conf;
|
|
|
|
|
2009-04-23 05:02:51 +08:00
|
|
|
/*
|
|
|
|
* If we are scanning right now then the parameters will
|
|
|
|
* take effect when scan finishes.
|
|
|
|
*/
|
2009-07-23 18:14:04 +08:00
|
|
|
if (local->scanning)
|
2009-04-23 05:02:51 +08:00
|
|
|
return;
|
|
|
|
|
2009-04-16 19:17:24 +08:00
|
|
|
if (conf->dynamic_ps_timeout > 0 &&
|
|
|
|
!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) {
|
|
|
|
mod_timer(&local->dynamic_ps_timer, jiffies +
|
|
|
|
msecs_to_jiffies(conf->dynamic_ps_timeout));
|
|
|
|
} else {
|
|
|
|
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
|
|
|
|
ieee80211_send_nullfunc(local, sdata, 1);
|
2010-02-09 17:20:28 +08:00
|
|
|
|
2010-03-09 20:25:02 +08:00
|
|
|
if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
|
|
|
|
(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS))
|
|
|
|
return;
|
|
|
|
|
|
|
|
conf->flags |= IEEE80211_CONF_PS;
|
|
|
|
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
2009-04-16 19:17:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_change_ps(struct ieee80211_local *local)
|
|
|
|
{
|
|
|
|
struct ieee80211_conf *conf = &local->hw.conf;
|
|
|
|
|
|
|
|
if (local->ps_sdata) {
|
|
|
|
ieee80211_enable_ps(local, local->ps_sdata);
|
|
|
|
} else if (conf->flags & IEEE80211_CONF_PS) {
|
|
|
|
conf->flags &= ~IEEE80211_CONF_PS;
|
|
|
|
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
|
|
|
del_timer_sync(&local->dynamic_ps_timer);
|
|
|
|
cancel_work_sync(&local->dynamic_ps_enable_work);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* need to hold RTNL or interface lock */
|
2009-04-16 19:17:25 +08:00
|
|
|
void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
|
2009-04-16 19:17:24 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata, *found = NULL;
|
|
|
|
int count = 0;
|
2010-04-27 17:47:40 +08:00
|
|
|
int timeout;
|
2009-04-16 19:17:24 +08:00
|
|
|
|
|
|
|
if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) {
|
|
|
|
local->ps_sdata = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
if (!list_empty(&local->work_list)) {
|
|
|
|
local->ps_sdata = NULL;
|
|
|
|
goto change;
|
|
|
|
}
|
|
|
|
|
2009-04-16 19:17:24 +08:00
|
|
|
list_for_each_entry(sdata, &local->interfaces, list) {
|
2009-12-23 20:15:31 +08:00
|
|
|
if (!ieee80211_sdata_running(sdata))
|
2009-04-16 19:17:24 +08:00
|
|
|
continue;
|
|
|
|
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
|
|
|
continue;
|
|
|
|
found = sdata;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
2009-06-10 21:16:15 +08:00
|
|
|
if (count == 1 && found->u.mgd.powersave &&
|
2009-12-23 20:15:35 +08:00
|
|
|
found->u.mgd.associated &&
|
2010-01-26 21:19:52 +08:00
|
|
|
found->u.mgd.associated->beacon_ies &&
|
2009-07-10 21:29:03 +08:00
|
|
|
!(found->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
|
|
|
|
IEEE80211_STA_CONNECTION_POLL))) {
|
mac80211: Add interface for driver to temporarily disable dynamic ps
This mechanism introduced in this patch applies (at least) for hardware
designs using a single shared antenna for both WLAN and BT. In these designs,
the antenna must be toggled between WLAN and BT.
In those hardware, managing WLAN co-existence with Bluetooth requires WLAN
full power save whenever there is Bluetooth activity in order for WLAN to be
able to periodically relinquish the antenna to be used for BT. This is because
BT can only access the shared antenna when WLAN is idle or asleep.
Some hardware, for instance the wl1271, are able to indicate to the host
whenever there is BT traffic. In essence, the hardware will send an indication
to the host whenever there is, for example, SCO traffic or A2DP traffic, and
will send another indication when the traffic is over.
The hardware gets information of Bluetooth traffic via hardware co-existence
control lines - these lines are used to negotiate the shared antenna
ownership. The hardware will give the antenna to BT whenever WLAN is sleeping.
This patch adds the interface to mac80211 to facilitate temporarily disabling
of dynamic power save as per request of the WLAN driver. This interface will
immediately force WLAN to full powersave, hence allowing BT coexistence as
described above.
In these kind of shared antenna desings, when WLAN powersave is fully disabled,
Bluetooth will not work simultaneously with WLAN at all. This patch does not
address that problem. This interface will not change PSM state, so if PSM is
disabled it will remain so. Solving this problem requires knowledge about BT
state, and is best done in user-space.
Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-06-21 13:59:39 +08:00
|
|
|
struct ieee80211_conf *conf = &local->hw.conf;
|
2009-04-16 19:17:25 +08:00
|
|
|
s32 beaconint_us;
|
|
|
|
|
|
|
|
if (latency < 0)
|
2010-05-06 07:59:26 +08:00
|
|
|
latency = pm_qos_request(PM_QOS_NETWORK_LATENCY);
|
2009-04-16 19:17:25 +08:00
|
|
|
|
|
|
|
beaconint_us = ieee80211_tu_to_usec(
|
|
|
|
found->vif.bss_conf.beacon_int);
|
|
|
|
|
2010-06-09 14:51:52 +08:00
|
|
|
timeout = local->dynamic_ps_forced_timeout;
|
2010-04-27 17:47:40 +08:00
|
|
|
if (timeout < 0) {
|
|
|
|
/*
|
2010-06-09 14:51:52 +08:00
|
|
|
* Go to full PSM if the user configures a very low
|
|
|
|
* latency requirement.
|
2010-04-27 17:47:40 +08:00
|
|
|
* The 2 second value is there for compatibility until
|
|
|
|
* the PM_QOS_NETWORK_LATENCY is configured with real
|
|
|
|
* values.
|
|
|
|
*/
|
2010-06-09 14:51:52 +08:00
|
|
|
if (latency > 1900000000 && latency != 2000000000)
|
2010-04-27 17:47:40 +08:00
|
|
|
timeout = 0;
|
2010-06-09 14:51:52 +08:00
|
|
|
else
|
|
|
|
timeout = 100;
|
2010-04-27 17:47:40 +08:00
|
|
|
}
|
mac80211: Add interface for driver to temporarily disable dynamic ps
This mechanism introduced in this patch applies (at least) for hardware
designs using a single shared antenna for both WLAN and BT. In these designs,
the antenna must be toggled between WLAN and BT.
In those hardware, managing WLAN co-existence with Bluetooth requires WLAN
full power save whenever there is Bluetooth activity in order for WLAN to be
able to periodically relinquish the antenna to be used for BT. This is because
BT can only access the shared antenna when WLAN is idle or asleep.
Some hardware, for instance the wl1271, are able to indicate to the host
whenever there is BT traffic. In essence, the hardware will send an indication
to the host whenever there is, for example, SCO traffic or A2DP traffic, and
will send another indication when the traffic is over.
The hardware gets information of Bluetooth traffic via hardware co-existence
control lines - these lines are used to negotiate the shared antenna
ownership. The hardware will give the antenna to BT whenever WLAN is sleeping.
This patch adds the interface to mac80211 to facilitate temporarily disabling
of dynamic power save as per request of the WLAN driver. This interface will
immediately force WLAN to full powersave, hence allowing BT coexistence as
described above.
In these kind of shared antenna desings, when WLAN powersave is fully disabled,
Bluetooth will not work simultaneously with WLAN at all. This patch does not
address that problem. This interface will not change PSM state, so if PSM is
disabled it will remain so. Solving this problem requires knowledge about BT
state, and is best done in user-space.
Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-06-21 13:59:39 +08:00
|
|
|
local->dynamic_ps_user_timeout = timeout;
|
|
|
|
if (!local->disable_dynamic_ps)
|
|
|
|
conf->dynamic_ps_timeout =
|
|
|
|
local->dynamic_ps_user_timeout;
|
2010-04-27 17:47:40 +08:00
|
|
|
|
2009-04-23 00:44:37 +08:00
|
|
|
if (beaconint_us > latency) {
|
2009-04-16 19:17:25 +08:00
|
|
|
local->ps_sdata = NULL;
|
2009-04-23 00:44:37 +08:00
|
|
|
} else {
|
2010-01-26 21:19:52 +08:00
|
|
|
struct ieee80211_bss *bss;
|
2009-04-23 00:44:37 +08:00
|
|
|
int maxslp = 1;
|
2010-01-26 21:19:52 +08:00
|
|
|
u8 dtimper;
|
2009-04-23 00:44:37 +08:00
|
|
|
|
2010-01-26 21:19:52 +08:00
|
|
|
bss = (void *)found->u.mgd.associated->priv;
|
|
|
|
dtimper = bss->dtim_period;
|
|
|
|
|
|
|
|
/* If the TIM IE is invalid, pretend the value is 1 */
|
|
|
|
if (!dtimper)
|
|
|
|
dtimper = 1;
|
|
|
|
else if (dtimper > 1)
|
2009-04-23 00:44:37 +08:00
|
|
|
maxslp = min_t(int, dtimper,
|
|
|
|
latency / beaconint_us);
|
|
|
|
|
2009-04-23 16:32:36 +08:00
|
|
|
local->hw.conf.max_sleep_period = maxslp;
|
2010-01-26 21:19:52 +08:00
|
|
|
local->hw.conf.ps_dtim_period = dtimper;
|
2009-04-16 19:17:25 +08:00
|
|
|
local->ps_sdata = found;
|
2009-04-23 00:44:37 +08:00
|
|
|
}
|
2009-04-16 19:17:25 +08:00
|
|
|
} else {
|
2009-04-16 19:17:24 +08:00
|
|
|
local->ps_sdata = NULL;
|
2009-04-16 19:17:25 +08:00
|
|
|
}
|
2009-04-16 19:17:24 +08:00
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
change:
|
2009-04-16 19:17:24 +08:00
|
|
|
ieee80211_change_ps(local);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local =
|
|
|
|
container_of(work, struct ieee80211_local,
|
|
|
|
dynamic_ps_disable_work);
|
|
|
|
|
|
|
|
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
|
|
|
|
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
|
|
|
|
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
|
|
|
}
|
|
|
|
|
|
|
|
ieee80211_wake_queues_by_reason(&local->hw,
|
|
|
|
IEEE80211_QUEUE_STOP_REASON_PS);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local =
|
|
|
|
container_of(work, struct ieee80211_local,
|
|
|
|
dynamic_ps_enable_work);
|
|
|
|
struct ieee80211_sub_if_data *sdata = local->ps_sdata;
|
2010-02-09 17:20:28 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2009-04-16 19:17:24 +08:00
|
|
|
|
|
|
|
/* can only happen when PS was just disabled anyway */
|
|
|
|
if (!sdata)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (local->hw.conf.flags & IEEE80211_CONF_PS)
|
|
|
|
return;
|
|
|
|
|
2010-02-09 17:20:28 +08:00
|
|
|
if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
|
|
|
|
(!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)))
|
2009-04-16 19:17:24 +08:00
|
|
|
ieee80211_send_nullfunc(local, sdata, 1);
|
|
|
|
|
2010-03-09 20:25:02 +08:00
|
|
|
if (!((local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
|
|
|
|
(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) ||
|
2010-02-09 17:20:28 +08:00
|
|
|
(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
|
|
|
|
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
|
|
|
|
local->hw.conf.flags |= IEEE80211_CONF_PS;
|
|
|
|
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
|
|
|
}
|
2009-04-16 19:17:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ieee80211_dynamic_ps_timer(unsigned long data)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = (void *) data;
|
|
|
|
|
2009-07-27 23:38:25 +08:00
|
|
|
if (local->quiescing || local->suspended)
|
2009-05-17 17:40:42 +08:00
|
|
|
return;
|
|
|
|
|
2009-07-30 08:08:07 +08:00
|
|
|
ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
|
2009-04-16 19:17:24 +08:00
|
|
|
}
|
|
|
|
|
2008-09-08 23:44:22 +08:00
|
|
|
/* MLME */
|
2008-08-03 08:04:37 +08:00
|
|
|
static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
2010-07-19 22:39:04 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata,
|
2007-05-06 02:45:53 +08:00
|
|
|
u8 *wmm_param, size_t wmm_param_len)
|
|
|
|
{
|
|
|
|
struct ieee80211_tx_queue_params params;
|
2010-07-19 22:39:04 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2007-05-06 02:45:53 +08:00
|
|
|
size_t left;
|
|
|
|
int count;
|
2010-01-12 16:42:31 +08:00
|
|
|
u8 *pos, uapsd_queues = 0;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2010-03-29 18:18:34 +08:00
|
|
|
if (!local->ops->conf_tx)
|
|
|
|
return;
|
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
if (local->hw.queues < 4)
|
2008-05-03 06:59:37 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (!wmm_param)
|
|
|
|
return;
|
|
|
|
|
2007-05-06 02:45:53 +08:00
|
|
|
if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1)
|
|
|
|
return;
|
2010-01-12 16:42:31 +08:00
|
|
|
|
|
|
|
if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED)
|
2010-01-12 16:42:39 +08:00
|
|
|
uapsd_queues = local->uapsd_queues;
|
2010-01-12 16:42:31 +08:00
|
|
|
|
2007-05-06 02:45:53 +08:00
|
|
|
count = wmm_param[6] & 0x0f;
|
2009-02-15 19:44:28 +08:00
|
|
|
if (count == ifmgd->wmm_last_param_set)
|
2007-05-06 02:45:53 +08:00
|
|
|
return;
|
2009-02-15 19:44:28 +08:00
|
|
|
ifmgd->wmm_last_param_set = count;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
|
|
|
pos = wmm_param + 8;
|
|
|
|
left = wmm_param_len - 8;
|
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
|
|
|
local->wmm_acm = 0;
|
|
|
|
for (; left >= 4; left -= 4, pos += 4) {
|
|
|
|
int aci = (pos[0] >> 5) & 0x03;
|
|
|
|
int acm = (pos[0] >> 4) & 0x01;
|
2010-01-12 16:42:31 +08:00
|
|
|
bool uapsd = false;
|
2007-05-06 02:45:53 +08:00
|
|
|
int queue;
|
|
|
|
|
|
|
|
switch (aci) {
|
2009-03-05 23:23:46 +08:00
|
|
|
case 1: /* AC_BK */
|
2008-05-01 00:51:21 +08:00
|
|
|
queue = 3;
|
2008-04-18 01:21:22 +08:00
|
|
|
if (acm)
|
2009-03-05 23:23:46 +08:00
|
|
|
local->wmm_acm |= BIT(1) | BIT(2); /* BK/- */
|
2010-01-12 16:42:31 +08:00
|
|
|
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
|
|
|
|
uapsd = true;
|
2007-05-06 02:45:53 +08:00
|
|
|
break;
|
2009-03-05 23:23:46 +08:00
|
|
|
case 2: /* AC_VI */
|
2008-05-01 00:51:21 +08:00
|
|
|
queue = 1;
|
2008-04-18 01:21:22 +08:00
|
|
|
if (acm)
|
2009-03-05 23:23:46 +08:00
|
|
|
local->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
|
2010-01-12 16:42:31 +08:00
|
|
|
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
|
|
|
|
uapsd = true;
|
2007-05-06 02:45:53 +08:00
|
|
|
break;
|
2009-03-05 23:23:46 +08:00
|
|
|
case 3: /* AC_VO */
|
2008-05-01 00:51:21 +08:00
|
|
|
queue = 0;
|
2008-04-18 01:21:22 +08:00
|
|
|
if (acm)
|
2009-03-05 23:23:46 +08:00
|
|
|
local->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
|
2010-01-12 16:42:31 +08:00
|
|
|
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
|
|
|
|
uapsd = true;
|
2007-05-06 02:45:53 +08:00
|
|
|
break;
|
2009-03-05 23:23:46 +08:00
|
|
|
case 0: /* AC_BE */
|
2007-05-06 02:45:53 +08:00
|
|
|
default:
|
2008-05-01 00:51:21 +08:00
|
|
|
queue = 2;
|
2008-04-18 01:21:22 +08:00
|
|
|
if (acm)
|
2009-03-05 23:23:46 +08:00
|
|
|
local->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
|
2010-01-12 16:42:31 +08:00
|
|
|
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
|
|
|
|
uapsd = true;
|
2007-05-06 02:45:53 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
params.aifs = pos[0] & 0x0f;
|
|
|
|
params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
|
|
|
|
params.cw_min = ecw2cw(pos[1] & 0x0f);
|
2008-07-10 17:22:31 +08:00
|
|
|
params.txop = get_unaligned_le16(pos + 2);
|
2010-01-12 16:42:31 +08:00
|
|
|
params.uapsd = uapsd;
|
|
|
|
|
2008-06-30 21:10:46 +08:00
|
|
|
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
2010-08-21 07:25:38 +08:00
|
|
|
wiphy_debug(local->hw.wiphy,
|
|
|
|
"WMM queue=%d aci=%d acm=%d aifs=%d "
|
|
|
|
"cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
|
|
|
|
queue, aci, acm,
|
|
|
|
params.aifs, params.cw_min, params.cw_max,
|
|
|
|
params.txop, params.uapsd);
|
2008-02-10 23:49:38 +08:00
|
|
|
#endif
|
2010-03-29 18:18:34 +08:00
|
|
|
if (drv_conf_tx(local, queue, ¶ms))
|
2010-08-21 07:25:38 +08:00
|
|
|
wiphy_debug(local->hw.wiphy,
|
|
|
|
"failed to set TX queue parameters for queue %d\n",
|
|
|
|
queue);
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
2010-03-29 18:18:34 +08:00
|
|
|
|
|
|
|
/* enable WMM or activate new settings */
|
2010-07-19 22:39:04 +08:00
|
|
|
sdata->vif.bss_conf.qos = true;
|
|
|
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS);
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2008-10-08 16:59:33 +08:00
|
|
|
static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
|
|
|
|
u16 capab, bool erp_valid, u8 erp)
|
2007-07-11 01:32:10 +08:00
|
|
|
{
|
2008-10-11 07:51:51 +08:00
|
|
|
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
|
2007-12-28 21:32:58 +08:00
|
|
|
u32 changed = 0;
|
2008-10-08 16:59:33 +08:00
|
|
|
bool use_protection;
|
|
|
|
bool use_short_preamble;
|
|
|
|
bool use_short_slot;
|
|
|
|
|
|
|
|
if (erp_valid) {
|
|
|
|
use_protection = (erp & WLAN_ERP_USE_PROTECTION) != 0;
|
|
|
|
use_short_preamble = (erp & WLAN_ERP_BARKER_PREAMBLE) == 0;
|
|
|
|
} else {
|
|
|
|
use_protection = false;
|
|
|
|
use_short_preamble = !!(capab & WLAN_CAPABILITY_SHORT_PREAMBLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
|
2010-01-15 10:00:48 +08:00
|
|
|
if (sdata->local->hw.conf.channel->band == IEEE80211_BAND_5GHZ)
|
|
|
|
use_short_slot = true;
|
2007-07-11 01:32:10 +08:00
|
|
|
|
2007-12-28 21:32:58 +08:00
|
|
|
if (use_protection != bss_conf->use_cts_prot) {
|
|
|
|
bss_conf->use_cts_prot = use_protection;
|
|
|
|
changed |= BSS_CHANGED_ERP_CTS_PROT;
|
2007-07-11 01:32:10 +08:00
|
|
|
}
|
2007-07-27 21:43:24 +08:00
|
|
|
|
2008-03-31 23:05:03 +08:00
|
|
|
if (use_short_preamble != bss_conf->use_short_preamble) {
|
|
|
|
bss_conf->use_short_preamble = use_short_preamble;
|
2007-12-28 21:32:58 +08:00
|
|
|
changed |= BSS_CHANGED_ERP_PREAMBLE;
|
2007-07-27 21:43:24 +08:00
|
|
|
}
|
2007-07-27 21:43:24 +08:00
|
|
|
|
2008-10-08 16:59:33 +08:00
|
|
|
if (use_short_slot != bss_conf->use_short_slot) {
|
|
|
|
bss_conf->use_short_slot = use_short_slot;
|
|
|
|
changed |= BSS_CHANGED_ERP_SLOT;
|
2008-04-16 02:09:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2008-08-03 08:04:37 +08:00
|
|
|
static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
|
2009-12-23 20:15:39 +08:00
|
|
|
struct cfg80211_bss *cbss,
|
2008-10-14 22:58:37 +08:00
|
|
|
u32 bss_info_changed)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2009-12-23 20:15:39 +08:00
|
|
|
struct ieee80211_bss *bss = (void *)cbss->priv;
|
2007-12-28 21:32:58 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2010-06-09 18:43:26 +08:00
|
|
|
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
|
2007-08-29 05:01:54 +08:00
|
|
|
|
2008-10-14 22:58:37 +08:00
|
|
|
bss_info_changed |= BSS_CHANGED_ASSOC;
|
2009-07-07 09:45:17 +08:00
|
|
|
/* set timing information */
|
2010-06-09 18:43:26 +08:00
|
|
|
bss_conf->beacon_int = cbss->beacon_interval;
|
|
|
|
bss_conf->timestamp = cbss->tsf;
|
2007-07-11 01:32:10 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
bss_info_changed |= BSS_CHANGED_BEACON_INT;
|
|
|
|
bss_info_changed |= ieee80211_handle_bss_capability(sdata,
|
2009-12-23 20:15:39 +08:00
|
|
|
cbss->capability, bss->has_erp_value, bss->erp_value);
|
2008-03-29 07:33:34 +08:00
|
|
|
|
2009-12-23 20:15:39 +08:00
|
|
|
sdata->u.mgd.associated = cbss;
|
|
|
|
memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2010-03-30 14:28:30 +08:00
|
|
|
sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
/* just to be sure */
|
|
|
|
sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
|
|
|
|
IEEE80211_STA_BEACON_POLL);
|
|
|
|
|
2009-12-17 23:16:53 +08:00
|
|
|
/*
|
|
|
|
* Always handle WMM once after association regardless
|
|
|
|
* of the first value the AP uses. Setting -1 here has
|
|
|
|
* that effect because the AP values is an unsigned
|
|
|
|
* 4-bit value.
|
|
|
|
*/
|
|
|
|
sdata->u.mgd.wmm_last_param_set = -1;
|
|
|
|
|
2008-09-09 16:57:09 +08:00
|
|
|
ieee80211_led_assoc(local, 1);
|
2008-05-29 16:35:23 +08:00
|
|
|
|
2010-07-29 22:08:55 +08:00
|
|
|
if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD)
|
|
|
|
bss_conf->dtim_period = bss->dtim_period;
|
|
|
|
else
|
|
|
|
bss_conf->dtim_period = 0;
|
|
|
|
|
2010-06-09 18:43:26 +08:00
|
|
|
bss_conf->assoc = 1;
|
2008-09-11 06:01:57 +08:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2008-10-14 22:58:37 +08:00
|
|
|
bss_info_changed |= BSS_CHANGED_BASIC_RATES;
|
2009-05-14 19:10:14 +08:00
|
|
|
|
|
|
|
/* And the BSSID changed - we're associated now */
|
|
|
|
bss_info_changed |= BSS_CHANGED_BSSID;
|
|
|
|
|
2010-03-23 15:02:34 +08:00
|
|
|
/* Tell the driver to monitor connection quality (if supported) */
|
|
|
|
if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) &&
|
2010-06-09 18:43:26 +08:00
|
|
|
bss_conf->cqm_rssi_thold)
|
2010-03-23 15:02:34 +08:00
|
|
|
bss_info_changed |= BSS_CHANGED_CQM;
|
|
|
|
|
2010-06-09 18:43:26 +08:00
|
|
|
/* Enable ARP filtering */
|
|
|
|
if (bss_conf->arp_filter_enabled != sdata->arp_filter_state) {
|
|
|
|
bss_conf->arp_filter_enabled = sdata->arp_filter_state;
|
|
|
|
bss_info_changed |= BSS_CHANGED_ARP_FILTER;
|
|
|
|
}
|
|
|
|
|
2008-10-14 22:58:37 +08:00
|
|
|
ieee80211_bss_info_change_notify(sdata, bss_info_changed);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-07-31 03:43:55 +08:00
|
|
|
mutex_lock(&local->iflist_mtx);
|
|
|
|
ieee80211_recalc_ps(local, -1);
|
2009-12-01 20:37:02 +08:00
|
|
|
ieee80211_recalc_smps(local, sdata);
|
2009-07-31 03:43:55 +08:00
|
|
|
mutex_unlock(&local->iflist_mtx);
|
2008-12-19 05:35:13 +08:00
|
|
|
|
2010-01-07 04:39:39 +08:00
|
|
|
netif_tx_start_all_queues(sdata->dev);
|
2008-09-09 16:57:09 +08:00
|
|
|
netif_carrier_on(sdata->dev);
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2010-03-30 14:29:31 +08:00
|
|
|
static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
|
|
|
|
bool remove_sta)
|
2008-09-09 05:32:12 +08:00
|
|
|
{
|
2009-02-15 19:44:28 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2008-09-09 05:32:12 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct sta_info *sta;
|
2008-12-19 05:35:13 +08:00
|
|
|
u32 changed = 0, config_changed = 0;
|
2009-07-10 21:29:03 +08:00
|
|
|
u8 bssid[ETH_ALEN];
|
2008-09-09 05:32:12 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
ASSERT_MGD_MTX(ifmgd);
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
if (WARN_ON(!ifmgd->associated))
|
|
|
|
return;
|
|
|
|
|
2009-12-23 20:15:39 +08:00
|
|
|
memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
|
2009-07-10 21:29:03 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
ifmgd->associated = NULL;
|
|
|
|
memset(ifmgd->bssid, 0, ETH_ALEN);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we need to commit the associated = NULL change because the
|
|
|
|
* scan code uses that to determine whether this iface should
|
|
|
|
* go to/wake up from powersave or not -- and could otherwise
|
|
|
|
* wake the queues erroneously.
|
|
|
|
*/
|
|
|
|
smp_mb();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Thus, we can only afterwards stop the queues -- to account
|
|
|
|
* for the case where another CPU is finishing a scan at this
|
|
|
|
* time -- we don't want the scan code to enable queues.
|
|
|
|
*/
|
2008-09-09 05:32:12 +08:00
|
|
|
|
2010-01-07 04:39:39 +08:00
|
|
|
netif_tx_stop_all_queues(sdata->dev);
|
2008-09-09 05:32:12 +08:00
|
|
|
netif_carrier_off(sdata->dev);
|
|
|
|
|
2010-06-10 16:21:29 +08:00
|
|
|
mutex_lock(&local->sta_mtx);
|
2009-11-26 00:46:18 +08:00
|
|
|
sta = sta_info_get(sdata, bssid);
|
2010-02-10 17:22:21 +08:00
|
|
|
if (sta) {
|
2010-06-10 16:21:29 +08:00
|
|
|
set_sta_flags(sta, WLAN_STA_BLOCK_BA);
|
2009-06-16 00:13:58 +08:00
|
|
|
ieee80211_sta_tear_down_BA_sessions(sta);
|
2010-02-10 17:22:21 +08:00
|
|
|
}
|
2010-06-10 16:21:29 +08:00
|
|
|
mutex_unlock(&local->sta_mtx);
|
2008-09-09 05:32:12 +08:00
|
|
|
|
2008-09-08 23:33:39 +08:00
|
|
|
changed |= ieee80211_reset_erp_info(sdata);
|
|
|
|
|
|
|
|
ieee80211_led_assoc(local, 0);
|
2008-10-14 22:58:37 +08:00
|
|
|
changed |= BSS_CHANGED_ASSOC;
|
|
|
|
sdata->vif.bss_conf.assoc = false;
|
2008-09-08 23:33:39 +08:00
|
|
|
|
2009-05-07 22:16:24 +08:00
|
|
|
ieee80211_set_wmm_default(sdata);
|
|
|
|
|
2009-01-07 17:13:27 +08:00
|
|
|
/* channel(_type) changes are handled by ieee80211_hw_config */
|
2010-05-05 21:28:27 +08:00
|
|
|
WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
|
2008-12-19 05:35:13 +08:00
|
|
|
|
2009-05-09 03:21:06 +08:00
|
|
|
/* on the next assoc, re-program HT parameters */
|
|
|
|
sdata->ht_opmode_valid = false;
|
|
|
|
|
2009-01-09 20:44:15 +08:00
|
|
|
local->power_constr_level = 0;
|
|
|
|
|
2008-12-19 05:35:27 +08:00
|
|
|
del_timer_sync(&local->dynamic_ps_timer);
|
|
|
|
cancel_work_sync(&local->dynamic_ps_enable_work);
|
|
|
|
|
2008-12-19 05:35:13 +08:00
|
|
|
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
|
|
|
|
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
|
|
|
|
config_changed |= IEEE80211_CONF_CHANGE_PS;
|
|
|
|
}
|
2008-10-14 22:58:37 +08:00
|
|
|
|
2008-12-19 05:35:13 +08:00
|
|
|
ieee80211_hw_config(local, config_changed);
|
2009-05-14 19:10:14 +08:00
|
|
|
|
2010-06-09 18:43:26 +08:00
|
|
|
/* Disable ARP filtering */
|
|
|
|
if (sdata->vif.bss_conf.arp_filter_enabled) {
|
|
|
|
sdata->vif.bss_conf.arp_filter_enabled = false;
|
|
|
|
changed |= BSS_CHANGED_ARP_FILTER;
|
|
|
|
}
|
|
|
|
|
2010-05-05 21:28:27 +08:00
|
|
|
/* The BSSID (not really interesting) and HT changed */
|
|
|
|
changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;
|
2008-10-14 22:58:37 +08:00
|
|
|
ieee80211_bss_info_change_notify(sdata, changed);
|
2008-11-25 19:05:44 +08:00
|
|
|
|
2010-03-30 14:29:31 +08:00
|
|
|
if (remove_sta)
|
|
|
|
sta_info_destroy_addr(sdata, bssid);
|
2008-09-09 05:32:12 +08:00
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-03-23 03:57:06 +08:00
|
|
|
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_hdr *hdr)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We can postpone the mgd.timer whenever receiving unicast frames
|
|
|
|
* from AP because we know that the connection is working both ways
|
|
|
|
* at that time. But multicast frames (and hence also beacons) must
|
|
|
|
* be ignored here, because we need to trigger the timer during
|
2009-07-10 21:29:03 +08:00
|
|
|
* data idle periods for sending the periodic probe request to the
|
|
|
|
* AP we're connected to.
|
2009-03-23 03:57:06 +08:00
|
|
|
*/
|
2009-07-10 21:29:03 +08:00
|
|
|
if (is_multicast_ether_addr(hdr->addr1))
|
|
|
|
return;
|
|
|
|
|
2010-03-19 13:14:53 +08:00
|
|
|
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
|
|
|
|
return;
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
mod_timer(&sdata->u.mgd.conn_mon_timer,
|
|
|
|
round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
|
2009-03-23 03:57:06 +08:00
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-07-31 23:54:12 +08:00
|
|
|
static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
const u8 *ssid;
|
|
|
|
|
2009-12-23 20:15:39 +08:00
|
|
|
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
|
|
|
|
ieee80211_send_probe_req(sdata, ifmgd->associated->bssid,
|
2009-07-31 23:54:12 +08:00
|
|
|
ssid + 2, ssid[1], NULL, 0);
|
|
|
|
|
|
|
|
ifmgd->probe_send_count++;
|
|
|
|
ifmgd->probe_timeout = jiffies + IEEE80211_PROBE_WAIT;
|
|
|
|
run_again(ifmgd, ifmgd->probe_timeout);
|
|
|
|
}
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
|
|
|
|
bool beacon)
|
2009-03-23 03:57:35 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2009-07-10 21:29:03 +08:00
|
|
|
bool already = false;
|
2009-05-13 01:58:12 +08:00
|
|
|
|
2009-12-23 20:15:31 +08:00
|
|
|
if (!ieee80211_sdata_running(sdata))
|
2009-07-13 19:23:39 +08:00
|
|
|
return;
|
|
|
|
|
2009-07-24 07:37:47 +08:00
|
|
|
if (sdata->local->scanning)
|
|
|
|
return;
|
|
|
|
|
2009-12-23 20:15:42 +08:00
|
|
|
if (sdata->local->tmp_channel)
|
|
|
|
return;
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
|
|
|
|
|
if (!ifmgd->associated)
|
|
|
|
goto out;
|
|
|
|
|
2009-04-16 02:41:22 +08:00
|
|
|
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
2009-07-10 21:29:03 +08:00
|
|
|
if (beacon && net_ratelimit())
|
|
|
|
printk(KERN_DEBUG "%s: detected beacon loss from AP "
|
2009-11-26 00:46:19 +08:00
|
|
|
"- sending probe request\n", sdata->name);
|
2009-04-16 02:41:22 +08:00
|
|
|
#endif
|
2009-03-23 03:57:35 +08:00
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
/*
|
|
|
|
* The driver/our work has already reported this event or the
|
|
|
|
* connection monitoring has kicked in and we have already sent
|
|
|
|
* a probe request. Or maybe the AP died and the driver keeps
|
|
|
|
* reporting until we disassociate...
|
|
|
|
*
|
|
|
|
* In either case we have to ignore the current call to this
|
|
|
|
* function (except for setting the correct probe reason bit)
|
|
|
|
* because otherwise we would reset the timer every time and
|
|
|
|
* never check whether we received a probe response!
|
|
|
|
*/
|
|
|
|
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
|
|
|
|
IEEE80211_STA_CONNECTION_POLL))
|
|
|
|
already = true;
|
|
|
|
|
|
|
|
if (beacon)
|
|
|
|
ifmgd->flags |= IEEE80211_STA_BEACON_POLL;
|
|
|
|
else
|
|
|
|
ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;
|
|
|
|
|
|
|
|
if (already)
|
|
|
|
goto out;
|
|
|
|
|
2009-06-10 21:16:52 +08:00
|
|
|
mutex_lock(&sdata->local->iflist_mtx);
|
|
|
|
ieee80211_recalc_ps(sdata->local, -1);
|
|
|
|
mutex_unlock(&sdata->local->iflist_mtx);
|
|
|
|
|
2009-07-31 23:54:12 +08:00
|
|
|
ifmgd->probe_send_count = 0;
|
|
|
|
ieee80211_mgd_probe_ap_send(sdata);
|
2009-07-07 09:45:17 +08:00
|
|
|
out:
|
|
|
|
mutex_unlock(&ifmgd->mtx);
|
2009-03-23 03:57:35 +08:00
|
|
|
}
|
|
|
|
|
2010-03-19 13:14:53 +08:00
|
|
|
static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
u8 bssid[ETH_ALEN];
|
|
|
|
|
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
|
if (!ifmgd->associated) {
|
|
|
|
mutex_unlock(&ifmgd->mtx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
|
|
|
|
|
|
|
|
printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid);
|
|
|
|
|
2010-03-30 14:29:31 +08:00
|
|
|
ieee80211_set_disassoc(sdata, true);
|
2010-03-19 13:14:53 +08:00
|
|
|
mutex_unlock(&ifmgd->mtx);
|
2010-08-05 23:02:38 +08:00
|
|
|
|
|
|
|
mutex_lock(&local->mtx);
|
|
|
|
ieee80211_recalc_idle(local);
|
|
|
|
mutex_unlock(&local->mtx);
|
2010-03-19 13:14:53 +08:00
|
|
|
/*
|
|
|
|
* must be outside lock due to cfg80211,
|
|
|
|
* but that's not a problem.
|
|
|
|
*/
|
|
|
|
ieee80211_send_deauth_disassoc(sdata, bssid,
|
|
|
|
IEEE80211_STYPE_DEAUTH,
|
|
|
|
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
2010-04-04 14:37:19 +08:00
|
|
|
NULL, true);
|
2010-03-19 13:14:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ieee80211_beacon_connection_loss_work(struct work_struct *work)
|
2009-07-10 21:29:03 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
container_of(work, struct ieee80211_sub_if_data,
|
2010-03-19 13:14:53 +08:00
|
|
|
u.mgd.beacon_connection_loss_work);
|
2009-07-10 21:29:03 +08:00
|
|
|
|
2010-03-19 13:14:53 +08:00
|
|
|
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
|
|
|
|
__ieee80211_connection_loss(sdata);
|
|
|
|
else
|
|
|
|
ieee80211_mgd_probe_ap(sdata, true);
|
2009-07-10 21:29:03 +08:00
|
|
|
}
|
|
|
|
|
2009-03-23 03:57:35 +08:00
|
|
|
void ieee80211_beacon_loss(struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
2010-03-19 13:14:53 +08:00
|
|
|
struct ieee80211_hw *hw = &sdata->local->hw;
|
2009-03-23 03:57:35 +08:00
|
|
|
|
2010-04-07 22:48:40 +08:00
|
|
|
trace_api_beacon_loss(sdata);
|
|
|
|
|
2010-03-19 13:14:53 +08:00
|
|
|
WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);
|
|
|
|
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
|
2009-03-23 03:57:35 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_beacon_loss);
|
|
|
|
|
2010-03-19 13:14:53 +08:00
|
|
|
void ieee80211_connection_loss(struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
|
struct ieee80211_hw *hw = &sdata->local->hw;
|
|
|
|
|
2010-04-07 22:48:40 +08:00
|
|
|
trace_api_connection_loss(sdata);
|
|
|
|
|
2010-03-19 13:14:53 +08:00
|
|
|
WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR));
|
|
|
|
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_connection_loss);
|
|
|
|
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
static enum rx_mgmt_action __must_check
|
|
|
|
ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2009-02-15 19:44:28 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2009-07-07 09:45:17 +08:00
|
|
|
const u8 *bssid = NULL;
|
2007-05-06 02:45:53 +08:00
|
|
|
u16 reason_code;
|
|
|
|
|
2008-06-30 21:10:46 +08:00
|
|
|
if (len < 24 + 2)
|
2009-07-07 09:45:17 +08:00
|
|
|
return RX_MGMT_NONE;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
ASSERT_MGD_MTX(ifmgd);
|
|
|
|
|
2009-12-23 20:15:39 +08:00
|
|
|
bssid = ifmgd->associated->bssid;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
|
|
|
reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n",
|
2009-11-26 00:46:19 +08:00
|
|
|
sdata->name, bssid, reason_code);
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2010-03-30 14:29:31 +08:00
|
|
|
ieee80211_set_disassoc(sdata, true);
|
2010-08-05 23:02:38 +08:00
|
|
|
mutex_lock(&sdata->local->mtx);
|
2009-12-23 20:15:33 +08:00
|
|
|
ieee80211_recalc_idle(sdata->local);
|
2010-08-05 23:02:38 +08:00
|
|
|
mutex_unlock(&sdata->local->mtx);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
return RX_MGMT_CFG80211_DEAUTH;
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
static enum rx_mgmt_action __must_check
|
|
|
|
ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2009-02-15 19:44:28 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2007-05-06 02:45:53 +08:00
|
|
|
u16 reason_code;
|
|
|
|
|
2008-06-30 21:10:46 +08:00
|
|
|
if (len < 24 + 2)
|
2009-07-07 09:45:17 +08:00
|
|
|
return RX_MGMT_NONE;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
ASSERT_MGD_MTX(ifmgd);
|
|
|
|
|
|
|
|
if (WARN_ON(!ifmgd->associated))
|
|
|
|
return RX_MGMT_NONE;
|
|
|
|
|
2009-12-23 20:15:39 +08:00
|
|
|
if (WARN_ON(memcmp(ifmgd->associated->bssid, mgmt->sa, ETH_ALEN)))
|
2009-07-07 09:45:17 +08:00
|
|
|
return RX_MGMT_NONE;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
|
|
|
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
|
|
|
|
|
2009-09-26 20:45:41 +08:00
|
|
|
printk(KERN_DEBUG "%s: disassociated from %pM (Reason: %u)\n",
|
2009-11-26 00:46:19 +08:00
|
|
|
sdata->name, mgmt->sa, reason_code);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2010-03-30 14:29:31 +08:00
|
|
|
ieee80211_set_disassoc(sdata, true);
|
2010-08-05 23:02:38 +08:00
|
|
|
mutex_lock(&sdata->local->mtx);
|
2009-11-29 19:19:06 +08:00
|
|
|
ieee80211_recalc_idle(sdata->local);
|
2010-08-05 23:02:38 +08:00
|
|
|
mutex_unlock(&sdata->local->mtx);
|
2009-07-07 09:45:17 +08:00
|
|
|
return RX_MGMT_CFG80211_DISASSOC;
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
static bool ieee80211_assoc_success(struct ieee80211_work *wk,
|
|
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2009-12-23 20:15:35 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = wk->sdata;
|
2009-02-15 19:44:28 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2007-12-28 21:32:58 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2008-01-25 02:38:38 +08:00
|
|
|
struct ieee80211_supported_band *sband;
|
2007-05-06 02:45:53 +08:00
|
|
|
struct sta_info *sta;
|
2009-12-23 20:15:39 +08:00
|
|
|
struct cfg80211_bss *cbss = wk->assoc.bss;
|
2009-12-23 20:15:35 +08:00
|
|
|
u8 *pos;
|
2009-01-21 22:13:48 +08:00
|
|
|
u32 rates, basic_rates;
|
2009-12-23 20:15:35 +08:00
|
|
|
u16 capab_info, aid;
|
2007-05-06 02:45:53 +08:00
|
|
|
struct ieee802_11_elems elems;
|
2008-10-11 07:51:51 +08:00
|
|
|
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
|
2008-10-14 22:58:37 +08:00
|
|
|
u32 changed = 0;
|
2009-12-02 19:43:43 +08:00
|
|
|
int i, j, err;
|
|
|
|
bool have_higher_than_11mbit = false;
|
2008-10-14 22:58:37 +08:00
|
|
|
u16 ap_ht_cap_flags;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
/* AssocResp and ReassocResp have identical structure */
|
2007-05-06 02:45:53 +08:00
|
|
|
|
|
|
|
aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
|
2009-12-23 20:15:35 +08:00
|
|
|
capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2007-10-10 18:03:41 +08:00
|
|
|
if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
|
|
|
|
printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not "
|
2009-11-26 00:46:19 +08:00
|
|
|
"set\n", sdata->name, aid);
|
2007-10-10 18:03:41 +08:00
|
|
|
aid &= ~(BIT(15) | BIT(14));
|
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
pos = mgmt->u.assoc_resp.variable;
|
|
|
|
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
|
|
|
|
|
2007-05-06 02:45:53 +08:00
|
|
|
if (!elems.supp_rates) {
|
|
|
|
printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n",
|
2009-11-26 00:46:19 +08:00
|
|
|
sdata->name);
|
2009-12-23 20:15:35 +08:00
|
|
|
return false;
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2009-02-15 19:44:28 +08:00
|
|
|
ifmgd->aid = aid;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-12-23 20:15:39 +08:00
|
|
|
sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL);
|
2007-05-06 02:45:53 +08:00
|
|
|
if (!sta) {
|
2009-12-02 19:43:43 +08:00
|
|
|
printk(KERN_DEBUG "%s: failed to alloc STA entry for"
|
|
|
|
" the AP\n", sdata->name);
|
2009-12-23 20:15:35 +08:00
|
|
|
return false;
|
2009-07-07 09:45:17 +08:00
|
|
|
}
|
2008-04-01 07:00:13 +08:00
|
|
|
|
2009-12-02 19:43:43 +08:00
|
|
|
set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC |
|
|
|
|
WLAN_STA_ASSOC_AP);
|
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
|
|
|
|
set_sta_flags(sta, WLAN_STA_AUTHORIZED);
|
|
|
|
|
2008-09-08 23:44:22 +08:00
|
|
|
rates = 0;
|
|
|
|
basic_rates = 0;
|
|
|
|
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
|
2008-02-23 22:17:12 +08:00
|
|
|
|
2008-09-08 23:44:22 +08:00
|
|
|
for (i = 0; i < elems.supp_rates_len; i++) {
|
|
|
|
int rate = (elems.supp_rates[i] & 0x7f) * 5;
|
2008-10-30 23:08:08 +08:00
|
|
|
bool is_basic = !!(elems.supp_rates[i] & 0x80);
|
2008-02-23 22:17:12 +08:00
|
|
|
|
2008-09-08 23:44:22 +08:00
|
|
|
if (rate > 110)
|
|
|
|
have_higher_than_11mbit = true;
|
|
|
|
|
|
|
|
for (j = 0; j < sband->n_bitrates; j++) {
|
2008-10-30 23:08:08 +08:00
|
|
|
if (sband->bitrates[j].bitrate == rate) {
|
2008-09-08 23:44:22 +08:00
|
|
|
rates |= BIT(j);
|
2008-10-30 23:08:08 +08:00
|
|
|
if (is_basic)
|
|
|
|
basic_rates |= BIT(j);
|
|
|
|
break;
|
|
|
|
}
|
2008-02-23 22:17:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-08 23:44:22 +08:00
|
|
|
for (i = 0; i < elems.ext_supp_rates_len; i++) {
|
|
|
|
int rate = (elems.ext_supp_rates[i] & 0x7f) * 5;
|
2009-04-19 19:22:11 +08:00
|
|
|
bool is_basic = !!(elems.ext_supp_rates[i] & 0x80);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2008-09-08 23:44:22 +08:00
|
|
|
if (rate > 110)
|
|
|
|
have_higher_than_11mbit = true;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2008-09-08 23:44:22 +08:00
|
|
|
for (j = 0; j < sband->n_bitrates; j++) {
|
2008-10-30 23:08:08 +08:00
|
|
|
if (sband->bitrates[j].bitrate == rate) {
|
2008-09-08 23:44:22 +08:00
|
|
|
rates |= BIT(j);
|
2008-10-30 23:08:08 +08:00
|
|
|
if (is_basic)
|
|
|
|
basic_rates |= BIT(j);
|
|
|
|
break;
|
|
|
|
}
|
2008-09-08 23:44:22 +08:00
|
|
|
}
|
2008-04-23 15:47:15 +08:00
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2008-09-11 08:45:11 +08:00
|
|
|
sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
|
2008-10-11 07:51:51 +08:00
|
|
|
sdata->vif.bss_conf.basic_rates = basic_rates;
|
2008-09-08 23:44:22 +08:00
|
|
|
|
|
|
|
/* cf. IEEE 802.11 9.2.12 */
|
|
|
|
if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
|
|
|
|
have_higher_than_11mbit)
|
|
|
|
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
|
|
|
|
else
|
|
|
|
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-07-02 03:41:17 +08:00
|
|
|
if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
|
2008-10-14 22:58:37 +08:00
|
|
|
ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
|
2008-10-09 18:13:49 +08:00
|
|
|
elems.ht_cap_elem, &sta->sta.ht_cap);
|
2008-10-14 22:58:37 +08:00
|
|
|
|
|
|
|
ap_ht_cap_flags = sta->sta.ht_cap.cap;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2008-09-19 00:14:18 +08:00
|
|
|
rate_control_rate_init(sta);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-02-15 19:44:28 +08:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
|
2009-01-08 19:31:59 +08:00
|
|
|
set_sta_flags(sta, WLAN_STA_MFP);
|
|
|
|
|
2008-10-22 17:41:38 +08:00
|
|
|
if (elems.wmm_param)
|
2008-09-08 23:44:22 +08:00
|
|
|
set_sta_flags(sta, WLAN_STA_WME);
|
2008-10-22 17:41:38 +08:00
|
|
|
|
2009-12-02 19:43:43 +08:00
|
|
|
err = sta_info_insert(sta);
|
|
|
|
sta = NULL;
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_DEBUG "%s: failed to insert STA entry for"
|
|
|
|
" the AP (error %d)\n", sdata->name, err);
|
2010-01-09 02:01:07 +08:00
|
|
|
return false;
|
2008-10-22 17:41:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (elems.wmm_param)
|
2010-07-19 22:39:04 +08:00
|
|
|
ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
|
2008-09-08 23:44:22 +08:00
|
|
|
elems.wmm_param_len);
|
2009-05-07 22:16:24 +08:00
|
|
|
else
|
|
|
|
ieee80211_set_wmm_default(sdata);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-12-23 20:15:43 +08:00
|
|
|
local->oper_channel = wk->chan;
|
|
|
|
|
2008-10-14 22:58:37 +08:00
|
|
|
if (elems.ht_info_elem && elems.wmm_param &&
|
2009-12-23 20:15:35 +08:00
|
|
|
(sdata->local->hw.queues >= 4) &&
|
2009-07-02 03:41:17 +08:00
|
|
|
!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
|
2008-10-14 22:58:37 +08:00
|
|
|
changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
|
2009-12-23 20:15:39 +08:00
|
|
|
cbss->bssid, ap_ht_cap_flags);
|
2008-10-14 22:58:37 +08:00
|
|
|
|
2008-09-08 23:44:22 +08:00
|
|
|
/* set AID and assoc capability,
|
|
|
|
* ieee80211_set_associated() will tell the driver */
|
|
|
|
bss_conf->aid = aid;
|
|
|
|
bss_conf->assoc_capability = capab_info;
|
2009-12-23 20:15:39 +08:00
|
|
|
ieee80211_set_associated(sdata, cbss, changed);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2010-01-09 01:06:26 +08:00
|
|
|
/*
|
|
|
|
* If we're using 4-addr mode, let the AP know that we're
|
|
|
|
* doing so, so that it can create the STA VLAN on its side
|
|
|
|
*/
|
|
|
|
if (ifmgd->use_4addr)
|
|
|
|
ieee80211_send_4addr_nullfunc(local, sdata);
|
|
|
|
|
2009-03-23 03:57:14 +08:00
|
|
|
/*
|
2009-07-10 21:29:03 +08:00
|
|
|
* Start timer to probe the connection to the AP now.
|
|
|
|
* Also start the timer that will detect beacon loss.
|
2009-03-23 03:57:14 +08:00
|
|
|
*/
|
2009-07-10 21:29:03 +08:00
|
|
|
ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);
|
|
|
|
mod_beacon_timer(sdata);
|
2009-03-23 03:57:14 +08:00
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
return true;
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-09-08 23:44:26 +08:00
|
|
|
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt,
|
|
|
|
size_t len,
|
|
|
|
struct ieee80211_rx_status *rx_status,
|
|
|
|
struct ieee802_11_elems *elems,
|
|
|
|
bool beacon)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
int freq;
|
2008-09-11 06:01:55 +08:00
|
|
|
struct ieee80211_bss *bss;
|
2008-09-08 23:44:26 +08:00
|
|
|
struct ieee80211_channel *channel;
|
2010-01-26 21:19:52 +08:00
|
|
|
bool need_ps = false;
|
|
|
|
|
|
|
|
if (sdata->u.mgd.associated) {
|
|
|
|
bss = (void *)sdata->u.mgd.associated->priv;
|
|
|
|
/* not previously set so we may need to recalc */
|
|
|
|
need_ps = !bss->dtim_period;
|
|
|
|
}
|
2008-09-08 23:44:26 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
|
2009-02-11 04:25:55 +08:00
|
|
|
channel, beacon);
|
2009-07-07 09:45:17 +08:00
|
|
|
if (bss)
|
|
|
|
ieee80211_rx_bss_put(local, bss);
|
|
|
|
|
|
|
|
if (!sdata->u.mgd.associated)
|
2008-09-08 23:44:26 +08:00
|
|
|
return;
|
|
|
|
|
2010-01-26 21:19:52 +08:00
|
|
|
if (need_ps) {
|
|
|
|
mutex_lock(&local->iflist_mtx);
|
|
|
|
ieee80211_recalc_ps(local, -1);
|
|
|
|
mutex_unlock(&local->iflist_mtx);
|
|
|
|
}
|
|
|
|
|
2009-01-06 11:58:37 +08:00
|
|
|
if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) &&
|
2009-12-23 20:15:39 +08:00
|
|
|
(memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid,
|
2009-07-07 09:45:17 +08:00
|
|
|
ETH_ALEN) == 0)) {
|
2009-01-06 11:58:37 +08:00
|
|
|
struct ieee80211_channel_sw_ie *sw_elem =
|
|
|
|
(struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
|
2010-05-11 22:20:57 +08:00
|
|
|
ieee80211_sta_process_chanswitch(sdata, sw_elem,
|
|
|
|
bss, rx_status->mactime);
|
2009-01-06 11:58:37 +08:00
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-03 08:04:37 +08:00
|
|
|
static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
|
2009-12-23 20:15:35 +08:00
|
|
|
struct sk_buff *skb)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2009-12-23 20:15:35 +08:00
|
|
|
struct ieee80211_mgmt *mgmt = (void *)skb->data;
|
2009-03-23 03:57:14 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd;
|
2009-12-23 20:15:35 +08:00
|
|
|
struct ieee80211_rx_status *rx_status = (void *) skb->cb;
|
|
|
|
size_t baselen, len = skb->len;
|
2008-06-27 23:54:48 +08:00
|
|
|
struct ieee802_11_elems elems;
|
|
|
|
|
2009-03-23 03:57:14 +08:00
|
|
|
ifmgd = &sdata->u.mgd;
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
ASSERT_MGD_MTX(ifmgd);
|
|
|
|
|
2009-11-26 00:46:19 +08:00
|
|
|
if (memcmp(mgmt->da, sdata->vif.addr, ETH_ALEN))
|
2008-08-03 19:32:01 +08:00
|
|
|
return; /* ignore ProbeResp to foreign address */
|
|
|
|
|
2008-06-27 23:54:48 +08:00
|
|
|
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);
|
|
|
|
|
2008-09-08 23:44:26 +08:00
|
|
|
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
|
2008-08-09 08:02:19 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
if (ifmgd->associated &&
|
2009-12-23 20:15:39 +08:00
|
|
|
memcmp(mgmt->bssid, ifmgd->associated->bssid, ETH_ALEN) == 0 &&
|
2009-07-10 21:29:03 +08:00
|
|
|
ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
|
|
|
|
IEEE80211_STA_CONNECTION_POLL)) {
|
|
|
|
ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
|
|
|
|
IEEE80211_STA_BEACON_POLL);
|
2009-06-10 21:16:52 +08:00
|
|
|
mutex_lock(&sdata->local->iflist_mtx);
|
|
|
|
ieee80211_recalc_ps(sdata->local, -1);
|
|
|
|
mutex_unlock(&sdata->local->iflist_mtx);
|
2010-04-20 18:15:56 +08:00
|
|
|
|
|
|
|
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
|
|
|
|
return;
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
/*
|
|
|
|
* We've received a probe response, but are not sure whether
|
|
|
|
* we have or will be receiving any beacons or data, so let's
|
|
|
|
* schedule the timers again, just in case.
|
|
|
|
*/
|
|
|
|
mod_beacon_timer(sdata);
|
2010-04-20 18:15:56 +08:00
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
mod_timer(&ifmgd->conn_mon_timer,
|
|
|
|
round_jiffies_up(jiffies +
|
|
|
|
IEEE80211_CONNECTION_IDLE_TIME));
|
2009-06-10 21:16:52 +08:00
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 19:17:26 +08:00
|
|
|
/*
|
|
|
|
* This is the canonical list of information elements we care about,
|
|
|
|
* the filter code also gives us all changes to the Microsoft OUI
|
|
|
|
* (00:50:F2) vendor IE which is used for WMM which we need to track.
|
|
|
|
*
|
|
|
|
* We implement beacon filtering in software since that means we can
|
|
|
|
* avoid processing the frame here and in cfg80211, and userspace
|
|
|
|
* will not be able to tell whether the hardware supports it or not.
|
|
|
|
*
|
|
|
|
* XXX: This list needs to be dynamic -- userspace needs to be able to
|
|
|
|
* add items it requires. It also needs to be able to tell us to
|
|
|
|
* look out for other vendor IEs.
|
|
|
|
*/
|
|
|
|
static const u64 care_about_ies =
|
2009-04-22 17:25:43 +08:00
|
|
|
(1ULL << WLAN_EID_COUNTRY) |
|
|
|
|
(1ULL << WLAN_EID_ERP_INFO) |
|
|
|
|
(1ULL << WLAN_EID_CHANNEL_SWITCH) |
|
|
|
|
(1ULL << WLAN_EID_PWR_CONSTRAINT) |
|
|
|
|
(1ULL << WLAN_EID_HT_CAPABILITY) |
|
|
|
|
(1ULL << WLAN_EID_HT_INFORMATION);
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 19:17:26 +08:00
|
|
|
|
2008-08-03 08:04:37 +08:00
|
|
|
static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
2007-05-06 02:45:53 +08:00
|
|
|
struct ieee80211_mgmt *mgmt,
|
|
|
|
size_t len,
|
|
|
|
struct ieee80211_rx_status *rx_status)
|
|
|
|
{
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 19:17:26 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2010-03-30 14:28:30 +08:00
|
|
|
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
|
2007-05-06 02:45:53 +08:00
|
|
|
size_t baselen;
|
|
|
|
struct ieee802_11_elems elems;
|
2008-08-03 08:04:37 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2007-12-28 21:32:58 +08:00
|
|
|
u32 changed = 0;
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 19:17:26 +08:00
|
|
|
bool erp_valid, directed_tim = false;
|
2008-10-08 16:59:33 +08:00
|
|
|
u8 erp_value = 0;
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 19:17:26 +08:00
|
|
|
u32 ncrc;
|
2009-07-07 09:45:17 +08:00
|
|
|
u8 *bssid;
|
|
|
|
|
|
|
|
ASSERT_MGD_MTX(ifmgd);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2008-06-27 23:54:48 +08:00
|
|
|
/* Process beacon from the current BSS */
|
|
|
|
baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
|
|
|
|
if (baselen > len)
|
|
|
|
return;
|
|
|
|
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 19:17:26 +08:00
|
|
|
if (rx_status->freq != local->hw.conf.channel->center_freq)
|
2007-05-06 02:45:53 +08:00
|
|
|
return;
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
/*
|
|
|
|
* We might have received a number of frames, among them a
|
|
|
|
* disassoc frame and a beacon...
|
|
|
|
*/
|
|
|
|
if (!ifmgd->associated)
|
2009-07-07 09:45:17 +08:00
|
|
|
return;
|
|
|
|
|
2009-12-23 20:15:39 +08:00
|
|
|
bssid = ifmgd->associated->bssid;
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
/*
|
|
|
|
* And in theory even frames from a different AP we were just
|
|
|
|
* associated to a split-second ago!
|
|
|
|
*/
|
|
|
|
if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0)
|
2007-05-06 02:45:53 +08:00
|
|
|
return;
|
|
|
|
|
2010-03-30 14:28:30 +08:00
|
|
|
/* Track average RSSI from the Beacon frames of the current AP */
|
|
|
|
ifmgd->last_beacon_signal = rx_status->signal;
|
|
|
|
if (ifmgd->flags & IEEE80211_STA_RESET_SIGNAL_AVE) {
|
|
|
|
ifmgd->flags &= ~IEEE80211_STA_RESET_SIGNAL_AVE;
|
|
|
|
ifmgd->ave_beacon_signal = rx_status->signal;
|
|
|
|
ifmgd->last_cqm_event_signal = 0;
|
|
|
|
} else {
|
|
|
|
ifmgd->ave_beacon_signal =
|
|
|
|
(IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 +
|
|
|
|
(16 - IEEE80211_SIGNAL_AVE_WEIGHT) *
|
|
|
|
ifmgd->ave_beacon_signal) / 16;
|
|
|
|
}
|
|
|
|
if (bss_conf->cqm_rssi_thold &&
|
|
|
|
!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) {
|
|
|
|
int sig = ifmgd->ave_beacon_signal / 16;
|
|
|
|
int last_event = ifmgd->last_cqm_event_signal;
|
|
|
|
int thold = bss_conf->cqm_rssi_thold;
|
|
|
|
int hyst = bss_conf->cqm_rssi_hyst;
|
|
|
|
if (sig < thold &&
|
|
|
|
(last_event == 0 || sig < last_event - hyst)) {
|
|
|
|
ifmgd->last_cqm_event_signal = sig;
|
|
|
|
ieee80211_cqm_rssi_notify(
|
|
|
|
&sdata->vif,
|
|
|
|
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
|
|
|
|
GFP_KERNEL);
|
|
|
|
} else if (sig > thold &&
|
|
|
|
(last_event == 0 || sig > last_event + hyst)) {
|
|
|
|
ifmgd->last_cqm_event_signal = sig;
|
|
|
|
ieee80211_cqm_rssi_notify(
|
|
|
|
&sdata->vif,
|
|
|
|
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
|
|
|
|
GFP_KERNEL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
|
2009-05-15 02:15:36 +08:00
|
|
|
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
|
|
|
if (net_ratelimit()) {
|
|
|
|
printk(KERN_DEBUG "%s: cancelling probereq poll due "
|
2009-11-26 00:46:19 +08:00
|
|
|
"to a received beacon\n", sdata->name);
|
2009-05-15 02:15:36 +08:00
|
|
|
}
|
|
|
|
#endif
|
2009-07-10 21:29:03 +08:00
|
|
|
ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
|
2009-06-10 21:16:52 +08:00
|
|
|
mutex_lock(&local->iflist_mtx);
|
|
|
|
ieee80211_recalc_ps(local, -1);
|
|
|
|
mutex_unlock(&local->iflist_mtx);
|
2009-05-15 02:15:36 +08:00
|
|
|
}
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
/*
|
|
|
|
* Push the beacon loss detection into the future since
|
|
|
|
* we are processing a beacon from the AP just now.
|
|
|
|
*/
|
|
|
|
mod_beacon_timer(sdata);
|
|
|
|
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 19:17:26 +08:00
|
|
|
ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
|
|
|
|
ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
|
|
|
|
len - baselen, &elems,
|
|
|
|
care_about_ies, ncrc);
|
|
|
|
|
|
|
|
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
|
2009-04-18 23:33:24 +08:00
|
|
|
directed_tim = ieee80211_check_tim(elems.tim, elems.tim_len,
|
|
|
|
ifmgd->aid);
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 19:17:26 +08:00
|
|
|
|
2009-05-19 22:01:43 +08:00
|
|
|
if (ncrc != ifmgd->beacon_crc) {
|
|
|
|
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems,
|
|
|
|
true);
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 19:17:26 +08:00
|
|
|
|
2010-07-19 22:39:04 +08:00
|
|
|
ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
|
2009-05-19 22:01:43 +08:00
|
|
|
elems.wmm_param_len);
|
|
|
|
}
|
2008-09-08 17:05:09 +08:00
|
|
|
|
2009-03-02 22:50:30 +08:00
|
|
|
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
|
2009-02-10 23:09:24 +08:00
|
|
|
if (directed_tim) {
|
2009-02-10 23:09:31 +08:00
|
|
|
if (local->hw.conf.dynamic_ps_timeout > 0) {
|
|
|
|
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
|
|
|
|
ieee80211_hw_config(local,
|
|
|
|
IEEE80211_CONF_CHANGE_PS);
|
|
|
|
ieee80211_send_nullfunc(local, sdata, 0);
|
|
|
|
} else {
|
|
|
|
local->pspolling = true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here is assumed that the driver will be
|
|
|
|
* able to send ps-poll frame and receive a
|
|
|
|
* response even though power save mode is
|
|
|
|
* enabled, but some drivers might require
|
|
|
|
* to disable power save here. This needs
|
|
|
|
* to be investigated.
|
|
|
|
*/
|
|
|
|
ieee80211_send_pspoll(local, sdata);
|
|
|
|
}
|
2008-12-24 10:39:02 +08:00
|
|
|
}
|
|
|
|
}
|
2008-10-08 16:59:33 +08:00
|
|
|
|
2009-05-19 22:01:43 +08:00
|
|
|
if (ncrc == ifmgd->beacon_crc)
|
|
|
|
return;
|
|
|
|
ifmgd->beacon_crc = ncrc;
|
|
|
|
|
2008-10-08 16:59:33 +08:00
|
|
|
if (elems.erp_info && elems.erp_info_len >= 1) {
|
|
|
|
erp_valid = true;
|
|
|
|
erp_value = elems.erp_info[0];
|
|
|
|
} else {
|
|
|
|
erp_valid = false;
|
2008-04-16 02:09:27 +08:00
|
|
|
}
|
2008-10-08 16:59:33 +08:00
|
|
|
changed |= ieee80211_handle_bss_capability(sdata,
|
|
|
|
le16_to_cpu(mgmt->u.beacon.capab_info),
|
|
|
|
erp_valid, erp_value);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2007-11-26 22:14:34 +08:00
|
|
|
|
2009-02-12 00:48:49 +08:00
|
|
|
if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param &&
|
2009-07-02 03:41:17 +08:00
|
|
|
!(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) {
|
2008-10-14 22:58:37 +08:00
|
|
|
struct sta_info *sta;
|
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
|
u16 ap_ht_cap_flags;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
|
2009-11-26 00:46:18 +08:00
|
|
|
sta = sta_info_get(sdata, bssid);
|
2009-07-07 09:45:17 +08:00
|
|
|
if (WARN_ON(!sta)) {
|
2008-10-14 22:58:37 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
|
|
|
|
|
|
|
|
ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
|
|
|
|
elems.ht_cap_elem, &sta->sta.ht_cap);
|
|
|
|
|
|
|
|
ap_ht_cap_flags = sta->sta.ht_cap.cap;
|
|
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
|
2009-07-07 09:45:17 +08:00
|
|
|
bssid, ap_ht_cap_flags);
|
2007-11-26 22:14:34 +08:00
|
|
|
}
|
|
|
|
|
2009-07-31 08:38:09 +08:00
|
|
|
/* Note: country IE parsing is done for us by cfg80211 */
|
2008-11-13 06:22:02 +08:00
|
|
|
if (elems.country_elem) {
|
2009-01-09 20:44:15 +08:00
|
|
|
/* TODO: IBSS also needs this */
|
|
|
|
if (elems.pwr_constr_elem)
|
|
|
|
ieee80211_handle_pwr_constr(sdata,
|
|
|
|
le16_to_cpu(mgmt->u.probe_resp.capab_info),
|
|
|
|
elems.pwr_constr_elem,
|
|
|
|
elems.pwr_constr_elem_len);
|
2008-11-13 06:22:02 +08:00
|
|
|
}
|
|
|
|
|
2007-12-28 21:32:58 +08:00
|
|
|
ieee80211_bss_info_change_notify(sdata, changed);
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2010-06-10 16:21:32 +08:00
|
|
|
void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct sk_buff *skb)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2009-07-07 09:45:17 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2007-05-06 02:45:53 +08:00
|
|
|
struct ieee80211_rx_status *rx_status;
|
|
|
|
struct ieee80211_mgmt *mgmt;
|
2009-07-07 09:45:17 +08:00
|
|
|
enum rx_mgmt_action rma = RX_MGMT_NONE;
|
2007-05-06 02:45:53 +08:00
|
|
|
u16 fc;
|
|
|
|
|
|
|
|
rx_status = (struct ieee80211_rx_status *) skb->cb;
|
|
|
|
mgmt = (struct ieee80211_mgmt *) skb->data;
|
|
|
|
fc = le16_to_cpu(mgmt->frame_control);
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
|
|
|
|
|
if (ifmgd->associated &&
|
2009-12-23 20:15:39 +08:00
|
|
|
memcmp(ifmgd->associated->bssid, mgmt->bssid, ETH_ALEN) == 0) {
|
2009-07-07 09:45:17 +08:00
|
|
|
switch (fc & IEEE80211_FCTL_STYPE) {
|
|
|
|
case IEEE80211_STYPE_BEACON:
|
|
|
|
ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
|
|
|
|
rx_status);
|
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_PROBE_RESP:
|
2009-12-23 20:15:35 +08:00
|
|
|
ieee80211_rx_mgmt_probe_resp(sdata, skb);
|
2009-07-07 09:45:17 +08:00
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_DEAUTH:
|
2009-12-23 20:15:33 +08:00
|
|
|
rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
|
2009-07-07 09:45:17 +08:00
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_DISASSOC:
|
|
|
|
rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
|
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_ACTION:
|
mac80211: process station blockack action frames from work
Processing an association response could take a bit
of time while we set up the hardware etc. During that
time, the AP might already send a blockack request.
If this happens very quickly on a fairly slow machine,
we can end up processing the blockack request before
the association processing has finished. Since the
blockack processing cannot sleep right now, we also
cannot make it wait in the driver.
As a result, sometimes on slow machines the iwlagn
driver gets totally confused, and no traffic can pass
when the aggregation setup was done before the assoc
setup completed.
I'm working on a proper fix for this, which involves
queuing all blockack category action frames from a
work struct, and also allowing the ampdu_action driver
callback to sleep, which will generally clean up the
code and make things easier.
However, this is a very involved and complex change.
To fix the problem at hand in a way that can also be
backported to stable, I've come up with this patch.
Here, I simply process all aggregation action frames
from the managed interface skb queue, which means
their processing will be serialized with processing
the association response, thereby fixing the problem.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Cc: stable@kernel.org
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-05-28 21:22:58 +08:00
|
|
|
switch (mgmt->u.action.category) {
|
|
|
|
case WLAN_CATEGORY_SPECTRUM_MGMT:
|
|
|
|
ieee80211_sta_process_chanswitch(sdata,
|
|
|
|
&mgmt->u.action.u.chan_switch.sw_elem,
|
|
|
|
(void *)ifmgd->associated->priv,
|
|
|
|
rx_status->mactime);
|
|
|
|
break;
|
|
|
|
}
|
2009-07-07 09:45:17 +08:00
|
|
|
}
|
|
|
|
mutex_unlock(&ifmgd->mtx);
|
|
|
|
|
|
|
|
switch (rma) {
|
|
|
|
case RX_MGMT_NONE:
|
|
|
|
/* no action */
|
|
|
|
break;
|
|
|
|
case RX_MGMT_CFG80211_DEAUTH:
|
2009-10-13 19:28:13 +08:00
|
|
|
cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
|
2009-07-07 09:45:17 +08:00
|
|
|
break;
|
|
|
|
case RX_MGMT_CFG80211_DISASSOC:
|
2009-10-13 19:28:13 +08:00
|
|
|
cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
|
2009-07-07 09:45:17 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
WARN(1, "unexpected: %d", rma);
|
|
|
|
}
|
2010-06-10 16:21:33 +08:00
|
|
|
return;
|
2009-07-07 09:45:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&ifmgd->mtx);
|
|
|
|
|
2009-12-23 20:15:33 +08:00
|
|
|
if (skb->len >= 24 + 2 /* mgmt + deauth reason */ &&
|
2010-06-08 03:50:07 +08:00
|
|
|
(fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DEAUTH) {
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_work *wk;
|
|
|
|
|
2010-07-30 22:46:07 +08:00
|
|
|
mutex_lock(&local->mtx);
|
2010-06-08 03:50:07 +08:00
|
|
|
list_for_each_entry(wk, &local->work_list, list) {
|
|
|
|
if (wk->sdata != sdata)
|
|
|
|
continue;
|
|
|
|
|
2010-07-29 22:08:55 +08:00
|
|
|
if (wk->type != IEEE80211_WORK_ASSOC &&
|
|
|
|
wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT)
|
2010-06-08 03:50:07 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (memcmp(mgmt->bssid, wk->filter_ta, ETH_ALEN))
|
|
|
|
continue;
|
|
|
|
if (memcmp(mgmt->sa, wk->filter_ta, ETH_ALEN))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Printing the message only here means we can't
|
|
|
|
* spuriously print it, but it also means that it
|
|
|
|
* won't be printed when the frame comes in before
|
|
|
|
* we even tried to associate or in similar cases.
|
|
|
|
*
|
|
|
|
* Ultimately, I suspect cfg80211 should print the
|
|
|
|
* messages instead.
|
|
|
|
*/
|
|
|
|
printk(KERN_DEBUG
|
|
|
|
"%s: deauthenticated from %pM (Reason: %u)\n",
|
|
|
|
sdata->name, mgmt->bssid,
|
|
|
|
le16_to_cpu(mgmt->u.deauth.reason_code));
|
|
|
|
|
|
|
|
list_del_rcu(&wk->list);
|
|
|
|
free_work(wk);
|
|
|
|
break;
|
|
|
|
}
|
2010-07-30 22:46:07 +08:00
|
|
|
mutex_unlock(&local->mtx);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-10-13 19:28:13 +08:00
|
|
|
cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
|
2010-06-08 03:50:07 +08:00
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2008-09-11 06:01:52 +08:00
|
|
|
static void ieee80211_sta_timer(unsigned long data)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2008-09-08 23:44:22 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
(struct ieee80211_sub_if_data *) data;
|
2009-02-15 19:44:28 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2008-09-08 23:44:22 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-05-17 17:40:42 +08:00
|
|
|
if (local->quiescing) {
|
|
|
|
set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-06-10 16:21:31 +08:00
|
|
|
ieee80211_queue_work(&local->hw, &sdata->work);
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2010-06-10 16:21:32 +08:00
|
|
|
void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
2008-09-11 06:01:52 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
2010-06-10 16:21:32 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2008-09-11 06:01:52 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
/* then process the rest of the work */
|
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
|
|
|
|
IEEE80211_STA_CONNECTION_POLL) &&
|
|
|
|
ifmgd->associated) {
|
2009-07-31 23:54:12 +08:00
|
|
|
u8 bssid[ETH_ALEN];
|
|
|
|
|
2009-12-23 20:15:39 +08:00
|
|
|
memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
|
2009-07-10 21:29:03 +08:00
|
|
|
if (time_is_after_jiffies(ifmgd->probe_timeout))
|
|
|
|
run_again(ifmgd, ifmgd->probe_timeout);
|
2009-07-31 23:54:12 +08:00
|
|
|
|
|
|
|
else if (ifmgd->probe_send_count < IEEE80211_MAX_PROBE_TRIES) {
|
|
|
|
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
|
|
|
printk(KERN_DEBUG "No probe response from AP %pM"
|
|
|
|
" after %dms, try %d\n", bssid,
|
|
|
|
(1000 * IEEE80211_PROBE_WAIT)/HZ,
|
|
|
|
ifmgd->probe_send_count);
|
|
|
|
#endif
|
|
|
|
ieee80211_mgd_probe_ap_send(sdata);
|
|
|
|
} else {
|
2009-07-10 21:29:03 +08:00
|
|
|
/*
|
|
|
|
* We actually lost the connection ... or did we?
|
|
|
|
* Let's make sure!
|
|
|
|
*/
|
|
|
|
ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
|
|
|
|
IEEE80211_STA_BEACON_POLL);
|
|
|
|
printk(KERN_DEBUG "No probe response from AP %pM"
|
|
|
|
" after %dms, disconnecting.\n",
|
|
|
|
bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
|
2010-03-30 14:29:31 +08:00
|
|
|
ieee80211_set_disassoc(sdata, true);
|
2009-07-10 21:29:03 +08:00
|
|
|
mutex_unlock(&ifmgd->mtx);
|
2010-08-05 23:02:38 +08:00
|
|
|
mutex_lock(&local->mtx);
|
|
|
|
ieee80211_recalc_idle(local);
|
|
|
|
mutex_unlock(&local->mtx);
|
2009-07-10 21:29:03 +08:00
|
|
|
/*
|
|
|
|
* must be outside lock due to cfg80211,
|
|
|
|
* but that's not a problem.
|
|
|
|
*/
|
|
|
|
ieee80211_send_deauth_disassoc(sdata, bssid,
|
|
|
|
IEEE80211_STYPE_DEAUTH,
|
|
|
|
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
2010-04-04 14:37:19 +08:00
|
|
|
NULL, true);
|
2009-07-10 21:29:03 +08:00
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
mutex_unlock(&ifmgd->mtx);
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
static void ieee80211_sta_bcn_mon_timer(unsigned long data)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
(struct ieee80211_sub_if_data *) data;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
|
|
|
|
if (local->quiescing)
|
|
|
|
return;
|
|
|
|
|
2010-03-19 13:14:53 +08:00
|
|
|
ieee80211_queue_work(&sdata->local->hw,
|
|
|
|
&sdata->u.mgd.beacon_connection_loss_work);
|
2009-07-10 21:29:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_sta_conn_mon_timer(unsigned long data)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
(struct ieee80211_sub_if_data *) data;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
|
|
|
|
if (local->quiescing)
|
|
|
|
return;
|
|
|
|
|
2009-07-30 08:08:07 +08:00
|
|
|
ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
|
2009-07-10 21:29:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_sta_monitor_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
container_of(work, struct ieee80211_sub_if_data,
|
|
|
|
u.mgd.monitor_work);
|
|
|
|
|
|
|
|
ieee80211_mgd_probe_ap(sdata, false);
|
|
|
|
}
|
|
|
|
|
2008-09-11 06:01:52 +08:00
|
|
|
static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
2009-04-19 13:47:19 +08:00
|
|
|
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
2009-07-10 21:29:03 +08:00
|
|
|
sdata->u.mgd.flags &= ~(IEEE80211_STA_BEACON_POLL |
|
|
|
|
IEEE80211_STA_CONNECTION_POLL);
|
2009-04-19 13:47:19 +08:00
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
/* let's probe the connection once */
|
2009-07-30 08:08:07 +08:00
|
|
|
ieee80211_queue_work(&sdata->local->hw,
|
2009-07-10 21:29:03 +08:00
|
|
|
&sdata->u.mgd.monitor_work);
|
|
|
|
/* and do all the other regular work too */
|
2010-06-10 16:21:31 +08:00
|
|
|
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
|
2009-04-19 13:47:19 +08:00
|
|
|
}
|
2008-09-11 06:01:52 +08:00
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-05-17 17:40:42 +08:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we need to use atomic bitops for the running bits
|
|
|
|
* only because both timers might fire at the same
|
|
|
|
* time -- the code here is properly synchronised.
|
|
|
|
*/
|
|
|
|
|
2010-08-05 23:05:55 +08:00
|
|
|
cancel_work_sync(&ifmgd->request_smps_work);
|
|
|
|
|
2010-03-19 13:14:53 +08:00
|
|
|
cancel_work_sync(&ifmgd->beacon_connection_loss_work);
|
2009-05-17 17:40:42 +08:00
|
|
|
if (del_timer_sync(&ifmgd->timer))
|
|
|
|
set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
|
|
|
|
|
|
|
|
cancel_work_sync(&ifmgd->chswitch_work);
|
|
|
|
if (del_timer_sync(&ifmgd->chswitch_timer))
|
|
|
|
set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
|
2009-07-10 21:29:03 +08:00
|
|
|
|
|
|
|
cancel_work_sync(&ifmgd->monitor_work);
|
|
|
|
/* these will just be re-established on connection */
|
|
|
|
del_timer_sync(&ifmgd->conn_mon_timer);
|
|
|
|
del_timer_sync(&ifmgd->bcn_mon_timer);
|
2009-05-17 17:40:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
|
|
|
|
if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running))
|
|
|
|
add_timer(&ifmgd->timer);
|
|
|
|
if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running))
|
|
|
|
add_timer(&ifmgd->chswitch_timer);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-09-11 06:01:52 +08:00
|
|
|
/* interface setup */
|
|
|
|
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2009-02-15 19:44:28 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd;
|
2008-04-18 01:21:22 +08:00
|
|
|
|
2009-02-15 19:44:28 +08:00
|
|
|
ifmgd = &sdata->u.mgd;
|
2009-07-10 21:29:03 +08:00
|
|
|
INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
|
2009-02-15 19:44:28 +08:00
|
|
|
INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
|
2010-03-19 13:14:53 +08:00
|
|
|
INIT_WORK(&ifmgd->beacon_connection_loss_work,
|
|
|
|
ieee80211_beacon_connection_loss_work);
|
2010-08-05 23:05:55 +08:00
|
|
|
INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
|
2009-02-15 19:44:28 +08:00
|
|
|
setup_timer(&ifmgd->timer, ieee80211_sta_timer,
|
2009-01-06 11:58:37 +08:00
|
|
|
(unsigned long) sdata);
|
2009-07-10 21:29:03 +08:00
|
|
|
setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
|
|
|
|
(unsigned long) sdata);
|
|
|
|
setup_timer(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer,
|
|
|
|
(unsigned long) sdata);
|
2009-02-15 19:44:28 +08:00
|
|
|
setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
|
2008-09-11 06:01:52 +08:00
|
|
|
(unsigned long) sdata);
|
|
|
|
|
2009-07-02 03:41:17 +08:00
|
|
|
ifmgd->flags = 0;
|
2009-04-16 19:27:42 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
mutex_init(&ifmgd->mtx);
|
2009-12-01 20:37:02 +08:00
|
|
|
|
|
|
|
if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
|
|
|
|
ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
|
|
|
|
else
|
|
|
|
ifmgd->req_smps = IEEE80211_SMPS_OFF;
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
/* scan finished notification */
|
|
|
|
void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
|
2008-09-08 23:44:22 +08:00
|
|
|
{
|
2009-07-07 09:45:17 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = local->scan_sdata;
|
2008-09-08 23:44:22 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
/* Restart STA timers */
|
|
|
|
rcu_read_lock();
|
|
|
|
list_for_each_entry_rcu(sdata, &local->interfaces, list)
|
|
|
|
ieee80211_restart_sta_timer(sdata);
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
2008-09-08 23:44:22 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
int ieee80211_max_network_latency(struct notifier_block *nb,
|
|
|
|
unsigned long data, void *dummy)
|
|
|
|
{
|
|
|
|
s32 latency_usec = (s32) data;
|
|
|
|
struct ieee80211_local *local =
|
|
|
|
container_of(nb, struct ieee80211_local,
|
|
|
|
network_latency_notifier);
|
2009-06-16 00:13:58 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
mutex_lock(&local->iflist_mtx);
|
|
|
|
ieee80211_recalc_ps(local, latency_usec);
|
|
|
|
mutex_unlock(&local->iflist_mtx);
|
2009-01-24 08:19:04 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
return 0;
|
2008-09-11 06:01:52 +08:00
|
|
|
}
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
/* config hooks */
|
2009-12-23 20:15:35 +08:00
|
|
|
static enum work_done_result
|
|
|
|
ieee80211_probe_auth_done(struct ieee80211_work *wk,
|
|
|
|
struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
if (!skb) {
|
|
|
|
cfg80211_send_auth_timeout(wk->sdata->dev, wk->filter_ta);
|
|
|
|
return WORK_DONE_DESTROY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wk->type == IEEE80211_WORK_AUTH) {
|
|
|
|
cfg80211_send_rx_auth(wk->sdata->dev, skb->data, skb->len);
|
|
|
|
return WORK_DONE_DESTROY;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_lock(&wk->sdata->u.mgd.mtx);
|
|
|
|
ieee80211_rx_mgmt_probe_resp(wk->sdata, skb);
|
|
|
|
mutex_unlock(&wk->sdata->u.mgd.mtx);
|
|
|
|
|
|
|
|
wk->type = IEEE80211_WORK_AUTH;
|
|
|
|
wk->probe_auth.tries = 0;
|
|
|
|
return WORK_DONE_REQUEUE;
|
|
|
|
}
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct cfg80211_auth_request *req)
|
2008-09-11 06:01:52 +08:00
|
|
|
{
|
2009-07-07 09:45:17 +08:00
|
|
|
const u8 *ssid;
|
2009-12-23 20:15:34 +08:00
|
|
|
struct ieee80211_work *wk;
|
2009-07-07 09:45:17 +08:00
|
|
|
u16 auth_alg;
|
2008-09-11 06:01:52 +08:00
|
|
|
|
2010-04-04 14:37:19 +08:00
|
|
|
if (req->local_state_change)
|
|
|
|
return 0; /* no need to update mac80211 state */
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
switch (req->auth_type) {
|
|
|
|
case NL80211_AUTHTYPE_OPEN_SYSTEM:
|
|
|
|
auth_alg = WLAN_AUTH_OPEN;
|
|
|
|
break;
|
|
|
|
case NL80211_AUTHTYPE_SHARED_KEY:
|
2010-07-21 16:09:25 +08:00
|
|
|
if (IS_ERR(sdata->local->wep_tx_tfm))
|
|
|
|
return -EOPNOTSUPP;
|
2009-07-07 09:45:17 +08:00
|
|
|
auth_alg = WLAN_AUTH_SHARED_KEY;
|
|
|
|
break;
|
|
|
|
case NL80211_AUTHTYPE_FT:
|
|
|
|
auth_alg = WLAN_AUTH_FT;
|
|
|
|
break;
|
|
|
|
case NL80211_AUTHTYPE_NETWORK_EAP:
|
|
|
|
auth_alg = WLAN_AUTH_LEAP;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
2008-09-08 23:44:22 +08:00
|
|
|
}
|
2009-07-07 09:45:17 +08:00
|
|
|
|
|
|
|
wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL);
|
|
|
|
if (!wk)
|
2008-09-11 06:01:52 +08:00
|
|
|
return -ENOMEM;
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2010-01-09 14:33:38 +08:00
|
|
|
memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN);
|
2009-07-07 09:45:17 +08:00
|
|
|
|
|
|
|
if (req->ie && req->ie_len) {
|
|
|
|
memcpy(wk->ie, req->ie, req->ie_len);
|
|
|
|
wk->ie_len = req->ie_len;
|
2008-09-11 06:01:52 +08:00
|
|
|
}
|
2009-07-07 09:45:17 +08:00
|
|
|
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 20:22:54 +08:00
|
|
|
if (req->key && req->key_len) {
|
2009-12-23 20:15:35 +08:00
|
|
|
wk->probe_auth.key_len = req->key_len;
|
|
|
|
wk->probe_auth.key_idx = req->key_idx;
|
|
|
|
memcpy(wk->probe_auth.key, req->key, req->key_len);
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 20:22:54 +08:00
|
|
|
}
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
|
2009-12-23 20:15:35 +08:00
|
|
|
memcpy(wk->probe_auth.ssid, ssid + 2, ssid[1]);
|
|
|
|
wk->probe_auth.ssid_len = ssid[1];
|
2009-12-23 20:15:34 +08:00
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
wk->probe_auth.algorithm = auth_alg;
|
|
|
|
wk->probe_auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY;
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2010-02-03 20:57:46 +08:00
|
|
|
/* if we already have a probe, don't probe again */
|
|
|
|
if (req->bss->proberesp_ies)
|
|
|
|
wk->type = IEEE80211_WORK_AUTH;
|
|
|
|
else
|
|
|
|
wk->type = IEEE80211_WORK_DIRECT_PROBE;
|
2009-12-23 20:15:34 +08:00
|
|
|
wk->chan = req->bss->channel;
|
2009-12-23 20:15:35 +08:00
|
|
|
wk->sdata = sdata;
|
|
|
|
wk->done = ieee80211_probe_auth_done;
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
ieee80211_add_work(wk);
|
2008-09-11 06:01:52 +08:00
|
|
|
return 0;
|
2008-09-08 23:44:22 +08:00
|
|
|
}
|
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
|
|
|
|
struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct ieee80211_mgmt *mgmt;
|
2010-07-29 22:08:55 +08:00
|
|
|
struct ieee80211_rx_status *rx_status;
|
|
|
|
struct ieee802_11_elems elems;
|
2009-12-23 20:15:35 +08:00
|
|
|
u16 status;
|
|
|
|
|
|
|
|
if (!skb) {
|
|
|
|
cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta);
|
|
|
|
return WORK_DONE_DESTROY;
|
|
|
|
}
|
|
|
|
|
2010-07-29 22:08:55 +08:00
|
|
|
if (wk->type == IEEE80211_WORK_ASSOC_BEACON_WAIT) {
|
|
|
|
mutex_lock(&wk->sdata->u.mgd.mtx);
|
|
|
|
rx_status = (void *) skb->cb;
|
|
|
|
ieee802_11_parse_elems(skb->data + 24 + 12, skb->len - 24 - 12, &elems);
|
|
|
|
ieee80211_rx_bss_info(wk->sdata, (void *)skb->data, skb->len, rx_status,
|
|
|
|
&elems, true);
|
|
|
|
mutex_unlock(&wk->sdata->u.mgd.mtx);
|
|
|
|
|
|
|
|
wk->type = IEEE80211_WORK_ASSOC;
|
|
|
|
/* not really done yet */
|
|
|
|
return WORK_DONE_REQUEUE;
|
|
|
|
}
|
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
mgmt = (void *)skb->data;
|
|
|
|
status = le16_to_cpu(mgmt->u.assoc_resp.status_code);
|
|
|
|
|
|
|
|
if (status == WLAN_STATUS_SUCCESS) {
|
|
|
|
mutex_lock(&wk->sdata->u.mgd.mtx);
|
|
|
|
if (!ieee80211_assoc_success(wk, mgmt, skb->len)) {
|
|
|
|
mutex_unlock(&wk->sdata->u.mgd.mtx);
|
|
|
|
/* oops -- internal error -- send timeout for now */
|
|
|
|
cfg80211_send_assoc_timeout(wk->sdata->dev,
|
|
|
|
wk->filter_ta);
|
|
|
|
return WORK_DONE_DESTROY;
|
|
|
|
}
|
2010-06-09 18:43:26 +08:00
|
|
|
|
|
|
|
mutex_unlock(&wk->sdata->u.mgd.mtx);
|
2009-12-23 20:15:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len);
|
|
|
|
return WORK_DONE_DESTROY;
|
|
|
|
}
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct cfg80211_assoc_request *req)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2009-07-07 09:45:17 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2009-12-23 20:15:39 +08:00
|
|
|
struct ieee80211_bss *bss = (void *)req->bss->priv;
|
2009-12-23 20:15:34 +08:00
|
|
|
struct ieee80211_work *wk;
|
2009-12-23 20:15:33 +08:00
|
|
|
const u8 *ssid;
|
2009-12-23 20:15:35 +08:00
|
|
|
int i;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
mutex_lock(&ifmgd->mtx);
|
2009-12-23 20:15:35 +08:00
|
|
|
if (ifmgd->associated) {
|
2010-02-28 18:13:46 +08:00
|
|
|
if (!req->prev_bssid ||
|
|
|
|
memcmp(req->prev_bssid, ifmgd->associated->bssid,
|
|
|
|
ETH_ALEN)) {
|
|
|
|
/*
|
|
|
|
* We are already associated and the request was not a
|
|
|
|
* reassociation request from the current BSS, so
|
|
|
|
* reject it.
|
|
|
|
*/
|
|
|
|
mutex_unlock(&ifmgd->mtx);
|
|
|
|
return -EALREADY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Trying to reassociate - clear previous association state */
|
2010-03-30 14:29:31 +08:00
|
|
|
ieee80211_set_disassoc(sdata, true);
|
2009-12-23 20:15:35 +08:00
|
|
|
}
|
|
|
|
mutex_unlock(&ifmgd->mtx);
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2009-12-23 20:15:33 +08:00
|
|
|
wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL);
|
2009-12-23 20:15:35 +08:00
|
|
|
if (!wk)
|
|
|
|
return -ENOMEM;
|
2009-07-07 09:45:17 +08:00
|
|
|
|
|
|
|
ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
|
2010-02-09 17:20:28 +08:00
|
|
|
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
|
2009-07-07 09:45:17 +08:00
|
|
|
|
|
|
|
for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
|
|
|
|
if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
|
|
|
|
req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP ||
|
|
|
|
req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104)
|
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
|
|
|
|
|
|
|
|
|
|
|
|
if (req->ie && req->ie_len) {
|
|
|
|
memcpy(wk->ie, req->ie, req->ie_len);
|
|
|
|
wk->ie_len = req->ie_len;
|
|
|
|
} else
|
|
|
|
wk->ie_len = 0;
|
|
|
|
|
2009-12-23 20:15:39 +08:00
|
|
|
wk->assoc.bss = req->bss;
|
2009-12-23 20:15:34 +08:00
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN);
|
2009-12-23 20:15:34 +08:00
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
/* new association always uses requested smps mode */
|
|
|
|
if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) {
|
|
|
|
if (ifmgd->powersave)
|
|
|
|
ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC;
|
|
|
|
else
|
|
|
|
ifmgd->ap_smps = IEEE80211_SMPS_OFF;
|
|
|
|
} else
|
|
|
|
ifmgd->ap_smps = ifmgd->req_smps;
|
|
|
|
|
|
|
|
wk->assoc.smps = ifmgd->ap_smps;
|
2009-12-23 20:15:37 +08:00
|
|
|
/*
|
|
|
|
* IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.
|
|
|
|
* We still associate in non-HT mode (11a/b/g) if any one of these
|
|
|
|
* ciphers is configured as pairwise.
|
|
|
|
* We can set this to true for non-11n hardware, that'll be checked
|
|
|
|
* separately along with the peer capabilities.
|
|
|
|
*/
|
2009-12-23 20:15:35 +08:00
|
|
|
wk->assoc.use_11n = !(ifmgd->flags & IEEE80211_STA_DISABLE_11N);
|
2009-12-23 20:15:34 +08:00
|
|
|
wk->assoc.capability = req->bss->capability;
|
2009-12-23 20:15:39 +08:00
|
|
|
wk->assoc.wmm_used = bss->wmm_used;
|
|
|
|
wk->assoc.supp_rates = bss->supp_rates;
|
|
|
|
wk->assoc.supp_rates_len = bss->supp_rates_len;
|
2009-12-23 20:15:34 +08:00
|
|
|
wk->assoc.ht_information_ie =
|
|
|
|
ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION);
|
2009-12-23 20:15:33 +08:00
|
|
|
|
2010-01-12 16:42:31 +08:00
|
|
|
if (bss->wmm_used && bss->uapsd_supported &&
|
|
|
|
(sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
|
|
|
|
wk->assoc.uapsd_used = true;
|
|
|
|
ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;
|
|
|
|
} else {
|
|
|
|
wk->assoc.uapsd_used = false;
|
|
|
|
ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED;
|
|
|
|
}
|
|
|
|
|
2009-12-23 20:15:33 +08:00
|
|
|
ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
|
2009-12-23 20:15:34 +08:00
|
|
|
memcpy(wk->assoc.ssid, ssid + 2, ssid[1]);
|
|
|
|
wk->assoc.ssid_len = ssid[1];
|
2009-12-23 20:15:33 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
if (req->prev_bssid)
|
2009-12-23 20:15:34 +08:00
|
|
|
memcpy(wk->assoc.prev_bssid, req->prev_bssid, ETH_ALEN);
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2009-12-23 20:15:34 +08:00
|
|
|
wk->chan = req->bss->channel;
|
2009-12-23 20:15:35 +08:00
|
|
|
wk->sdata = sdata;
|
|
|
|
wk->done = ieee80211_assoc_done;
|
2010-07-29 22:08:55 +08:00
|
|
|
if (!bss->dtim_period &&
|
|
|
|
sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD)
|
|
|
|
wk->type = IEEE80211_WORK_ASSOC_BEACON_WAIT;
|
|
|
|
else
|
|
|
|
wk->type = IEEE80211_WORK_ASSOC;
|
2009-07-07 09:45:17 +08:00
|
|
|
|
|
|
|
if (req->use_mfp) {
|
|
|
|
ifmgd->mfp = IEEE80211_MFP_REQUIRED;
|
|
|
|
ifmgd->flags |= IEEE80211_STA_MFP_ENABLED;
|
|
|
|
} else {
|
|
|
|
ifmgd->mfp = IEEE80211_MFP_DISABLED;
|
|
|
|
ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (req->crypto.control_port)
|
|
|
|
ifmgd->flags |= IEEE80211_STA_CONTROL_PORT;
|
|
|
|
else
|
|
|
|
ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT;
|
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
ieee80211_add_work(wk);
|
|
|
|
return 0;
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
|
2009-07-07 09:56:11 +08:00
|
|
|
struct cfg80211_deauth_request *req,
|
|
|
|
void *cookie)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2009-12-23 20:15:35 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2009-02-15 19:44:28 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2009-12-23 20:15:34 +08:00
|
|
|
struct ieee80211_work *wk;
|
2010-06-15 02:55:56 +08:00
|
|
|
u8 bssid[ETH_ALEN];
|
|
|
|
bool assoc_bss = false;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
|
|
2010-06-15 02:55:56 +08:00
|
|
|
memcpy(bssid, req->bss->bssid, ETH_ALEN);
|
2009-12-23 20:15:39 +08:00
|
|
|
if (ifmgd->associated == req->bss) {
|
2010-06-15 02:55:56 +08:00
|
|
|
ieee80211_set_disassoc(sdata, false);
|
2009-12-23 20:15:35 +08:00
|
|
|
mutex_unlock(&ifmgd->mtx);
|
2010-06-15 02:55:56 +08:00
|
|
|
assoc_bss = true;
|
2009-12-23 20:15:35 +08:00
|
|
|
} else {
|
|
|
|
bool not_auth_yet = false;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-11-19 19:45:42 +08:00
|
|
|
mutex_unlock(&ifmgd->mtx);
|
|
|
|
|
2010-07-30 22:46:07 +08:00
|
|
|
mutex_lock(&local->mtx);
|
2009-12-23 20:15:35 +08:00
|
|
|
list_for_each_entry(wk, &local->work_list, list) {
|
2010-02-06 22:20:13 +08:00
|
|
|
if (wk->sdata != sdata)
|
2009-12-23 20:15:35 +08:00
|
|
|
continue;
|
2010-02-06 22:20:13 +08:00
|
|
|
|
|
|
|
if (wk->type != IEEE80211_WORK_DIRECT_PROBE &&
|
2010-05-05 07:04:49 +08:00
|
|
|
wk->type != IEEE80211_WORK_AUTH &&
|
2010-07-29 22:08:55 +08:00
|
|
|
wk->type != IEEE80211_WORK_ASSOC &&
|
|
|
|
wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT)
|
2010-02-06 22:20:13 +08:00
|
|
|
continue;
|
|
|
|
|
2009-12-23 20:15:35 +08:00
|
|
|
if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN))
|
|
|
|
continue;
|
2010-02-06 22:20:13 +08:00
|
|
|
|
|
|
|
not_auth_yet = wk->type == IEEE80211_WORK_DIRECT_PROBE;
|
|
|
|
list_del_rcu(&wk->list);
|
2009-12-23 20:15:35 +08:00
|
|
|
free_work(wk);
|
|
|
|
break;
|
|
|
|
}
|
2010-07-30 22:46:07 +08:00
|
|
|
mutex_unlock(&local->mtx);
|
2009-12-23 20:15:35 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If somebody requests authentication and we haven't
|
|
|
|
* sent out an auth frame yet there's no need to send
|
|
|
|
* out a deauth frame either. If the state was PROBE,
|
|
|
|
* then this is the case. If it's AUTH we have sent a
|
|
|
|
* frame, and if it's IDLE we have completed the auth
|
|
|
|
* process already.
|
|
|
|
*/
|
|
|
|
if (not_auth_yet) {
|
|
|
|
__cfg80211_auth_canceled(sdata->dev, bssid);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2009-09-26 20:45:41 +08:00
|
|
|
printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n",
|
2009-11-26 00:46:19 +08:00
|
|
|
sdata->name, bssid, req->reason_code);
|
2009-09-26 20:45:41 +08:00
|
|
|
|
2010-04-04 14:37:19 +08:00
|
|
|
ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH,
|
|
|
|
req->reason_code, cookie,
|
|
|
|
!req->local_state_change);
|
2010-06-15 02:55:56 +08:00
|
|
|
if (assoc_bss)
|
|
|
|
sta_info_destroy_addr(sdata, bssid);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2010-08-05 23:02:38 +08:00
|
|
|
mutex_lock(&sdata->local->mtx);
|
2009-11-29 19:19:06 +08:00
|
|
|
ieee80211_recalc_idle(sdata->local);
|
2010-08-05 23:02:38 +08:00
|
|
|
mutex_unlock(&sdata->local->mtx);
|
2009-11-29 19:19:06 +08:00
|
|
|
|
2007-05-06 02:45:53 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2008-04-05 07:59:58 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
|
2009-07-07 09:56:11 +08:00
|
|
|
struct cfg80211_disassoc_request *req,
|
|
|
|
void *cookie)
|
2008-09-11 06:01:52 +08:00
|
|
|
{
|
2009-07-07 09:45:17 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2010-03-30 14:29:31 +08:00
|
|
|
u8 bssid[ETH_ALEN];
|
2008-09-11 06:01:52 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
mutex_lock(&ifmgd->mtx);
|
2009-04-16 19:17:25 +08:00
|
|
|
|
2009-07-25 17:58:36 +08:00
|
|
|
/*
|
|
|
|
* cfg80211 should catch this ... but it's racy since
|
|
|
|
* we can receive a disassoc frame, process it, hand it
|
|
|
|
* to cfg80211 while that's in a locked section already
|
|
|
|
* trying to tell us that the user wants to disconnect.
|
|
|
|
*/
|
2009-12-23 20:15:39 +08:00
|
|
|
if (ifmgd->associated != req->bss) {
|
2009-07-07 09:45:17 +08:00
|
|
|
mutex_unlock(&ifmgd->mtx);
|
|
|
|
return -ENOLINK;
|
|
|
|
}
|
|
|
|
|
2009-09-26 20:45:41 +08:00
|
|
|
printk(KERN_DEBUG "%s: disassociating from %pM by local choice (reason=%d)\n",
|
2009-11-26 00:46:19 +08:00
|
|
|
sdata->name, req->bss->bssid, req->reason_code);
|
2009-09-26 20:45:41 +08:00
|
|
|
|
2010-03-30 14:29:31 +08:00
|
|
|
memcpy(bssid, req->bss->bssid, ETH_ALEN);
|
|
|
|
ieee80211_set_disassoc(sdata, false);
|
2009-07-07 09:45:17 +08:00
|
|
|
|
|
|
|
mutex_unlock(&ifmgd->mtx);
|
2009-04-16 19:17:25 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
ieee80211_send_deauth_disassoc(sdata, req->bss->bssid,
|
2009-07-07 09:56:11 +08:00
|
|
|
IEEE80211_STYPE_DISASSOC, req->reason_code,
|
2010-04-04 14:37:19 +08:00
|
|
|
cookie, !req->local_state_change);
|
2010-03-30 14:29:31 +08:00
|
|
|
sta_info_destroy_addr(sdata, bssid);
|
2009-11-29 19:19:06 +08:00
|
|
|
|
2010-08-05 23:02:38 +08:00
|
|
|
mutex_lock(&sdata->local->mtx);
|
2009-11-29 19:19:06 +08:00
|
|
|
ieee80211_recalc_idle(sdata->local);
|
2010-08-05 23:02:38 +08:00
|
|
|
mutex_unlock(&sdata->local->mtx);
|
2009-11-29 19:19:06 +08:00
|
|
|
|
2009-04-16 19:17:25 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2010-02-15 18:53:10 +08:00
|
|
|
|
2010-03-23 15:02:34 +08:00
|
|
|
void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
|
|
|
|
enum nl80211_cqm_rssi_threshold_event rssi_event,
|
|
|
|
gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
|
|
2010-04-07 22:48:40 +08:00
|
|
|
trace_api_cqm_rssi_notify(sdata, rssi_event);
|
|
|
|
|
2010-03-23 15:02:34 +08:00
|
|
|
cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
|