summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2015-11-03 19:30:57 +0300
committerDavid S. Miller <davem@davemloft.net>2015-11-03 19:30:57 +0300
commitb3047a77cba193658ebf534ee4abab9cb5446842 (patch)
tree3d631c99faa0520c077046988fc40c9961546665 /net
parent3d131f070948e4ad93189cb379b3a45d53dce71b (diff)
parente4208427247ecc7306c8f71ab3c5c08e08cf9fda (diff)
downloadlinux-b3047a77cba193658ebf534ee4abab9cb5446842.tar.xz
Merge tag 'mac80211-for-davem-2015-11-03' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211
Johannes Berg says: ==================== Another set of fixes: * remove a warning on a check that can trigger without any errors having happened (Andrei) * correctly handle deauth request while in the process of associating (Andrei) * fix TDLS HT operation (Arik) * allow changing AID/listen interval during client setup (Ayala) * be more forgiving with WMM parameters to get HT/VHT in case of broken APs with bad WMM settings (Emmanuel, myself) * a number of other fixes (some in documentation) ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/cfg.c6
-rw-r--r--net/mac80211/driver-ops.c46
-rw-r--r--net/mac80211/driver-ops.h42
-rw-r--r--net/mac80211/ibss.c4
-rw-r--r--net/mac80211/ieee80211_i.h6
-rw-r--r--net/mac80211/iface.c8
-rw-r--r--net/mac80211/main.c2
-rw-r--r--net/mac80211/mesh.c3
-rw-r--r--net/mac80211/mesh_plink.c3
-rw-r--r--net/mac80211/mlme.c173
-rw-r--r--net/mac80211/pm.c11
-rw-r--r--net/mac80211/rate.c2
-rw-r--r--net/mac80211/scan.c12
-rw-r--r--net/mac80211/tdls.c13
-rw-r--r--net/mac80211/trace.h10
-rw-r--r--net/mac80211/util.c94
-rw-r--r--net/wireless/nl80211.c39
17 files changed, 264 insertions, 210 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 713cdbf6fb3c..c2bd1b6a6922 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2010,12 +2010,12 @@ ieee80211_sched_scan_start(struct wiphy *wiphy,
static int
ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = wiphy_priv(wiphy);
- if (!sdata->local->ops->sched_scan_stop)
+ if (!local->ops->sched_scan_stop)
return -EOPNOTSUPP;
- return ieee80211_request_sched_scan_stop(sdata);
+ return ieee80211_request_sched_scan_stop(local);
}
static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c
index a1d54318f16c..ca1fe5576103 100644
--- a/net/mac80211/driver-ops.c
+++ b/net/mac80211/driver-ops.c
@@ -1,4 +1,6 @@
/*
+ * Copyright 2015 Intel Deutschland GmbH
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
@@ -8,6 +10,48 @@
#include "trace.h"
#include "driver-ops.h"
+int drv_start(struct ieee80211_local *local)
+{
+ int ret;
+
+ might_sleep();
+
+ if (WARN_ON(local->started))
+ return -EALREADY;
+
+ trace_drv_start(local);
+ local->started = true;
+ /* allow rx frames */
+ smp_mb();
+ ret = local->ops->start(&local->hw);
+ trace_drv_return_int(local, ret);
+
+ if (ret)
+ local->started = false;
+
+ return ret;
+}
+
+void drv_stop(struct ieee80211_local *local)
+{
+ might_sleep();
+
+ if (WARN_ON(!local->started))
+ return;
+
+ trace_drv_stop(local);
+ local->ops->stop(&local->hw);
+ trace_drv_return_void(local);
+
+ /* sync away all work on the tasklet before clearing started */
+ tasklet_disable(&local->tasklet);
+ tasklet_enable(&local->tasklet);
+
+ barrier();
+
+ local->started = false;
+}
+
int drv_add_interface(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
{
@@ -192,6 +236,8 @@ int drv_switch_vif_chanctx(struct ieee80211_local *local,
int ret = 0;
int i;
+ might_sleep();
+
if (!local->ops->switch_vif_chanctx)
return -EOPNOTSUPP;
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 30987099eb8f..154ce4b13406 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -66,36 +66,8 @@ static inline int drv_get_et_sset_count(struct ieee80211_sub_if_data *sdata,
return rv;
}
-static inline int drv_start(struct ieee80211_local *local)
-{
- int ret;
-
- might_sleep();
-
- trace_drv_start(local);
- local->started = true;
- smp_mb();
- ret = local->ops->start(&local->hw);
- trace_drv_return_int(local, ret);
- return ret;
-}
-
-static inline void drv_stop(struct ieee80211_local *local)
-{
- might_sleep();
-
- trace_drv_stop(local);
- local->ops->stop(&local->hw);
- trace_drv_return_void(local);
-
- /* sync away all work on the tasklet before clearing started */
- tasklet_disable(&local->tasklet);
- tasklet_enable(&local->tasklet);
-
- barrier();
-
- local->started = false;
-}
+int drv_start(struct ieee80211_local *local);
+void drv_stop(struct ieee80211_local *local);
#ifdef CONFIG_PM
static inline int drv_suspend(struct ieee80211_local *local,
@@ -871,6 +843,8 @@ static inline int drv_add_chanctx(struct ieee80211_local *local,
{
int ret = -EOPNOTSUPP;
+ might_sleep();
+
trace_drv_add_chanctx(local, ctx);
if (local->ops->add_chanctx)
ret = local->ops->add_chanctx(&local->hw, &ctx->conf);
@@ -884,6 +858,8 @@ static inline int drv_add_chanctx(struct ieee80211_local *local,
static inline void drv_remove_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx)
{
+ might_sleep();
+
if (WARN_ON(!ctx->driver_present))
return;
@@ -898,6 +874,8 @@ static inline void drv_change_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx,
u32 changed)
{
+ might_sleep();
+
trace_drv_change_chanctx(local, ctx, changed);
if (local->ops->change_chanctx) {
WARN_ON_ONCE(!ctx->driver_present);
@@ -931,6 +909,8 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_chanctx *ctx)
{
+ might_sleep();
+
if (!check_sdata_in_driver(sdata))
return;
@@ -953,6 +933,8 @@ static inline int drv_start_ap(struct ieee80211_local *local,
{
int ret = 0;
+ might_sleep();
+
if (!check_sdata_in_driver(sdata))
return -EIO;
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 2001555d49cb..337bb5d78003 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -188,7 +188,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
* keep them at 0
*/
pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
- chandef, 0);
+ chandef, 0, false);
/* add VHT capability and information IEs */
if (chandef->width != NL80211_CHAN_WIDTH_20 &&
@@ -356,7 +356,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
else
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
- ieee80211_set_wmm_default(sdata, true);
+ ieee80211_set_wmm_default(sdata, true, false);
sdata->vif.bss_conf.ibss_joined = true;
sdata->vif.bss_conf.ibss_creator = creator;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 62f2a97cd2a6..d832bd59236b 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1573,7 +1573,7 @@ __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
struct cfg80211_sched_scan_request *req);
int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
struct cfg80211_sched_scan_request *req);
-int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata);
+int ieee80211_request_sched_scan_stop(struct ieee80211_local *local);
void ieee80211_sched_scan_end(struct ieee80211_local *local);
void ieee80211_sched_scan_stopped_work(struct work_struct *work);
@@ -1769,7 +1769,7 @@ int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
int rate, int erp, int short_preamble,
int shift);
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
- bool bss_notify);
+ bool bss_notify, bool enable_qos);
void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, struct sk_buff *skb);
@@ -1962,7 +1962,7 @@ u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
u16 cap);
u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
const struct cfg80211_chan_def *chandef,
- u16 prot_mode);
+ u16 prot_mode, bool rifs_mode);
u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
u32 cap);
u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index f848c75518a2..d0dc1bfaeec2 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -661,11 +661,13 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
}
/*
- * set default queue parameters so drivers don't
+ * Set default queue parameters so drivers don't
* need to initialise the hardware if the hardware
- * doesn't start up with sane defaults
+ * doesn't start up with sane defaults.
+ * Enable QoS for anything but station interfaces.
*/
- ieee80211_set_wmm_default(sdata, true);
+ ieee80211_set_wmm_default(sdata, true,
+ sdata->vif.type != NL80211_IFTYPE_STATION);
}
set_bit(SDATA_STATE_RUNNING, &sdata->state);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 273c96de4910..858f6b1cb149 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -281,7 +281,7 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
local->in_reconfig = true;
barrier();
- schedule_work(&local->restart_work);
+ queue_work(system_freezable_wq, &local->restart_work);
}
EXPORT_SYMBOL(ieee80211_restart_hw);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 626e8de70842..fa28500f28fd 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -466,7 +466,8 @@ int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata,
pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
ieee80211_ie_build_ht_oper(pos, ht_cap, &sdata->vif.bss_conf.chandef,
- sdata->vif.bss_conf.ht_operation_mode);
+ sdata->vif.bss_conf.ht_operation_mode,
+ false);
return 0;
}
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index c1f889270484..bd3d55eb21d4 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -686,6 +686,9 @@ static bool llid_in_use(struct ieee80211_sub_if_data *sdata,
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
+ if (sdata != sta->sdata)
+ continue;
+
if (!memcmp(&sta->mesh->llid, &llid, sizeof(llid))) {
in_use = true;
break;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index ded4b976bb48..b140cc6651f4 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1744,10 +1744,10 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
const u8 *wmm_param, size_t wmm_param_len)
{
- struct ieee80211_tx_queue_params params;
+ struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS];
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
size_t left;
- int count;
+ int count, ac;
const u8 *pos;
u8 uapsd_queues = 0;
@@ -1781,25 +1781,24 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
int aci = (pos[0] >> 5) & 0x03;
int acm = (pos[0] >> 4) & 0x01;
bool uapsd = false;
- int queue;
switch (aci) {
case 1: /* AC_BK */
- queue = 3;
+ ac = IEEE80211_AC_BK;
if (acm)
sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
uapsd = true;
break;
case 2: /* AC_VI */
- queue = 1;
+ ac = IEEE80211_AC_VI;
if (acm)
sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
uapsd = true;
break;
case 3: /* AC_VO */
- queue = 0;
+ ac = IEEE80211_AC_VO;
if (acm)
sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
@@ -1807,7 +1806,7 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
break;
case 0: /* AC_BE */
default:
- queue = 2;
+ ac = IEEE80211_AC_BE;
if (acm)
sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
@@ -1815,25 +1814,41 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
break;
}
- params.aifs = pos[0] & 0x0f;
- params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
- params.cw_min = ecw2cw(pos[1] & 0x0f);
- params.txop = get_unaligned_le16(pos + 2);
- params.acm = acm;
- params.uapsd = uapsd;
+ params[ac].aifs = pos[0] & 0x0f;
+ if (params[ac].aifs < 2) {
+ sdata_info(sdata,
+ "AP has invalid WMM params (AIFSN=%d for ACI %d), will use 2\n",
+ params[ac].aifs, aci);
+ params[ac].aifs = 2;
+ }
+ params[ac].cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
+ params[ac].cw_min = ecw2cw(pos[1] & 0x0f);
+ params[ac].txop = get_unaligned_le16(pos + 2);
+ params[ac].acm = acm;
+ params[ac].uapsd = uapsd;
+
+ if (params[ac].cw_min > params[ac].cw_max) {
+ sdata_info(sdata,
+ "AP has invalid WMM params (CWmin/max=%d/%d for ACI %d), using defaults\n",
+ params[ac].cw_min, params[ac].cw_max, aci);
+ return false;
+ }
+ }
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
mlme_dbg(sdata,
- "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
- queue, aci, acm,
- params.aifs, params.cw_min, params.cw_max,
- params.txop, params.uapsd,
- ifmgd->tx_tspec[queue].downgraded);
- sdata->tx_conf[queue] = params;
- if (!ifmgd->tx_tspec[queue].downgraded &&
- drv_conf_tx(local, sdata, queue, &params))
+ "WMM AC=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
+ ac, params[ac].acm,
+ params[ac].aifs, params[ac].cw_min, params[ac].cw_max,
+ params[ac].txop, params[ac].uapsd,
+ ifmgd->tx_tspec[ac].downgraded);
+ sdata->tx_conf[ac] = params[ac];
+ if (!ifmgd->tx_tspec[ac].downgraded &&
+ drv_conf_tx(local, sdata, ac, &params[ac]))
sdata_err(sdata,
- "failed to set TX queue parameters for queue %d\n",
- queue);
+ "failed to set TX queue parameters for AC %d\n",
+ ac);
}
/* enable WMM or activate new settings */
@@ -2077,7 +2092,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
ieee80211_bss_info_change_notify(sdata, changed);
/* disassociated - set to defaults now */
- ieee80211_set_wmm_default(sdata, false);
+ ieee80211_set_wmm_default(sdata, false, false);
del_timer_sync(&sdata->u.mgd.conn_mon_timer);
del_timer_sync(&sdata->u.mgd.bcn_mon_timer);
@@ -3044,11 +3059,21 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
*/
ifmgd->wmm_last_param_set = -1;
- if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && elems.wmm_param)
- ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
- elems.wmm_param_len);
- else
- ieee80211_set_wmm_default(sdata, false);
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
+ ieee80211_set_wmm_default(sdata, false, false);
+ } else if (!ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
+ elems.wmm_param_len)) {
+ /* still enable QoS since we might have HT/VHT */
+ ieee80211_set_wmm_default(sdata, false, true);
+ /* set the disable-WMM flag in this case to disable
+ * tracking WMM parameter changes in the beacon if
+ * the parameters weren't actually valid. Doing so
+ * avoids changing parameters very strangely when
+ * the AP is going back and forth between valid and
+ * invalid parameters.
+ */
+ ifmgd->flags |= IEEE80211_STA_DISABLE_WMM;
+ }
changed |= BSS_CHANGED_QOS;
/* set AID and assoc capability,
@@ -4543,44 +4568,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
return err;
}
-static bool ieee80211_usable_wmm_params(struct ieee80211_sub_if_data *sdata,
- const u8 *wmm_param, int len)
-{
- const u8 *pos;
- size_t left;
-
- if (len < 8)
- return false;
-
- if (wmm_param[5] != 1 /* version */)
- return false;
-
- pos = wmm_param + 8;
- left = len - 8;
-
- for (; left >= 4; left -= 4, pos += 4) {
- u8 aifsn = pos[0] & 0x0f;
- u8 ecwmin = pos[1] & 0x0f;
- u8 ecwmax = (pos[1] & 0xf0) >> 4;
- int aci = (pos[0] >> 5) & 0x03;
-
- if (aifsn < 2) {
- sdata_info(sdata,
- "AP has invalid WMM params (AIFSN=%d for ACI %d), disabling WMM\n",
- aifsn, aci);
- return false;
- }
- if (ecwmin > ecwmax) {
- sdata_info(sdata,
- "AP has invalid WMM params (ECWmin/max=%d/%d for ACI %d), disabling WMM\n",
- ecwmin, ecwmax, aci);
- return false;
- }
- }
-
- return true;
-}
-
int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
struct cfg80211_assoc_request *req)
{
@@ -4645,39 +4632,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
assoc_data->wmm = bss->wmm_used &&
(local->hw.queues >= IEEE80211_NUM_ACS);
- if (assoc_data->wmm) {
- /* try to check validity of WMM params IE */
- const struct cfg80211_bss_ies *ies;
- const u8 *wp, *start, *end;
-
- rcu_read_lock();
- ies = rcu_dereference(req->bss->ies);
- start = ies->data;
- end = start + ies->len;
-
- while (true) {
- wp = cfg80211_find_vendor_ie(
- WLAN_OUI_MICROSOFT,
- WLAN_OUI_TYPE_MICROSOFT_WMM,
- start, end - start);
- if (!wp)
- break;
- start = wp + wp[1] + 2;
- /* if this IE is too short, try the next */
- if (wp[1] <= 4)
- continue;
- /* if this IE is WMM params, we found what we wanted */
- if (wp[6] == 1)
- break;
- }
-
- if (!wp || !ieee80211_usable_wmm_params(sdata, wp + 2,
- wp[1] - 2)) {
- assoc_data->wmm = false;
- ifmgd->flags |= IEEE80211_STA_DISABLE_WMM;
- }
- rcu_read_unlock();
- }
/*
* IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.
@@ -4936,6 +4890,25 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
return 0;
}
+ if (ifmgd->assoc_data &&
+ ether_addr_equal(ifmgd->assoc_data->bss->bssid, req->bssid)) {
+ sdata_info(sdata,
+ "aborting association with %pM by local choice (Reason: %u=%s)\n",
+ req->bssid, req->reason_code,
+ ieee80211_get_reason_code_string(req->reason_code));
+
+ drv_mgd_prepare_tx(sdata->local, sdata);
+ ieee80211_send_deauth_disassoc(sdata, req->bssid,
+ IEEE80211_STYPE_DEAUTH,
+ req->reason_code, tx,
+ frame_buf);
+ ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_report_disconnect(sdata, frame_buf,
+ sizeof(frame_buf), true,
+ req->reason_code);
+ return 0;
+ }
+
if (ifmgd->associated &&
ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
sdata_info(sdata,
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index ad88ad4e8eb1..00a43a70e1fc 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -6,6 +6,13 @@
#include "driver-ops.h"
#include "led.h"
+static void ieee80211_sched_scan_cancel(struct ieee80211_local *local)
+{
+ if (ieee80211_request_sched_scan_stop(local))
+ return;
+ cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy);
+}
+
int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
{
struct ieee80211_local *local = hw_to_local(hw);
@@ -34,6 +41,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
mutex_unlock(&local->sta_mtx);
}
+ /* keep sched_scan only in case of 'any' trigger */
+ if (!(wowlan && wowlan->any))
+ ieee80211_sched_scan_cancel(local);
+
ieee80211_stop_queues_by_reason(hw,
IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_SUSPEND,
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index b07e2f748f93..a4e2f4e67f94 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -307,7 +307,7 @@ static void __rate_control_send_low(struct ieee80211_hw *hw,
}
WARN_ONCE(i == sband->n_bitrates,
"no supported rates (0x%x) in rate_mask 0x%x with flags 0x%x\n",
- sta ? sta->supp_rates[sband->band] : 0,
+ sta ? sta->supp_rates[sband->band] : -1,
rate_mask, rate_flags);
info->control.rates[0].count =
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index b64fd2b2d95a..4aeca4b0c3cb 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -1140,10 +1140,10 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
return ret;
}
-int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
+int ieee80211_request_sched_scan_stop(struct ieee80211_local *local)
{
- struct ieee80211_local *local = sdata->local;
- int ret = 0;
+ struct ieee80211_sub_if_data *sched_scan_sdata;
+ int ret = -ENOENT;
mutex_lock(&local->mtx);
@@ -1155,8 +1155,10 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
/* We don't want to restart sched scan anymore. */
RCU_INIT_POINTER(local->sched_scan_req, NULL);
- if (rcu_access_pointer(local->sched_scan_sdata)) {
- ret = drv_sched_scan_stop(local, sdata);
+ sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
+ lockdep_is_held(&local->mtx));
+ if (sched_scan_sdata) {
+ ret = drv_sched_scan_stop(local, sched_scan_sdata);
if (!ret)
RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
}
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index ecc5e2a8f80b..c9eeb3f12808 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -591,12 +591,19 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
offset = noffset;
}
- /* if HT support is only added in TDLS, we need an HT-operation IE */
+ /*
+ * if HT support is only added in TDLS, we need an HT-operation IE.
+ * add the IE as required by IEEE802.11-2012 9.23.3.2.
+ */
if (!ap_sta->sta.ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
+ u16 prot = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED |
+ IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT |
+ IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT;
+
pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
- /* send an empty HT operation IE */
ieee80211_ie_build_ht_oper(pos, &sta->sta.ht_cap,
- &sdata->vif.bss_conf.chandef, 0);
+ &sdata->vif.bss_conf.chandef, prot,
+ true);
}
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 5cf8f4ea077f..56c6d6cfa5a1 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -33,11 +33,11 @@
__field(u32, chan_width) \
__field(u32, center_freq1) \
__field(u32, center_freq2)
-#define CHANDEF_ASSIGN(c) \
- __entry->control_freq = (c)->chan ? (c)->chan->center_freq : 0; \
- __entry->chan_width = (c)->width; \
- __entry->center_freq1 = (c)->center_freq1; \
- __entry->center_freq2 = (c)->center_freq2;
+#define CHANDEF_ASSIGN(c) \
+ __entry->control_freq = (c) ? ((c)->chan ? (c)->chan->center_freq : 0) : 0; \
+ __entry->chan_width = (c) ? (c)->width : 0; \
+ __entry->center_freq1 = (c) ? (c)->center_freq1 : 0; \
+ __entry->center_freq2 = (c) ? (c)->center_freq2 : 0;
#define CHANDEF_PR_FMT " control:%d MHz width:%d center: %d/%d MHz"
#define CHANDEF_PR_ARG __entry->control_freq, __entry->chan_width, \
__entry->center_freq1, __entry->center_freq2
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 8274c86296f9..74058020b7d6 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -4,6 +4,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright (C) 2015 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -1104,13 +1105,13 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
}
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
- bool bss_notify)
+ bool bss_notify, bool enable_qos)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_queue_params qparam;
struct ieee80211_chanctx_conf *chanctx_conf;
int ac;
- bool use_11b, enable_qos;
+ bool use_11b;
bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */
int aCWmin, aCWmax;
@@ -1129,13 +1130,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
!(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE);
rcu_read_unlock();
- /*
- * By default disable QoS in STA mode for old access points, which do
- * not support 802.11e. New APs will provide proper queue parameters,
- * that we will configure later.
- */
- enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
-
is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB);
/* Set defaults according to 802.11-2007 Table 7-37 */
@@ -1664,7 +1658,6 @@ static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
local->resuming = false;
local->suspended = false;
- local->started = false;
local->in_reconfig = false;
/* scheduled scan clearly can't be running any more, but tell
@@ -1754,6 +1747,18 @@ int ieee80211_reconfig(struct ieee80211_local *local)
#endif
/*
+ * In case of hw_restart during suspend (without wowlan),
+ * cancel restart work, as we are reconfiguring the device
+ * anyway.
+ * Note that restart_work is scheduled on a frozen workqueue,
+ * so we can't deadlock in this case.
+ */
+ if (suspended && local->in_reconfig && !reconfig_due_to_wowlan)
+ cancel_work_sync(&local->restart_work);
+
+ local->started = false;
+
+ /*
* Upon resume hardware can sometimes be goofy due to
* various platform / driver / bus issues, so restarting
* the device may at times not work immediately. Propagate
@@ -1996,6 +2001,29 @@ int ieee80211_reconfig(struct ieee80211_local *local)
if (ieee80211_sdata_running(sdata))
ieee80211_enable_keys(sdata);
+ /* Reconfigure sched scan if it was interrupted by FW restart */
+ mutex_lock(&local->mtx);
+ sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
+ lockdep_is_held(&local->mtx));
+ sched_scan_req = rcu_dereference_protected(local->sched_scan_req,
+ lockdep_is_held(&local->mtx));
+ if (sched_scan_sdata && sched_scan_req)
+ /*
+ * Sched scan stopped, but we don't want to report it. Instead,
+ * we're trying to reschedule. However, if more than one scan
+ * plan was set, we cannot reschedule since we don't know which
+ * scan plan was currently running (and some scan plans may have
+ * already finished).
+ */
+ if (sched_scan_req->n_scan_plans > 1 ||
+ __ieee80211_request_sched_scan_start(sched_scan_sdata,
+ sched_scan_req))
+ sched_scan_stopped = true;
+ mutex_unlock(&local->mtx);
+
+ if (sched_scan_stopped)
+ cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy);
+
wake_up:
local->in_reconfig = false;
barrier();
@@ -2031,32 +2059,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
false);
/*
- * Reconfigure sched scan if it was interrupted by FW restart or
- * suspend.
- */
- mutex_lock(&local->mtx);
- sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
- lockdep_is_held(&local->mtx));
- sched_scan_req = rcu_dereference_protected(local->sched_scan_req,
- lockdep_is_held(&local->mtx));
- if (sched_scan_sdata && sched_scan_req)
- /*
- * Sched scan stopped, but we don't want to report it. Instead,
- * we're trying to reschedule. However, if more than one scan
- * plan was set, we cannot reschedule since we don't know which
- * scan plan was currently running (and some scan plans may have
- * already finished).
- */
- if (sched_scan_req->n_scan_plans > 1 ||
- __ieee80211_request_sched_scan_start(sched_scan_sdata,
- sched_scan_req))
- sched_scan_stopped = true;
- mutex_unlock(&local->mtx);
-
- if (sched_scan_stopped)
- cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy);
-
- /*
* If this is for hw restart things are still running.
* We may want to change that later, however.
*/
@@ -2140,7 +2142,13 @@ void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata)
chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
lockdep_is_held(&local->chanctx_mtx));
- if (WARN_ON_ONCE(!chanctx_conf))
+ /*
+ * This function can be called from a work, thus it may be possible
+ * that the chanctx_conf is removed (due to a disconnection, for
+ * example).
+ * So nothing should be done in such case.
+ */
+ if (!chanctx_conf)
goto unlock;
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
@@ -2277,7 +2285,7 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
const struct cfg80211_chan_def *chandef,
- u16 prot_mode)
+ u16 prot_mode, bool rifs_mode)
{
struct ieee80211_ht_operation *ht_oper;
/* Build HT Information */
@@ -2305,6 +2313,9 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
chandef->width != NL80211_CHAN_WIDTH_20)
ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
+ if (rifs_mode)
+ ht_oper->ht_param |= IEEE80211_HT_PARAM_RIFS_MODE;
+
ht_oper->operation_mode = cpu_to_le16(prot_mode);
ht_oper->stbc_param = 0x0000;
@@ -2958,6 +2969,13 @@ ieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i)
if (end > 0)
return false;
+ /* One shot NOA */
+ if (data->count[i] == 1)
+ return false;
+
+ if (data->desc[i].interval == 0)
+ return false;
+
/* End time is in the past, check for repetitions */
skip = DIV_ROUND_UP(-end, data->desc[i].interval);
if (data->count[i] < 255) {
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index d693c9d031fc..c71e274c810a 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3432,12 +3432,6 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
wdev->iftype))
return -EINVAL;
- if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
- params.acl = parse_acl_data(&rdev->wiphy, info);
- if (IS_ERR(params.acl))
- return PTR_ERR(params.acl);
- }
-
if (info->attrs[NL80211_ATTR_SMPS_MODE]) {
params.smps_mode =
nla_get_u8(info->attrs[NL80211_ATTR_SMPS_MODE]);
@@ -3461,6 +3455,12 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
params.smps_mode = NL80211_SMPS_OFF;
}
+ if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
+ params.acl = parse_acl_data(&rdev->wiphy, info);
+ if (IS_ERR(params.acl))
+ return PTR_ERR(params.acl);
+ }
+
wdev_lock(wdev);
err = rdev_start_ap(rdev, dev, &params);
if (!err) {
@@ -3968,10 +3968,13 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
struct station_parameters *params,
enum cfg80211_station_type statype)
{
- if (params->listen_interval != -1)
+ if (params->listen_interval != -1 &&
+ statype != CFG80211_STA_AP_CLIENT_UNASSOC)
return -EINVAL;
+
if (params->aid &&
- !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
+ !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
+ statype != CFG80211_STA_AP_CLIENT_UNASSOC)
return -EINVAL;
/* When you run into this, adjust the code below for the new flag */
@@ -4245,13 +4248,22 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
memset(&params, 0, sizeof(params));
- params.listen_interval = -1;
-
if (!rdev->ops->change_station)
return -EOPNOTSUPP;
- if (info->attrs[NL80211_ATTR_STA_AID])
- return -EINVAL;
+ /*
+ * AID and listen_interval properties can be set only for unassociated
+ * station. Include these parameters here and will check them in
+ * cfg80211_check_station_change().
+ */
+ if (info->attrs[NL80211_ATTR_PEER_AID])
+ params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
+
+ if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
+ params.listen_interval =
+ nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
+ else
+ params.listen_interval = -1;
if (!info->attrs[NL80211_ATTR_MAC])
return -EINVAL;
@@ -4278,9 +4290,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
}
- if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
- return -EINVAL;
-
if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
return -EINVAL;