2007-05-06 02:45:53 +08:00
|
|
|
/*
|
|
|
|
* mac80211 configuration hooks for cfg80211
|
|
|
|
*
|
2010-02-15 18:53:10 +08:00
|
|
|
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
|
2007-05-06 02:45:53 +08:00
|
|
|
*
|
|
|
|
* This file is GPLv2 as found in COPYING.
|
|
|
|
*/
|
|
|
|
|
2007-12-19 09:03:30 +08:00
|
|
|
#include <linux/ieee80211.h>
|
2007-05-06 02:45:53 +08:00
|
|
|
#include <linux/nl80211.h>
|
|
|
|
#include <linux/rtnetlink.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-09-18 02:56:21 +08:00
|
|
|
#include <net/net_namespace.h>
|
2007-12-19 09:03:33 +08:00
|
|
|
#include <linux/rcupdate.h>
|
2011-09-28 19:12:52 +08:00
|
|
|
#include <linux/if_ether.h>
|
2007-05-06 02:45:53 +08:00
|
|
|
#include <net/cfg80211.h>
|
|
|
|
#include "ieee80211_i.h"
|
2009-04-24 00:52:52 +08:00
|
|
|
#include "driver-ops.h"
|
2007-09-19 05:29:21 +08:00
|
|
|
#include "cfg.h"
|
2008-04-09 03:14:40 +08:00
|
|
|
#include "rate.h"
|
2008-02-23 22:17:17 +08:00
|
|
|
#include "mesh.h"
|
|
|
|
|
2012-09-19 15:26:06 +08:00
|
|
|
static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
|
|
|
|
const char *name,
|
2012-06-16 06:00:26 +08:00
|
|
|
enum nl80211_iftype type,
|
|
|
|
u32 *flags,
|
|
|
|
struct vif_params *params)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
2012-06-16 06:00:26 +08:00
|
|
|
struct wireless_dev *wdev;
|
2008-02-01 02:48:23 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata;
|
|
|
|
int err;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2012-06-16 06:00:26 +08:00
|
|
|
err = ieee80211_if_add(local, name, &wdev, type, params);
|
2010-12-03 16:20:42 +08:00
|
|
|
if (err)
|
|
|
|
return ERR_PTR(err);
|
2008-02-01 02:48:23 +08:00
|
|
|
|
2010-12-03 16:20:42 +08:00
|
|
|
if (type == NL80211_IFTYPE_MONITOR && flags) {
|
2012-06-16 06:00:26 +08:00
|
|
|
sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
2010-12-03 16:20:42 +08:00
|
|
|
sdata->u.mntr_flags = *flags;
|
|
|
|
}
|
|
|
|
|
2012-06-16 06:00:26 +08:00
|
|
|
return wdev;
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2012-06-16 06:00:26 +08:00
|
|
|
static int ieee80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2012-06-16 06:00:26 +08:00
|
|
|
ieee80211_if_remove(IEEE80211_WDEV_TO_SUB_IF(wdev));
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2008-07-09 20:40:35 +08:00
|
|
|
return 0;
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2009-06-10 03:04:43 +08:00
|
|
|
static int ieee80211_change_iface(struct wiphy *wiphy,
|
|
|
|
struct net_device *dev,
|
2008-02-23 22:17:06 +08:00
|
|
|
enum nl80211_iftype type, u32 *flags,
|
|
|
|
struct vif_params *params)
|
2007-09-29 03:52:27 +08:00
|
|
|
{
|
2009-12-23 20:15:31 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
2008-07-09 20:40:36 +08:00
|
|
|
int ret;
|
2007-09-29 03:52:27 +08:00
|
|
|
|
2008-09-11 06:01:58 +08:00
|
|
|
ret = ieee80211_if_change_type(sdata, type);
|
2008-07-09 20:40:36 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2007-09-29 03:52:27 +08:00
|
|
|
|
2009-11-19 18:55:19 +08:00
|
|
|
if (type == NL80211_IFTYPE_AP_VLAN &&
|
|
|
|
params && params->use_4addr == 0)
|
2011-08-02 00:19:00 +08:00
|
|
|
RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
|
2009-11-19 18:55:19 +08:00
|
|
|
else if (type == NL80211_IFTYPE_STATION &&
|
|
|
|
params && params->use_4addr >= 0)
|
|
|
|
sdata->u.mgd.use_4addr = params->use_4addr;
|
|
|
|
|
2010-10-02 19:17:07 +08:00
|
|
|
if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags) {
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
|
|
|
|
if (ieee80211_sdata_running(sdata)) {
|
2013-05-28 19:01:53 +08:00
|
|
|
u32 mask = MONITOR_FLAG_COOK_FRAMES |
|
|
|
|
MONITOR_FLAG_ACTIVE;
|
|
|
|
|
2010-10-02 19:17:07 +08:00
|
|
|
/*
|
2013-05-28 19:01:53 +08:00
|
|
|
* Prohibit MONITOR_FLAG_COOK_FRAMES and
|
|
|
|
* MONITOR_FLAG_ACTIVE to be changed while the
|
|
|
|
* interface is up.
|
2010-10-02 19:17:07 +08:00
|
|
|
* Else we would need to add a lot of cruft
|
|
|
|
* to update everything:
|
|
|
|
* cooked_mntrs, monitor and all fif_* counters
|
|
|
|
* reconfigure hardware
|
|
|
|
*/
|
2013-05-28 19:01:53 +08:00
|
|
|
if ((*flags & mask) != (sdata->u.mntr_flags & mask))
|
2010-10-02 19:17:07 +08:00
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
ieee80211_adjust_monitor_flags(sdata, -1);
|
|
|
|
sdata->u.mntr_flags = *flags;
|
|
|
|
ieee80211_adjust_monitor_flags(sdata, 1);
|
|
|
|
|
|
|
|
ieee80211_configure_filter(local);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Because the interface is down, ieee80211_do_stop
|
|
|
|
* and ieee80211_do_open take care of "everything"
|
|
|
|
* mentioned in the comment above.
|
|
|
|
*/
|
|
|
|
sdata->u.mntr_flags = *flags;
|
|
|
|
}
|
|
|
|
}
|
2010-04-27 06:26:34 +08:00
|
|
|
|
2007-09-29 03:52:27 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-19 02:07:15 +08:00
|
|
|
static int ieee80211_start_p2p_device(struct wiphy *wiphy,
|
|
|
|
struct wireless_dev *wdev)
|
|
|
|
{
|
|
|
|
return ieee80211_do_open(wdev, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_stop_p2p_device(struct wiphy *wiphy,
|
|
|
|
struct wireless_dev *wdev)
|
|
|
|
{
|
|
|
|
ieee80211_sdata_stop(IEEE80211_WDEV_TO_SUB_IF(wdev));
|
|
|
|
}
|
|
|
|
|
2011-11-18 21:20:44 +08:00
|
|
|
static int ieee80211_set_noack_map(struct wiphy *wiphy,
|
|
|
|
struct net_device *dev,
|
|
|
|
u16 noack_map)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
|
|
|
sdata->noack_map = noack_map;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-12-19 09:03:30 +08:00
|
|
|
static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
|
2010-10-06 01:39:30 +08:00
|
|
|
u8 key_idx, bool pairwise, const u8 *mac_addr,
|
2007-12-19 09:03:30 +08:00
|
|
|
struct key_params *params)
|
|
|
|
{
|
2010-08-27 18:35:55 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
2013-03-24 20:23:27 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2007-12-19 09:03:30 +08:00
|
|
|
struct sta_info *sta = NULL;
|
2013-03-24 20:23:27 +08:00
|
|
|
const struct ieee80211_cipher_scheme *cs = NULL;
|
2008-02-25 23:27:45 +08:00
|
|
|
struct ieee80211_key *key;
|
2008-04-08 23:56:52 +08:00
|
|
|
int err;
|
2007-12-19 09:03:30 +08:00
|
|
|
|
2010-08-27 18:35:55 +08:00
|
|
|
if (!ieee80211_sdata_running(sdata))
|
2010-06-01 16:19:19 +08:00
|
|
|
return -ENETDOWN;
|
|
|
|
|
2010-08-10 15:46:38 +08:00
|
|
|
/* reject WEP and TKIP keys if WEP failed to initialize */
|
2007-12-19 09:03:30 +08:00
|
|
|
switch (params->cipher) {
|
|
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
2010-08-10 15:46:38 +08:00
|
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
2013-03-24 20:23:27 +08:00
|
|
|
if (IS_ERR(local->wep_tx_tfm))
|
2010-08-10 15:46:38 +08:00
|
|
|
return -EINVAL;
|
2009-01-08 19:32:02 +08:00
|
|
|
break;
|
2013-03-24 20:23:27 +08:00
|
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
|
|
case WLAN_CIPHER_SUITE_AES_CMAC:
|
|
|
|
case WLAN_CIPHER_SUITE_GCMP:
|
|
|
|
break;
|
2007-12-19 09:03:30 +08:00
|
|
|
default:
|
2013-03-24 20:23:27 +08:00
|
|
|
cs = ieee80211_cs_get(local, params->cipher, sdata->vif.type);
|
2010-08-10 15:46:38 +08:00
|
|
|
break;
|
2007-12-19 09:03:30 +08:00
|
|
|
}
|
|
|
|
|
2010-08-10 15:46:38 +08:00
|
|
|
key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len,
|
2013-03-24 20:23:27 +08:00
|
|
|
params->key, params->seq_len, params->seq,
|
|
|
|
cs);
|
2010-08-02 00:37:03 +08:00
|
|
|
if (IS_ERR(key))
|
|
|
|
return PTR_ERR(key);
|
2008-02-25 23:27:45 +08:00
|
|
|
|
2010-10-06 01:39:30 +08:00
|
|
|
if (pairwise)
|
|
|
|
key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
|
|
|
|
|
2013-03-24 20:23:27 +08:00
|
|
|
mutex_lock(&local->sta_mtx);
|
2008-04-08 23:56:52 +08:00
|
|
|
|
2007-12-19 09:03:30 +08:00
|
|
|
if (mac_addr) {
|
2011-05-04 07:57:12 +08:00
|
|
|
if (ieee80211_vif_is_mesh(&sdata->vif))
|
|
|
|
sta = sta_info_get(sdata, mac_addr);
|
|
|
|
else
|
|
|
|
sta = sta_info_get_bss(sdata, mac_addr);
|
2013-01-11 21:34:25 +08:00
|
|
|
/*
|
|
|
|
* The ASSOC test makes sure the driver is ready to
|
|
|
|
* receive the key. When wpa_supplicant has roamed
|
|
|
|
* using FT, it attempts to set the key before
|
|
|
|
* association has completed, this rejects that attempt
|
|
|
|
* so it will set the key again after assocation.
|
|
|
|
*
|
|
|
|
* TODO: accept the key if we have a station entry and
|
|
|
|
* add it to the device after the station.
|
|
|
|
*/
|
|
|
|
if (!sta || !test_sta_flag(sta, WLAN_STA_ASSOC)) {
|
2013-03-07 05:53:52 +08:00
|
|
|
ieee80211_key_free_unused(key);
|
2008-04-08 23:56:52 +08:00
|
|
|
err = -ENOENT;
|
|
|
|
goto out_unlock;
|
2008-02-25 23:27:45 +08:00
|
|
|
}
|
2007-12-19 09:03:30 +08:00
|
|
|
}
|
|
|
|
|
2012-09-04 23:08:23 +08:00
|
|
|
switch (sdata->vif.type) {
|
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
|
if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
|
|
|
|
key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
|
|
/* Keys without a station are used for TX only */
|
|
|
|
if (key->sta && test_sta_flag(key->sta, WLAN_STA_MFP))
|
|
|
|
key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
|
/* no MFP (yet) */
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
|
|
#ifdef CONFIG_MAC80211_MESH
|
|
|
|
if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE)
|
|
|
|
key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case NL80211_IFTYPE_WDS:
|
|
|
|
case NL80211_IFTYPE_MONITOR:
|
|
|
|
case NL80211_IFTYPE_P2P_DEVICE:
|
|
|
|
case NL80211_IFTYPE_UNSPECIFIED:
|
|
|
|
case NUM_NL80211_IFTYPES:
|
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
|
|
/* shouldn't happen */
|
|
|
|
WARN_ON_ONCE(1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-03-24 20:23:27 +08:00
|
|
|
if (sta)
|
|
|
|
sta->cipher_scheme = cs;
|
|
|
|
|
2010-08-27 19:26:52 +08:00
|
|
|
err = ieee80211_key_link(key, sdata, sta);
|
2008-02-25 23:27:45 +08:00
|
|
|
|
2008-04-08 23:56:52 +08:00
|
|
|
out_unlock:
|
2013-03-24 20:23:27 +08:00
|
|
|
mutex_unlock(&local->sta_mtx);
|
2008-04-08 23:56:52 +08:00
|
|
|
|
|
|
|
return err;
|
2007-12-19 09:03:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
|
2010-10-06 01:39:30 +08:00
|
|
|
u8 key_idx, bool pairwise, const u8 *mac_addr)
|
2007-12-19 09:03:30 +08:00
|
|
|
{
|
2011-05-12 20:31:49 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
2007-12-19 09:03:30 +08:00
|
|
|
struct sta_info *sta;
|
2011-05-12 20:31:49 +08:00
|
|
|
struct ieee80211_key *key = NULL;
|
2007-12-19 09:03:30 +08:00
|
|
|
int ret;
|
|
|
|
|
2011-05-12 20:31:49 +08:00
|
|
|
mutex_lock(&local->sta_mtx);
|
|
|
|
mutex_lock(&local->key_mtx);
|
2008-04-08 23:56:52 +08:00
|
|
|
|
2007-12-19 09:03:30 +08:00
|
|
|
if (mac_addr) {
|
2008-04-08 23:56:52 +08:00
|
|
|
ret = -ENOENT;
|
|
|
|
|
2010-01-09 01:10:58 +08:00
|
|
|
sta = sta_info_get_bss(sdata, mac_addr);
|
2007-12-19 09:03:30 +08:00
|
|
|
if (!sta)
|
2008-04-08 23:56:52 +08:00
|
|
|
goto out_unlock;
|
2007-12-19 09:03:30 +08:00
|
|
|
|
2011-05-12 20:31:49 +08:00
|
|
|
if (pairwise)
|
2013-03-24 20:23:27 +08:00
|
|
|
key = key_mtx_dereference(local, sta->ptk[key_idx]);
|
2011-05-12 20:31:49 +08:00
|
|
|
else
|
2011-05-13 20:15:49 +08:00
|
|
|
key = key_mtx_dereference(local, sta->gtk[key_idx]);
|
2011-05-12 20:31:49 +08:00
|
|
|
} else
|
2011-05-13 20:15:49 +08:00
|
|
|
key = key_mtx_dereference(local, sdata->keys[key_idx]);
|
2007-12-19 09:03:30 +08:00
|
|
|
|
2011-05-12 20:31:49 +08:00
|
|
|
if (!key) {
|
2008-04-08 23:56:52 +08:00
|
|
|
ret = -ENOENT;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
2007-12-19 09:03:30 +08:00
|
|
|
|
2013-03-07 05:58:23 +08:00
|
|
|
ieee80211_key_free(key, true);
|
2007-12-19 09:03:30 +08:00
|
|
|
|
2008-04-08 23:56:52 +08:00
|
|
|
ret = 0;
|
|
|
|
out_unlock:
|
2011-05-12 20:31:49 +08:00
|
|
|
mutex_unlock(&local->key_mtx);
|
|
|
|
mutex_unlock(&local->sta_mtx);
|
2008-04-08 23:56:52 +08:00
|
|
|
|
|
|
|
return ret;
|
2007-12-19 09:03:30 +08:00
|
|
|
}
|
|
|
|
|
2007-12-19 09:03:31 +08:00
|
|
|
static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
|
2010-10-06 01:39:30 +08:00
|
|
|
u8 key_idx, bool pairwise, const u8 *mac_addr,
|
|
|
|
void *cookie,
|
2007-12-19 09:03:31 +08:00
|
|
|
void (*callback)(void *cookie,
|
|
|
|
struct key_params *params))
|
|
|
|
{
|
2008-07-29 19:22:52 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata;
|
2007-12-19 09:03:31 +08:00
|
|
|
struct sta_info *sta = NULL;
|
|
|
|
u8 seq[6] = {0};
|
|
|
|
struct key_params params;
|
2010-10-06 01:39:30 +08:00
|
|
|
struct ieee80211_key *key = NULL;
|
2011-07-07 03:59:39 +08:00
|
|
|
u64 pn64;
|
2007-12-19 09:03:31 +08:00
|
|
|
u32 iv32;
|
|
|
|
u16 iv16;
|
|
|
|
int err = -ENOENT;
|
|
|
|
|
2008-07-29 19:22:52 +08:00
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
2008-04-08 23:56:52 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2007-12-19 09:03:31 +08:00
|
|
|
if (mac_addr) {
|
2010-01-09 01:10:58 +08:00
|
|
|
sta = sta_info_get_bss(sdata, mac_addr);
|
2007-12-19 09:03:31 +08:00
|
|
|
if (!sta)
|
|
|
|
goto out;
|
|
|
|
|
2010-10-06 01:39:30 +08:00
|
|
|
if (pairwise)
|
2013-03-24 20:23:27 +08:00
|
|
|
key = rcu_dereference(sta->ptk[key_idx]);
|
2010-10-06 01:39:30 +08:00
|
|
|
else if (key_idx < NUM_DEFAULT_KEYS)
|
2011-05-12 21:11:37 +08:00
|
|
|
key = rcu_dereference(sta->gtk[key_idx]);
|
2007-12-19 09:03:31 +08:00
|
|
|
} else
|
2011-05-12 21:11:37 +08:00
|
|
|
key = rcu_dereference(sdata->keys[key_idx]);
|
2007-12-19 09:03:31 +08:00
|
|
|
|
|
|
|
if (!key)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
2010-08-10 15:46:38 +08:00
|
|
|
params.cipher = key->conf.cipher;
|
2007-12-19 09:03:31 +08:00
|
|
|
|
2010-08-10 15:46:38 +08:00
|
|
|
switch (key->conf.cipher) {
|
|
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
2008-05-15 07:26:19 +08:00
|
|
|
iv32 = key->u.tkip.tx.iv32;
|
|
|
|
iv16 = key->u.tkip.tx.iv16;
|
2007-12-19 09:03:31 +08:00
|
|
|
|
2009-04-24 00:52:52 +08:00
|
|
|
if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)
|
|
|
|
drv_get_tkip_seq(sdata->local,
|
|
|
|
key->conf.hw_key_idx,
|
|
|
|
&iv32, &iv16);
|
2007-12-19 09:03:31 +08:00
|
|
|
|
|
|
|
seq[0] = iv16 & 0xff;
|
|
|
|
seq[1] = (iv16 >> 8) & 0xff;
|
|
|
|
seq[2] = iv32 & 0xff;
|
|
|
|
seq[3] = (iv32 >> 8) & 0xff;
|
|
|
|
seq[4] = (iv32 >> 16) & 0xff;
|
|
|
|
seq[5] = (iv32 >> 24) & 0xff;
|
|
|
|
params.seq = seq;
|
|
|
|
params.seq_len = 6;
|
|
|
|
break;
|
2010-08-10 15:46:38 +08:00
|
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
2011-07-07 03:59:39 +08:00
|
|
|
pn64 = atomic64_read(&key->u.ccmp.tx_pn);
|
|
|
|
seq[0] = pn64;
|
|
|
|
seq[1] = pn64 >> 8;
|
|
|
|
seq[2] = pn64 >> 16;
|
|
|
|
seq[3] = pn64 >> 24;
|
|
|
|
seq[4] = pn64 >> 32;
|
|
|
|
seq[5] = pn64 >> 40;
|
2007-12-19 09:03:31 +08:00
|
|
|
params.seq = seq;
|
|
|
|
params.seq_len = 6;
|
|
|
|
break;
|
2010-08-10 15:46:38 +08:00
|
|
|
case WLAN_CIPHER_SUITE_AES_CMAC:
|
2011-07-07 04:00:35 +08:00
|
|
|
pn64 = atomic64_read(&key->u.aes_cmac.tx_pn);
|
|
|
|
seq[0] = pn64;
|
|
|
|
seq[1] = pn64 >> 8;
|
|
|
|
seq[2] = pn64 >> 16;
|
|
|
|
seq[3] = pn64 >> 24;
|
|
|
|
seq[4] = pn64 >> 32;
|
|
|
|
seq[5] = pn64 >> 40;
|
2009-01-08 19:32:02 +08:00
|
|
|
params.seq = seq;
|
|
|
|
params.seq_len = 6;
|
|
|
|
break;
|
2007-12-19 09:03:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
params.key = key->conf.key;
|
|
|
|
params.key_len = key->conf.keylen;
|
|
|
|
|
|
|
|
callback(cookie, ¶ms);
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
out:
|
2008-04-08 23:56:52 +08:00
|
|
|
rcu_read_unlock();
|
2007-12-19 09:03:31 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2007-12-19 09:03:30 +08:00
|
|
|
static int ieee80211_config_default_key(struct wiphy *wiphy,
|
|
|
|
struct net_device *dev,
|
2010-12-10 02:58:59 +08:00
|
|
|
u8 key_idx, bool uni,
|
|
|
|
bool multi)
|
2007-12-19 09:03:30 +08:00
|
|
|
{
|
2010-06-01 16:19:19 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
2008-04-08 23:56:52 +08:00
|
|
|
|
2010-12-10 02:49:02 +08:00
|
|
|
ieee80211_set_default_key(sdata, key_idx, uni, multi);
|
2007-12-19 09:03:30 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-08 19:32:02 +08:00
|
|
|
static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy,
|
|
|
|
struct net_device *dev,
|
|
|
|
u8 key_idx)
|
|
|
|
{
|
2010-07-22 19:58:51 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
2009-01-08 19:32:02 +08:00
|
|
|
|
|
|
|
ieee80211_set_default_mgmt_key(sdata, key_idx);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-06 07:31:48 +08:00
|
|
|
void sta_set_rate_info_tx(struct sta_info *sta,
|
|
|
|
const struct ieee80211_tx_rate *rate,
|
|
|
|
struct rate_info *rinfo)
|
|
|
|
{
|
|
|
|
rinfo->flags = 0;
|
2012-11-10 01:38:32 +08:00
|
|
|
if (rate->flags & IEEE80211_TX_RC_MCS) {
|
2012-03-06 07:31:48 +08:00
|
|
|
rinfo->flags |= RATE_INFO_FLAGS_MCS;
|
2012-11-10 01:38:32 +08:00
|
|
|
rinfo->mcs = rate->idx;
|
|
|
|
} else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
|
|
|
|
rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
|
|
|
|
rinfo->mcs = ieee80211_rate_get_vht_mcs(rate);
|
|
|
|
rinfo->nss = ieee80211_rate_get_vht_nss(rate);
|
|
|
|
} else {
|
|
|
|
struct ieee80211_supported_band *sband;
|
2013-07-08 22:55:53 +08:00
|
|
|
int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
|
|
|
|
u16 brate;
|
|
|
|
|
2012-11-10 01:38:32 +08:00
|
|
|
sband = sta->local->hw.wiphy->bands[
|
|
|
|
ieee80211_get_sdata_band(sta->sdata)];
|
2013-07-08 22:55:53 +08:00
|
|
|
brate = sband->bitrates[rate->idx].bitrate;
|
|
|
|
rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
|
2012-11-10 01:38:32 +08:00
|
|
|
}
|
2012-03-06 07:31:48 +08:00
|
|
|
if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
|
|
|
|
rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
|
2012-11-10 01:38:32 +08:00
|
|
|
if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
|
|
|
|
rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
|
|
|
|
if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
|
|
|
|
rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
|
2012-03-06 07:31:48 +08:00
|
|
|
if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
|
|
|
|
rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
|
|
|
|
}
|
|
|
|
|
2012-11-28 20:59:38 +08:00
|
|
|
void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
|
|
|
|
{
|
|
|
|
rinfo->flags = 0;
|
|
|
|
|
|
|
|
if (sta->last_rx_rate_flag & RX_FLAG_HT) {
|
|
|
|
rinfo->flags |= RATE_INFO_FLAGS_MCS;
|
|
|
|
rinfo->mcs = sta->last_rx_rate_idx;
|
|
|
|
} else if (sta->last_rx_rate_flag & RX_FLAG_VHT) {
|
|
|
|
rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
|
|
|
|
rinfo->nss = sta->last_rx_rate_vht_nss;
|
|
|
|
rinfo->mcs = sta->last_rx_rate_idx;
|
|
|
|
} else {
|
|
|
|
struct ieee80211_supported_band *sband;
|
2013-07-08 22:55:53 +08:00
|
|
|
int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
|
|
|
|
u16 brate;
|
2012-11-28 20:59:38 +08:00
|
|
|
|
|
|
|
sband = sta->local->hw.wiphy->bands[
|
|
|
|
ieee80211_get_sdata_band(sta->sdata)];
|
2013-07-08 22:55:53 +08:00
|
|
|
brate = sband->bitrates[sta->last_rx_rate_idx].bitrate;
|
|
|
|
rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
|
2012-11-28 20:59:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
|
|
|
|
rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
|
|
|
|
if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
|
|
|
|
rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
|
|
|
|
if (sta->last_rx_rate_flag & RX_FLAG_80MHZ)
|
|
|
|
rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
|
|
|
|
if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ)
|
|
|
|
rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
|
|
|
|
if (sta->last_rx_rate_flag & RX_FLAG_160MHZ)
|
|
|
|
rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
|
|
|
|
}
|
|
|
|
|
2008-02-23 22:17:17 +08:00
|
|
|
static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
|
|
|
{
|
2008-02-25 23:27:46 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
2012-06-21 15:56:46 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2011-04-08 23:54:24 +08:00
|
|
|
struct timespec uptime;
|
2013-02-05 17:55:21 +08:00
|
|
|
u64 packets = 0;
|
2013-04-22 22:29:31 +08:00
|
|
|
int i, ac;
|
2008-02-23 22:17:17 +08:00
|
|
|
|
2009-08-07 22:17:38 +08:00
|
|
|
sinfo->generation = sdata->local->sta_generation;
|
|
|
|
|
2008-02-23 22:17:17 +08:00
|
|
|
sinfo->filled = STATION_INFO_INACTIVE_TIME |
|
2013-02-05 17:55:21 +08:00
|
|
|
STATION_INFO_RX_BYTES64 |
|
|
|
|
STATION_INFO_TX_BYTES64 |
|
2009-02-17 19:24:57 +08:00
|
|
|
STATION_INFO_RX_PACKETS |
|
|
|
|
STATION_INFO_TX_PACKETS |
|
2010-10-06 17:34:12 +08:00
|
|
|
STATION_INFO_TX_RETRIES |
|
|
|
|
STATION_INFO_TX_FAILED |
|
2010-10-08 07:39:20 +08:00
|
|
|
STATION_INFO_TX_BITRATE |
|
2011-02-28 05:08:01 +08:00
|
|
|
STATION_INFO_RX_BITRATE |
|
2011-04-01 00:25:41 +08:00
|
|
|
STATION_INFO_RX_DROP_MISC |
|
2011-04-08 23:54:24 +08:00
|
|
|
STATION_INFO_BSS_PARAM |
|
2011-10-13 22:30:40 +08:00
|
|
|
STATION_INFO_CONNECTED_TIME |
|
2011-12-10 03:01:49 +08:00
|
|
|
STATION_INFO_STA_FLAGS |
|
|
|
|
STATION_INFO_BEACON_LOSS_COUNT;
|
2011-04-08 23:54:24 +08:00
|
|
|
|
|
|
|
do_posix_clock_monotonic_gettime(&uptime);
|
|
|
|
sinfo->connected_time = uptime.tv_sec - sta->last_connected;
|
2008-02-23 22:17:17 +08:00
|
|
|
|
|
|
|
sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
|
2013-02-05 17:55:21 +08:00
|
|
|
sinfo->tx_bytes = 0;
|
|
|
|
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
|
|
|
sinfo->tx_bytes += sta->tx_bytes[ac];
|
|
|
|
packets += sta->tx_packets[ac];
|
|
|
|
}
|
|
|
|
sinfo->tx_packets = packets;
|
2008-02-23 22:17:17 +08:00
|
|
|
sinfo->rx_bytes = sta->rx_bytes;
|
2009-02-17 19:24:57 +08:00
|
|
|
sinfo->rx_packets = sta->rx_packets;
|
2010-10-06 17:34:12 +08:00
|
|
|
sinfo->tx_retries = sta->tx_retry_count;
|
|
|
|
sinfo->tx_failed = sta->tx_retry_failed;
|
2010-10-08 07:39:20 +08:00
|
|
|
sinfo->rx_dropped_misc = sta->rx_dropped;
|
2011-12-10 03:01:49 +08:00
|
|
|
sinfo->beacon_loss_count = sta->beacon_loss_count;
|
2008-02-23 22:17:17 +08:00
|
|
|
|
2009-12-09 06:10:13 +08:00
|
|
|
if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
|
|
|
|
(sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
|
2010-12-02 18:12:43 +08:00
|
|
|
sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
|
2012-06-21 15:56:46 +08:00
|
|
|
if (!local->ops->get_rssi ||
|
|
|
|
drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal))
|
|
|
|
sinfo->signal = (s8)sta->last_signal;
|
2010-12-02 18:12:43 +08:00
|
|
|
sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
|
2008-12-12 05:04:19 +08:00
|
|
|
}
|
2013-04-22 22:29:31 +08:00
|
|
|
if (sta->chains) {
|
|
|
|
sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
|
|
|
|
STATION_INFO_CHAIN_SIGNAL_AVG;
|
|
|
|
|
|
|
|
sinfo->chains = sta->chains;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
|
|
|
|
sinfo->chain_signal[i] = sta->chain_signal_last[i];
|
|
|
|
sinfo->chain_signal_avg[i] =
|
|
|
|
(s8) -ewma_read(&sta->chain_signal_avg[i]);
|
|
|
|
}
|
|
|
|
}
|
2008-12-12 05:04:19 +08:00
|
|
|
|
2012-03-06 07:31:48 +08:00
|
|
|
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
|
2012-11-28 20:59:38 +08:00
|
|
|
sta_set_rate_info_rx(sta, &sinfo->rxrate);
|
2008-12-12 05:04:19 +08:00
|
|
|
|
2008-02-23 22:17:19 +08:00
|
|
|
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
2008-02-23 22:17:17 +08:00
|
|
|
#ifdef CONFIG_MAC80211_MESH
|
|
|
|
sinfo->filled |= STATION_INFO_LLID |
|
|
|
|
STATION_INFO_PLID |
|
2013-01-31 01:14:08 +08:00
|
|
|
STATION_INFO_PLINK_STATE |
|
|
|
|
STATION_INFO_LOCAL_PM |
|
|
|
|
STATION_INFO_PEER_PM |
|
|
|
|
STATION_INFO_NONPEER_PM;
|
2008-02-23 22:17:17 +08:00
|
|
|
|
2013-11-13 15:43:03 +08:00
|
|
|
sinfo->llid = sta->llid;
|
|
|
|
sinfo->plid = sta->plid;
|
2008-02-23 22:17:17 +08:00
|
|
|
sinfo->plink_state = sta->plink_state;
|
2012-04-01 02:31:33 +08:00
|
|
|
if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
|
|
|
|
sinfo->filled |= STATION_INFO_T_OFFSET;
|
|
|
|
sinfo->t_offset = sta->t_offset;
|
|
|
|
}
|
2013-01-31 01:14:08 +08:00
|
|
|
sinfo->local_pm = sta->local_pm;
|
|
|
|
sinfo->peer_pm = sta->peer_pm;
|
|
|
|
sinfo->nonpeer_pm = sta->nonpeer_pm;
|
2008-02-23 22:17:17 +08:00
|
|
|
#endif
|
2008-02-23 22:17:19 +08:00
|
|
|
}
|
2011-04-01 00:25:41 +08:00
|
|
|
|
|
|
|
sinfo->bss_param.flags = 0;
|
|
|
|
if (sdata->vif.bss_conf.use_cts_prot)
|
|
|
|
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
|
|
|
|
if (sdata->vif.bss_conf.use_short_preamble)
|
|
|
|
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
|
|
|
|
if (sdata->vif.bss_conf.use_short_slot)
|
|
|
|
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
|
|
|
|
sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period;
|
|
|
|
sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int;
|
2011-10-13 22:30:40 +08:00
|
|
|
|
|
|
|
sinfo->sta_flags.set = 0;
|
|
|
|
sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
|
|
|
|
BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
|
|
|
|
BIT(NL80211_STA_FLAG_WME) |
|
|
|
|
BIT(NL80211_STA_FLAG_MFP) |
|
2011-11-05 21:15:24 +08:00
|
|
|
BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
nl80211/mac80211: support full station state in AP mode
Today, stations are added already associated. That is
inefficient if, for example, the driver has no room
for stations any more because then the station will
go through the entire auth/assoc handshake, only to
be kicked out afterwards.
To address this a bit better, at least with drivers
using the new station state callback, allow hostapd
to add stations in unauthenticated mode, just after
receiving the AUTH frame, before even replying. Thus
if there's no more space at that point, it can send
a negative auth frame back. It still needs to handle
later state transition errors though, of course.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2012-10-26 23:53:44 +08:00
|
|
|
BIT(NL80211_STA_FLAG_ASSOCIATED) |
|
2011-11-05 21:15:24 +08:00
|
|
|
BIT(NL80211_STA_FLAG_TDLS_PEER);
|
2011-10-13 22:30:40 +08:00
|
|
|
if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
|
|
|
|
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
|
|
|
|
if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE))
|
|
|
|
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
|
|
|
|
if (test_sta_flag(sta, WLAN_STA_WME))
|
|
|
|
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME);
|
|
|
|
if (test_sta_flag(sta, WLAN_STA_MFP))
|
|
|
|
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP);
|
|
|
|
if (test_sta_flag(sta, WLAN_STA_AUTH))
|
|
|
|
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
|
nl80211/mac80211: support full station state in AP mode
Today, stations are added already associated. That is
inefficient if, for example, the driver has no room
for stations any more because then the station will
go through the entire auth/assoc handshake, only to
be kicked out afterwards.
To address this a bit better, at least with drivers
using the new station state callback, allow hostapd
to add stations in unauthenticated mode, just after
receiving the AUTH frame, before even replying. Thus
if there's no more space at that point, it can send
a negative auth frame back. It still needs to handle
later state transition errors though, of course.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2012-10-26 23:53:44 +08:00
|
|
|
if (test_sta_flag(sta, WLAN_STA_ASSOC))
|
|
|
|
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
|
2011-11-05 21:15:24 +08:00
|
|
|
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
|
|
|
|
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
|
2008-02-23 22:17:17 +08:00
|
|
|
}
|
|
|
|
|
2012-04-24 03:50:30 +08:00
|
|
|
static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
|
|
|
|
"rx_packets", "rx_bytes", "wep_weak_iv_count",
|
|
|
|
"rx_duplicates", "rx_fragments", "rx_dropped",
|
|
|
|
"tx_packets", "tx_bytes", "tx_fragments",
|
|
|
|
"tx_filtered", "tx_retry_failed", "tx_retries",
|
2012-04-24 03:50:32 +08:00
|
|
|
"beacon_loss", "sta_state", "txrate", "rxrate", "signal",
|
|
|
|
"channel", "noise", "ch_time", "ch_time_busy",
|
|
|
|
"ch_time_ext_busy", "ch_time_rx", "ch_time_tx"
|
2012-04-24 03:50:30 +08:00
|
|
|
};
|
|
|
|
#define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats)
|
|
|
|
|
|
|
|
static int ieee80211_get_et_sset_count(struct wiphy *wiphy,
|
|
|
|
struct net_device *dev,
|
|
|
|
int sset)
|
|
|
|
{
|
2012-04-24 03:50:31 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
int rv = 0;
|
|
|
|
|
2012-04-24 03:50:30 +08:00
|
|
|
if (sset == ETH_SS_STATS)
|
2012-04-24 03:50:31 +08:00
|
|
|
rv += STA_STATS_LEN;
|
|
|
|
|
|
|
|
rv += drv_get_et_sset_count(sdata, sset);
|
2012-04-24 03:50:30 +08:00
|
|
|
|
2012-04-24 03:50:31 +08:00
|
|
|
if (rv == 0)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
return rv;
|
2012-04-24 03:50:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_get_et_stats(struct wiphy *wiphy,
|
|
|
|
struct net_device *dev,
|
|
|
|
struct ethtool_stats *stats,
|
|
|
|
u64 *data)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
2012-07-26 23:24:39 +08:00
|
|
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
|
|
|
struct ieee80211_channel *channel;
|
2012-04-24 03:50:30 +08:00
|
|
|
struct sta_info *sta;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
2012-04-24 03:50:32 +08:00
|
|
|
struct station_info sinfo;
|
|
|
|
struct survey_info survey;
|
|
|
|
int i, q;
|
|
|
|
#define STA_STATS_SURVEY_LEN 7
|
2012-04-24 03:50:30 +08:00
|
|
|
|
|
|
|
memset(data, 0, sizeof(u64) * STA_STATS_LEN);
|
|
|
|
|
|
|
|
#define ADD_STA_STATS(sta) \
|
|
|
|
do { \
|
|
|
|
data[i++] += sta->rx_packets; \
|
|
|
|
data[i++] += sta->rx_bytes; \
|
|
|
|
data[i++] += sta->wep_weak_iv_count; \
|
|
|
|
data[i++] += sta->num_duplicates; \
|
|
|
|
data[i++] += sta->rx_fragments; \
|
|
|
|
data[i++] += sta->rx_dropped; \
|
|
|
|
\
|
2013-02-05 17:55:21 +08:00
|
|
|
data[i++] += sinfo.tx_packets; \
|
|
|
|
data[i++] += sinfo.tx_bytes; \
|
2012-04-24 03:50:30 +08:00
|
|
|
data[i++] += sta->tx_fragments; \
|
|
|
|
data[i++] += sta->tx_filtered_count; \
|
|
|
|
data[i++] += sta->tx_retry_failed; \
|
|
|
|
data[i++] += sta->tx_retry_count; \
|
|
|
|
data[i++] += sta->beacon_loss_count; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
/* For Managed stations, find the single station based on BSSID
|
|
|
|
* and use that. For interface types, iterate through all available
|
|
|
|
* stations and add stats for any station that is assigned to this
|
|
|
|
* network device.
|
|
|
|
*/
|
|
|
|
|
2012-06-21 15:56:46 +08:00
|
|
|
mutex_lock(&local->sta_mtx);
|
2012-04-24 03:50:30 +08:00
|
|
|
|
|
|
|
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
|
|
|
sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid);
|
2012-04-24 03:50:32 +08:00
|
|
|
|
|
|
|
if (!(sta && !WARN_ON(sta->sdata->dev != dev)))
|
|
|
|
goto do_survey;
|
|
|
|
|
2013-02-05 17:55:21 +08:00
|
|
|
sinfo.filled = 0;
|
|
|
|
sta_set_sinfo(sta, &sinfo);
|
|
|
|
|
2012-04-24 03:50:32 +08:00
|
|
|
i = 0;
|
|
|
|
ADD_STA_STATS(sta);
|
|
|
|
|
|
|
|
data[i++] = sta->sta_state;
|
|
|
|
|
|
|
|
|
2012-05-31 04:25:54 +08:00
|
|
|
if (sinfo.filled & STATION_INFO_TX_BITRATE)
|
2012-04-24 03:50:32 +08:00
|
|
|
data[i] = 100000 *
|
|
|
|
cfg80211_calculate_bitrate(&sinfo.txrate);
|
|
|
|
i++;
|
2012-05-31 04:25:54 +08:00
|
|
|
if (sinfo.filled & STATION_INFO_RX_BITRATE)
|
2012-04-24 03:50:32 +08:00
|
|
|
data[i] = 100000 *
|
|
|
|
cfg80211_calculate_bitrate(&sinfo.rxrate);
|
|
|
|
i++;
|
|
|
|
|
2012-05-31 04:25:54 +08:00
|
|
|
if (sinfo.filled & STATION_INFO_SIGNAL_AVG)
|
2012-04-24 03:50:32 +08:00
|
|
|
data[i] = (u8)sinfo.signal_avg;
|
|
|
|
i++;
|
2012-04-24 03:50:30 +08:00
|
|
|
} else {
|
2012-06-21 15:56:46 +08:00
|
|
|
list_for_each_entry(sta, &local->sta_list, list) {
|
2012-04-24 03:50:30 +08:00
|
|
|
/* Make sure this station belongs to the proper dev */
|
|
|
|
if (sta->sdata->dev != dev)
|
|
|
|
continue;
|
|
|
|
|
2013-07-08 16:43:31 +08:00
|
|
|
sinfo.filled = 0;
|
|
|
|
sta_set_sinfo(sta, &sinfo);
|
2012-04-24 03:50:30 +08:00
|
|
|
i = 0;
|
|
|
|
ADD_STA_STATS(sta);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-24 03:50:32 +08:00
|
|
|
do_survey:
|
|
|
|
i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
|
|
|
|
/* Get survey stats for current channel */
|
2012-07-26 23:24:39 +08:00
|
|
|
survey.filled = 0;
|
2012-04-24 03:50:32 +08:00
|
|
|
|
2012-07-26 23:24:39 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
|
|
|
if (chanctx_conf)
|
2012-11-09 18:39:59 +08:00
|
|
|
channel = chanctx_conf->def.chan;
|
2012-07-26 23:24:39 +08:00
|
|
|
else
|
|
|
|
channel = NULL;
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
if (channel) {
|
|
|
|
q = 0;
|
|
|
|
do {
|
|
|
|
survey.filled = 0;
|
|
|
|
if (drv_get_survey(local, q, &survey) != 0) {
|
|
|
|
survey.filled = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
q++;
|
|
|
|
} while (channel != survey.channel);
|
2012-04-24 03:50:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (survey.filled)
|
|
|
|
data[i++] = survey.channel->center_freq;
|
|
|
|
else
|
|
|
|
data[i++] = 0;
|
|
|
|
if (survey.filled & SURVEY_INFO_NOISE_DBM)
|
|
|
|
data[i++] = (u8)survey.noise;
|
|
|
|
else
|
|
|
|
data[i++] = -1LL;
|
|
|
|
if (survey.filled & SURVEY_INFO_CHANNEL_TIME)
|
|
|
|
data[i++] = survey.channel_time;
|
|
|
|
else
|
|
|
|
data[i++] = -1LL;
|
|
|
|
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
|
|
|
|
data[i++] = survey.channel_time_busy;
|
|
|
|
else
|
|
|
|
data[i++] = -1LL;
|
|
|
|
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
|
|
|
|
data[i++] = survey.channel_time_ext_busy;
|
|
|
|
else
|
|
|
|
data[i++] = -1LL;
|
|
|
|
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX)
|
|
|
|
data[i++] = survey.channel_time_rx;
|
|
|
|
else
|
|
|
|
data[i++] = -1LL;
|
|
|
|
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX)
|
|
|
|
data[i++] = survey.channel_time_tx;
|
|
|
|
else
|
|
|
|
data[i++] = -1LL;
|
|
|
|
|
2012-06-21 15:56:46 +08:00
|
|
|
mutex_unlock(&local->sta_mtx);
|
2012-04-24 03:50:31 +08:00
|
|
|
|
2012-04-24 03:50:32 +08:00
|
|
|
if (WARN_ON(i != STA_STATS_LEN))
|
|
|
|
return;
|
|
|
|
|
2012-04-24 03:50:31 +08:00
|
|
|
drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN]));
|
2012-04-24 03:50:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_get_et_strings(struct wiphy *wiphy,
|
|
|
|
struct net_device *dev,
|
|
|
|
u32 sset, u8 *data)
|
|
|
|
{
|
2012-04-24 03:50:31 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
int sz_sta_stats = 0;
|
|
|
|
|
2012-04-24 03:50:30 +08:00
|
|
|
if (sset == ETH_SS_STATS) {
|
2012-04-24 03:50:31 +08:00
|
|
|
sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats);
|
2013-05-07 03:09:46 +08:00
|
|
|
memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats);
|
2012-04-24 03:50:30 +08:00
|
|
|
}
|
2012-04-24 03:50:31 +08:00
|
|
|
drv_get_et_strings(sdata, sset, &(data[sz_sta_stats]));
|
2012-04-24 03:50:30 +08:00
|
|
|
}
|
2008-02-23 22:17:17 +08:00
|
|
|
|
|
|
|
static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
int idx, u8 *mac, struct station_info *sinfo)
|
|
|
|
{
|
2009-11-16 19:00:37 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
2012-06-21 15:56:46 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2008-02-23 22:17:17 +08:00
|
|
|
struct sta_info *sta;
|
2008-02-25 23:27:46 +08:00
|
|
|
int ret = -ENOENT;
|
|
|
|
|
2012-06-21 15:56:46 +08:00
|
|
|
mutex_lock(&local->sta_mtx);
|
2008-02-23 22:17:17 +08:00
|
|
|
|
2009-11-16 19:00:37 +08:00
|
|
|
sta = sta_info_get_by_idx(sdata, idx);
|
2008-02-25 23:27:46 +08:00
|
|
|
if (sta) {
|
|
|
|
ret = 0;
|
2008-09-11 06:02:02 +08:00
|
|
|
memcpy(mac, sta->sta.addr, ETH_ALEN);
|
2008-02-25 23:27:46 +08:00
|
|
|
sta_set_sinfo(sta, sinfo);
|
|
|
|
}
|
2008-02-23 22:17:17 +08:00
|
|
|
|
2012-06-21 15:56:46 +08:00
|
|
|
mutex_unlock(&local->sta_mtx);
|
2008-02-23 22:17:17 +08:00
|
|
|
|
2008-02-25 23:27:46 +08:00
|
|
|
return ret;
|
2008-02-23 22:17:17 +08:00
|
|
|
}
|
|
|
|
|
2010-04-19 16:23:57 +08:00
|
|
|
static int ieee80211_dump_survey(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
int idx, struct survey_info *survey)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
|
|
|
|
|
|
return drv_get_survey(local, idx, survey);
|
|
|
|
}
|
|
|
|
|
2007-12-19 09:03:37 +08:00
|
|
|
static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
|
2008-02-23 22:17:06 +08:00
|
|
|
u8 *mac, struct station_info *sinfo)
|
2007-12-19 09:03:37 +08:00
|
|
|
{
|
2009-11-26 00:46:18 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
2012-06-21 15:56:46 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2007-12-19 09:03:37 +08:00
|
|
|
struct sta_info *sta;
|
2008-02-25 23:27:46 +08:00
|
|
|
int ret = -ENOENT;
|
2007-12-19 09:03:37 +08:00
|
|
|
|
2012-06-21 15:56:46 +08:00
|
|
|
mutex_lock(&local->sta_mtx);
|
2007-12-19 09:03:37 +08:00
|
|
|
|
2010-01-09 01:10:58 +08:00
|
|
|
sta = sta_info_get_bss(sdata, mac);
|
2008-02-25 23:27:46 +08:00
|
|
|
if (sta) {
|
|
|
|
ret = 0;
|
|
|
|
sta_set_sinfo(sta, sinfo);
|
|
|
|
}
|
|
|
|
|
2012-06-21 15:56:46 +08:00
|
|
|
mutex_unlock(&local->sta_mtx);
|
2008-02-25 23:27:46 +08:00
|
|
|
|
|
|
|
return ret;
|
2007-12-19 09:03:37 +08:00
|
|
|
}
|
|
|
|
|
2012-07-26 23:24:39 +08:00
|
|
|
static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
|
2012-11-09 04:25:48 +08:00
|
|
|
struct cfg80211_chan_def *chandef)
|
2012-05-17 05:50:16 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
2012-07-26 23:24:39 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata;
|
|
|
|
int ret = 0;
|
2012-05-17 05:50:16 +08:00
|
|
|
|
2012-11-09 18:39:59 +08:00
|
|
|
if (cfg80211_chandef_identical(&local->monitor_chandef, chandef))
|
2012-07-26 23:24:39 +08:00
|
|
|
return 0;
|
2012-05-17 05:50:16 +08:00
|
|
|
|
2012-07-26 23:24:39 +08:00
|
|
|
mutex_lock(&local->iflist_mtx);
|
|
|
|
if (local->use_chanctx) {
|
|
|
|
sdata = rcu_dereference_protected(
|
|
|
|
local->monitor_sdata,
|
|
|
|
lockdep_is_held(&local->iflist_mtx));
|
|
|
|
if (sdata) {
|
|
|
|
ieee80211_vif_release_channel(sdata);
|
2012-11-09 18:39:59 +08:00
|
|
|
ret = ieee80211_vif_use_channel(sdata, chandef,
|
2012-07-26 23:24:39 +08:00
|
|
|
IEEE80211_CHANCTX_EXCLUSIVE);
|
|
|
|
}
|
|
|
|
} else if (local->open_count == local->monitors) {
|
2013-03-25 23:26:57 +08:00
|
|
|
local->_oper_chandef = *chandef;
|
2012-07-26 23:24:39 +08:00
|
|
|
ieee80211_hw_config(local, 0);
|
|
|
|
}
|
2012-05-17 05:50:16 +08:00
|
|
|
|
2012-11-09 18:39:59 +08:00
|
|
|
if (ret == 0)
|
|
|
|
local->monitor_chandef = *chandef;
|
2012-07-26 23:24:39 +08:00
|
|
|
mutex_unlock(&local->iflist_mtx);
|
2012-05-17 05:50:16 +08:00
|
|
|
|
2012-07-26 23:24:39 +08:00
|
|
|
return ret;
|
2012-06-06 14:18:22 +08:00
|
|
|
}
|
|
|
|
|
2011-11-10 17:28:57 +08:00
|
|
|
static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
|
2012-02-13 22:17:18 +08:00
|
|
|
const u8 *resp, size_t resp_len)
|
2011-11-10 17:28:57 +08:00
|
|
|
{
|
2012-08-06 19:26:16 +08:00
|
|
|
struct probe_resp *new, *old;
|
2011-11-10 17:28:57 +08:00
|
|
|
|
|
|
|
if (!resp || !resp_len)
|
2012-08-22 16:51:07 +08:00
|
|
|
return 1;
|
2011-11-10 17:28:57 +08:00
|
|
|
|
2013-11-22 01:19:50 +08:00
|
|
|
old = sdata_dereference(sdata->u.ap.probe_resp, sdata);
|
2011-11-10 17:28:57 +08:00
|
|
|
|
2012-08-06 19:26:16 +08:00
|
|
|
new = kzalloc(sizeof(struct probe_resp) + resp_len, GFP_KERNEL);
|
2011-11-10 17:28:57 +08:00
|
|
|
if (!new)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2012-08-06 19:26:16 +08:00
|
|
|
new->len = resp_len;
|
|
|
|
memcpy(new->data, resp, resp_len);
|
2011-11-10 17:28:57 +08:00
|
|
|
|
|
|
|
rcu_assign_pointer(sdata->u.ap.probe_resp, new);
|
2012-08-06 19:26:16 +08:00
|
|
|
if (old)
|
|
|
|
kfree_rcu(old, rcu_head);
|
2011-11-10 17:28:57 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-08 15:42:25 +08:00
|
|
|
static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct cfg80211_beacon_data *params)
|
2007-12-19 09:03:33 +08:00
|
|
|
{
|
|
|
|
struct beacon_data *new, *old;
|
|
|
|
int new_head_len, new_tail_len;
|
2012-02-13 22:17:18 +08:00
|
|
|
int size, err;
|
|
|
|
u32 changed = BSS_CHANGED_BEACON;
|
2007-12-19 09:03:33 +08:00
|
|
|
|
2013-11-22 01:19:50 +08:00
|
|
|
old = sdata_dereference(sdata->u.ap.beacon, sdata);
|
|
|
|
|
2007-12-19 09:03:33 +08:00
|
|
|
|
|
|
|
/* Need to have a beacon head if we don't have one yet */
|
|
|
|
if (!params->head && !old)
|
2012-02-13 22:17:18 +08:00
|
|
|
return -EINVAL;
|
2007-12-19 09:03:33 +08:00
|
|
|
|
|
|
|
/* new or old head? */
|
|
|
|
if (params->head)
|
|
|
|
new_head_len = params->head_len;
|
|
|
|
else
|
|
|
|
new_head_len = old->head_len;
|
|
|
|
|
|
|
|
/* new or old tail? */
|
|
|
|
if (params->tail || !old)
|
|
|
|
/* params->tail_len will be zero for !params->tail */
|
|
|
|
new_tail_len = params->tail_len;
|
|
|
|
else
|
|
|
|
new_tail_len = old->tail_len;
|
|
|
|
|
|
|
|
size = sizeof(*new) + new_head_len + new_tail_len;
|
|
|
|
|
|
|
|
new = kzalloc(size, GFP_KERNEL);
|
|
|
|
if (!new)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/* start filling the new info now */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pointers go into the block we allocated,
|
|
|
|
* memory is | beacon_data | head | tail |
|
|
|
|
*/
|
|
|
|
new->head = ((u8 *) new) + sizeof(*new);
|
|
|
|
new->tail = new->head + new_head_len;
|
|
|
|
new->head_len = new_head_len;
|
|
|
|
new->tail_len = new_tail_len;
|
|
|
|
|
|
|
|
/* copy in head */
|
|
|
|
if (params->head)
|
|
|
|
memcpy(new->head, params->head, new_head_len);
|
|
|
|
else
|
|
|
|
memcpy(new->head, old->head, new_head_len);
|
|
|
|
|
|
|
|
/* copy in optional tail */
|
|
|
|
if (params->tail)
|
|
|
|
memcpy(new->tail, params->tail, new_tail_len);
|
|
|
|
else
|
|
|
|
if (old)
|
|
|
|
memcpy(new->tail, old->tail, new_tail_len);
|
|
|
|
|
2011-11-10 17:28:57 +08:00
|
|
|
err = ieee80211_set_probe_resp(sdata, params->probe_resp,
|
|
|
|
params->probe_resp_len);
|
2012-02-13 22:17:18 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
if (err == 0)
|
2011-11-10 17:28:57 +08:00
|
|
|
changed |= BSS_CHANGED_AP_PROBE_RESP;
|
|
|
|
|
2012-02-13 22:17:18 +08:00
|
|
|
rcu_assign_pointer(sdata->u.ap.beacon, new);
|
|
|
|
|
|
|
|
if (old)
|
|
|
|
kfree_rcu(old, rcu_head);
|
2011-09-04 16:11:32 +08:00
|
|
|
|
2012-02-13 22:17:18 +08:00
|
|
|
return changed;
|
2007-12-19 09:03:33 +08:00
|
|
|
}
|
|
|
|
|
2012-02-13 22:17:18 +08:00
|
|
|
static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
struct cfg80211_ap_settings *params)
|
2007-12-19 09:03:33 +08:00
|
|
|
{
|
2012-02-13 22:17:18 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
2007-12-19 09:03:33 +08:00
|
|
|
struct beacon_data *old;
|
2011-11-04 18:18:11 +08:00
|
|
|
struct ieee80211_sub_if_data *vlan;
|
2012-02-13 22:17:18 +08:00
|
|
|
u32 changed = BSS_CHANGED_BEACON_INT |
|
|
|
|
BSS_CHANGED_BEACON_ENABLED |
|
|
|
|
BSS_CHANGED_BEACON |
|
2012-11-14 22:21:17 +08:00
|
|
|
BSS_CHANGED_SSID |
|
|
|
|
BSS_CHANGED_P2P_PS;
|
2012-02-13 22:17:18 +08:00
|
|
|
int err;
|
2008-07-29 19:22:52 +08:00
|
|
|
|
2013-11-22 01:19:50 +08:00
|
|
|
old = sdata_dereference(sdata->u.ap.beacon, sdata);
|
2007-12-19 09:03:33 +08:00
|
|
|
if (old)
|
|
|
|
return -EALREADY;
|
|
|
|
|
2012-09-11 20:34:12 +08:00
|
|
|
/* TODO: make hostapd tell us what it wants */
|
|
|
|
sdata->smps_mode = IEEE80211_SMPS_OFF;
|
|
|
|
sdata->needed_rx_chains = sdata->local->rx_chains;
|
2013-02-09 01:16:20 +08:00
|
|
|
sdata->radar_required = params->radar_required;
|
2012-09-11 20:34:12 +08:00
|
|
|
|
2012-11-09 18:39:59 +08:00
|
|
|
err = ieee80211_vif_use_channel(sdata, ¶ms->chandef,
|
2012-07-26 23:24:39 +08:00
|
|
|
IEEE80211_CHANCTX_SHARED);
|
2012-05-17 05:50:18 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
2013-02-08 19:07:44 +08:00
|
|
|
ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
|
2012-05-17 05:50:18 +08:00
|
|
|
|
2011-11-04 18:18:11 +08:00
|
|
|
/*
|
|
|
|
* Apply control port protocol, this allows us to
|
|
|
|
* not encrypt dynamic WEP control frames.
|
|
|
|
*/
|
|
|
|
sdata->control_port_protocol = params->crypto.control_port_ethertype;
|
|
|
|
sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt;
|
2013-03-24 20:23:27 +08:00
|
|
|
sdata->encrypt_headroom = ieee80211_cs_headroom(sdata->local,
|
|
|
|
¶ms->crypto,
|
|
|
|
sdata->vif.type);
|
|
|
|
|
2011-11-04 18:18:11 +08:00
|
|
|
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
|
|
|
|
vlan->control_port_protocol =
|
|
|
|
params->crypto.control_port_ethertype;
|
|
|
|
vlan->control_port_no_encrypt =
|
|
|
|
params->crypto.control_port_no_encrypt;
|
2013-03-24 20:23:27 +08:00
|
|
|
vlan->encrypt_headroom =
|
|
|
|
ieee80211_cs_headroom(sdata->local,
|
|
|
|
¶ms->crypto,
|
|
|
|
vlan->vif.type);
|
2011-11-04 18:18:11 +08:00
|
|
|
}
|
|
|
|
|
2012-02-13 22:17:18 +08:00
|
|
|
sdata->vif.bss_conf.beacon_int = params->beacon_interval;
|
|
|
|
sdata->vif.bss_conf.dtim_period = params->dtim_period;
|
2012-12-14 21:06:28 +08:00
|
|
|
sdata->vif.bss_conf.enable_beacon = true;
|
2012-02-13 22:17:18 +08:00
|
|
|
|
|
|
|
sdata->vif.bss_conf.ssid_len = params->ssid_len;
|
|
|
|
if (params->ssid_len)
|
|
|
|
memcpy(sdata->vif.bss_conf.ssid, params->ssid,
|
|
|
|
params->ssid_len);
|
|
|
|
sdata->vif.bss_conf.hidden_ssid =
|
|
|
|
(params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE);
|
|
|
|
|
2013-03-21 22:47:56 +08:00
|
|
|
memset(&sdata->vif.bss_conf.p2p_noa_attr, 0,
|
|
|
|
sizeof(sdata->vif.bss_conf.p2p_noa_attr));
|
|
|
|
sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow =
|
|
|
|
params->p2p_ctwindow & IEEE80211_P2P_OPPPS_CTWINDOW_MASK;
|
|
|
|
if (params->p2p_opp_ps)
|
|
|
|
sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |=
|
|
|
|
IEEE80211_P2P_OPPPS_ENABLE_BIT;
|
2012-11-14 22:21:17 +08:00
|
|
|
|
2012-02-13 22:17:18 +08:00
|
|
|
err = ieee80211_assign_beacon(sdata, ¶ms->beacon);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
changed |= err;
|
|
|
|
|
2012-10-19 21:44:42 +08:00
|
|
|
err = drv_start_ap(sdata->local, sdata);
|
|
|
|
if (err) {
|
2013-11-22 01:19:50 +08:00
|
|
|
old = sdata_dereference(sdata->u.ap.beacon, sdata);
|
|
|
|
|
2012-10-19 21:44:42 +08:00
|
|
|
if (old)
|
|
|
|
kfree_rcu(old, rcu_head);
|
|
|
|
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2012-02-13 22:17:18 +08:00
|
|
|
ieee80211_bss_info_change_notify(sdata, changed);
|
|
|
|
|
2012-04-03 16:24:00 +08:00
|
|
|
netif_carrier_on(dev);
|
|
|
|
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
|
|
|
|
netif_carrier_on(vlan->dev);
|
|
|
|
|
2011-11-04 18:18:11 +08:00
|
|
|
return 0;
|
2007-12-19 09:03:33 +08:00
|
|
|
}
|
|
|
|
|
2012-02-13 22:17:18 +08:00
|
|
|
static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
struct cfg80211_beacon_data *params)
|
2007-12-19 09:03:33 +08:00
|
|
|
{
|
2008-07-29 19:22:52 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata;
|
2007-12-19 09:03:33 +08:00
|
|
|
struct beacon_data *old;
|
2012-02-13 22:17:18 +08:00
|
|
|
int err;
|
2007-12-19 09:03:33 +08:00
|
|
|
|
2008-07-29 19:22:52 +08:00
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
2013-07-11 22:09:06 +08:00
|
|
|
/* don't allow changing the beacon while CSA is in place - offset
|
|
|
|
* of channel switch counter may change
|
|
|
|
*/
|
|
|
|
if (sdata->vif.csa_active)
|
|
|
|
return -EBUSY;
|
|
|
|
|
2013-11-22 01:19:50 +08:00
|
|
|
old = sdata_dereference(sdata->u.ap.beacon, sdata);
|
2007-12-19 09:03:33 +08:00
|
|
|
if (!old)
|
|
|
|
return -ENOENT;
|
|
|
|
|
2012-02-13 22:17:18 +08:00
|
|
|
err = ieee80211_assign_beacon(sdata, params);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
ieee80211_bss_info_change_notify(sdata, err);
|
|
|
|
return 0;
|
2007-12-19 09:03:33 +08:00
|
|
|
}
|
|
|
|
|
2012-02-13 22:17:18 +08:00
|
|
|
static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
|
2007-12-19 09:03:33 +08:00
|
|
|
{
|
2012-10-26 01:02:42 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
struct ieee80211_sub_if_data *vlan;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct beacon_data *old_beacon;
|
|
|
|
struct probe_resp *old_probe_resp;
|
2013-11-06 20:55:51 +08:00
|
|
|
struct cfg80211_chan_def chandef;
|
2008-07-29 19:22:52 +08:00
|
|
|
|
2013-11-22 01:19:50 +08:00
|
|
|
old_beacon = sdata_dereference(sdata->u.ap.beacon, sdata);
|
2012-10-26 01:02:42 +08:00
|
|
|
if (!old_beacon)
|
2007-12-19 09:03:33 +08:00
|
|
|
return -ENOENT;
|
2013-11-22 01:19:50 +08:00
|
|
|
old_probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata);
|
2007-12-19 09:03:33 +08:00
|
|
|
|
2013-07-11 22:09:06 +08:00
|
|
|
/* abort any running channel switch */
|
|
|
|
sdata->vif.csa_active = false;
|
2013-11-22 01:19:53 +08:00
|
|
|
kfree(sdata->u.ap.next_beacon);
|
|
|
|
sdata->u.ap.next_beacon = NULL;
|
|
|
|
|
2013-10-01 21:45:43 +08:00
|
|
|
cancel_work_sync(&sdata->u.ap.request_smps_work);
|
2013-07-11 22:09:06 +08:00
|
|
|
|
2012-10-26 01:02:42 +08:00
|
|
|
/* turn off carrier for this interface and dependent VLANs */
|
2012-04-03 16:24:00 +08:00
|
|
|
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
|
|
|
|
netif_carrier_off(vlan->dev);
|
|
|
|
netif_carrier_off(dev);
|
|
|
|
|
2012-10-26 01:02:42 +08:00
|
|
|
/* remove beacon and probe response */
|
2011-08-02 00:19:00 +08:00
|
|
|
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
|
2012-10-26 01:02:42 +08:00
|
|
|
RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
|
|
|
|
kfree_rcu(old_beacon, rcu_head);
|
|
|
|
if (old_probe_resp)
|
|
|
|
kfree_rcu(old_probe_resp, rcu_head);
|
2012-02-13 22:17:18 +08:00
|
|
|
|
2013-12-05 06:18:37 +08:00
|
|
|
__sta_info_flush(sdata, true);
|
2013-12-05 06:47:09 +08:00
|
|
|
ieee80211_free_keys(sdata, true);
|
2012-12-14 21:56:03 +08:00
|
|
|
|
2012-12-14 21:06:28 +08:00
|
|
|
sdata->vif.bss_conf.enable_beacon = false;
|
2013-04-10 19:47:45 +08:00
|
|
|
sdata->vif.bss_conf.ssid_len = 0;
|
2012-12-14 21:06:28 +08:00
|
|
|
clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
|
2009-04-23 22:13:26 +08:00
|
|
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
|
2012-02-13 22:17:18 +08:00
|
|
|
|
2013-06-11 16:44:39 +08:00
|
|
|
if (sdata->wdev.cac_started) {
|
2013-11-06 20:55:51 +08:00
|
|
|
chandef = sdata->vif.bss_conf.chandef;
|
2013-06-11 16:44:39 +08:00
|
|
|
cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
|
2013-11-06 20:55:51 +08:00
|
|
|
cfg80211_cac_event(sdata->dev, &chandef,
|
|
|
|
NL80211_RADAR_CAC_ABORTED,
|
2013-06-11 16:44:39 +08:00
|
|
|
GFP_KERNEL);
|
|
|
|
}
|
|
|
|
|
2012-10-19 21:44:42 +08:00
|
|
|
drv_stop_ap(sdata->local, sdata);
|
|
|
|
|
2012-10-26 01:02:42 +08:00
|
|
|
/* free all potentially still buffered bcast frames */
|
|
|
|
local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
|
|
|
|
skb_queue_purge(&sdata->u.ap.ps.bc_buf);
|
|
|
|
|
2013-02-08 19:07:44 +08:00
|
|
|
ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
|
2012-07-26 23:24:39 +08:00
|
|
|
ieee80211_vif_release_channel(sdata);
|
|
|
|
|
2009-04-23 22:13:26 +08:00
|
|
|
return 0;
|
2007-12-19 09:03:33 +08:00
|
|
|
}
|
|
|
|
|
2007-12-19 09:03:35 +08:00
|
|
|
/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
|
|
|
|
struct iapp_layer2_update {
|
|
|
|
u8 da[ETH_ALEN]; /* broadcast */
|
|
|
|
u8 sa[ETH_ALEN]; /* STA addr */
|
|
|
|
__be16 len; /* 6 */
|
|
|
|
u8 dsap; /* 0 */
|
|
|
|
u8 ssap; /* 0 */
|
|
|
|
u8 control;
|
|
|
|
u8 xid_info[3];
|
2010-06-03 18:21:52 +08:00
|
|
|
} __packed;
|
2007-12-19 09:03:35 +08:00
|
|
|
|
|
|
|
static void ieee80211_send_layer2_update(struct sta_info *sta)
|
|
|
|
{
|
|
|
|
struct iapp_layer2_update *msg;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
/* Send Level 2 Update Frame to update forwarding tables in layer 2
|
|
|
|
* bridge devices */
|
|
|
|
|
|
|
|
skb = dev_alloc_skb(sizeof(*msg));
|
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
msg = (struct iapp_layer2_update *)skb_put(skb, sizeof(*msg));
|
|
|
|
|
|
|
|
/* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)
|
|
|
|
* Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */
|
|
|
|
|
2012-07-13 22:23:07 +08:00
|
|
|
eth_broadcast_addr(msg->da);
|
2008-09-11 06:02:02 +08:00
|
|
|
memcpy(msg->sa, sta->sta.addr, ETH_ALEN);
|
2007-12-19 09:03:35 +08:00
|
|
|
msg->len = htons(6);
|
|
|
|
msg->dsap = 0;
|
|
|
|
msg->ssap = 0x01; /* NULL LSAP, CR Bit: Response */
|
|
|
|
msg->control = 0xaf; /* XID response lsb.1111F101.
|
|
|
|
* F=0 (no poll command; unsolicited frame) */
|
|
|
|
msg->xid_info[0] = 0x81; /* XID format identifier */
|
|
|
|
msg->xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */
|
|
|
|
msg->xid_info[2] = 0; /* XID sender's receive window size (RW) */
|
|
|
|
|
2008-02-25 23:27:46 +08:00
|
|
|
skb->dev = sta->sdata->dev;
|
|
|
|
skb->protocol = eth_type_trans(skb, sta->sdata->dev);
|
2007-12-19 09:03:35 +08:00
|
|
|
memset(skb->cb, 0, sizeof(skb->cb));
|
2010-07-19 23:52:59 +08:00
|
|
|
netif_rx_ni(skb);
|
2007-12-19 09:03:35 +08:00
|
|
|
}
|
|
|
|
|
nl80211/mac80211: support full station state in AP mode
Today, stations are added already associated. That is
inefficient if, for example, the driver has no room
for stations any more because then the station will
go through the entire auth/assoc handshake, only to
be kicked out afterwards.
To address this a bit better, at least with drivers
using the new station state callback, allow hostapd
to add stations in unauthenticated mode, just after
receiving the AUTH frame, before even replying. Thus
if there's no more space at that point, it can send
a negative auth frame back. It still needs to handle
later state transition errors though, of course.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2012-10-26 23:53:44 +08:00
|
|
|
static int sta_apply_auth_flags(struct ieee80211_local *local,
|
|
|
|
struct sta_info *sta,
|
|
|
|
u32 mask, u32 set)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
|
|
|
|
set & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
|
|
|
|
!test_sta_flag(sta, WLAN_STA_AUTH)) {
|
|
|
|
ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
|
|
|
|
set & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
|
|
|
|
!test_sta_flag(sta, WLAN_STA_ASSOC)) {
|
|
|
|
ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
|
|
|
|
if (set & BIT(NL80211_STA_FLAG_AUTHORIZED))
|
|
|
|
ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
|
|
|
|
else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
|
|
|
|
ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
|
|
|
|
else
|
|
|
|
ret = 0;
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
|
|
|
|
!(set & BIT(NL80211_STA_FLAG_ASSOCIATED)) &&
|
|
|
|
test_sta_flag(sta, WLAN_STA_ASSOC)) {
|
|
|
|
ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
|
|
|
|
!(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) &&
|
|
|
|
test_sta_flag(sta, WLAN_STA_AUTH)) {
|
|
|
|
ret = sta_info_move_state(sta, IEEE80211_STA_NONE);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-12-14 19:35:30 +08:00
|
|
|
static int sta_apply_parameters(struct ieee80211_local *local,
|
|
|
|
struct sta_info *sta,
|
|
|
|
struct station_parameters *params)
|
2007-12-19 09:03:35 +08:00
|
|
|
{
|
2011-12-14 19:35:30 +08:00
|
|
|
int ret = 0;
|
2008-01-25 02:38:38 +08:00
|
|
|
struct ieee80211_supported_band *sband;
|
2008-02-25 23:27:46 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
2012-07-26 23:24:39 +08:00
|
|
|
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
2009-05-12 02:57:56 +08:00
|
|
|
u32 mask, set;
|
2007-12-19 09:03:35 +08:00
|
|
|
|
2012-07-26 23:24:39 +08:00
|
|
|
sband = local->hw.wiphy->bands[band];
|
2008-10-14 22:58:37 +08:00
|
|
|
|
2009-05-12 02:57:56 +08:00
|
|
|
mask = params->sta_flags_mask;
|
|
|
|
set = params->sta_flags_set;
|
2008-02-25 23:27:47 +08:00
|
|
|
|
nl80211/mac80211: support full station state in AP mode
Today, stations are added already associated. That is
inefficient if, for example, the driver has no room
for stations any more because then the station will
go through the entire auth/assoc handshake, only to
be kicked out afterwards.
To address this a bit better, at least with drivers
using the new station state callback, allow hostapd
to add stations in unauthenticated mode, just after
receiving the AUTH frame, before even replying. Thus
if there's no more space at that point, it can send
a negative auth frame back. It still needs to handle
later state transition errors though, of course.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2012-10-26 23:53:44 +08:00
|
|
|
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
|
|
|
/*
|
|
|
|
* In mesh mode, ASSOCIATED isn't part of the nl80211
|
|
|
|
* API but must follow AUTHENTICATED for driver state.
|
|
|
|
*/
|
|
|
|
if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED))
|
|
|
|
mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
|
|
|
|
if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
|
|
|
|
set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
|
2013-02-15 07:48:33 +08:00
|
|
|
} else if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
|
|
|
|
/*
|
|
|
|
* TDLS -- everything follows authorized, but
|
|
|
|
* only becoming authorized is possible, not
|
|
|
|
* going back
|
|
|
|
*/
|
|
|
|
if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
|
|
|
|
set |= BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
|
|
|
BIT(NL80211_STA_FLAG_ASSOCIATED);
|
|
|
|
mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
|
|
|
BIT(NL80211_STA_FLAG_ASSOCIATED);
|
|
|
|
}
|
2009-05-12 02:57:56 +08:00
|
|
|
}
|
2007-12-19 09:03:35 +08:00
|
|
|
|
nl80211/mac80211: support full station state in AP mode
Today, stations are added already associated. That is
inefficient if, for example, the driver has no room
for stations any more because then the station will
go through the entire auth/assoc handshake, only to
be kicked out afterwards.
To address this a bit better, at least with drivers
using the new station state callback, allow hostapd
to add stations in unauthenticated mode, just after
receiving the AUTH frame, before even replying. Thus
if there's no more space at that point, it can send
a negative auth frame back. It still needs to handle
later state transition errors though, of course.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2012-10-26 23:53:44 +08:00
|
|
|
ret = sta_apply_auth_flags(local, sta, mask, set);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2011-12-14 19:35:30 +08:00
|
|
|
|
2009-05-12 02:57:56 +08:00
|
|
|
if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
|
|
|
|
if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
|
2011-09-29 22:04:36 +08:00
|
|
|
set_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);
|
|
|
|
else
|
|
|
|
clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);
|
2009-05-12 02:57:56 +08:00
|
|
|
}
|
2007-12-19 09:03:35 +08:00
|
|
|
|
2009-05-12 02:57:56 +08:00
|
|
|
if (mask & BIT(NL80211_STA_FLAG_WME)) {
|
2011-06-28 04:58:45 +08:00
|
|
|
if (set & BIT(NL80211_STA_FLAG_WME)) {
|
2011-09-29 22:04:36 +08:00
|
|
|
set_sta_flag(sta, WLAN_STA_WME);
|
2011-06-28 04:58:45 +08:00
|
|
|
sta->sta.wme = true;
|
2011-09-29 22:04:36 +08:00
|
|
|
} else {
|
|
|
|
clear_sta_flag(sta, WLAN_STA_WME);
|
|
|
|
sta->sta.wme = false;
|
2011-06-28 04:58:45 +08:00
|
|
|
}
|
2009-05-12 02:57:56 +08:00
|
|
|
}
|
2009-01-08 19:31:59 +08:00
|
|
|
|
2009-05-12 02:57:56 +08:00
|
|
|
if (mask & BIT(NL80211_STA_FLAG_MFP)) {
|
|
|
|
if (set & BIT(NL80211_STA_FLAG_MFP))
|
2011-09-29 22:04:36 +08:00
|
|
|
set_sta_flag(sta, WLAN_STA_MFP);
|
|
|
|
else
|
|
|
|
clear_sta_flag(sta, WLAN_STA_MFP);
|
2007-12-19 09:03:35 +08:00
|
|
|
}
|
2011-04-08 06:08:30 +08:00
|
|
|
|
2011-09-28 19:12:53 +08:00
|
|
|
if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
|
|
|
|
if (set & BIT(NL80211_STA_FLAG_TDLS_PEER))
|
2011-09-29 22:04:36 +08:00
|
|
|
set_sta_flag(sta, WLAN_STA_TDLS_PEER);
|
|
|
|
else
|
|
|
|
clear_sta_flag(sta, WLAN_STA_TDLS_PEER);
|
2011-09-28 19:12:53 +08:00
|
|
|
}
|
2007-12-19 09:03:35 +08:00
|
|
|
|
2011-09-28 02:56:12 +08:00
|
|
|
if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) {
|
|
|
|
sta->sta.uapsd_queues = params->uapsd_queues;
|
|
|
|
sta->sta.max_sp = params->max_sp;
|
|
|
|
}
|
2011-08-23 19:37:47 +08:00
|
|
|
|
2009-05-24 22:42:30 +08:00
|
|
|
/*
|
|
|
|
* cfg80211 validates this (1-2007) and allows setting the AID
|
|
|
|
* only when creating a new station entry
|
|
|
|
*/
|
|
|
|
if (params->aid)
|
|
|
|
sta->sta.aid = params->aid;
|
|
|
|
|
2008-02-25 23:27:47 +08:00
|
|
|
/*
|
2012-12-28 00:32:09 +08:00
|
|
|
* Some of the following updates would be racy if called on an
|
|
|
|
* existing station, via ieee80211_change_station(). However,
|
|
|
|
* all such changes are rejected by cfg80211 except for updates
|
|
|
|
* changing the supported rates on an existing but not yet used
|
|
|
|
* TDLS peer.
|
2008-02-25 23:27:47 +08:00
|
|
|
*/
|
|
|
|
|
2007-12-19 09:03:35 +08:00
|
|
|
if (params->listen_interval >= 0)
|
|
|
|
sta->listen_interval = params->listen_interval;
|
|
|
|
|
|
|
|
if (params->supported_rates) {
|
2013-07-08 22:55:53 +08:00
|
|
|
ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
|
|
|
|
sband, params->supported_rates,
|
|
|
|
params->supported_rates_len,
|
|
|
|
&sta->sta.supp_rates[band]);
|
2007-12-19 09:03:35 +08:00
|
|
|
}
|
2008-02-23 22:17:17 +08:00
|
|
|
|
2008-10-09 18:13:49 +08:00
|
|
|
if (params->ht_capa)
|
2011-11-19 03:32:00 +08:00
|
|
|
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
|
2013-02-07 18:47:44 +08:00
|
|
|
params->ht_capa, sta);
|
2008-08-25 16:58:58 +08:00
|
|
|
|
2012-10-11 16:04:52 +08:00
|
|
|
if (params->vht_capa)
|
|
|
|
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
|
2013-02-07 18:58:58 +08:00
|
|
|
params->vht_capa, sta);
|
2012-10-11 16:04:52 +08:00
|
|
|
|
2011-05-04 07:57:11 +08:00
|
|
|
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
2011-05-12 21:32:17 +08:00
|
|
|
#ifdef CONFIG_MAC80211_MESH
|
2013-02-14 04:14:19 +08:00
|
|
|
u32 changed = 0;
|
2013-02-15 07:48:33 +08:00
|
|
|
|
|
|
|
if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
|
2011-05-04 07:57:11 +08:00
|
|
|
switch (params->plink_state) {
|
2011-05-14 01:45:43 +08:00
|
|
|
case NL80211_PLINK_ESTAB:
|
2013-01-07 23:04:49 +08:00
|
|
|
if (sta->plink_state != NL80211_PLINK_ESTAB)
|
|
|
|
changed = mesh_plink_inc_estab_count(
|
|
|
|
sdata);
|
|
|
|
sta->plink_state = params->plink_state;
|
2013-01-31 01:14:08 +08:00
|
|
|
|
|
|
|
ieee80211_mps_sta_status_update(sta);
|
2013-02-14 04:14:19 +08:00
|
|
|
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
|
|
|
sdata->u.mesh.mshcfg.power_mode);
|
2013-01-07 23:04:49 +08:00
|
|
|
break;
|
|
|
|
case NL80211_PLINK_LISTEN:
|
2011-05-14 01:45:43 +08:00
|
|
|
case NL80211_PLINK_BLOCKED:
|
2013-01-07 23:04:49 +08:00
|
|
|
case NL80211_PLINK_OPN_SNT:
|
|
|
|
case NL80211_PLINK_OPN_RCVD:
|
|
|
|
case NL80211_PLINK_CNF_RCVD:
|
|
|
|
case NL80211_PLINK_HOLDING:
|
|
|
|
if (sta->plink_state == NL80211_PLINK_ESTAB)
|
|
|
|
changed = mesh_plink_dec_estab_count(
|
|
|
|
sdata);
|
2011-05-04 07:57:11 +08:00
|
|
|
sta->plink_state = params->plink_state;
|
2013-01-31 01:14:08 +08:00
|
|
|
|
|
|
|
ieee80211_mps_sta_status_update(sta);
|
2013-10-15 18:29:24 +08:00
|
|
|
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
|
|
|
NL80211_MESH_POWER_UNKNOWN);
|
2011-05-04 07:57:11 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* nothing */
|
|
|
|
break;
|
|
|
|
}
|
2013-02-15 07:48:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (params->plink_action) {
|
|
|
|
case NL80211_PLINK_ACTION_NO_ACTION:
|
|
|
|
/* nothing */
|
|
|
|
break;
|
|
|
|
case NL80211_PLINK_ACTION_OPEN:
|
|
|
|
changed |= mesh_plink_open(sta);
|
|
|
|
break;
|
|
|
|
case NL80211_PLINK_ACTION_BLOCK:
|
|
|
|
changed |= mesh_plink_block(sta);
|
|
|
|
break;
|
2013-01-07 23:04:49 +08:00
|
|
|
}
|
2013-01-31 01:14:08 +08:00
|
|
|
|
|
|
|
if (params->local_pm)
|
2013-02-14 04:14:19 +08:00
|
|
|
changed |=
|
|
|
|
ieee80211_mps_set_sta_local_pm(sta,
|
|
|
|
params->local_pm);
|
2013-11-07 02:04:29 +08:00
|
|
|
ieee80211_mbss_info_change_notify(sdata, changed);
|
2011-05-12 21:32:17 +08:00
|
|
|
#endif
|
2008-02-23 22:17:19 +08:00
|
|
|
}
|
2011-12-14 19:35:30 +08:00
|
|
|
|
|
|
|
return 0;
|
2007-12-19 09:03:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
u8 *mac, struct station_parameters *params)
|
|
|
|
{
|
2008-07-29 19:22:52 +08:00
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
2007-12-19 09:03:35 +08:00
|
|
|
struct sta_info *sta;
|
|
|
|
struct ieee80211_sub_if_data *sdata;
|
2008-02-25 23:27:47 +08:00
|
|
|
int err;
|
2008-12-12 23:08:31 +08:00
|
|
|
int layer2_update;
|
2007-12-19 09:03:35 +08:00
|
|
|
|
|
|
|
if (params->vlan) {
|
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
|
|
|
|
|
2008-09-11 06:01:58 +08:00
|
|
|
if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
|
|
|
|
sdata->vif.type != NL80211_IFTYPE_AP)
|
2007-12-19 09:03:35 +08:00
|
|
|
return -EINVAL;
|
|
|
|
} else
|
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
mac80211: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-09 02:56:52 +08:00
|
|
|
if (ether_addr_equal(mac, sdata->vif.addr))
|
2008-02-27 16:56:40 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (is_multicast_ether_addr(mac))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
sta = sta_info_alloc(sdata, mac, GFP_KERNEL);
|
2008-02-25 23:27:47 +08:00
|
|
|
if (!sta)
|
|
|
|
return -ENOMEM;
|
2007-12-19 09:03:35 +08:00
|
|
|
|
nl80211/mac80211: support full station state in AP mode
Today, stations are added already associated. That is
inefficient if, for example, the driver has no room
for stations any more because then the station will
go through the entire auth/assoc handshake, only to
be kicked out afterwards.
To address this a bit better, at least with drivers
using the new station state callback, allow hostapd
to add stations in unauthenticated mode, just after
receiving the AUTH frame, before even replying. Thus
if there's no more space at that point, it can send
a negative auth frame back. It still needs to handle
later state transition errors though, of course.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2012-10-26 23:53:44 +08:00
|
|
|
/*
|
|
|
|
* defaults -- if userspace wants something else we'll
|
|
|
|
* change it accordingly in sta_apply_parameters()
|
|
|
|
*/
|
2013-02-15 07:48:33 +08:00
|
|
|
if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) {
|
|
|
|
sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
|
|
|
|
sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
|
|
|
|
}
|
2007-12-19 09:03:35 +08:00
|
|
|
|
2011-12-14 19:35:30 +08:00
|
|
|
err = sta_apply_parameters(local, sta, params);
|
|
|
|
if (err) {
|
|
|
|
sta_info_free(local, sta);
|
|
|
|
return err;
|
|
|
|
}
|
2007-12-19 09:03:35 +08:00
|
|
|
|
2011-11-08 05:24:39 +08:00
|
|
|
/*
|
2013-02-15 07:48:33 +08:00
|
|
|
* for TDLS, rate control should be initialized only when
|
|
|
|
* rates are known and station is marked authorized
|
2011-11-08 05:24:39 +08:00
|
|
|
*/
|
|
|
|
if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER))
|
|
|
|
rate_control_rate_init(sta);
|
2007-12-19 09:03:35 +08:00
|
|
|
|
2008-12-12 23:08:31 +08:00
|
|
|
layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
|
|
|
|
sdata->vif.type == NL80211_IFTYPE_AP;
|
|
|
|
|
2010-02-03 20:59:58 +08:00
|
|
|
err = sta_info_insert_rcu(sta);
|
2008-02-25 23:27:47 +08:00
|
|
|
if (err) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2008-12-12 23:08:31 +08:00
|
|
|
if (layer2_update)
|
2008-02-25 23:27:47 +08:00
|
|
|
ieee80211_send_layer2_update(sta);
|
|
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
2007-12-19 09:03:35 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
u8 *mac)
|
|
|
|
{
|
2008-07-29 19:22:52 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata;
|
2007-12-19 09:03:35 +08:00
|
|
|
|
2008-07-29 19:22:52 +08:00
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
2010-02-03 20:59:58 +08:00
|
|
|
if (mac)
|
|
|
|
return sta_info_destroy_addr_bss(sdata, mac);
|
2007-12-19 09:03:35 +08:00
|
|
|
|
2012-12-14 06:07:46 +08:00
|
|
|
sta_info_flush(sdata);
|
2007-12-19 09:03:35 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_change_station(struct wiphy *wiphy,
|
2013-02-15 07:48:33 +08:00
|
|
|
struct net_device *dev, u8 *mac,
|
2007-12-19 09:03:35 +08:00
|
|
|
struct station_parameters *params)
|
|
|
|
{
|
2009-11-26 00:46:18 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
2008-07-29 19:22:52 +08:00
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
2007-12-19 09:03:35 +08:00
|
|
|
struct sta_info *sta;
|
|
|
|
struct ieee80211_sub_if_data *vlansdata;
|
2013-02-15 07:48:33 +08:00
|
|
|
enum cfg80211_station_type statype;
|
2011-12-29 20:41:39 +08:00
|
|
|
int err;
|
2007-12-19 09:03:35 +08:00
|
|
|
|
2011-12-14 19:20:29 +08:00
|
|
|
mutex_lock(&local->sta_mtx);
|
2008-04-10 21:36:09 +08:00
|
|
|
|
2010-01-09 01:10:58 +08:00
|
|
|
sta = sta_info_get_bss(sdata, mac);
|
2008-04-10 21:36:09 +08:00
|
|
|
if (!sta) {
|
2013-02-15 07:48:33 +08:00
|
|
|
err = -ENOENT;
|
|
|
|
goto out_err;
|
2008-04-10 21:36:09 +08:00
|
|
|
}
|
2007-12-19 09:03:35 +08:00
|
|
|
|
2013-02-15 07:48:33 +08:00
|
|
|
switch (sdata->vif.type) {
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
2013-03-05 05:06:12 +08:00
|
|
|
if (sdata->u.mesh.user_mpm)
|
2013-03-05 05:06:11 +08:00
|
|
|
statype = CFG80211_STA_MESH_PEER_USER;
|
2013-02-15 07:48:33 +08:00
|
|
|
else
|
2013-03-05 05:06:11 +08:00
|
|
|
statype = CFG80211_STA_MESH_PEER_KERNEL;
|
2013-02-15 07:48:33 +08:00
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
|
statype = CFG80211_STA_IBSS;
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
|
if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
|
|
|
|
statype = CFG80211_STA_AP_STA;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
|
|
|
|
statype = CFG80211_STA_TDLS_PEER_ACTIVE;
|
|
|
|
else
|
|
|
|
statype = CFG80211_STA_TDLS_PEER_SETUP;
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
|
|
statype = CFG80211_STA_AP_CLIENT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out_err;
|
2011-12-14 19:20:27 +08:00
|
|
|
}
|
|
|
|
|
2013-02-15 07:48:33 +08:00
|
|
|
err = cfg80211_check_station_change(wiphy, params, statype);
|
|
|
|
if (err)
|
|
|
|
goto out_err;
|
|
|
|
|
2008-02-25 23:27:46 +08:00
|
|
|
if (params->vlan && params->vlan != sta->sdata->dev) {
|
2012-04-24 01:49:03 +08:00
|
|
|
bool prev_4addr = false;
|
|
|
|
bool new_4addr = false;
|
|
|
|
|
2007-12-19 09:03:35 +08:00
|
|
|
vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
|
|
|
|
|
2009-11-19 18:55:19 +08:00
|
|
|
if (params->vlan->ieee80211_ptr->use_4addr) {
|
2009-11-20 17:09:14 +08:00
|
|
|
if (vlansdata->u.vlan.sta) {
|
2013-02-15 07:48:33 +08:00
|
|
|
err = -EBUSY;
|
|
|
|
goto out_err;
|
2009-11-20 17:09:14 +08:00
|
|
|
}
|
2009-11-11 03:10:05 +08:00
|
|
|
|
2012-01-12 12:41:32 +08:00
|
|
|
rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
|
2012-04-24 01:49:03 +08:00
|
|
|
new_4addr = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
|
|
|
|
sta->sdata->u.vlan.sta) {
|
|
|
|
rcu_assign_pointer(sta->sdata->u.vlan.sta, NULL);
|
|
|
|
prev_4addr = true;
|
2009-11-11 03:10:05 +08:00
|
|
|
}
|
|
|
|
|
2008-07-29 19:22:52 +08:00
|
|
|
sta->sdata = vlansdata;
|
2012-04-24 01:49:03 +08:00
|
|
|
|
|
|
|
if (sta->sta_state == IEEE80211_STA_AUTHORIZED &&
|
|
|
|
prev_4addr != new_4addr) {
|
|
|
|
if (new_4addr)
|
|
|
|
atomic_dec(&sta->sdata->bss->num_mcast_sta);
|
|
|
|
else
|
|
|
|
atomic_inc(&sta->sdata->bss->num_mcast_sta);
|
|
|
|
}
|
|
|
|
|
2007-12-19 09:03:35 +08:00
|
|
|
ieee80211_send_layer2_update(sta);
|
|
|
|
}
|
|
|
|
|
2011-12-29 20:41:39 +08:00
|
|
|
err = sta_apply_parameters(local, sta, params);
|
2013-02-15 07:48:33 +08:00
|
|
|
if (err)
|
|
|
|
goto out_err;
|
2007-12-19 09:03:35 +08:00
|
|
|
|
2013-02-15 07:48:33 +08:00
|
|
|
/* When peer becomes authorized, init rate control as well */
|
|
|
|
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
|
|
|
|
test_sta_flag(sta, WLAN_STA_AUTHORIZED))
|
2011-11-08 05:24:39 +08:00
|
|
|
rate_control_rate_init(sta);
|
|
|
|
|
2011-12-14 19:20:29 +08:00
|
|
|
mutex_unlock(&local->sta_mtx);
|
2008-04-10 21:36:09 +08:00
|
|
|
|
2013-10-01 21:45:43 +08:00
|
|
|
if ((sdata->vif.type == NL80211_IFTYPE_AP ||
|
|
|
|
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
|
|
|
|
sta->known_smps_mode != sta->sdata->bss->req_smps &&
|
|
|
|
test_sta_flag(sta, WLAN_STA_AUTHORIZED) &&
|
|
|
|
sta_info_tx_streams(sta) != 1) {
|
|
|
|
ht_dbg(sta->sdata,
|
|
|
|
"%pM just authorized and MIMO capable - update SMPS\n",
|
|
|
|
sta->sta.addr);
|
|
|
|
ieee80211_send_smps_action(sta->sdata,
|
|
|
|
sta->sdata->bss->req_smps,
|
|
|
|
sta->sta.addr,
|
|
|
|
sta->sdata->vif.bss_conf.bssid);
|
|
|
|
}
|
|
|
|
|
2011-03-11 08:43:19 +08:00
|
|
|
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
|
2012-07-27 17:33:22 +08:00
|
|
|
params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
|
2011-03-11 08:43:19 +08:00
|
|
|
ieee80211_recalc_ps(local, -1);
|
2012-07-27 17:33:22 +08:00
|
|
|
ieee80211_recalc_ps_vif(sdata);
|
|
|
|
}
|
2013-02-15 07:48:33 +08:00
|
|
|
|
2007-12-19 09:03:35 +08:00
|
|
|
return 0;
|
2013-02-15 07:48:33 +08:00
|
|
|
out_err:
|
|
|
|
mutex_unlock(&local->sta_mtx);
|
|
|
|
return err;
|
2007-12-19 09:03:35 +08:00
|
|
|
}
|
|
|
|
|
2008-02-23 22:17:17 +08:00
|
|
|
#ifdef CONFIG_MAC80211_MESH
|
|
|
|
static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
u8 *dst, u8 *next_hop)
|
|
|
|
{
|
2008-07-29 19:22:52 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata;
|
2008-02-23 22:17:17 +08:00
|
|
|
struct mesh_path *mpath;
|
|
|
|
struct sta_info *sta;
|
|
|
|
|
2008-07-29 19:22:52 +08:00
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
2008-02-25 23:27:46 +08:00
|
|
|
rcu_read_lock();
|
2009-11-26 00:46:18 +08:00
|
|
|
sta = sta_info_get(sdata, next_hop);
|
2008-02-25 23:27:46 +08:00
|
|
|
if (!sta) {
|
|
|
|
rcu_read_unlock();
|
2008-02-23 22:17:17 +08:00
|
|
|
return -ENOENT;
|
2008-02-25 23:27:46 +08:00
|
|
|
}
|
2008-02-23 22:17:17 +08:00
|
|
|
|
2013-03-29 21:38:39 +08:00
|
|
|
mpath = mesh_path_add(sdata, dst);
|
|
|
|
if (IS_ERR(mpath)) {
|
2008-02-25 23:27:46 +08:00
|
|
|
rcu_read_unlock();
|
2013-03-29 21:38:39 +08:00
|
|
|
return PTR_ERR(mpath);
|
2008-02-25 23:27:46 +08:00
|
|
|
}
|
2008-02-23 22:17:17 +08:00
|
|
|
|
|
|
|
mesh_path_fix_nexthop(mpath, sta);
|
2008-02-25 23:27:46 +08:00
|
|
|
|
2008-02-23 22:17:17 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
|
2013-02-15 21:40:31 +08:00
|
|
|
u8 *dst)
|
2008-02-23 22:17:17 +08:00
|
|
|
{
|
2008-08-03 08:04:37 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
2008-02-23 22:17:17 +08:00
|
|
|
if (dst)
|
2013-02-15 21:40:31 +08:00
|
|
|
return mesh_path_del(sdata, dst);
|
2008-02-23 22:17:17 +08:00
|
|
|
|
2011-08-30 04:23:04 +08:00
|
|
|
mesh_path_flush_by_iface(sdata);
|
2008-02-23 22:17:17 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_change_mpath(struct wiphy *wiphy,
|
|
|
|
struct net_device *dev,
|
|
|
|
u8 *dst, u8 *next_hop)
|
|
|
|
{
|
2008-07-29 19:22:52 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata;
|
2008-02-23 22:17:17 +08:00
|
|
|
struct mesh_path *mpath;
|
|
|
|
struct sta_info *sta;
|
|
|
|
|
2008-07-29 19:22:52 +08:00
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
2008-02-25 23:27:46 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2009-11-26 00:46:18 +08:00
|
|
|
sta = sta_info_get(sdata, next_hop);
|
2008-02-25 23:27:46 +08:00
|
|
|
if (!sta) {
|
|
|
|
rcu_read_unlock();
|
2008-02-23 22:17:17 +08:00
|
|
|
return -ENOENT;
|
2008-02-25 23:27:46 +08:00
|
|
|
}
|
2008-02-23 22:17:17 +08:00
|
|
|
|
2013-02-15 21:40:31 +08:00
|
|
|
mpath = mesh_path_lookup(sdata, dst);
|
2008-02-23 22:17:17 +08:00
|
|
|
if (!mpath) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
mesh_path_fix_nexthop(mpath, sta);
|
2008-02-25 23:27:46 +08:00
|
|
|
|
2008-02-23 22:17:17 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
|
|
|
|
struct mpath_info *pinfo)
|
|
|
|
{
|
2011-05-12 21:11:37 +08:00
|
|
|
struct sta_info *next_hop_sta = rcu_dereference(mpath->next_hop);
|
|
|
|
|
|
|
|
if (next_hop_sta)
|
|
|
|
memcpy(next_hop, next_hop_sta->sta.addr, ETH_ALEN);
|
2008-02-23 22:17:17 +08:00
|
|
|
else
|
|
|
|
memset(next_hop, 0, ETH_ALEN);
|
|
|
|
|
2012-08-27 21:28:16 +08:00
|
|
|
memset(pinfo, 0, sizeof(*pinfo));
|
|
|
|
|
2009-08-07 22:17:38 +08:00
|
|
|
pinfo->generation = mesh_paths_generation;
|
|
|
|
|
2008-02-23 22:17:17 +08:00
|
|
|
pinfo->filled = MPATH_INFO_FRAME_QLEN |
|
2009-11-10 07:46:55 +08:00
|
|
|
MPATH_INFO_SN |
|
2008-02-23 22:17:17 +08:00
|
|
|
MPATH_INFO_METRIC |
|
|
|
|
MPATH_INFO_EXPTIME |
|
|
|
|
MPATH_INFO_DISCOVERY_TIMEOUT |
|
|
|
|
MPATH_INFO_DISCOVERY_RETRIES |
|
|
|
|
MPATH_INFO_FLAGS;
|
|
|
|
|
|
|
|
pinfo->frame_qlen = mpath->frame_queue.qlen;
|
2009-11-10 07:46:55 +08:00
|
|
|
pinfo->sn = mpath->sn;
|
2008-02-23 22:17:17 +08:00
|
|
|
pinfo->metric = mpath->metric;
|
|
|
|
if (time_before(jiffies, mpath->exp_time))
|
|
|
|
pinfo->exptime = jiffies_to_msecs(mpath->exp_time - jiffies);
|
|
|
|
pinfo->discovery_timeout =
|
|
|
|
jiffies_to_msecs(mpath->discovery_timeout);
|
|
|
|
pinfo->discovery_retries = mpath->discovery_retries;
|
|
|
|
if (mpath->flags & MESH_PATH_ACTIVE)
|
|
|
|
pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE;
|
|
|
|
if (mpath->flags & MESH_PATH_RESOLVING)
|
|
|
|
pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
|
2009-11-10 07:46:55 +08:00
|
|
|
if (mpath->flags & MESH_PATH_SN_VALID)
|
|
|
|
pinfo->flags |= NL80211_MPATH_FLAG_SN_VALID;
|
2008-02-23 22:17:17 +08:00
|
|
|
if (mpath->flags & MESH_PATH_FIXED)
|
|
|
|
pinfo->flags |= NL80211_MPATH_FLAG_FIXED;
|
2012-08-27 21:28:16 +08:00
|
|
|
if (mpath->flags & MESH_PATH_RESOLVED)
|
|
|
|
pinfo->flags |= NL80211_MPATH_FLAG_RESOLVED;
|
2008-02-23 22:17:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
|
|
|
|
|
|
|
|
{
|
2008-07-29 19:22:52 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata;
|
2008-02-23 22:17:17 +08:00
|
|
|
struct mesh_path *mpath;
|
|
|
|
|
2008-07-29 19:22:52 +08:00
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
2008-02-23 22:17:17 +08:00
|
|
|
rcu_read_lock();
|
2013-02-15 21:40:31 +08:00
|
|
|
mpath = mesh_path_lookup(sdata, dst);
|
2008-02-23 22:17:17 +08:00
|
|
|
if (!mpath) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
memcpy(dst, mpath->dst, ETH_ALEN);
|
|
|
|
mpath_set_pinfo(mpath, next_hop, pinfo);
|
|
|
|
rcu_read_unlock();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
int idx, u8 *dst, u8 *next_hop,
|
|
|
|
struct mpath_info *pinfo)
|
|
|
|
{
|
2008-07-29 19:22:52 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata;
|
2008-02-23 22:17:17 +08:00
|
|
|
struct mesh_path *mpath;
|
|
|
|
|
2008-07-29 19:22:52 +08:00
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
2008-02-23 22:17:17 +08:00
|
|
|
rcu_read_lock();
|
2013-02-15 21:40:31 +08:00
|
|
|
mpath = mesh_path_lookup_by_idx(sdata, idx);
|
2008-02-23 22:17:17 +08:00
|
|
|
if (!mpath) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
memcpy(dst, mpath->dst, ETH_ALEN);
|
|
|
|
mpath_set_pinfo(mpath, next_hop, pinfo);
|
|
|
|
rcu_read_unlock();
|
|
|
|
return 0;
|
|
|
|
}
|
2008-10-22 03:03:48 +08:00
|
|
|
|
2010-12-17 09:37:48 +08:00
|
|
|
static int ieee80211_get_mesh_config(struct wiphy *wiphy,
|
2008-10-22 03:03:48 +08:00
|
|
|
struct net_device *dev,
|
|
|
|
struct mesh_config *conf)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata;
|
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
|
|
|
memcpy(conf, &(sdata->u.mesh.mshcfg), sizeof(struct mesh_config));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool _chg_mesh_attr(enum nl80211_meshconf_params parm, u32 mask)
|
|
|
|
{
|
|
|
|
return (mask >> (parm-1)) & 0x1;
|
|
|
|
}
|
|
|
|
|
2010-12-17 09:37:49 +08:00
|
|
|
static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
|
|
|
|
const struct mesh_setup *setup)
|
|
|
|
{
|
|
|
|
u8 *new_ie;
|
|
|
|
const u8 *old_ie;
|
2011-11-25 09:15:20 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = container_of(ifmsh,
|
|
|
|
struct ieee80211_sub_if_data, u.mesh);
|
2010-12-17 09:37:49 +08:00
|
|
|
|
2011-04-08 06:08:27 +08:00
|
|
|
/* allocate information elements */
|
2010-12-17 09:37:49 +08:00
|
|
|
new_ie = NULL;
|
2011-04-08 06:08:27 +08:00
|
|
|
old_ie = ifmsh->ie;
|
2010-12-17 09:37:49 +08:00
|
|
|
|
2011-04-08 06:08:27 +08:00
|
|
|
if (setup->ie_len) {
|
|
|
|
new_ie = kmemdup(setup->ie, setup->ie_len,
|
2010-12-17 09:37:49 +08:00
|
|
|
GFP_KERNEL);
|
|
|
|
if (!new_ie)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2011-04-08 06:08:27 +08:00
|
|
|
ifmsh->ie_len = setup->ie_len;
|
|
|
|
ifmsh->ie = new_ie;
|
|
|
|
kfree(old_ie);
|
2010-12-17 09:37:49 +08:00
|
|
|
|
|
|
|
/* now copy the rest of the setup parameters */
|
|
|
|
ifmsh->mesh_id_len = setup->mesh_id_len;
|
|
|
|
memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len);
|
2012-04-01 02:31:33 +08:00
|
|
|
ifmsh->mesh_sp_id = setup->sync_method;
|
2010-12-17 09:37:49 +08:00
|
|
|
ifmsh->mesh_pp_id = setup->path_sel_proto;
|
|
|
|
ifmsh->mesh_pm_id = setup->path_metric;
|
2013-03-05 05:06:12 +08:00
|
|
|
ifmsh->user_mpm = setup->user_mpm;
|
2013-05-09 02:46:00 +08:00
|
|
|
ifmsh->mesh_auth_id = setup->auth_id;
|
2011-05-04 07:57:07 +08:00
|
|
|
ifmsh->security = IEEE80211_MESH_SEC_NONE;
|
|
|
|
if (setup->is_authenticated)
|
|
|
|
ifmsh->security |= IEEE80211_MESH_SEC_AUTHED;
|
|
|
|
if (setup->is_secure)
|
|
|
|
ifmsh->security |= IEEE80211_MESH_SEC_SECURED;
|
2010-12-17 09:37:49 +08:00
|
|
|
|
2011-11-25 09:15:20 +08:00
|
|
|
/* mcast rate setting in Mesh Node */
|
|
|
|
memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate,
|
|
|
|
sizeof(setup->mcast_rate));
|
2013-06-04 01:33:36 +08:00
|
|
|
sdata->vif.bss_conf.basic_rates = setup->basic_rates;
|
2011-11-25 09:15:20 +08:00
|
|
|
|
2013-01-07 23:04:51 +08:00
|
|
|
sdata->vif.bss_conf.beacon_int = setup->beacon_interval;
|
|
|
|
sdata->vif.bss_conf.dtim_period = setup->dtim_period;
|
|
|
|
|
2010-12-17 09:37:49 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-17 09:37:48 +08:00
|
|
|
static int ieee80211_update_mesh_config(struct wiphy *wiphy,
|
2010-12-03 16:20:44 +08:00
|
|
|
struct net_device *dev, u32 mask,
|
|
|
|
const struct mesh_config *nconf)
|
2008-10-22 03:03:48 +08:00
|
|
|
{
|
|
|
|
struct mesh_config *conf;
|
|
|
|
struct ieee80211_sub_if_data *sdata;
|
2009-11-10 07:46:57 +08:00
|
|
|
struct ieee80211_if_mesh *ifmsh;
|
|
|
|
|
2008-10-22 03:03:48 +08:00
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
2009-11-10 07:46:57 +08:00
|
|
|
ifmsh = &sdata->u.mesh;
|
2008-10-22 03:03:48 +08:00
|
|
|
|
|
|
|
/* Set the config options which we are interested in setting */
|
|
|
|
conf = &(sdata->u.mesh.mshcfg);
|
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_RETRY_TIMEOUT, mask))
|
|
|
|
conf->dot11MeshRetryTimeout = nconf->dot11MeshRetryTimeout;
|
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_CONFIRM_TIMEOUT, mask))
|
|
|
|
conf->dot11MeshConfirmTimeout = nconf->dot11MeshConfirmTimeout;
|
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HOLDING_TIMEOUT, mask))
|
|
|
|
conf->dot11MeshHoldingTimeout = nconf->dot11MeshHoldingTimeout;
|
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_MAX_PEER_LINKS, mask))
|
|
|
|
conf->dot11MeshMaxPeerLinks = nconf->dot11MeshMaxPeerLinks;
|
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_MAX_RETRIES, mask))
|
|
|
|
conf->dot11MeshMaxRetries = nconf->dot11MeshMaxRetries;
|
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask))
|
|
|
|
conf->dot11MeshTTL = nconf->dot11MeshTTL;
|
2010-12-03 16:20:40 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_ELEMENT_TTL, mask))
|
2012-06-15 00:23:53 +08:00
|
|
|
conf->element_ttl = nconf->element_ttl;
|
2013-03-05 05:06:14 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) {
|
|
|
|
if (ifmsh->user_mpm)
|
|
|
|
return -EBUSY;
|
2008-10-22 03:03:48 +08:00
|
|
|
conf->auto_open_plinks = nconf->auto_open_plinks;
|
2013-03-05 05:06:14 +08:00
|
|
|
}
|
2012-04-01 02:31:33 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, mask))
|
|
|
|
conf->dot11MeshNbrOffsetMaxNeighbor =
|
|
|
|
nconf->dot11MeshNbrOffsetMaxNeighbor;
|
2008-10-22 03:03:48 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, mask))
|
|
|
|
conf->dot11MeshHWMPmaxPREQretries =
|
|
|
|
nconf->dot11MeshHWMPmaxPREQretries;
|
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_PATH_REFRESH_TIME, mask))
|
|
|
|
conf->path_refresh_time = nconf->path_refresh_time;
|
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, mask))
|
|
|
|
conf->min_discovery_timeout = nconf->min_discovery_timeout;
|
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, mask))
|
|
|
|
conf->dot11MeshHWMPactivePathTimeout =
|
|
|
|
nconf->dot11MeshHWMPactivePathTimeout;
|
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, mask))
|
|
|
|
conf->dot11MeshHWMPpreqMinInterval =
|
|
|
|
nconf->dot11MeshHWMPpreqMinInterval;
|
2011-11-25 09:15:24 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, mask))
|
|
|
|
conf->dot11MeshHWMPperrMinInterval =
|
|
|
|
nconf->dot11MeshHWMPperrMinInterval;
|
2008-10-22 03:03:48 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
|
|
|
|
mask))
|
|
|
|
conf->dot11MeshHWMPnetDiameterTraversalTime =
|
|
|
|
nconf->dot11MeshHWMPnetDiameterTraversalTime;
|
2009-11-10 07:46:57 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ROOTMODE, mask)) {
|
|
|
|
conf->dot11MeshHWMPRootMode = nconf->dot11MeshHWMPRootMode;
|
|
|
|
ieee80211_mesh_root_setup(ifmsh);
|
|
|
|
}
|
2011-08-10 07:45:11 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_GATE_ANNOUNCEMENTS, mask)) {
|
2011-08-26 01:36:14 +08:00
|
|
|
/* our current gate announcement implementation rides on root
|
|
|
|
* announcements, so require this ifmsh to also be a root node
|
|
|
|
* */
|
|
|
|
if (nconf->dot11MeshGateAnnouncementProtocol &&
|
2012-06-14 02:06:09 +08:00
|
|
|
!(conf->dot11MeshHWMPRootMode > IEEE80211_ROOTMODE_ROOT)) {
|
|
|
|
conf->dot11MeshHWMPRootMode = IEEE80211_PROACTIVE_RANN;
|
2011-08-26 01:36:14 +08:00
|
|
|
ieee80211_mesh_root_setup(ifmsh);
|
|
|
|
}
|
2011-08-10 07:45:11 +08:00
|
|
|
conf->dot11MeshGateAnnouncementProtocol =
|
|
|
|
nconf->dot11MeshGateAnnouncementProtocol;
|
|
|
|
}
|
2012-06-11 11:59:36 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_RANN_INTERVAL, mask))
|
2011-08-10 07:45:10 +08:00
|
|
|
conf->dot11MeshHWMPRannInterval =
|
|
|
|
nconf->dot11MeshHWMPRannInterval;
|
2012-01-21 01:02:16 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_FORWARDING, mask))
|
|
|
|
conf->dot11MeshForwarding = nconf->dot11MeshForwarding;
|
2012-02-29 09:04:08 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_RSSI_THRESHOLD, mask)) {
|
|
|
|
/* our RSSI threshold implementation is supported only for
|
|
|
|
* devices that report signal in dBm.
|
|
|
|
*/
|
|
|
|
if (!(sdata->local->hw.flags & IEEE80211_HW_SIGNAL_DBM))
|
|
|
|
return -ENOTSUPP;
|
|
|
|
conf->rssi_threshold = nconf->rssi_threshold;
|
|
|
|
}
|
2012-05-01 05:20:32 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HT_OPMODE, mask)) {
|
|
|
|
conf->ht_opmode = nconf->ht_opmode;
|
|
|
|
sdata->vif.bss_conf.ht_operation_mode = nconf->ht_opmode;
|
|
|
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT);
|
|
|
|
}
|
2012-06-14 02:06:06 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, mask))
|
|
|
|
conf->dot11MeshHWMPactivePathToRootTimeout =
|
|
|
|
nconf->dot11MeshHWMPactivePathToRootTimeout;
|
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ROOT_INTERVAL, mask))
|
|
|
|
conf->dot11MeshHWMProotInterval =
|
|
|
|
nconf->dot11MeshHWMProotInterval;
|
2012-06-14 02:06:10 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, mask))
|
|
|
|
conf->dot11MeshHWMPconfirmationInterval =
|
|
|
|
nconf->dot11MeshHWMPconfirmationInterval;
|
2013-01-31 01:14:08 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask)) {
|
|
|
|
conf->power_mode = nconf->power_mode;
|
|
|
|
ieee80211_mps_local_status_update(sdata);
|
|
|
|
}
|
2013-02-15 03:20:13 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask))
|
2013-01-31 01:14:08 +08:00
|
|
|
conf->dot11MeshAwakeWindowDuration =
|
|
|
|
nconf->dot11MeshAwakeWindowDuration;
|
2013-06-04 00:53:40 +08:00
|
|
|
if (_chg_mesh_attr(NL80211_MESHCONF_PLINK_TIMEOUT, mask))
|
|
|
|
conf->plink_timeout = nconf->plink_timeout;
|
2013-02-15 03:20:13 +08:00
|
|
|
ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
|
2008-10-22 03:03:48 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-03 16:20:44 +08:00
|
|
|
static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
const struct mesh_config *conf,
|
|
|
|
const struct mesh_setup *setup)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
2010-12-17 09:37:49 +08:00
|
|
|
int err;
|
2010-12-03 16:20:44 +08:00
|
|
|
|
2010-12-17 09:37:49 +08:00
|
|
|
memcpy(&ifmsh->mshcfg, conf, sizeof(struct mesh_config));
|
|
|
|
err = copy_mesh_setup(ifmsh, setup);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2012-05-17 05:50:20 +08:00
|
|
|
|
2012-09-11 20:34:12 +08:00
|
|
|
/* can mesh use other SMPS modes? */
|
|
|
|
sdata->smps_mode = IEEE80211_SMPS_OFF;
|
|
|
|
sdata->needed_rx_chains = sdata->local->rx_chains;
|
|
|
|
|
2012-11-09 18:39:59 +08:00
|
|
|
err = ieee80211_vif_use_channel(sdata, &setup->chandef,
|
2012-07-26 23:24:39 +08:00
|
|
|
IEEE80211_CHANCTX_SHARED);
|
2012-05-17 05:50:20 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2013-02-15 03:20:13 +08:00
|
|
|
return ieee80211_start_mesh(sdata);
|
2010-12-03 16:20:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
|
|
|
ieee80211_stop_mesh(sdata);
|
2012-07-26 23:24:39 +08:00
|
|
|
ieee80211_vif_release_channel(sdata);
|
2010-12-03 16:20:44 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2008-02-23 22:17:17 +08:00
|
|
|
#endif
|
|
|
|
|
2008-08-08 01:07:01 +08:00
|
|
|
static int ieee80211_change_bss(struct wiphy *wiphy,
|
|
|
|
struct net_device *dev,
|
|
|
|
struct bss_parameters *params)
|
|
|
|
{
|
2012-07-26 23:24:39 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
enum ieee80211_band band;
|
2008-08-08 01:07:01 +08:00
|
|
|
u32 changed = 0;
|
|
|
|
|
2013-11-22 01:19:50 +08:00
|
|
|
if (!sdata_dereference(sdata->u.ap.beacon, sdata))
|
2012-07-26 23:24:39 +08:00
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
band = ieee80211_get_sdata_band(sdata);
|
2008-08-08 01:07:01 +08:00
|
|
|
|
|
|
|
if (params->use_cts_prot >= 0) {
|
2008-10-11 07:51:51 +08:00
|
|
|
sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot;
|
2008-08-08 01:07:01 +08:00
|
|
|
changed |= BSS_CHANGED_ERP_CTS_PROT;
|
|
|
|
}
|
|
|
|
if (params->use_short_preamble >= 0) {
|
2008-10-11 07:51:51 +08:00
|
|
|
sdata->vif.bss_conf.use_short_preamble =
|
2008-08-08 01:07:01 +08:00
|
|
|
params->use_short_preamble;
|
|
|
|
changed |= BSS_CHANGED_ERP_PREAMBLE;
|
|
|
|
}
|
2010-01-15 10:00:48 +08:00
|
|
|
|
|
|
|
if (!sdata->vif.bss_conf.use_short_slot &&
|
2012-07-26 23:24:39 +08:00
|
|
|
band == IEEE80211_BAND_5GHZ) {
|
2010-01-15 10:00:48 +08:00
|
|
|
sdata->vif.bss_conf.use_short_slot = true;
|
|
|
|
changed |= BSS_CHANGED_ERP_SLOT;
|
|
|
|
}
|
|
|
|
|
2008-08-08 01:07:01 +08:00
|
|
|
if (params->use_short_slot_time >= 0) {
|
2008-10-11 07:51:51 +08:00
|
|
|
sdata->vif.bss_conf.use_short_slot =
|
2008-08-08 01:07:01 +08:00
|
|
|
params->use_short_slot_time;
|
|
|
|
changed |= BSS_CHANGED_ERP_SLOT;
|
|
|
|
}
|
|
|
|
|
2008-10-30 22:59:22 +08:00
|
|
|
if (params->basic_rates) {
|
2013-07-08 22:55:53 +08:00
|
|
|
ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
|
|
|
|
wiphy->bands[band],
|
|
|
|
params->basic_rates,
|
|
|
|
params->basic_rates_len,
|
|
|
|
&sdata->vif.bss_conf.basic_rates);
|
2008-10-30 22:59:22 +08:00
|
|
|
changed |= BSS_CHANGED_BASIC_RATES;
|
|
|
|
}
|
|
|
|
|
2010-04-27 07:23:36 +08:00
|
|
|
if (params->ap_isolate >= 0) {
|
|
|
|
if (params->ap_isolate)
|
|
|
|
sdata->flags |= IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
|
|
|
|
else
|
|
|
|
sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
|
|
|
|
}
|
|
|
|
|
2010-11-19 19:40:26 +08:00
|
|
|
if (params->ht_opmode >= 0) {
|
|
|
|
sdata->vif.bss_conf.ht_operation_mode =
|
|
|
|
(u16) params->ht_opmode;
|
|
|
|
changed |= BSS_CHANGED_HT;
|
|
|
|
}
|
|
|
|
|
2012-11-14 22:21:17 +08:00
|
|
|
if (params->p2p_ctwindow >= 0) {
|
2013-03-21 22:47:56 +08:00
|
|
|
sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow &=
|
|
|
|
~IEEE80211_P2P_OPPPS_CTWINDOW_MASK;
|
|
|
|
sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |=
|
|
|
|
params->p2p_ctwindow & IEEE80211_P2P_OPPPS_CTWINDOW_MASK;
|
2012-11-14 22:21:17 +08:00
|
|
|
changed |= BSS_CHANGED_P2P_PS;
|
|
|
|
}
|
|
|
|
|
2013-03-21 22:47:56 +08:00
|
|
|
if (params->p2p_opp_ps > 0) {
|
|
|
|
sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |=
|
|
|
|
IEEE80211_P2P_OPPPS_ENABLE_BIT;
|
|
|
|
changed |= BSS_CHANGED_P2P_PS;
|
|
|
|
} else if (params->p2p_opp_ps == 0) {
|
|
|
|
sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow &=
|
|
|
|
~IEEE80211_P2P_OPPPS_ENABLE_BIT;
|
2012-11-14 22:21:17 +08:00
|
|
|
changed |= BSS_CHANGED_P2P_PS;
|
|
|
|
}
|
|
|
|
|
2008-08-08 01:07:01 +08:00
|
|
|
ieee80211_bss_info_change_notify(sdata, changed);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-10-30 22:59:24 +08:00
|
|
|
static int ieee80211_set_txq_params(struct wiphy *wiphy,
|
2011-09-26 01:06:53 +08:00
|
|
|
struct net_device *dev,
|
2008-10-30 22:59:24 +08:00
|
|
|
struct ieee80211_txq_params *params)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
2011-09-26 01:06:54 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
2008-10-30 22:59:24 +08:00
|
|
|
struct ieee80211_tx_queue_params p;
|
|
|
|
|
|
|
|
if (!local->ops->conf_tx)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2012-03-28 17:04:25 +08:00
|
|
|
if (local->hw.queues < IEEE80211_NUM_ACS)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2008-10-30 22:59:24 +08:00
|
|
|
memset(&p, 0, sizeof(p));
|
|
|
|
p.aifs = params->aifs;
|
|
|
|
p.cw_max = params->cwmax;
|
|
|
|
p.cw_min = params->cwmin;
|
|
|
|
p.txop = params->txop;
|
2010-01-12 16:42:31 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Setting tx queue params disables u-apsd because it's only
|
|
|
|
* called in master mode.
|
|
|
|
*/
|
|
|
|
p.uapsd = false;
|
|
|
|
|
2012-03-28 17:04:24 +08:00
|
|
|
sdata->tx_conf[params->ac] = p;
|
|
|
|
if (drv_conf_tx(local, sdata, params->ac, &p)) {
|
2010-08-21 07:25:38 +08:00
|
|
|
wiphy_debug(local->hw.wiphy,
|
2012-03-28 17:04:24 +08:00
|
|
|
"failed to set TX queue parameters for AC %d\n",
|
|
|
|
params->ac);
|
2008-10-30 22:59:24 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2012-07-06 23:37:43 +08:00
|
|
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS);
|
|
|
|
|
2008-10-30 22:59:24 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-20 00:20:53 +08:00
|
|
|
#ifdef CONFIG_PM
|
2011-05-04 21:37:28 +08:00
|
|
|
static int ieee80211_suspend(struct wiphy *wiphy,
|
|
|
|
struct cfg80211_wowlan *wowlan)
|
2009-01-20 00:20:53 +08:00
|
|
|
{
|
2011-05-04 21:37:29 +08:00
|
|
|
return __ieee80211_suspend(wiphy_priv(wiphy), wowlan);
|
2009-01-20 00:20:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_resume(struct wiphy *wiphy)
|
|
|
|
{
|
|
|
|
return __ieee80211_resume(wiphy_priv(wiphy));
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define ieee80211_suspend NULL
|
|
|
|
#define ieee80211_resume NULL
|
|
|
|
#endif
|
|
|
|
|
2009-02-11 04:25:55 +08:00
|
|
|
static int ieee80211_scan(struct wiphy *wiphy,
|
|
|
|
struct cfg80211_scan_request *req)
|
|
|
|
{
|
2012-06-19 01:17:03 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata;
|
|
|
|
|
|
|
|
sdata = IEEE80211_WDEV_TO_SUB_IF(req->wdev);
|
2009-02-11 04:25:55 +08:00
|
|
|
|
2010-09-16 20:58:23 +08:00
|
|
|
switch (ieee80211_vif_type_p2p(&sdata->vif)) {
|
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
2012-06-19 02:07:15 +08:00
|
|
|
case NL80211_IFTYPE_P2P_DEVICE:
|
2010-09-16 20:58:23 +08:00
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
|
|
if (sdata->local->ops->hw_scan)
|
|
|
|
break;
|
2011-02-01 22:35:36 +08:00
|
|
|
/*
|
|
|
|
* FIXME: implement NoA while scanning in software,
|
|
|
|
* for now fall through to allow scanning only when
|
|
|
|
* beaconing hasn't been configured yet
|
|
|
|
*/
|
2010-09-16 20:58:23 +08:00
|
|
|
case NL80211_IFTYPE_AP:
|
2012-10-16 14:39:22 +08:00
|
|
|
/*
|
|
|
|
* If the scan has been forced (and the driver supports
|
|
|
|
* forcing), don't care about being beaconing already.
|
|
|
|
* This will create problems to the attached stations (e.g. all
|
|
|
|
* the frames sent while scanning on other channel will be
|
|
|
|
* lost)
|
|
|
|
*/
|
|
|
|
if (sdata->u.ap.beacon &&
|
|
|
|
(!(wiphy->features & NL80211_FEATURE_AP_SCAN) ||
|
|
|
|
!(req->flags & NL80211_SCAN_FLAG_AP)))
|
2010-09-16 20:58:23 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
2009-02-11 04:25:55 +08:00
|
|
|
|
|
|
|
return ieee80211_request_scan(sdata, req);
|
|
|
|
}
|
|
|
|
|
2011-05-11 22:09:36 +08:00
|
|
|
static int
|
|
|
|
ieee80211_sched_scan_start(struct wiphy *wiphy,
|
|
|
|
struct net_device *dev,
|
|
|
|
struct cfg80211_sched_scan_request *req)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
|
|
|
if (!sdata->local->ops->sched_scan_start)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return ieee80211_request_sched_scan_start(sdata, req);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2011-05-12 21:28:29 +08:00
|
|
|
ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev)
|
2011-05-11 22:09:36 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
|
|
|
if (!sdata->local->ops->sched_scan_stop)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2011-05-12 21:28:29 +08:00
|
|
|
return ieee80211_request_sched_scan_stop(sdata);
|
2011-05-11 22:09:36 +08:00
|
|
|
}
|
|
|
|
|
2009-03-19 19:39:22 +08:00
|
|
|
static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
struct cfg80211_auth_request *req)
|
|
|
|
{
|
2009-07-07 09:45:17 +08:00
|
|
|
return ieee80211_mgd_auth(IEEE80211_DEV_TO_SUB_IF(dev), req);
|
2009-03-19 19:39:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
struct cfg80211_assoc_request *req)
|
|
|
|
{
|
2009-07-07 09:45:17 +08:00
|
|
|
return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
|
2009-03-19 19:39:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev,
|
2012-02-24 20:50:51 +08:00
|
|
|
struct cfg80211_deauth_request *req)
|
2009-03-19 19:39:22 +08:00
|
|
|
{
|
2012-02-24 20:50:51 +08:00
|
|
|
return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), req);
|
2009-03-19 19:39:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev,
|
2012-02-24 20:50:51 +08:00
|
|
|
struct cfg80211_disassoc_request *req)
|
2009-03-19 19:39:22 +08:00
|
|
|
{
|
2012-02-24 20:50:51 +08:00
|
|
|
return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
|
2009-03-19 19:39:22 +08:00
|
|
|
}
|
|
|
|
|
2009-04-20 03:25:43 +08:00
|
|
|
static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
struct cfg80211_ibss_params *params)
|
|
|
|
{
|
2012-07-26 23:24:39 +08:00
|
|
|
return ieee80211_ibss_join(IEEE80211_DEV_TO_SUB_IF(dev), params);
|
2009-04-20 03:25:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
|
|
|
|
{
|
2012-07-26 23:24:39 +08:00
|
|
|
return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
|
2009-04-20 03:25:43 +08:00
|
|
|
}
|
|
|
|
|
2012-11-02 20:27:49 +08:00
|
|
|
static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
int rate[IEEE80211_NUM_BANDS])
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
2013-02-07 00:23:45 +08:00
|
|
|
memcpy(sdata->vif.bss_conf.mcast_rate, rate,
|
|
|
|
sizeof(int) * IEEE80211_NUM_BANDS);
|
2012-11-02 20:27:49 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-04-21 00:39:05 +08:00
|
|
|
static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
2009-04-24 00:52:52 +08:00
|
|
|
int err;
|
2009-04-21 00:39:05 +08:00
|
|
|
|
2010-11-08 17:51:06 +08:00
|
|
|
if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
|
|
|
|
err = drv_set_frag_threshold(local, wiphy->frag_threshold);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-12-22 05:50:48 +08:00
|
|
|
if (changed & WIPHY_PARAM_COVERAGE_CLASS) {
|
|
|
|
err = drv_set_coverage_class(local, wiphy->coverage_class);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-04-21 00:39:05 +08:00
|
|
|
if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
|
2009-04-24 00:52:52 +08:00
|
|
|
err = drv_set_rts_threshold(local, wiphy->rts_threshold);
|
2009-04-21 00:39:05 +08:00
|
|
|
|
2009-04-24 00:52:52 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
2009-04-21 00:39:05 +08:00
|
|
|
}
|
|
|
|
|
2012-11-10 01:38:32 +08:00
|
|
|
if (changed & WIPHY_PARAM_RETRY_SHORT) {
|
|
|
|
if (wiphy->retry_short > IEEE80211_MAX_TX_RETRY)
|
|
|
|
return -EINVAL;
|
2009-04-21 00:39:05 +08:00
|
|
|
local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
|
2012-11-10 01:38:32 +08:00
|
|
|
}
|
|
|
|
if (changed & WIPHY_PARAM_RETRY_LONG) {
|
|
|
|
if (wiphy->retry_long > IEEE80211_MAX_TX_RETRY)
|
|
|
|
return -EINVAL;
|
2009-04-21 00:39:05 +08:00
|
|
|
local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
|
2012-11-10 01:38:32 +08:00
|
|
|
}
|
2009-04-21 00:39:05 +08:00
|
|
|
if (changed &
|
|
|
|
(WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG))
|
|
|
|
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-02 19:01:39 +08:00
|
|
|
static int ieee80211_set_tx_power(struct wiphy *wiphy,
|
2012-10-24 16:17:18 +08:00
|
|
|
struct wireless_dev *wdev,
|
2010-06-23 17:12:37 +08:00
|
|
|
enum nl80211_tx_power_setting type, int mbm)
|
2009-06-02 19:01:39 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
2012-10-24 16:59:25 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata;
|
2009-06-02 19:01:39 +08:00
|
|
|
|
2012-10-24 16:59:25 +08:00
|
|
|
if (wdev) {
|
|
|
|
sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case NL80211_TX_POWER_AUTOMATIC:
|
|
|
|
sdata->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
|
|
|
|
break;
|
|
|
|
case NL80211_TX_POWER_LIMITED:
|
|
|
|
case NL80211_TX_POWER_FIXED:
|
|
|
|
if (mbm < 0 || (mbm % 100))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
sdata->user_power_level = MBM_TO_DBM(mbm);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ieee80211_recalc_txpower(sdata);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-07-26 23:24:39 +08:00
|
|
|
|
2009-06-02 19:01:39 +08:00
|
|
|
switch (type) {
|
2010-06-23 17:12:37 +08:00
|
|
|
case NL80211_TX_POWER_AUTOMATIC:
|
2012-10-24 16:59:25 +08:00
|
|
|
local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
|
2009-06-02 19:01:39 +08:00
|
|
|
break;
|
2010-06-23 17:12:37 +08:00
|
|
|
case NL80211_TX_POWER_LIMITED:
|
|
|
|
case NL80211_TX_POWER_FIXED:
|
|
|
|
if (mbm < 0 || (mbm % 100))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
local->user_power_level = MBM_TO_DBM(mbm);
|
2009-06-02 19:01:39 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-10-24 16:59:25 +08:00
|
|
|
mutex_lock(&local->iflist_mtx);
|
|
|
|
list_for_each_entry(sdata, &local->interfaces, list)
|
|
|
|
sdata->user_power_level = local->user_power_level;
|
|
|
|
list_for_each_entry(sdata, &local->interfaces, list)
|
|
|
|
ieee80211_recalc_txpower(sdata);
|
|
|
|
mutex_unlock(&local->iflist_mtx);
|
2009-06-02 19:01:39 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-24 16:17:18 +08:00
|
|
|
static int ieee80211_get_tx_power(struct wiphy *wiphy,
|
|
|
|
struct wireless_dev *wdev,
|
|
|
|
int *dbm)
|
2009-06-02 19:01:39 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
2012-10-24 16:59:25 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
2009-06-02 19:01:39 +08:00
|
|
|
|
2012-10-24 16:59:25 +08:00
|
|
|
if (!local->use_chanctx)
|
|
|
|
*dbm = local->hw.conf.power_level;
|
|
|
|
else
|
|
|
|
*dbm = sdata->vif.bss_conf.txpower;
|
2009-06-02 19:01:39 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-07-02 03:26:58 +08:00
|
|
|
static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev,
|
2010-10-07 19:11:09 +08:00
|
|
|
const u8 *addr)
|
2009-07-02 03:26:58 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
|
|
|
memcpy(&sdata->u.wds.remote_addr, addr, ETH_ALEN);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-02 19:01:41 +08:00
|
|
|
static void ieee80211_rfkill_poll(struct wiphy *wiphy)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
|
|
|
|
|
|
drv_rfkill_poll(local);
|
|
|
|
}
|
|
|
|
|
2009-07-02 03:26:51 +08:00
|
|
|
#ifdef CONFIG_NL80211_TESTMODE
|
2013-07-31 23:04:15 +08:00
|
|
|
static int ieee80211_testmode_cmd(struct wiphy *wiphy,
|
|
|
|
struct wireless_dev *wdev,
|
|
|
|
void *data, int len)
|
2009-07-02 03:26:51 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
2013-07-31 23:06:22 +08:00
|
|
|
struct ieee80211_vif *vif = NULL;
|
2009-07-02 03:26:51 +08:00
|
|
|
|
|
|
|
if (!local->ops->testmode_cmd)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2013-07-31 23:06:22 +08:00
|
|
|
if (wdev) {
|
|
|
|
struct ieee80211_sub_if_data *sdata;
|
|
|
|
|
|
|
|
sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
|
|
|
if (sdata->flags & IEEE80211_SDATA_IN_DRIVER)
|
|
|
|
vif = &sdata->vif;
|
|
|
|
}
|
|
|
|
|
|
|
|
return local->ops->testmode_cmd(&local->hw, vif, data, len);
|
2009-07-02 03:26:51 +08:00
|
|
|
}
|
2011-05-21 00:05:54 +08:00
|
|
|
|
|
|
|
static int ieee80211_testmode_dump(struct wiphy *wiphy,
|
|
|
|
struct sk_buff *skb,
|
|
|
|
struct netlink_callback *cb,
|
|
|
|
void *data, int len)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
|
|
|
|
|
|
if (!local->ops->testmode_dump)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return local->ops->testmode_dump(&local->hw, skb, cb, data, len);
|
|
|
|
}
|
2009-07-02 03:26:51 +08:00
|
|
|
#endif
|
|
|
|
|
2013-10-01 21:45:43 +08:00
|
|
|
int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
|
|
|
|
enum ieee80211_smps_mode smps_mode)
|
|
|
|
{
|
|
|
|
struct sta_info *sta;
|
|
|
|
enum ieee80211_smps_mode old_req;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
old_req = sdata->u.ap.req_smps;
|
|
|
|
sdata->u.ap.req_smps = smps_mode;
|
|
|
|
|
|
|
|
/* AUTOMATIC doesn't mean much for AP - don't allow it */
|
|
|
|
if (old_req == smps_mode ||
|
|
|
|
smps_mode == IEEE80211_SMPS_AUTOMATIC)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* If no associated stations, there's no need to do anything */
|
|
|
|
if (!atomic_read(&sdata->u.ap.num_mcast_sta)) {
|
|
|
|
sdata->smps_mode = smps_mode;
|
|
|
|
ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ht_dbg(sdata,
|
|
|
|
"SMSP %d requested in AP mode, sending Action frame to %d stations\n",
|
|
|
|
smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta));
|
|
|
|
|
|
|
|
mutex_lock(&sdata->local->sta_mtx);
|
|
|
|
for (i = 0; i < STA_HASH_SIZE; i++) {
|
|
|
|
for (sta = rcu_dereference_protected(sdata->local->sta_hash[i],
|
|
|
|
lockdep_is_held(&sdata->local->sta_mtx));
|
|
|
|
sta;
|
|
|
|
sta = rcu_dereference_protected(sta->hnext,
|
|
|
|
lockdep_is_held(&sdata->local->sta_mtx))) {
|
|
|
|
/*
|
|
|
|
* Only stations associated to our AP and
|
|
|
|
* associated VLANs
|
|
|
|
*/
|
|
|
|
if (sta->sdata->bss != &sdata->u.ap)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* This station doesn't support MIMO - skip it */
|
|
|
|
if (sta_info_tx_streams(sta) == 1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't wake up a STA just to send the action frame
|
|
|
|
* unless we are getting more restrictive.
|
|
|
|
*/
|
|
|
|
if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
|
|
|
|
!ieee80211_smps_is_restrictive(sta->known_smps_mode,
|
|
|
|
smps_mode)) {
|
|
|
|
ht_dbg(sdata,
|
|
|
|
"Won't send SMPS to sleeping STA %pM\n",
|
|
|
|
sta->sta.addr);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the STA is not authorized, wait until it gets
|
|
|
|
* authorized and the action frame will be sent then.
|
|
|
|
*/
|
|
|
|
if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
|
|
|
|
ieee80211_send_smps_action(sdata, smps_mode,
|
|
|
|
sta->sta.addr,
|
|
|
|
sdata->vif.bss_conf.bssid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mutex_unlock(&sdata->local->sta_mtx);
|
|
|
|
|
|
|
|
sdata->smps_mode = smps_mode;
|
|
|
|
ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
|
|
|
|
enum ieee80211_smps_mode smps_mode)
|
2009-12-01 20:37:02 +08:00
|
|
|
{
|
|
|
|
const u8 *ap;
|
|
|
|
enum ieee80211_smps_mode old_req;
|
|
|
|
int err;
|
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
lockdep_assert_held(&sdata->wdev.mtx);
|
2011-04-20 02:44:04 +08:00
|
|
|
|
2013-10-01 21:45:43 +08:00
|
|
|
if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-12-01 20:37:02 +08:00
|
|
|
old_req = sdata->u.mgd.req_smps;
|
|
|
|
sdata->u.mgd.req_smps = smps_mode;
|
|
|
|
|
|
|
|
if (old_req == smps_mode &&
|
|
|
|
smps_mode != IEEE80211_SMPS_AUTOMATIC)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If not associated, or current association is not an HT
|
2012-09-11 20:34:12 +08:00
|
|
|
* association, there's no need to do anything, just store
|
|
|
|
* the new value until we associate.
|
2009-12-01 20:37:02 +08:00
|
|
|
*/
|
|
|
|
if (!sdata->u.mgd.associated ||
|
2012-11-09 18:39:59 +08:00
|
|
|
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
|
2009-12-01 20:37:02 +08:00
|
|
|
return 0;
|
|
|
|
|
2009-12-23 20:15:39 +08:00
|
|
|
ap = sdata->u.mgd.associated->bssid;
|
2009-12-01 20:37:02 +08:00
|
|
|
|
|
|
|
if (smps_mode == IEEE80211_SMPS_AUTOMATIC) {
|
|
|
|
if (sdata->u.mgd.powersave)
|
|
|
|
smps_mode = IEEE80211_SMPS_DYNAMIC;
|
|
|
|
else
|
|
|
|
smps_mode = IEEE80211_SMPS_OFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* send SM PS frame to AP */
|
|
|
|
err = ieee80211_send_smps_action(sdata, smps_mode,
|
|
|
|
ap, ap);
|
|
|
|
if (err)
|
|
|
|
sdata->u.mgd.req_smps = old_req;
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-07-02 03:26:57 +08:00
|
|
|
static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
bool enabled, int timeout)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
|
|
|
2013-10-30 06:11:59 +08:00
|
|
|
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
2010-01-15 19:21:37 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2009-07-02 03:26:57 +08:00
|
|
|
if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (enabled == sdata->u.mgd.powersave &&
|
2010-06-09 14:51:52 +08:00
|
|
|
timeout == local->dynamic_ps_forced_timeout)
|
2009-07-02 03:26:57 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
sdata->u.mgd.powersave = enabled;
|
2010-06-09 14:51:52 +08:00
|
|
|
local->dynamic_ps_forced_timeout = timeout;
|
2009-07-02 03:26:57 +08:00
|
|
|
|
2009-12-01 20:37:02 +08:00
|
|
|
/* no change, but if automatic follow powersave */
|
2013-06-03 19:51:59 +08:00
|
|
|
sdata_lock(sdata);
|
2013-10-01 21:45:43 +08:00
|
|
|
__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps);
|
2013-06-03 19:51:59 +08:00
|
|
|
sdata_unlock(sdata);
|
2009-12-01 20:37:02 +08:00
|
|
|
|
2009-07-02 03:26:57 +08:00
|
|
|
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
|
|
|
|
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
|
|
|
|
|
|
|
ieee80211_recalc_ps(local, -1);
|
2012-07-27 17:33:22 +08:00
|
|
|
ieee80211_recalc_ps_vif(sdata);
|
2009-07-02 03:26:57 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-03-23 15:02:34 +08:00
|
|
|
static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,
|
|
|
|
struct net_device *dev,
|
|
|
|
s32 rssi_thold, u32 rssi_hyst)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
struct ieee80211_vif *vif = &sdata->vif;
|
|
|
|
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
|
|
|
|
|
|
|
|
if (rssi_thold == bss_conf->cqm_rssi_thold &&
|
|
|
|
rssi_hyst == bss_conf->cqm_rssi_hyst)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
bss_conf->cqm_rssi_thold = rssi_thold;
|
|
|
|
bss_conf->cqm_rssi_hyst = rssi_hyst;
|
|
|
|
|
|
|
|
/* tell the driver upon association, unless already associated */
|
2012-01-19 16:29:58 +08:00
|
|
|
if (sdata->u.mgd.associated &&
|
|
|
|
sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)
|
2010-03-23 15:02:34 +08:00
|
|
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-07-02 03:26:59 +08:00
|
|
|
static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
|
|
|
|
struct net_device *dev,
|
|
|
|
const u8 *addr,
|
|
|
|
const struct cfg80211_bitrate_mask *mask)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
2011-04-27 19:26:51 +08:00
|
|
|
int i, ret;
|
2009-12-04 16:26:38 +08:00
|
|
|
|
2012-06-12 17:41:15 +08:00
|
|
|
if (!ieee80211_sdata_running(sdata))
|
|
|
|
return -ENETDOWN;
|
|
|
|
|
2011-04-27 19:26:51 +08:00
|
|
|
if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) {
|
|
|
|
ret = drv_set_bitrate_mask(local, sdata, mask);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
2009-07-02 03:26:59 +08:00
|
|
|
|
2012-01-29 00:25:33 +08:00
|
|
|
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
|
2013-04-16 19:38:42 +08:00
|
|
|
struct ieee80211_supported_band *sband = wiphy->bands[i];
|
|
|
|
int j;
|
|
|
|
|
2010-01-06 19:09:08 +08:00
|
|
|
sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
|
2013-12-05 17:02:15 +08:00
|
|
|
memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs,
|
|
|
|
sizeof(mask->control[i].ht_mcs));
|
2013-04-16 19:38:42 +08:00
|
|
|
|
|
|
|
sdata->rc_has_mcs_mask[i] = false;
|
|
|
|
if (!sband)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++)
|
|
|
|
if (~sdata->rc_rateidx_mcs_mask[i][j]) {
|
|
|
|
sdata->rc_has_mcs_mask[i] = true;
|
|
|
|
break;
|
|
|
|
}
|
2012-01-29 00:25:33 +08:00
|
|
|
}
|
2009-07-02 03:26:59 +08:00
|
|
|
|
2010-01-06 19:09:08 +08:00
|
|
|
return 0;
|
2009-07-02 03:26:59 +08:00
|
|
|
}
|
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
static int ieee80211_start_roc_work(struct ieee80211_local *local,
|
|
|
|
struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_channel *channel,
|
|
|
|
unsigned int duration, u64 *cookie,
|
2013-02-12 15:34:13 +08:00
|
|
|
struct sk_buff *txskb,
|
|
|
|
enum ieee80211_roc_type type)
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_roc_work *roc, *tmp;
|
|
|
|
bool queued = false;
|
2010-12-19 00:20:47 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
lockdep_assert_held(&local->mtx);
|
|
|
|
|
2012-07-26 20:55:08 +08:00
|
|
|
if (local->use_chanctx && !local->ops->remain_on_channel)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
roc = kzalloc(sizeof(*roc), GFP_KERNEL);
|
|
|
|
if (!roc)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
roc->chan = channel;
|
|
|
|
roc->duration = duration;
|
|
|
|
roc->req_duration = duration;
|
|
|
|
roc->frame = txskb;
|
2013-02-12 15:34:13 +08:00
|
|
|
roc->type = type;
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
roc->mgmt_tx_cookie = (unsigned long)txskb;
|
|
|
|
roc->sdata = sdata;
|
|
|
|
INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
|
|
|
|
INIT_LIST_HEAD(&roc->dependents);
|
|
|
|
|
|
|
|
/* if there's one pending or we're scanning, queue this one */
|
2013-02-09 01:16:20 +08:00
|
|
|
if (!list_empty(&local->roc_list) ||
|
|
|
|
local->scanning || local->radar_detect_enabled)
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
goto out_check_combine;
|
|
|
|
|
|
|
|
/* if not HW assist, just queue & schedule work */
|
|
|
|
if (!local->ops->remain_on_channel) {
|
|
|
|
ieee80211_queue_delayed_work(&local->hw, &roc->work, 0);
|
|
|
|
goto out_queue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* otherwise actually kick it off here (for error handling) */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the duration is zero, then the driver
|
|
|
|
* wouldn't actually do anything. Set it to
|
|
|
|
* 10 for now.
|
|
|
|
*
|
|
|
|
* TODO: cancel the off-channel operation
|
|
|
|
* when we get the SKB's TX status and
|
|
|
|
* the wait time was zero before.
|
|
|
|
*/
|
|
|
|
if (!duration)
|
|
|
|
duration = 10;
|
|
|
|
|
2013-02-12 15:34:13 +08:00
|
|
|
ret = drv_remain_on_channel(local, sdata, channel, duration, type);
|
2010-12-19 00:20:47 +08:00
|
|
|
if (ret) {
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
kfree(roc);
|
|
|
|
return ret;
|
2010-12-19 00:20:47 +08:00
|
|
|
}
|
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
roc->started = true;
|
|
|
|
goto out_queue;
|
|
|
|
|
|
|
|
out_check_combine:
|
|
|
|
list_for_each_entry(tmp, &local->roc_list, list) {
|
2012-11-09 01:31:02 +08:00
|
|
|
if (tmp->chan != channel || tmp->sdata != sdata)
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extend this ROC if possible:
|
|
|
|
*
|
|
|
|
* If it hasn't started yet, just increase the duration
|
|
|
|
* and add the new one to the list of dependents.
|
2013-02-12 15:34:13 +08:00
|
|
|
* If the type of the new ROC has higher priority, modify the
|
|
|
|
* type of the previous one to match that of the new one.
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
*/
|
|
|
|
if (!tmp->started) {
|
|
|
|
list_add_tail(&roc->list, &tmp->dependents);
|
|
|
|
tmp->duration = max(tmp->duration, roc->duration);
|
2013-02-12 15:34:13 +08:00
|
|
|
tmp->type = max(tmp->type, roc->type);
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
queued = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If it has already started, it's more difficult ... */
|
|
|
|
if (local->ops->remain_on_channel) {
|
|
|
|
unsigned long j = jiffies;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In the offloaded ROC case, if it hasn't begun, add
|
|
|
|
* this new one to the dependent list to be handled
|
2013-02-12 15:34:13 +08:00
|
|
|
* when the master one begins. If it has begun,
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
* check that there's still a minimum time left and
|
|
|
|
* if so, start this one, transmitting the frame, but
|
2013-02-12 15:34:13 +08:00
|
|
|
* add it to the list directly after this one with
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
* a reduced time so we'll ask the driver to execute
|
|
|
|
* it right after finishing the previous one, in the
|
|
|
|
* hope that it'll also be executed right afterwards,
|
|
|
|
* effectively extending the old one.
|
|
|
|
* If there's no minimum time left, just add it to the
|
|
|
|
* normal list.
|
2013-02-12 15:34:13 +08:00
|
|
|
* TODO: the ROC type is ignored here, assuming that it
|
|
|
|
* is better to immediately use the current ROC.
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
*/
|
|
|
|
if (!tmp->hw_begun) {
|
|
|
|
list_add_tail(&roc->list, &tmp->dependents);
|
|
|
|
queued = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (time_before(j + IEEE80211_ROC_MIN_LEFT,
|
|
|
|
tmp->hw_start_time +
|
|
|
|
msecs_to_jiffies(tmp->duration))) {
|
|
|
|
int new_dur;
|
|
|
|
|
|
|
|
ieee80211_handle_roc_started(roc);
|
|
|
|
|
|
|
|
new_dur = roc->duration -
|
|
|
|
jiffies_to_msecs(tmp->hw_start_time +
|
|
|
|
msecs_to_jiffies(
|
|
|
|
tmp->duration) -
|
|
|
|
j);
|
|
|
|
|
|
|
|
if (new_dur > 0) {
|
|
|
|
/* add right after tmp */
|
|
|
|
list_add(&roc->list, &tmp->list);
|
|
|
|
} else {
|
|
|
|
list_add_tail(&roc->list,
|
|
|
|
&tmp->dependents);
|
|
|
|
}
|
|
|
|
queued = true;
|
|
|
|
}
|
|
|
|
} else if (del_timer_sync(&tmp->work.timer)) {
|
|
|
|
unsigned long new_end;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In the software ROC case, cancel the timer, if
|
|
|
|
* that fails then the finish work is already
|
|
|
|
* queued/pending and thus we queue the new ROC
|
|
|
|
* normally, if that succeeds then we can extend
|
|
|
|
* the timer duration and TX the frame (if any.)
|
|
|
|
*/
|
|
|
|
|
|
|
|
list_add_tail(&roc->list, &tmp->dependents);
|
|
|
|
queued = true;
|
|
|
|
|
|
|
|
new_end = jiffies + msecs_to_jiffies(roc->duration);
|
|
|
|
|
|
|
|
/* ok, it was started & we canceled timer */
|
|
|
|
if (time_after(new_end, tmp->work.timer.expires))
|
|
|
|
mod_timer(&tmp->work.timer, new_end);
|
|
|
|
else
|
|
|
|
add_timer(&tmp->work.timer);
|
|
|
|
|
|
|
|
ieee80211_handle_roc_started(roc);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
out_queue:
|
|
|
|
if (!queued)
|
|
|
|
list_add_tail(&roc->list, &local->roc_list);
|
|
|
|
|
|
|
|
/*
|
2012-10-26 22:13:06 +08:00
|
|
|
* cookie is either the roc cookie (for normal roc)
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
* or the SKB (for mgmt TX)
|
|
|
|
*/
|
2012-10-26 22:13:06 +08:00
|
|
|
if (!txskb) {
|
|
|
|
/* local->mtx protects this */
|
|
|
|
local->roc_cookie_counter++;
|
|
|
|
roc->cookie = local->roc_cookie_counter;
|
|
|
|
/* wow, you wrapped 64 bits ... more likely a bug */
|
|
|
|
if (WARN_ON(roc->cookie == 0)) {
|
|
|
|
roc->cookie = 1;
|
|
|
|
local->roc_cookie_counter++;
|
|
|
|
}
|
|
|
|
*cookie = roc->cookie;
|
|
|
|
} else {
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
*cookie = (unsigned long)txskb;
|
2012-10-26 22:13:06 +08:00
|
|
|
}
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
|
|
|
|
return 0;
|
2010-12-19 00:20:47 +08:00
|
|
|
}
|
|
|
|
|
2009-12-23 20:15:42 +08:00
|
|
|
static int ieee80211_remain_on_channel(struct wiphy *wiphy,
|
2012-06-15 21:30:18 +08:00
|
|
|
struct wireless_dev *wdev,
|
2009-12-23 20:15:42 +08:00
|
|
|
struct ieee80211_channel *chan,
|
|
|
|
unsigned int duration,
|
|
|
|
u64 *cookie)
|
|
|
|
{
|
2012-06-15 21:30:18 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
2010-12-19 00:20:47 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
int ret;
|
2010-12-19 00:20:47 +08:00
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
mutex_lock(&local->mtx);
|
2012-11-09 01:31:02 +08:00
|
|
|
ret = ieee80211_start_roc_work(local, sdata, chan,
|
2013-02-12 15:34:13 +08:00
|
|
|
duration, cookie, NULL,
|
|
|
|
IEEE80211_ROC_TYPE_NORMAL);
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
mutex_unlock(&local->mtx);
|
2009-12-23 20:15:42 +08:00
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
return ret;
|
2009-12-23 20:15:42 +08:00
|
|
|
}
|
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
static int ieee80211_cancel_roc(struct ieee80211_local *local,
|
|
|
|
u64 cookie, bool mgmt_tx)
|
2010-12-19 00:20:47 +08:00
|
|
|
{
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
struct ieee80211_roc_work *roc, *tmp, *found = NULL;
|
2010-12-19 00:20:47 +08:00
|
|
|
int ret;
|
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
mutex_lock(&local->mtx);
|
|
|
|
list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
|
2012-06-11 23:09:41 +08:00
|
|
|
struct ieee80211_roc_work *dep, *tmp2;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) {
|
2012-10-26 22:13:06 +08:00
|
|
|
if (!mgmt_tx && dep->cookie != cookie)
|
2012-06-11 23:09:41 +08:00
|
|
|
continue;
|
|
|
|
else if (mgmt_tx && dep->mgmt_tx_cookie != cookie)
|
|
|
|
continue;
|
|
|
|
/* found dependent item -- just remove it */
|
|
|
|
list_del(&dep->list);
|
|
|
|
mutex_unlock(&local->mtx);
|
|
|
|
|
2013-03-25 18:51:14 +08:00
|
|
|
ieee80211_roc_notify_destroy(dep, true);
|
2012-06-11 23:09:41 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-26 22:13:06 +08:00
|
|
|
if (!mgmt_tx && roc->cookie != cookie)
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
continue;
|
|
|
|
else if (mgmt_tx && roc->mgmt_tx_cookie != cookie)
|
|
|
|
continue;
|
2010-12-19 00:20:47 +08:00
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
found = roc;
|
|
|
|
break;
|
|
|
|
}
|
2010-12-19 00:20:47 +08:00
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
if (!found) {
|
|
|
|
mutex_unlock(&local->mtx);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2010-12-19 00:20:47 +08:00
|
|
|
|
2012-06-11 23:09:41 +08:00
|
|
|
/*
|
|
|
|
* We found the item to cancel, so do that. Note that it
|
|
|
|
* may have dependents, which we also cancel (and send
|
|
|
|
* the expired signal for.) Not doing so would be quite
|
|
|
|
* tricky here, but we may need to fix it later.
|
|
|
|
*/
|
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
if (local->ops->remain_on_channel) {
|
|
|
|
if (found->started) {
|
|
|
|
ret = drv_cancel_remain_on_channel(local);
|
|
|
|
if (WARN_ON_ONCE(ret)) {
|
|
|
|
mutex_unlock(&local->mtx);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
2010-12-19 00:20:47 +08:00
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
list_del(&found->list);
|
2010-12-19 00:20:47 +08:00
|
|
|
|
2012-06-21 02:11:33 +08:00
|
|
|
if (found->started)
|
|
|
|
ieee80211_start_next_roc(local);
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
mutex_unlock(&local->mtx);
|
2010-12-19 00:20:47 +08:00
|
|
|
|
2013-03-25 18:51:14 +08:00
|
|
|
ieee80211_roc_notify_destroy(found, true);
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
} else {
|
|
|
|
/* work may be pending so use it all the time */
|
|
|
|
found->abort = true;
|
|
|
|
ieee80211_queue_delayed_work(&local->hw, &found->work, 0);
|
2010-12-19 00:20:47 +08:00
|
|
|
|
|
|
|
mutex_unlock(&local->mtx);
|
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
/* work will clean up etc */
|
|
|
|
flush_delayed_work(&found->work);
|
2013-03-25 18:51:14 +08:00
|
|
|
WARN_ON(!found->to_be_freed);
|
|
|
|
kfree(found);
|
2010-12-19 00:20:47 +08:00
|
|
|
}
|
2009-12-23 20:15:42 +08:00
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
return 0;
|
2009-12-23 20:15:42 +08:00
|
|
|
}
|
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
|
2012-06-15 21:30:18 +08:00
|
|
|
struct wireless_dev *wdev,
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
u64 cookie)
|
2010-11-25 17:02:30 +08:00
|
|
|
{
|
2012-06-15 21:30:18 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2010-11-25 17:02:30 +08:00
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
return ieee80211_cancel_roc(local, cookie, false);
|
2010-11-25 17:02:30 +08:00
|
|
|
}
|
|
|
|
|
2013-02-09 01:16:20 +08:00
|
|
|
static int ieee80211_start_radar_detection(struct wiphy *wiphy,
|
|
|
|
struct net_device *dev,
|
|
|
|
struct cfg80211_chan_def *chandef)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
unsigned long timeout;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!list_empty(&local->roc_list) || local->scanning)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
/* whatever, but channel contexts should not complain about that one */
|
|
|
|
sdata->smps_mode = IEEE80211_SMPS_OFF;
|
|
|
|
sdata->needed_rx_chains = local->rx_chains;
|
|
|
|
sdata->radar_required = true;
|
|
|
|
|
|
|
|
mutex_lock(&local->iflist_mtx);
|
|
|
|
err = ieee80211_vif_use_channel(sdata, chandef,
|
|
|
|
IEEE80211_CHANCTX_SHARED);
|
|
|
|
mutex_unlock(&local->iflist_mtx);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
|
|
|
|
ieee80211_queue_delayed_work(&sdata->local->hw,
|
|
|
|
&sdata->dfs_cac_timer_work, timeout);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-11 22:09:06 +08:00
|
|
|
static struct cfg80211_beacon_data *
|
|
|
|
cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
|
|
|
|
{
|
|
|
|
struct cfg80211_beacon_data *new_beacon;
|
|
|
|
u8 *pos;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
|
|
|
|
beacon->proberesp_ies_len + beacon->assocresp_ies_len +
|
|
|
|
beacon->probe_resp_len;
|
|
|
|
|
|
|
|
new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
|
|
|
|
if (!new_beacon)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pos = (u8 *)(new_beacon + 1);
|
|
|
|
if (beacon->head_len) {
|
|
|
|
new_beacon->head_len = beacon->head_len;
|
|
|
|
new_beacon->head = pos;
|
|
|
|
memcpy(pos, beacon->head, beacon->head_len);
|
|
|
|
pos += beacon->head_len;
|
|
|
|
}
|
|
|
|
if (beacon->tail_len) {
|
|
|
|
new_beacon->tail_len = beacon->tail_len;
|
|
|
|
new_beacon->tail = pos;
|
|
|
|
memcpy(pos, beacon->tail, beacon->tail_len);
|
|
|
|
pos += beacon->tail_len;
|
|
|
|
}
|
|
|
|
if (beacon->beacon_ies_len) {
|
|
|
|
new_beacon->beacon_ies_len = beacon->beacon_ies_len;
|
|
|
|
new_beacon->beacon_ies = pos;
|
|
|
|
memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len);
|
|
|
|
pos += beacon->beacon_ies_len;
|
|
|
|
}
|
|
|
|
if (beacon->proberesp_ies_len) {
|
|
|
|
new_beacon->proberesp_ies_len = beacon->proberesp_ies_len;
|
|
|
|
new_beacon->proberesp_ies = pos;
|
|
|
|
memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len);
|
|
|
|
pos += beacon->proberesp_ies_len;
|
|
|
|
}
|
|
|
|
if (beacon->assocresp_ies_len) {
|
|
|
|
new_beacon->assocresp_ies_len = beacon->assocresp_ies_len;
|
|
|
|
new_beacon->assocresp_ies = pos;
|
|
|
|
memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len);
|
|
|
|
pos += beacon->assocresp_ies_len;
|
|
|
|
}
|
|
|
|
if (beacon->probe_resp_len) {
|
|
|
|
new_beacon->probe_resp_len = beacon->probe_resp_len;
|
|
|
|
beacon->probe_resp = pos;
|
|
|
|
memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
|
|
|
|
pos += beacon->probe_resp_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new_beacon;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ieee80211_csa_finalize_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
container_of(work, struct ieee80211_sub_if_data,
|
|
|
|
csa_finalize_work);
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
2013-10-19 05:57:00 +08:00
|
|
|
int err, changed = 0;
|
2013-07-11 22:09:06 +08:00
|
|
|
|
2013-11-22 01:19:51 +08:00
|
|
|
sdata_lock(sdata);
|
|
|
|
/* AP might have been stopped while waiting for the lock. */
|
|
|
|
if (!sdata->vif.csa_active)
|
|
|
|
goto unlock;
|
|
|
|
|
2013-07-11 22:09:06 +08:00
|
|
|
if (!ieee80211_sdata_running(sdata))
|
2013-11-22 01:19:51 +08:00
|
|
|
goto unlock;
|
2013-07-11 22:09:06 +08:00
|
|
|
|
|
|
|
sdata->radar_required = sdata->csa_radar_required;
|
2013-11-12 02:34:54 +08:00
|
|
|
err = ieee80211_vif_change_channel(sdata, &changed);
|
2013-07-11 22:09:06 +08:00
|
|
|
if (WARN_ON(err < 0))
|
2013-11-22 01:19:51 +08:00
|
|
|
goto unlock;
|
2013-07-11 22:09:06 +08:00
|
|
|
|
2013-09-01 22:15:51 +08:00
|
|
|
if (!local->use_chanctx) {
|
2013-11-12 02:34:54 +08:00
|
|
|
local->_oper_chandef = sdata->csa_chandef;
|
2013-09-01 22:15:51 +08:00
|
|
|
ieee80211_hw_config(local, 0);
|
|
|
|
}
|
|
|
|
|
2013-08-28 19:41:31 +08:00
|
|
|
ieee80211_bss_info_change_notify(sdata, changed);
|
2013-07-11 22:09:06 +08:00
|
|
|
|
2013-11-22 01:19:51 +08:00
|
|
|
sdata->vif.csa_active = false;
|
2013-08-28 19:41:31 +08:00
|
|
|
switch (sdata->vif.type) {
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
|
|
|
|
if (err < 0)
|
2013-11-22 01:19:51 +08:00
|
|
|
goto unlock;
|
|
|
|
|
2013-08-28 19:41:31 +08:00
|
|
|
changed |= err;
|
|
|
|
kfree(sdata->u.ap.next_beacon);
|
|
|
|
sdata->u.ap.next_beacon = NULL;
|
|
|
|
|
|
|
|
ieee80211_bss_info_change_notify(sdata, err);
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
|
ieee80211_ibss_finish_csa(sdata);
|
|
|
|
break;
|
2013-10-18 06:55:02 +08:00
|
|
|
#ifdef CONFIG_MAC80211_MESH
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
|
|
err = ieee80211_mesh_finish_csa(sdata);
|
|
|
|
if (err < 0)
|
2013-11-22 01:19:51 +08:00
|
|
|
goto unlock;
|
2013-10-18 06:55:02 +08:00
|
|
|
break;
|
|
|
|
#endif
|
2013-08-28 19:41:31 +08:00
|
|
|
default:
|
|
|
|
WARN_ON(1);
|
2013-11-22 01:19:51 +08:00
|
|
|
goto unlock;
|
2013-08-28 19:41:31 +08:00
|
|
|
}
|
2013-07-11 22:09:06 +08:00
|
|
|
|
|
|
|
ieee80211_wake_queues_by_reason(&sdata->local->hw,
|
|
|
|
IEEE80211_MAX_QUEUE_MAP,
|
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
|
|
|
2013-11-12 02:34:54 +08:00
|
|
|
cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
|
2013-11-22 01:19:51 +08:00
|
|
|
|
|
|
|
unlock:
|
|
|
|
sdata_unlock(sdata);
|
2013-07-11 22:09:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
struct cfg80211_csa_settings *params)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
|
|
|
struct ieee80211_chanctx *chanctx;
|
2013-10-15 10:08:28 +08:00
|
|
|
struct ieee80211_if_mesh __maybe_unused *ifmsh;
|
2013-07-11 22:09:06 +08:00
|
|
|
int err, num_chanctx;
|
|
|
|
|
2013-11-22 01:19:50 +08:00
|
|
|
lockdep_assert_held(&sdata->wdev.mtx);
|
|
|
|
|
2013-07-11 22:09:06 +08:00
|
|
|
if (!list_empty(&local->roc_list) || local->scanning)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
if (sdata->wdev.cac_started)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
if (cfg80211_chandef_identical(¶ms->chandef,
|
|
|
|
&sdata->vif.bss_conf.chandef))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
|
|
|
if (!chanctx_conf) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* don't handle for multi-VIF cases */
|
|
|
|
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
|
|
|
|
if (chanctx->refcount > 1) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
num_chanctx = 0;
|
|
|
|
list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
|
|
|
|
num_chanctx++;
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
if (num_chanctx > 1)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
/* don't allow another channel switch if one is already active. */
|
|
|
|
if (sdata->vif.csa_active)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
switch (sdata->vif.type) {
|
|
|
|
case NL80211_IFTYPE_AP:
|
2013-08-28 19:41:31 +08:00
|
|
|
sdata->csa_counter_offset_beacon =
|
|
|
|
params->counter_offset_beacon;
|
|
|
|
sdata->csa_counter_offset_presp = params->counter_offset_presp;
|
|
|
|
sdata->u.ap.next_beacon =
|
|
|
|
cfg80211_beacon_dup(¶ms->beacon_after);
|
|
|
|
if (!sdata->u.ap.next_beacon)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa);
|
|
|
|
if (err < 0) {
|
|
|
|
kfree(sdata->u.ap.next_beacon);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
|
if (!sdata->vif.bss_conf.ibss_joined)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (params->chandef.width != sdata->u.ibss.chandef.width)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
switch (params->chandef.width) {
|
|
|
|
case NL80211_CHAN_WIDTH_40:
|
|
|
|
if (cfg80211_get_chandef_type(¶ms->chandef) !=
|
|
|
|
cfg80211_get_chandef_type(&sdata->u.ibss.chandef))
|
|
|
|
return -EINVAL;
|
|
|
|
case NL80211_CHAN_WIDTH_5:
|
|
|
|
case NL80211_CHAN_WIDTH_10:
|
|
|
|
case NL80211_CHAN_WIDTH_20_NOHT:
|
|
|
|
case NL80211_CHAN_WIDTH_20:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* changes into another band are not supported */
|
|
|
|
if (sdata->u.ibss.chandef.chan->band !=
|
|
|
|
params->chandef.chan->band)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
err = ieee80211_ibss_csa_beacon(sdata, params);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2013-07-11 22:09:06 +08:00
|
|
|
break;
|
2013-10-15 10:08:28 +08:00
|
|
|
#ifdef CONFIG_MAC80211_MESH
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
|
|
ifmsh = &sdata->u.mesh;
|
|
|
|
|
|
|
|
if (!ifmsh->mesh_id)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* changes into another band are not supported */
|
|
|
|
if (sdata->vif.bss_conf.chandef.chan->band !=
|
|
|
|
params->chandef.chan->band)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-11-08 15:09:43 +08:00
|
|
|
ifmsh->chsw_init = true;
|
|
|
|
if (!ifmsh->pre_value)
|
|
|
|
ifmsh->pre_value = 1;
|
|
|
|
else
|
|
|
|
ifmsh->pre_value++;
|
|
|
|
|
2013-10-18 06:55:02 +08:00
|
|
|
err = ieee80211_mesh_csa_beacon(sdata, params, true);
|
2013-11-08 15:09:43 +08:00
|
|
|
if (err < 0) {
|
|
|
|
ifmsh->chsw_init = false;
|
2013-10-15 10:08:28 +08:00
|
|
|
return err;
|
2013-11-08 15:09:43 +08:00
|
|
|
}
|
2013-10-15 10:08:28 +08:00
|
|
|
break;
|
|
|
|
#endif
|
2013-07-11 22:09:06 +08:00
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
sdata->csa_radar_required = params->radar_required;
|
|
|
|
|
|
|
|
if (params->block_tx)
|
|
|
|
ieee80211_stop_queues_by_reason(&local->hw,
|
|
|
|
IEEE80211_MAX_QUEUE_MAP,
|
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
|
|
|
2013-11-12 02:34:54 +08:00
|
|
|
sdata->csa_chandef = params->chandef;
|
2013-07-11 22:09:06 +08:00
|
|
|
sdata->vif.csa_active = true;
|
|
|
|
|
|
|
|
ieee80211_bss_info_change_notify(sdata, err);
|
|
|
|
drv_channel_switch_beacon(sdata, ¶ms->chandef);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-15 21:30:18 +08:00
|
|
|
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
2013-11-19 01:06:49 +08:00
|
|
|
struct cfg80211_mgmt_tx_params *params,
|
|
|
|
u64 *cookie)
|
2010-02-15 18:53:10 +08:00
|
|
|
{
|
2012-06-15 21:30:18 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
2010-06-09 23:20:33 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct sta_info *sta;
|
2013-11-19 01:06:49 +08:00
|
|
|
const struct ieee80211_mgmt *mgmt = (void *)params->buf;
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
bool need_offchan = false;
|
2011-11-04 18:18:21 +08:00
|
|
|
u32 flags;
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
int ret;
|
2010-11-25 17:02:29 +08:00
|
|
|
|
2013-11-19 01:06:49 +08:00
|
|
|
if (params->dont_wait_for_ack)
|
2011-11-04 18:18:21 +08:00
|
|
|
flags = IEEE80211_TX_CTL_NO_ACK;
|
|
|
|
else
|
|
|
|
flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
|
|
|
|
IEEE80211_TX_CTL_REQ_TX_STATUS;
|
|
|
|
|
2013-11-19 01:06:49 +08:00
|
|
|
if (params->no_cck)
|
2011-09-25 17:23:31 +08:00
|
|
|
flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
|
|
|
|
|
2010-06-09 23:20:33 +08:00
|
|
|
switch (sdata->vif.type) {
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
if (!sdata->vif.bss_conf.ibss_joined)
|
|
|
|
need_offchan = true;
|
|
|
|
/* fall through */
|
|
|
|
#ifdef CONFIG_MAC80211_MESH
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
|
|
if (ieee80211_vif_is_mesh(&sdata->vif) &&
|
|
|
|
!sdata->u.mesh.mesh_id_len)
|
|
|
|
need_offchan = true;
|
|
|
|
/* fall through */
|
|
|
|
#endif
|
2010-10-01 03:06:09 +08:00
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
|
|
|
!ieee80211_vif_is_mesh(&sdata->vif) &&
|
|
|
|
!rcu_access_pointer(sdata->bss->beacon))
|
|
|
|
need_offchan = true;
|
2010-10-01 03:06:09 +08:00
|
|
|
if (!ieee80211_is_action(mgmt->frame_control) ||
|
2013-06-21 14:50:58 +08:00
|
|
|
mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
|
2013-08-28 19:41:31 +08:00
|
|
|
mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED ||
|
|
|
|
mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)
|
2010-06-09 23:20:33 +08:00
|
|
|
break;
|
|
|
|
rcu_read_lock();
|
|
|
|
sta = sta_info_get(sdata, mgmt->da);
|
|
|
|
rcu_read_unlock();
|
|
|
|
if (!sta)
|
|
|
|
return -ENOLINK;
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_STATION:
|
2010-10-01 03:06:09 +08:00
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
if (!sdata->u.mgd.associated)
|
|
|
|
need_offchan = true;
|
2010-06-09 23:20:33 +08:00
|
|
|
break;
|
2012-06-19 02:07:15 +08:00
|
|
|
case NL80211_IFTYPE_P2P_DEVICE:
|
|
|
|
need_offchan = true;
|
|
|
|
break;
|
2010-06-09 23:20:33 +08:00
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2013-06-11 20:20:00 +08:00
|
|
|
/* configurations requiring offchan cannot work if no channel has been
|
|
|
|
* specified
|
|
|
|
*/
|
2013-11-19 01:06:49 +08:00
|
|
|
if (need_offchan && !params->chan)
|
2013-06-11 20:20:00 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
mutex_lock(&local->mtx);
|
|
|
|
|
|
|
|
/* Check if the operating channel is the requested channel */
|
|
|
|
if (!need_offchan) {
|
2012-07-26 23:24:39 +08:00
|
|
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
|
|
|
|
2013-06-11 20:20:00 +08:00
|
|
|
if (chanctx_conf) {
|
2013-11-19 01:06:49 +08:00
|
|
|
need_offchan = params->chan &&
|
|
|
|
(params->chan !=
|
|
|
|
chanctx_conf->def.chan);
|
|
|
|
} else if (!params->chan) {
|
2013-06-11 20:20:00 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
rcu_read_unlock();
|
|
|
|
goto out_unlock;
|
|
|
|
} else {
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
need_offchan = true;
|
2013-06-11 20:20:00 +08:00
|
|
|
}
|
2012-07-26 23:24:39 +08:00
|
|
|
rcu_read_unlock();
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
}
|
|
|
|
|
2013-11-19 01:06:49 +08:00
|
|
|
if (need_offchan && !params->offchan) {
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
ret = -EBUSY;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2013-11-19 01:06:49 +08:00
|
|
|
skb = dev_alloc_skb(local->hw.extra_tx_headroom + params->len);
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
if (!skb) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
2010-06-09 23:20:33 +08:00
|
|
|
skb_reserve(skb, local->hw.extra_tx_headroom);
|
|
|
|
|
2013-11-19 01:06:49 +08:00
|
|
|
memcpy(skb_put(skb, params->len), params->buf, params->len);
|
2010-06-09 23:20:33 +08:00
|
|
|
|
|
|
|
IEEE80211_SKB_CB(skb)->flags = flags;
|
|
|
|
|
|
|
|
skb->dev = sdata->dev;
|
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
if (!need_offchan) {
|
2012-07-17 00:36:52 +08:00
|
|
|
*cookie = (unsigned long) skb;
|
2010-11-25 17:02:30 +08:00
|
|
|
ieee80211_tx_skb(sdata, skb);
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
ret = 0;
|
|
|
|
goto out_unlock;
|
2010-11-25 17:02:30 +08:00
|
|
|
}
|
|
|
|
|
2013-02-12 01:21:07 +08:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN |
|
|
|
|
IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
|
|
|
|
IEEE80211_SKB_CB(skb)->hw_queue =
|
|
|
|
local->hw.offchannel_tx_hw_queue;
|
2010-11-25 17:02:30 +08:00
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
/* This will handle all kinds of coalescing and immediate TX */
|
2013-11-19 01:06:49 +08:00
|
|
|
ret = ieee80211_start_roc_work(local, sdata, params->chan,
|
|
|
|
params->wait, cookie, skb,
|
2013-02-12 15:34:13 +08:00
|
|
|
IEEE80211_ROC_TYPE_MGMT_TX);
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
if (ret)
|
|
|
|
kfree_skb(skb);
|
|
|
|
out_unlock:
|
|
|
|
mutex_unlock(&local->mtx);
|
|
|
|
return ret;
|
2010-02-15 18:53:10 +08:00
|
|
|
}
|
|
|
|
|
2010-11-25 17:02:30 +08:00
|
|
|
static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
|
2012-06-15 21:30:18 +08:00
|
|
|
struct wireless_dev *wdev,
|
2010-11-25 17:02:30 +08:00
|
|
|
u64 cookie)
|
|
|
|
{
|
2012-06-15 21:30:18 +08:00
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
2010-11-25 17:02:30 +08:00
|
|
|
|
mac80211: unify SW/offload remain-on-channel
Redesign all the off-channel code, getting rid of
the generic off-channel work concept, replacing
it with a simple remain-on-channel list.
This fixes a number of small issues with the ROC
implementation:
* offloaded remain-on-channel couldn't be queued,
now we can queue it as well, if needed
* in iwlwifi (the only user) offloaded ROC is
mutually exclusive with scanning, use the new
queue to handle that case -- I expect that it
will later depend on a HW flag
The bigger issue though is that there's a bad bug
in the current implementation: if we get a mgmt
TX request while HW roc is active, and this new
request has a wait time, we actually schedule a
software ROC instead since we can't guarantee the
existing offloaded ROC will still be that long.
To fix this, the queuing mechanism was needed.
The queuing mechanism for offloaded ROC isn't yet
optimal, ideally we should add API to have the HW
extend the ROC if needed. We could add that later
but for now use a software implementation.
Overall, this unifies the behaviour between the
offloaded and software-implemented case as much
as possible.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-06-05 20:28:42 +08:00
|
|
|
return ieee80211_cancel_roc(local, cookie, true);
|
2010-11-25 17:02:30 +08:00
|
|
|
}
|
|
|
|
|
2010-10-13 18:06:24 +08:00
|
|
|
static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
|
2012-06-15 21:30:18 +08:00
|
|
|
struct wireless_dev *wdev,
|
2010-10-13 18:06:24 +08:00
|
|
|
u16 frame_type, bool reg)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
|
|
|
2012-06-20 23:51:14 +08:00
|
|
|
switch (frame_type) {
|
|
|
|
case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ:
|
|
|
|
if (reg)
|
|
|
|
local->probe_req_reg++;
|
|
|
|
else
|
|
|
|
local->probe_req_reg--;
|
2010-10-13 18:06:24 +08:00
|
|
|
|
2012-10-31 22:50:34 +08:00
|
|
|
if (!local->open_count)
|
|
|
|
break;
|
|
|
|
|
2012-06-20 23:51:14 +08:00
|
|
|
ieee80211_queue_work(&local->hw, &local->reconfig_filter);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2010-10-13 18:06:24 +08:00
|
|
|
}
|
|
|
|
|
2010-11-10 11:50:56 +08:00
|
|
|
static int ieee80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
|
|
|
|
|
|
if (local->started)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return drv_set_antenna(local, tx_ant, rx_ant);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
|
|
|
|
|
|
return drv_get_antenna(local, tx_ant, rx_ant);
|
|
|
|
}
|
|
|
|
|
2011-03-08 05:19:18 +08:00
|
|
|
static int ieee80211_set_ringparam(struct wiphy *wiphy, u32 tx, u32 rx)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
|
|
|
|
|
|
return drv_set_ringparam(local, tx, rx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_get_ringparam(struct wiphy *wiphy,
|
|
|
|
u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
|
|
|
|
|
|
drv_get_ringparam(local, tx, tx_max, rx, rx_max);
|
|
|
|
}
|
|
|
|
|
2011-07-05 22:35:41 +08:00
|
|
|
static int ieee80211_set_rekey_data(struct wiphy *wiphy,
|
|
|
|
struct net_device *dev,
|
|
|
|
struct cfg80211_gtk_rekey_data *data)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
|
|
|
if (!local->ops->set_rekey_data)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
drv_set_rekey_data(local, sdata, data);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-09-28 19:12:52 +08:00
|
|
|
static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
u8 *pos = (void *)skb_put(skb, 7);
|
|
|
|
|
|
|
|
*pos++ = WLAN_EID_EXT_CAPABILITY;
|
|
|
|
*pos++ = 5; /* len */
|
|
|
|
*pos++ = 0x0;
|
|
|
|
*pos++ = 0x0;
|
|
|
|
*pos++ = 0x0;
|
|
|
|
*pos++ = 0x0;
|
|
|
|
*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
u16 capab;
|
|
|
|
|
|
|
|
capab = 0;
|
2012-07-26 23:24:39 +08:00
|
|
|
if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
|
2011-09-28 19:12:52 +08:00
|
|
|
return capab;
|
|
|
|
|
|
|
|
if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
|
|
|
|
capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
|
|
|
|
if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
|
|
|
|
capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
|
|
|
|
|
|
|
|
return capab;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr,
|
|
|
|
u8 *peer, u8 *bssid)
|
|
|
|
{
|
|
|
|
struct ieee80211_tdls_lnkie *lnkid;
|
|
|
|
|
|
|
|
lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
|
|
|
|
|
|
|
|
lnkid->ie_type = WLAN_EID_LINK_ID;
|
|
|
|
lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
|
|
|
|
|
|
|
|
memcpy(lnkid->bssid, bssid, ETH_ALEN);
|
|
|
|
memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
|
|
|
|
memcpy(lnkid->resp_sta, peer, ETH_ALEN);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
u8 *peer, u8 action_code, u8 dialog_token,
|
|
|
|
u16 status_code, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
2012-07-26 23:24:39 +08:00
|
|
|
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
2011-09-28 19:12:52 +08:00
|
|
|
struct ieee80211_tdls_data *tf;
|
|
|
|
|
|
|
|
tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
|
|
|
|
|
|
|
|
memcpy(tf->da, peer, ETH_ALEN);
|
|
|
|
memcpy(tf->sa, sdata->vif.addr, ETH_ALEN);
|
|
|
|
tf->ether_type = cpu_to_be16(ETH_P_TDLS);
|
|
|
|
tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
|
|
|
|
|
|
|
|
switch (action_code) {
|
|
|
|
case WLAN_TDLS_SETUP_REQUEST:
|
|
|
|
tf->category = WLAN_CATEGORY_TDLS;
|
|
|
|
tf->action_code = WLAN_TDLS_SETUP_REQUEST;
|
|
|
|
|
|
|
|
skb_put(skb, sizeof(tf->u.setup_req));
|
|
|
|
tf->u.setup_req.dialog_token = dialog_token;
|
|
|
|
tf->u.setup_req.capability =
|
|
|
|
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
|
|
|
|
|
2012-07-26 23:24:39 +08:00
|
|
|
ieee80211_add_srates_ie(sdata, skb, false, band);
|
|
|
|
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
|
2011-09-28 19:12:52 +08:00
|
|
|
ieee80211_tdls_add_ext_capab(skb);
|
|
|
|
break;
|
|
|
|
case WLAN_TDLS_SETUP_RESPONSE:
|
|
|
|
tf->category = WLAN_CATEGORY_TDLS;
|
|
|
|
tf->action_code = WLAN_TDLS_SETUP_RESPONSE;
|
|
|
|
|
|
|
|
skb_put(skb, sizeof(tf->u.setup_resp));
|
|
|
|
tf->u.setup_resp.status_code = cpu_to_le16(status_code);
|
|
|
|
tf->u.setup_resp.dialog_token = dialog_token;
|
|
|
|
tf->u.setup_resp.capability =
|
|
|
|
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
|
|
|
|
|
2012-07-26 23:24:39 +08:00
|
|
|
ieee80211_add_srates_ie(sdata, skb, false, band);
|
|
|
|
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
|
2011-09-28 19:12:52 +08:00
|
|
|
ieee80211_tdls_add_ext_capab(skb);
|
|
|
|
break;
|
|
|
|
case WLAN_TDLS_SETUP_CONFIRM:
|
|
|
|
tf->category = WLAN_CATEGORY_TDLS;
|
|
|
|
tf->action_code = WLAN_TDLS_SETUP_CONFIRM;
|
|
|
|
|
|
|
|
skb_put(skb, sizeof(tf->u.setup_cfm));
|
|
|
|
tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
|
|
|
|
tf->u.setup_cfm.dialog_token = dialog_token;
|
|
|
|
break;
|
|
|
|
case WLAN_TDLS_TEARDOWN:
|
|
|
|
tf->category = WLAN_CATEGORY_TDLS;
|
|
|
|
tf->action_code = WLAN_TDLS_TEARDOWN;
|
|
|
|
|
|
|
|
skb_put(skb, sizeof(tf->u.teardown));
|
|
|
|
tf->u.teardown.reason_code = cpu_to_le16(status_code);
|
|
|
|
break;
|
|
|
|
case WLAN_TDLS_DISCOVERY_REQUEST:
|
|
|
|
tf->category = WLAN_CATEGORY_TDLS;
|
|
|
|
tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST;
|
|
|
|
|
|
|
|
skb_put(skb, sizeof(tf->u.discover_req));
|
|
|
|
tf->u.discover_req.dialog_token = dialog_token;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
u8 *peer, u8 action_code, u8 dialog_token,
|
|
|
|
u16 status_code, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
2012-07-26 23:24:39 +08:00
|
|
|
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
2011-09-28 19:12:52 +08:00
|
|
|
struct ieee80211_mgmt *mgmt;
|
|
|
|
|
|
|
|
mgmt = (void *)skb_put(skb, 24);
|
|
|
|
memset(mgmt, 0, 24);
|
|
|
|
memcpy(mgmt->da, peer, ETH_ALEN);
|
|
|
|
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
|
|
|
memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
|
|
|
|
|
|
|
|
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
|
|
|
IEEE80211_STYPE_ACTION);
|
|
|
|
|
|
|
|
switch (action_code) {
|
|
|
|
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
|
|
|
|
skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
|
|
|
|
mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
|
|
|
|
mgmt->u.action.u.tdls_discover_resp.action_code =
|
|
|
|
WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
|
|
|
|
mgmt->u.action.u.tdls_discover_resp.dialog_token =
|
|
|
|
dialog_token;
|
|
|
|
mgmt->u.action.u.tdls_discover_resp.capability =
|
|
|
|
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
|
|
|
|
|
2012-07-26 23:24:39 +08:00
|
|
|
ieee80211_add_srates_ie(sdata, skb, false, band);
|
|
|
|
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
|
2011-09-28 19:12:52 +08:00
|
|
|
ieee80211_tdls_add_ext_capab(skb);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
u8 *peer, u8 action_code, u8 dialog_token,
|
|
|
|
u16 status_code, const u8 *extra_ies,
|
|
|
|
size_t extra_ies_len)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct sk_buff *skb = NULL;
|
|
|
|
bool send_direct;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
|
|
|
|
return -ENOTSUPP;
|
|
|
|
|
|
|
|
/* make sure we are in managed mode, and associated */
|
|
|
|
if (sdata->vif.type != NL80211_IFTYPE_STATION ||
|
|
|
|
!sdata->u.mgd.associated)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-06-22 17:29:50 +08:00
|
|
|
tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n",
|
|
|
|
action_code, peer);
|
2011-09-28 19:12:52 +08:00
|
|
|
|
|
|
|
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
|
|
|
|
max(sizeof(struct ieee80211_mgmt),
|
|
|
|
sizeof(struct ieee80211_tdls_data)) +
|
|
|
|
50 + /* supported rates */
|
|
|
|
7 + /* ext capab */
|
|
|
|
extra_ies_len +
|
|
|
|
sizeof(struct ieee80211_tdls_lnkie));
|
|
|
|
if (!skb)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
skb_reserve(skb, local->hw.extra_tx_headroom);
|
|
|
|
|
|
|
|
switch (action_code) {
|
|
|
|
case WLAN_TDLS_SETUP_REQUEST:
|
|
|
|
case WLAN_TDLS_SETUP_RESPONSE:
|
|
|
|
case WLAN_TDLS_SETUP_CONFIRM:
|
|
|
|
case WLAN_TDLS_TEARDOWN:
|
|
|
|
case WLAN_TDLS_DISCOVERY_REQUEST:
|
|
|
|
ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer,
|
|
|
|
action_code, dialog_token,
|
|
|
|
status_code, skb);
|
|
|
|
send_direct = false;
|
|
|
|
break;
|
|
|
|
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
|
|
|
|
ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code,
|
|
|
|
dialog_token, status_code,
|
|
|
|
skb);
|
|
|
|
send_direct = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = -ENOTSUPP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
if (extra_ies_len)
|
|
|
|
memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
|
|
|
|
|
|
|
|
/* the TDLS link IE is always added last */
|
|
|
|
switch (action_code) {
|
|
|
|
case WLAN_TDLS_SETUP_REQUEST:
|
|
|
|
case WLAN_TDLS_SETUP_CONFIRM:
|
|
|
|
case WLAN_TDLS_TEARDOWN:
|
|
|
|
case WLAN_TDLS_DISCOVERY_REQUEST:
|
|
|
|
/* we are the initiator */
|
|
|
|
ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer,
|
|
|
|
sdata->u.mgd.bssid);
|
|
|
|
break;
|
|
|
|
case WLAN_TDLS_SETUP_RESPONSE:
|
|
|
|
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
|
|
|
|
/* we are the responder */
|
|
|
|
ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr,
|
|
|
|
sdata->u.mgd.bssid);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = -ENOTSUPP;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (send_direct) {
|
|
|
|
ieee80211_tx_skb(sdata, skb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
|
|
|
|
* we should default to AC_VI.
|
|
|
|
*/
|
|
|
|
switch (action_code) {
|
|
|
|
case WLAN_TDLS_SETUP_REQUEST:
|
|
|
|
case WLAN_TDLS_SETUP_RESPONSE:
|
|
|
|
skb_set_queue_mapping(skb, IEEE80211_AC_BK);
|
|
|
|
skb->priority = 2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
skb_set_queue_mapping(skb, IEEE80211_AC_VI);
|
|
|
|
skb->priority = 5;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* disable bottom halves when entering the Tx path */
|
|
|
|
local_bh_disable();
|
|
|
|
ret = ieee80211_subif_start_xmit(skb, dev);
|
|
|
|
local_bh_enable();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
u8 *peer, enum nl80211_tdls_operation oper)
|
|
|
|
{
|
2011-09-28 19:12:54 +08:00
|
|
|
struct sta_info *sta;
|
2011-09-28 19:12:52 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
|
|
|
if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
|
|
|
|
return -ENOTSUPP;
|
|
|
|
|
|
|
|
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-06-22 17:29:50 +08:00
|
|
|
tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
|
2011-09-28 19:12:52 +08:00
|
|
|
|
|
|
|
switch (oper) {
|
|
|
|
case NL80211_TDLS_ENABLE_LINK:
|
2011-09-28 19:12:54 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
sta = sta_info_get(sdata, peer);
|
|
|
|
if (!sta) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return -ENOLINK;
|
|
|
|
}
|
|
|
|
|
2011-09-29 22:04:36 +08:00
|
|
|
set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
|
2011-09-28 19:12:54 +08:00
|
|
|
rcu_read_unlock();
|
2011-09-28 19:12:52 +08:00
|
|
|
break;
|
|
|
|
case NL80211_TDLS_DISABLE_LINK:
|
|
|
|
return sta_info_destroy_addr(sdata, peer);
|
|
|
|
case NL80211_TDLS_TEARDOWN:
|
|
|
|
case NL80211_TDLS_SETUP:
|
|
|
|
case NL80211_TDLS_DISCOVERY_REQ:
|
|
|
|
/* We don't support in-driver setup/teardown/discovery */
|
|
|
|
return -ENOTSUPP;
|
|
|
|
default:
|
|
|
|
return -ENOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-04 18:18:16 +08:00
|
|
|
static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
const u8 *peer, u64 *cookie)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_qos_hdr *nullfunc;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
int size = sizeof(*nullfunc);
|
|
|
|
__le16 fc;
|
|
|
|
bool qos;
|
|
|
|
struct ieee80211_tx_info *info;
|
|
|
|
struct sta_info *sta;
|
2012-07-26 23:24:39 +08:00
|
|
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
|
|
|
enum ieee80211_band band;
|
2011-11-04 18:18:16 +08:00
|
|
|
|
|
|
|
rcu_read_lock();
|
2012-07-26 23:24:39 +08:00
|
|
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
|
|
|
if (WARN_ON(!chanctx_conf)) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2012-11-09 18:39:59 +08:00
|
|
|
band = chanctx_conf->def.chan->band;
|
2013-09-30 03:39:33 +08:00
|
|
|
sta = sta_info_get_bss(sdata, peer);
|
2011-11-12 03:22:30 +08:00
|
|
|
if (sta) {
|
2011-11-04 18:18:16 +08:00
|
|
|
qos = test_sta_flag(sta, WLAN_STA_WME);
|
2011-11-12 03:22:30 +08:00
|
|
|
} else {
|
|
|
|
rcu_read_unlock();
|
2011-11-04 18:18:16 +08:00
|
|
|
return -ENOLINK;
|
2011-11-12 03:22:30 +08:00
|
|
|
}
|
2011-11-04 18:18:16 +08:00
|
|
|
|
|
|
|
if (qos) {
|
|
|
|
fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
|
|
|
|
IEEE80211_STYPE_QOS_NULLFUNC |
|
|
|
|
IEEE80211_FCTL_FROMDS);
|
|
|
|
} else {
|
|
|
|
size -= 2;
|
|
|
|
fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
|
|
|
|
IEEE80211_STYPE_NULLFUNC |
|
|
|
|
IEEE80211_FCTL_FROMDS);
|
|
|
|
}
|
|
|
|
|
|
|
|
skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
|
2012-07-26 23:24:39 +08:00
|
|
|
if (!skb) {
|
|
|
|
rcu_read_unlock();
|
2011-11-04 18:18:16 +08:00
|
|
|
return -ENOMEM;
|
2012-07-26 23:24:39 +08:00
|
|
|
}
|
2011-11-04 18:18:16 +08:00
|
|
|
|
|
|
|
skb->dev = dev;
|
|
|
|
|
|
|
|
skb_reserve(skb, local->hw.extra_tx_headroom);
|
|
|
|
|
|
|
|
nullfunc = (void *) skb_put(skb, size);
|
|
|
|
nullfunc->frame_control = fc;
|
|
|
|
nullfunc->duration_id = 0;
|
|
|
|
memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
|
|
|
|
memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
|
|
|
|
memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN);
|
|
|
|
nullfunc->seq_ctrl = 0;
|
|
|
|
|
|
|
|
info = IEEE80211_SKB_CB(skb);
|
|
|
|
|
|
|
|
info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
|
|
|
|
IEEE80211_TX_INTFL_NL80211_FRAME_TX;
|
|
|
|
|
|
|
|
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
|
|
|
|
skb->priority = 7;
|
|
|
|
if (qos)
|
|
|
|
nullfunc->qos_ctrl = cpu_to_le16(7);
|
|
|
|
|
|
|
|
local_bh_disable();
|
2012-07-26 23:24:39 +08:00
|
|
|
ieee80211_xmit(sdata, skb, band);
|
2011-11-04 18:18:16 +08:00
|
|
|
local_bh_enable();
|
2012-07-26 23:24:39 +08:00
|
|
|
rcu_read_unlock();
|
2011-11-04 18:18:16 +08:00
|
|
|
|
|
|
|
*cookie = (unsigned long) skb;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-09 04:25:48 +08:00
|
|
|
static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
|
|
|
|
struct wireless_dev *wdev,
|
|
|
|
struct cfg80211_chan_def *chandef)
|
2012-07-13 01:45:08 +08:00
|
|
|
{
|
2012-07-26 23:24:39 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
2013-02-24 02:02:14 +08:00
|
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
2012-07-26 23:24:39 +08:00
|
|
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
2012-11-09 04:25:48 +08:00
|
|
|
int ret = -ENODATA;
|
2012-07-26 23:24:39 +08:00
|
|
|
|
|
|
|
rcu_read_lock();
|
2013-02-28 16:59:22 +08:00
|
|
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
|
|
|
if (chanctx_conf) {
|
|
|
|
*chandef = chanctx_conf->def;
|
|
|
|
ret = 0;
|
|
|
|
} else if (local->open_count > 0 &&
|
|
|
|
local->open_count == local->monitors &&
|
|
|
|
sdata->vif.type == NL80211_IFTYPE_MONITOR) {
|
|
|
|
if (local->use_chanctx)
|
|
|
|
*chandef = local->monitor_chandef;
|
|
|
|
else
|
2013-03-25 23:26:57 +08:00
|
|
|
*chandef = local->_oper_chandef;
|
2012-11-09 04:25:48 +08:00
|
|
|
ret = 0;
|
2012-07-26 23:24:39 +08:00
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
2012-07-13 01:45:08 +08:00
|
|
|
|
2012-11-09 04:25:48 +08:00
|
|
|
return ret;
|
2012-07-13 01:45:08 +08:00
|
|
|
}
|
|
|
|
|
2012-04-04 21:05:25 +08:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
static void ieee80211_set_wakeup(struct wiphy *wiphy, bool enabled)
|
|
|
|
{
|
|
|
|
drv_set_wakeup(wiphy_priv(wiphy), enabled);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-05-06 02:45:53 +08:00
|
|
|
struct cfg80211_ops mac80211_config_ops = {
|
|
|
|
.add_virtual_intf = ieee80211_add_iface,
|
|
|
|
.del_virtual_intf = ieee80211_del_iface,
|
2007-09-29 03:52:27 +08:00
|
|
|
.change_virtual_intf = ieee80211_change_iface,
|
2012-06-19 02:07:15 +08:00
|
|
|
.start_p2p_device = ieee80211_start_p2p_device,
|
|
|
|
.stop_p2p_device = ieee80211_stop_p2p_device,
|
2007-12-19 09:03:30 +08:00
|
|
|
.add_key = ieee80211_add_key,
|
|
|
|
.del_key = ieee80211_del_key,
|
2007-12-19 09:03:31 +08:00
|
|
|
.get_key = ieee80211_get_key,
|
2007-12-19 09:03:30 +08:00
|
|
|
.set_default_key = ieee80211_config_default_key,
|
2009-01-08 19:32:02 +08:00
|
|
|
.set_default_mgmt_key = ieee80211_config_default_mgmt_key,
|
2012-02-13 22:17:18 +08:00
|
|
|
.start_ap = ieee80211_start_ap,
|
|
|
|
.change_beacon = ieee80211_change_beacon,
|
|
|
|
.stop_ap = ieee80211_stop_ap,
|
2007-12-19 09:03:35 +08:00
|
|
|
.add_station = ieee80211_add_station,
|
|
|
|
.del_station = ieee80211_del_station,
|
|
|
|
.change_station = ieee80211_change_station,
|
2007-12-19 09:03:37 +08:00
|
|
|
.get_station = ieee80211_get_station,
|
2008-02-23 22:17:17 +08:00
|
|
|
.dump_station = ieee80211_dump_station,
|
2010-04-19 16:23:57 +08:00
|
|
|
.dump_survey = ieee80211_dump_survey,
|
2008-02-23 22:17:17 +08:00
|
|
|
#ifdef CONFIG_MAC80211_MESH
|
|
|
|
.add_mpath = ieee80211_add_mpath,
|
|
|
|
.del_mpath = ieee80211_del_mpath,
|
|
|
|
.change_mpath = ieee80211_change_mpath,
|
|
|
|
.get_mpath = ieee80211_get_mpath,
|
|
|
|
.dump_mpath = ieee80211_dump_mpath,
|
2010-12-17 09:37:48 +08:00
|
|
|
.update_mesh_config = ieee80211_update_mesh_config,
|
|
|
|
.get_mesh_config = ieee80211_get_mesh_config,
|
2010-12-03 16:20:44 +08:00
|
|
|
.join_mesh = ieee80211_join_mesh,
|
|
|
|
.leave_mesh = ieee80211_leave_mesh,
|
2008-02-23 22:17:17 +08:00
|
|
|
#endif
|
2008-08-08 01:07:01 +08:00
|
|
|
.change_bss = ieee80211_change_bss,
|
2008-10-30 22:59:24 +08:00
|
|
|
.set_txq_params = ieee80211_set_txq_params,
|
2012-06-06 14:18:22 +08:00
|
|
|
.set_monitor_channel = ieee80211_set_monitor_channel,
|
2009-01-20 00:20:53 +08:00
|
|
|
.suspend = ieee80211_suspend,
|
|
|
|
.resume = ieee80211_resume,
|
2009-02-11 04:25:55 +08:00
|
|
|
.scan = ieee80211_scan,
|
2011-05-11 22:09:36 +08:00
|
|
|
.sched_scan_start = ieee80211_sched_scan_start,
|
|
|
|
.sched_scan_stop = ieee80211_sched_scan_stop,
|
2009-03-19 19:39:22 +08:00
|
|
|
.auth = ieee80211_auth,
|
|
|
|
.assoc = ieee80211_assoc,
|
|
|
|
.deauth = ieee80211_deauth,
|
|
|
|
.disassoc = ieee80211_disassoc,
|
2009-04-20 03:25:43 +08:00
|
|
|
.join_ibss = ieee80211_join_ibss,
|
|
|
|
.leave_ibss = ieee80211_leave_ibss,
|
2012-11-02 20:27:49 +08:00
|
|
|
.set_mcast_rate = ieee80211_set_mcast_rate,
|
2009-04-21 00:39:05 +08:00
|
|
|
.set_wiphy_params = ieee80211_set_wiphy_params,
|
2009-06-02 19:01:39 +08:00
|
|
|
.set_tx_power = ieee80211_set_tx_power,
|
|
|
|
.get_tx_power = ieee80211_get_tx_power,
|
2009-07-02 03:26:58 +08:00
|
|
|
.set_wds_peer = ieee80211_set_wds_peer,
|
2009-06-02 19:01:41 +08:00
|
|
|
.rfkill_poll = ieee80211_rfkill_poll,
|
2009-07-02 03:26:51 +08:00
|
|
|
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
|
2011-05-21 00:05:54 +08:00
|
|
|
CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump)
|
2009-07-02 03:26:57 +08:00
|
|
|
.set_power_mgmt = ieee80211_set_power_mgmt,
|
2009-07-02 03:26:59 +08:00
|
|
|
.set_bitrate_mask = ieee80211_set_bitrate_mask,
|
2009-12-23 20:15:42 +08:00
|
|
|
.remain_on_channel = ieee80211_remain_on_channel,
|
|
|
|
.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
|
2010-08-12 21:38:38 +08:00
|
|
|
.mgmt_tx = ieee80211_mgmt_tx,
|
2010-11-25 17:02:30 +08:00
|
|
|
.mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,
|
2010-03-23 15:02:34 +08:00
|
|
|
.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
|
2010-10-13 18:06:24 +08:00
|
|
|
.mgmt_frame_register = ieee80211_mgmt_frame_register,
|
2010-11-10 11:50:56 +08:00
|
|
|
.set_antenna = ieee80211_set_antenna,
|
|
|
|
.get_antenna = ieee80211_get_antenna,
|
2011-03-08 05:19:18 +08:00
|
|
|
.set_ringparam = ieee80211_set_ringparam,
|
|
|
|
.get_ringparam = ieee80211_get_ringparam,
|
2011-07-05 22:35:41 +08:00
|
|
|
.set_rekey_data = ieee80211_set_rekey_data,
|
2011-09-28 19:12:52 +08:00
|
|
|
.tdls_oper = ieee80211_tdls_oper,
|
|
|
|
.tdls_mgmt = ieee80211_tdls_mgmt,
|
2011-11-04 18:18:16 +08:00
|
|
|
.probe_client = ieee80211_probe_client,
|
2011-11-18 21:20:44 +08:00
|
|
|
.set_noack_map = ieee80211_set_noack_map,
|
2012-04-04 21:05:25 +08:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
.set_wakeup = ieee80211_set_wakeup,
|
|
|
|
#endif
|
2012-04-24 03:50:30 +08:00
|
|
|
.get_et_sset_count = ieee80211_get_et_sset_count,
|
|
|
|
.get_et_stats = ieee80211_get_et_stats,
|
|
|
|
.get_et_strings = ieee80211_get_et_strings,
|
2012-07-13 01:45:08 +08:00
|
|
|
.get_channel = ieee80211_cfg_get_channel,
|
2013-02-09 01:16:20 +08:00
|
|
|
.start_radar_detection = ieee80211_start_radar_detection,
|
2013-07-11 22:09:06 +08:00
|
|
|
.channel_switch = ieee80211_channel_switch,
|
2007-05-06 02:45:53 +08:00
|
|
|
};
|