Commit b3047a77 authored by David S. Miller's avatar David S. Miller

Merge tag 'mac80211-for-davem-2015-11-03' of...

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 3d131f07 e4208427
......@@ -2399,6 +2399,10 @@ struct cfg80211_qos_map {
* @set_power_mgmt: Configure WLAN power management. A timeout value of -1
* allows the driver to adjust the dynamic ps timeout value.
* @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
* After configuration, the driver should (soon) send an event indicating
* the current level is above/below the configured threshold; this may
* need some care when the configuration is changed (without first being
* disabled.)
* @set_cqm_txe_config: Configure connection quality monitor TX error
* thresholds.
* @sched_scan_start: Tell the driver to start a scheduled scan.
......
......@@ -482,7 +482,9 @@ struct ieee80211_event {
* Note that with TDLS this can be the case (channel is HT, protection must
* be used from this field) even when the BSS association isn't using HT.
* @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value
* implies disabled
* implies disabled. As with the cfg80211 callback, a change here should
* cause an event to be sent indicating where the current value is in
* relation to the newly configured threshold.
* @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis
* @arp_addr_list: List of IPv4 addresses for hardware ARP filtering. The
* may filter ARP queries targeted for other addresses than listed here.
......@@ -3170,18 +3172,24 @@ enum ieee80211_reconfig_type {
* The callback is optional and can sleep.
*
* @add_chanctx: Notifies device driver about new channel context creation.
* This callback may sleep.
* @remove_chanctx: Notifies device driver about channel context destruction.
* This callback may sleep.
* @change_chanctx: Notifies device driver about channel context changes that
* may happen when combining different virtual interfaces on the same
* channel context with different settings
* This callback may sleep.
* @assign_vif_chanctx: Notifies device driver about channel context being bound
* to vif. Possible use is for hw queue remapping.
* This callback may sleep.
* @unassign_vif_chanctx: Notifies device driver about channel context being
* unbound from vif.
* This callback may sleep.
* @switch_vif_chanctx: switch a number of vifs from one chanctx to
* another, as specified in the list of
* @ieee80211_vif_chanctx_switch passed to the driver, according
* to the mode defined in &ieee80211_chanctx_switch_mode.
* This callback may sleep.
*
* @start_ap: Start operation on the AP interface, this is called after all the
* information in bss_conf is set and beacon can be retrieved. A channel
......
......@@ -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,
......
/*
* 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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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,
......
......@@ -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);
......
......@@ -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);
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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,
......
......@@ -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,
......
......@@ -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 =
......
......@@ -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);
}
......
......@@ -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);
......
......@@ -34,10 +34,10 @@
__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;
__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
......
......@@ -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
......@@ -1753,6 +1746,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
......@@ -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();
......@@ -2030,32 +2058,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
IEEE80211_QUEUE_STOP_REASON_SUSPEND,
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) {
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment