Merge tag 'mac80211-next-for-davem-2017-04-18' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Johannes Berg says: ==================== My last pull request has been a while, we now have: * connection quality monitoring with multiple thresholds * support for FILS shared key authentication offload * pre-CAC regulatory compliance - only ETSI allows this * sanity check for some rate confusion that hit ChromeOS (but nobody else uses it, evidently) * some documentation updates * lots of cleanups ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -357,14 +357,14 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
|
||||
spin_lock_init(&tid_agg_rx->reorder_lock);
|
||||
|
||||
/* rx timer */
|
||||
tid_agg_rx->session_timer.function = sta_rx_agg_session_timer_expired;
|
||||
tid_agg_rx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid];
|
||||
init_timer_deferrable(&tid_agg_rx->session_timer);
|
||||
setup_deferrable_timer(&tid_agg_rx->session_timer,
|
||||
sta_rx_agg_session_timer_expired,
|
||||
(unsigned long)&sta->timer_to_tid[tid]);
|
||||
|
||||
/* rx reorder timer */
|
||||
tid_agg_rx->reorder_timer.function = sta_rx_agg_reorder_timer_expired;
|
||||
tid_agg_rx->reorder_timer.data = (unsigned long)&sta->timer_to_tid[tid];
|
||||
init_timer(&tid_agg_rx->reorder_timer);
|
||||
setup_timer(&tid_agg_rx->reorder_timer,
|
||||
sta_rx_agg_reorder_timer_expired,
|
||||
(unsigned long)&sta->timer_to_tid[tid]);
|
||||
|
||||
/* prepare reordering buffer */
|
||||
tid_agg_rx->reorder_buf =
|
||||
|
@@ -670,14 +670,14 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
|
||||
tid_tx->timeout = timeout;
|
||||
|
||||
/* response timer */
|
||||
tid_tx->addba_resp_timer.function = sta_addba_resp_timer_expired;
|
||||
tid_tx->addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[tid];
|
||||
init_timer(&tid_tx->addba_resp_timer);
|
||||
setup_timer(&tid_tx->addba_resp_timer,
|
||||
sta_addba_resp_timer_expired,
|
||||
(unsigned long)&sta->timer_to_tid[tid]);
|
||||
|
||||
/* tx timer */
|
||||
tid_tx->session_timer.function = sta_tx_agg_session_timer_expired;
|
||||
tid_tx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid];
|
||||
init_timer_deferrable(&tid_tx->session_timer);
|
||||
setup_deferrable_timer(&tid_tx->session_timer,
|
||||
sta_tx_agg_session_timer_expired,
|
||||
(unsigned long)&sta->timer_to_tid[tid]);
|
||||
|
||||
/* assign a dialog token */
|
||||
sta->ampdu_mlme.dialog_token_allocator++;
|
||||
|
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2015-2016 Intel Deutschland GmbH
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
*
|
||||
* This file is GPLv2 as found in COPYING.
|
||||
*/
|
||||
@@ -22,11 +22,98 @@
|
||||
#include "mesh.h"
|
||||
#include "wme.h"
|
||||
|
||||
static void ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata,
|
||||
struct vif_params *params)
|
||||
{
|
||||
bool mu_mimo_groups = false;
|
||||
bool mu_mimo_follow = false;
|
||||
|
||||
if (params->vht_mumimo_groups) {
|
||||
u64 membership;
|
||||
|
||||
BUILD_BUG_ON(sizeof(membership) != WLAN_MEMBERSHIP_LEN);
|
||||
|
||||
memcpy(sdata->vif.bss_conf.mu_group.membership,
|
||||
params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN);
|
||||
memcpy(sdata->vif.bss_conf.mu_group.position,
|
||||
params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN,
|
||||
WLAN_USER_POSITION_LEN);
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_MU_GROUPS);
|
||||
/* don't care about endianness - just check for 0 */
|
||||
memcpy(&membership, params->vht_mumimo_groups,
|
||||
WLAN_MEMBERSHIP_LEN);
|
||||
mu_mimo_groups = membership != 0;
|
||||
}
|
||||
|
||||
if (params->vht_mumimo_follow_addr) {
|
||||
mu_mimo_follow =
|
||||
is_valid_ether_addr(params->vht_mumimo_follow_addr);
|
||||
ether_addr_copy(sdata->u.mntr.mu_follow_addr,
|
||||
params->vht_mumimo_follow_addr);
|
||||
}
|
||||
|
||||
sdata->vif.mu_mimo_owner = mu_mimo_groups || mu_mimo_follow;
|
||||
}
|
||||
|
||||
static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
|
||||
struct vif_params *params)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_sub_if_data *monitor_sdata;
|
||||
|
||||
/* check flags first */
|
||||
if (params->flags && ieee80211_sdata_running(sdata)) {
|
||||
u32 mask = MONITOR_FLAG_COOK_FRAMES | MONITOR_FLAG_ACTIVE;
|
||||
|
||||
/*
|
||||
* Prohibit MONITOR_FLAG_COOK_FRAMES and
|
||||
* MONITOR_FLAG_ACTIVE to be changed while the
|
||||
* interface is up.
|
||||
* Else we would need to add a lot of cruft
|
||||
* to update everything:
|
||||
* cooked_mntrs, monitor and all fif_* counters
|
||||
* reconfigure hardware
|
||||
*/
|
||||
if ((params->flags & mask) != (sdata->u.mntr.flags & mask))
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* also validate MU-MIMO change */
|
||||
monitor_sdata = rtnl_dereference(local->monitor_sdata);
|
||||
|
||||
if (!monitor_sdata &&
|
||||
(params->vht_mumimo_groups || params->vht_mumimo_follow_addr))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* apply all changes now - no failures allowed */
|
||||
|
||||
if (monitor_sdata)
|
||||
ieee80211_set_mu_mimo_follow(monitor_sdata, params);
|
||||
|
||||
if (params->flags) {
|
||||
if (ieee80211_sdata_running(sdata)) {
|
||||
ieee80211_adjust_monitor_flags(sdata, -1);
|
||||
sdata->u.mntr.flags = params->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 = params->flags;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
|
||||
const char *name,
|
||||
unsigned char name_assign_type,
|
||||
enum nl80211_iftype type,
|
||||
u32 *flags,
|
||||
struct vif_params *params)
|
||||
{
|
||||
struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||
@@ -38,9 +125,14 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
if (type == NL80211_IFTYPE_MONITOR && flags) {
|
||||
sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
||||
sdata->u.mntr.flags = *flags;
|
||||
sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
||||
|
||||
if (type == NL80211_IFTYPE_MONITOR) {
|
||||
err = ieee80211_set_mon_options(sdata, params);
|
||||
if (err) {
|
||||
ieee80211_if_remove(sdata);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return wdev;
|
||||
@@ -55,7 +147,7 @@ static int ieee80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
|
||||
|
||||
static int ieee80211_change_iface(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
enum nl80211_iftype type, u32 *flags,
|
||||
enum nl80211_iftype type,
|
||||
struct vif_params *params)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
@@ -75,58 +167,9 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
|
||||
}
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_sub_if_data *monitor_sdata;
|
||||
u32 mu_mntr_cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER;
|
||||
|
||||
monitor_sdata = rtnl_dereference(local->monitor_sdata);
|
||||
if (monitor_sdata &&
|
||||
wiphy_ext_feature_isset(wiphy, mu_mntr_cap_flag)) {
|
||||
memcpy(monitor_sdata->vif.bss_conf.mu_group.membership,
|
||||
params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN);
|
||||
memcpy(monitor_sdata->vif.bss_conf.mu_group.position,
|
||||
params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN,
|
||||
WLAN_USER_POSITION_LEN);
|
||||
monitor_sdata->vif.mu_mimo_owner = true;
|
||||
ieee80211_bss_info_change_notify(monitor_sdata,
|
||||
BSS_CHANGED_MU_GROUPS);
|
||||
|
||||
ether_addr_copy(monitor_sdata->u.mntr.mu_follow_addr,
|
||||
params->macaddr);
|
||||
}
|
||||
|
||||
if (!flags)
|
||||
return 0;
|
||||
|
||||
if (ieee80211_sdata_running(sdata)) {
|
||||
u32 mask = MONITOR_FLAG_COOK_FRAMES |
|
||||
MONITOR_FLAG_ACTIVE;
|
||||
|
||||
/*
|
||||
* Prohibit MONITOR_FLAG_COOK_FRAMES and
|
||||
* MONITOR_FLAG_ACTIVE to be changed while the
|
||||
* interface is up.
|
||||
* Else we would need to add a lot of cruft
|
||||
* to update everything:
|
||||
* cooked_mntrs, monitor and all fif_* counters
|
||||
* reconfigure hardware
|
||||
*/
|
||||
if ((*flags & mask) != (sdata->u.mntr.flags & mask))
|
||||
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;
|
||||
}
|
||||
ret = ieee80211_set_mon_options(sdata, params);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -2042,6 +2085,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
|
||||
params->basic_rates_len,
|
||||
&sdata->vif.bss_conf.basic_rates);
|
||||
changed |= BSS_CHANGED_BASIC_RATES;
|
||||
ieee80211_check_rate_mask(sdata);
|
||||
}
|
||||
|
||||
if (params->ap_isolate >= 0) {
|
||||
@@ -2630,6 +2674,33 @@ static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,
|
||||
|
||||
bss_conf->cqm_rssi_thold = rssi_thold;
|
||||
bss_conf->cqm_rssi_hyst = rssi_hyst;
|
||||
bss_conf->cqm_rssi_low = 0;
|
||||
bss_conf->cqm_rssi_high = 0;
|
||||
sdata->u.mgd.last_cqm_event_signal = 0;
|
||||
|
||||
/* tell the driver upon association, unless already associated */
|
||||
if (sdata->u.mgd.associated &&
|
||||
sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_set_cqm_rssi_range_config(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
s32 rssi_low, s32 rssi_high)
|
||||
{
|
||||
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 (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
bss_conf->cqm_rssi_low = rssi_low;
|
||||
bss_conf->cqm_rssi_high = rssi_high;
|
||||
bss_conf->cqm_rssi_thold = 0;
|
||||
bss_conf->cqm_rssi_hyst = 0;
|
||||
sdata->u.mgd.last_cqm_event_signal = 0;
|
||||
|
||||
/* tell the driver upon association, unless already associated */
|
||||
@@ -2658,6 +2729,21 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* If active validate the setting and reject it if it doesn't leave
|
||||
* at least one basic rate usable, since we really have to be able
|
||||
* to send something, and if we're an AP we have to be able to do
|
||||
* so at a basic rate so that all clients can receive it.
|
||||
*/
|
||||
if (rcu_access_pointer(sdata->vif.chanctx_conf) &&
|
||||
sdata->vif.bss_conf.chandef.chan) {
|
||||
u32 basic_rates = sdata->vif.bss_conf.basic_rates;
|
||||
enum nl80211_band band = sdata->vif.bss_conf.chandef.chan->band;
|
||||
|
||||
if (!(mask->control[band].legacy & basic_rates))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_NL80211_BANDS; i++) {
|
||||
struct ieee80211_supported_band *sband = wiphy->bands[i];
|
||||
int j;
|
||||
@@ -3639,6 +3725,7 @@ const struct cfg80211_ops mac80211_config_ops = {
|
||||
.mgmt_tx = ieee80211_mgmt_tx,
|
||||
.mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,
|
||||
.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
|
||||
.set_cqm_rssi_range_config = ieee80211_set_cqm_rssi_range_config,
|
||||
.mgmt_frame_register = ieee80211_mgmt_frame_register,
|
||||
.set_antenna = ieee80211_set_antenna,
|
||||
.get_antenna = ieee80211_get_antenna,
|
||||
|
@@ -425,7 +425,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
cfg80211_chandef_create(&chandef, cbss->channel,
|
||||
NL80211_CHAN_WIDTH_20_NOHT);
|
||||
NL80211_CHAN_NO_HT);
|
||||
chandef.width = sdata->u.ibss.chandef.width;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
@@ -437,7 +437,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
default:
|
||||
/* fall back to 20 MHz for unsupported modes */
|
||||
cfg80211_chandef_create(&chandef, cbss->channel,
|
||||
NL80211_CHAN_WIDTH_20_NOHT);
|
||||
NL80211_CHAN_NO_HT);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@@ -839,6 +839,8 @@ struct txq_info {
|
||||
struct ieee80211_if_mntr {
|
||||
u32 flags;
|
||||
u8 mu_follow_addr[ETH_ALEN] __aligned(2);
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1259,6 +1261,7 @@ struct ieee80211_local {
|
||||
|
||||
/* see iface.c */
|
||||
struct list_head interfaces;
|
||||
struct list_head mon_list; /* only that are IFF_UP && !cooked */
|
||||
struct mutex iflist_mtx;
|
||||
|
||||
/*
|
||||
|
@@ -676,7 +676,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
|
||||
|
||||
set_bit(SDATA_STATE_RUNNING, &sdata->state);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_WDS) {
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_WDS:
|
||||
/* Create STA entry for the WDS peer */
|
||||
sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr,
|
||||
GFP_KERNEL);
|
||||
@@ -697,8 +698,17 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
|
||||
|
||||
rate_control_rate_init(sta);
|
||||
netif_carrier_on(dev);
|
||||
} else if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) {
|
||||
break;
|
||||
case NL80211_IFTYPE_P2P_DEVICE:
|
||||
rcu_assign_pointer(local->p2p_sdata, sdata);
|
||||
break;
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES)
|
||||
break;
|
||||
list_add_tail_rcu(&sdata->u.mntr.list, &local->mon_list);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -817,6 +827,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
||||
case NL80211_IFTYPE_AP:
|
||||
cancel_work_sync(&sdata->u.ap.request_smps_work);
|
||||
break;
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES)
|
||||
break;
|
||||
list_del_rcu(&sdata->u.mntr.list);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@@ -603,6 +603,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
|
||||
ARRAY_SIZE(local->ext_capa);
|
||||
|
||||
INIT_LIST_HEAD(&local->interfaces);
|
||||
INIT_LIST_HEAD(&local->mon_list);
|
||||
|
||||
__hw_addr_init(&local->mc_list);
|
||||
|
||||
|
@@ -1100,8 +1100,14 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
|
||||
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
|
||||
return;
|
||||
|
||||
if (mesh_matches_local(sdata, &elems))
|
||||
mesh_neighbour_update(sdata, mgmt->sa, &elems);
|
||||
if (mesh_matches_local(sdata, &elems)) {
|
||||
mpl_dbg(sdata, "rssi_threshold=%d,rx_status->signal=%d\n",
|
||||
sdata->u.mesh.mshcfg.rssi_threshold, rx_status->signal);
|
||||
if (!sdata->u.mesh.user_mpm ||
|
||||
sdata->u.mesh.mshcfg.rssi_threshold == 0 ||
|
||||
sdata->u.mesh.mshcfg.rssi_threshold < rx_status->signal)
|
||||
mesh_neighbour_update(sdata, mgmt->sa, &elems);
|
||||
}
|
||||
|
||||
if (ifmsh->sync_ops)
|
||||
ifmsh->sync_ops->rx_bcn_presp(sdata,
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#define TEST_FRAME_LEN 8192
|
||||
#define MAX_METRIC 0xffffffff
|
||||
#define ARITH_SHIFT 8
|
||||
#define LINK_FAIL_THRESH 95
|
||||
|
||||
#define MAX_PREQ_QUEUE_LEN 64
|
||||
|
||||
@@ -307,10 +308,12 @@ void ieee80211s_update_metric(struct ieee80211_local *local,
|
||||
|
||||
failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK);
|
||||
|
||||
/* moving average, scaled to 100 */
|
||||
sta->mesh->fail_avg =
|
||||
((80 * sta->mesh->fail_avg + 5) / 100 + 20 * failed);
|
||||
if (sta->mesh->fail_avg > 95)
|
||||
/* moving average, scaled to 100.
|
||||
* feed failure as 100 and success as 0
|
||||
*/
|
||||
ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, failed * 100);
|
||||
if (ewma_mesh_fail_avg_read(&sta->mesh->fail_avg) >
|
||||
LINK_FAIL_THRESH)
|
||||
mesh_plink_broken(sta);
|
||||
}
|
||||
|
||||
@@ -325,6 +328,8 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
|
||||
int rate, err;
|
||||
u32 tx_time, estimated_retx;
|
||||
u64 result;
|
||||
unsigned long fail_avg =
|
||||
ewma_mesh_fail_avg_read(&sta->mesh->fail_avg);
|
||||
|
||||
/* Try to get rate based on HW/SW RC algorithm.
|
||||
* Rate is returned in units of Kbps, correct this
|
||||
@@ -336,7 +341,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
|
||||
if (rate) {
|
||||
err = 0;
|
||||
} else {
|
||||
if (sta->mesh->fail_avg >= 100)
|
||||
if (fail_avg > LINK_FAIL_THRESH)
|
||||
return MAX_METRIC;
|
||||
|
||||
sta_set_rate_info_tx(sta, &sta->tx_stats.last_rate, &rinfo);
|
||||
@@ -344,7 +349,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
|
||||
if (WARN_ON(!rate))
|
||||
return MAX_METRIC;
|
||||
|
||||
err = (sta->mesh->fail_avg << ARITH_SHIFT) / 100;
|
||||
err = (fail_avg << ARITH_SHIFT) / 100;
|
||||
}
|
||||
|
||||
/* bitrate is in units of 100 Kbps, while we need rate in units of
|
||||
@@ -484,6 +489,9 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
|
||||
? mpath->exp_time : exp_time;
|
||||
mesh_path_activate(mpath);
|
||||
spin_unlock_bh(&mpath->state_lock);
|
||||
ewma_mesh_fail_avg_init(&sta->mesh->fail_avg);
|
||||
/* init it at a low value - 0 start is tricky */
|
||||
ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, 1);
|
||||
mesh_path_tx_pending(mpath);
|
||||
/* draft says preq_id should be saved to, but there does
|
||||
* not seem to be any use for it, skipping by now
|
||||
@@ -522,6 +530,9 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
|
||||
? mpath->exp_time : exp_time;
|
||||
mesh_path_activate(mpath);
|
||||
spin_unlock_bh(&mpath->state_lock);
|
||||
ewma_mesh_fail_avg_init(&sta->mesh->fail_avg);
|
||||
/* init it at a low value - 0 start is tricky */
|
||||
ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, 1);
|
||||
mesh_path_tx_pending(mpath);
|
||||
} else
|
||||
spin_unlock_bh(&mpath->state_lock);
|
||||
|
@@ -397,11 +397,10 @@ struct mesh_path *mesh_path_new(struct ieee80211_sub_if_data *sdata,
|
||||
new_mpath->sdata = sdata;
|
||||
new_mpath->flags = 0;
|
||||
skb_queue_head_init(&new_mpath->frame_queue);
|
||||
new_mpath->timer.data = (unsigned long) new_mpath;
|
||||
new_mpath->timer.function = mesh_path_timer;
|
||||
new_mpath->exp_time = jiffies;
|
||||
spin_lock_init(&new_mpath->state_lock);
|
||||
init_timer(&new_mpath->timer);
|
||||
setup_timer(&new_mpath->timer, mesh_path_timer,
|
||||
(unsigned long) new_mpath);
|
||||
|
||||
return new_mpath;
|
||||
}
|
||||
@@ -829,6 +828,9 @@ void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop)
|
||||
mpath->flags = MESH_PATH_FIXED | MESH_PATH_SN_VALID;
|
||||
mesh_path_activate(mpath);
|
||||
spin_unlock_bh(&mpath->state_lock);
|
||||
ewma_mesh_fail_avg_init(&next_hop->mesh->fail_avg);
|
||||
/* init it at a low value - 0 start is tricky */
|
||||
ewma_mesh_fail_avg_add(&next_hop->mesh->fail_avg, 1);
|
||||
mesh_path_tx_pending(mpath);
|
||||
}
|
||||
|
||||
|
@@ -1908,6 +1908,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
|
||||
sdata->u.mgd.associated = cbss;
|
||||
memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN);
|
||||
|
||||
ieee80211_check_rate_mask(sdata);
|
||||
|
||||
sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
|
||||
|
||||
if (sdata->vif.p2p ||
|
||||
@@ -2797,8 +2799,9 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
|
||||
|
||||
sdata_info(sdata, "disassociated from %pM (Reason: %u)\n",
|
||||
mgmt->sa, reason_code);
|
||||
sdata_info(sdata, "disassociated from %pM (Reason: %u=%s)\n",
|
||||
mgmt->sa, reason_code,
|
||||
ieee80211_get_reason_code_string(reason_code));
|
||||
|
||||
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
|
||||
|
||||
@@ -2822,15 +2825,15 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
|
||||
*have_higher_than_11mbit = true;
|
||||
|
||||
/*
|
||||
* BSS_MEMBERSHIP_SELECTOR_HT_PHY is defined in 802.11n-2009
|
||||
* 7.3.2.2 as a magic value instead of a rate. Hence, skip it.
|
||||
* Skip HT and VHT BSS membership selectors since they're not
|
||||
* rates.
|
||||
*
|
||||
* Note: Even through the membership selector and the basic
|
||||
* Note: Even though the membership selector and the basic
|
||||
* rate flag share the same bit, they are not exactly
|
||||
* the same.
|
||||
*/
|
||||
if (!!(supp_rates[i] & 0x80) &&
|
||||
(supp_rates[i] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
|
||||
if (supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY) ||
|
||||
supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY))
|
||||
continue;
|
||||
|
||||
for (j = 0; j < sband->n_bitrates; j++) {
|
||||
@@ -3430,6 +3433,30 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
}
|
||||
|
||||
if (bss_conf->cqm_rssi_low &&
|
||||
ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
|
||||
int sig = -ewma_beacon_signal_read(&ifmgd->ave_beacon_signal);
|
||||
int last_event = ifmgd->last_cqm_event_signal;
|
||||
int low = bss_conf->cqm_rssi_low;
|
||||
int high = bss_conf->cqm_rssi_high;
|
||||
|
||||
if (sig < low &&
|
||||
(last_event == 0 || last_event >= low)) {
|
||||
ifmgd->last_cqm_event_signal = sig;
|
||||
ieee80211_cqm_rssi_notify(
|
||||
&sdata->vif,
|
||||
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
|
||||
sig, GFP_KERNEL);
|
||||
} else if (sig > high &&
|
||||
(last_event == 0 || last_event <= high)) {
|
||||
ifmgd->last_cqm_event_signal = sig;
|
||||
ieee80211_cqm_rssi_notify(
|
||||
&sdata->vif,
|
||||
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
|
||||
sig, GFP_KERNEL);
|
||||
}
|
||||
}
|
||||
|
||||
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) {
|
||||
mlme_dbg_ratelimited(sdata,
|
||||
"cancelling AP probe due to a received beacon\n");
|
||||
|
@@ -2,6 +2,7 @@
|
||||
* Copyright 2002-2005, Instant802 Networks, Inc.
|
||||
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||
* Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
|
||||
* Copyright 2017 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
|
||||
@@ -173,9 +174,11 @@ ieee80211_rate_control_ops_get(const char *name)
|
||||
/* try default if specific alg requested but not found */
|
||||
ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo);
|
||||
|
||||
/* try built-in one if specific alg requested but not found */
|
||||
if (!ops && strlen(CONFIG_MAC80211_RC_DEFAULT))
|
||||
/* Note: check for > 0 is intentional to avoid clang warning */
|
||||
if (!ops && (strlen(CONFIG_MAC80211_RC_DEFAULT) > 0))
|
||||
/* try built-in one if specific alg requested but not found */
|
||||
ops = ieee80211_try_rate_control_ops_get(CONFIG_MAC80211_RC_DEFAULT);
|
||||
|
||||
kernel_param_unlock(THIS_MODULE);
|
||||
|
||||
return ops;
|
||||
@@ -208,7 +211,6 @@ static struct rate_control_ref *rate_control_alloc(const char *name,
|
||||
ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL);
|
||||
if (!ref)
|
||||
return NULL;
|
||||
ref->local = local;
|
||||
ref->ops = ieee80211_rate_control_ops_get(name);
|
||||
if (!ref->ops)
|
||||
goto free;
|
||||
@@ -229,18 +231,45 @@ free:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rate_control_free(struct rate_control_ref *ctrl_ref)
|
||||
static void rate_control_free(struct ieee80211_local *local,
|
||||
struct rate_control_ref *ctrl_ref)
|
||||
{
|
||||
ctrl_ref->ops->free(ctrl_ref->priv);
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
debugfs_remove_recursive(ctrl_ref->local->debugfs.rcdir);
|
||||
ctrl_ref->local->debugfs.rcdir = NULL;
|
||||
debugfs_remove_recursive(local->debugfs.rcdir);
|
||||
local->debugfs.rcdir = NULL;
|
||||
#endif
|
||||
|
||||
kfree(ctrl_ref);
|
||||
}
|
||||
|
||||
void ieee80211_check_rate_mask(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_supported_band *sband;
|
||||
u32 user_mask, basic_rates = sdata->vif.bss_conf.basic_rates;
|
||||
enum nl80211_band band;
|
||||
|
||||
if (WARN_ON(!sdata->vif.bss_conf.chandef.chan))
|
||||
return;
|
||||
|
||||
if (WARN_ON_ONCE(!basic_rates))
|
||||
return;
|
||||
|
||||
band = sdata->vif.bss_conf.chandef.chan->band;
|
||||
user_mask = sdata->rc_rateidx_mask[band];
|
||||
sband = local->hw.wiphy->bands[band];
|
||||
|
||||
if (user_mask & basic_rates)
|
||||
return;
|
||||
|
||||
sdata_dbg(sdata,
|
||||
"no overlap between basic rates (0x%x) and user mask (0x%x on band %d) - clearing the latter",
|
||||
basic_rates, user_mask, band);
|
||||
sdata->rc_rateidx_mask[band] = (1 << sband->n_bitrates) - 1;
|
||||
}
|
||||
|
||||
static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc)
|
||||
{
|
||||
struct sk_buff *skb = txrc->skb;
|
||||
@@ -936,6 +965,6 @@ void rate_control_deinitialize(struct ieee80211_local *local)
|
||||
return;
|
||||
|
||||
local->rate_ctrl = NULL;
|
||||
rate_control_free(ref);
|
||||
rate_control_free(local, ref);
|
||||
}
|
||||
|
||||
|
@@ -20,7 +20,6 @@
|
||||
#include "driver-ops.h"
|
||||
|
||||
struct rate_control_ref {
|
||||
struct ieee80211_local *local;
|
||||
const struct rate_control_ops *ops;
|
||||
void *priv;
|
||||
};
|
||||
@@ -111,6 +110,8 @@ static inline void rate_control_remove_sta_debugfs(struct sta_info *sta)
|
||||
#endif
|
||||
}
|
||||
|
||||
void ieee80211_check_rate_mask(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
/* Get a reference to the rate control algorithm. If `name' is NULL, get the
|
||||
* first available algorithm. */
|
||||
int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
|
||||
|
@@ -95,24 +95,13 @@ static u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
|
||||
* This function cleans up the SKB, i.e. it removes all the stuff
|
||||
* only useful for monitoring.
|
||||
*/
|
||||
static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
|
||||
struct sk_buff *skb,
|
||||
unsigned int rtap_vendor_space)
|
||||
static void remove_monitor_info(struct sk_buff *skb,
|
||||
unsigned int present_fcs_len,
|
||||
unsigned int rtap_vendor_space)
|
||||
{
|
||||
if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) {
|
||||
if (likely(skb->len > FCS_LEN))
|
||||
__pskb_trim(skb, skb->len - FCS_LEN);
|
||||
else {
|
||||
/* driver bug */
|
||||
WARN_ON(1);
|
||||
dev_kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (present_fcs_len)
|
||||
__pskb_trim(skb, skb->len - present_fcs_len);
|
||||
__pskb_pull(skb, rtap_vendor_space);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len,
|
||||
@@ -534,8 +523,15 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
||||
* the SKB because it has a bad FCS/PLCP checksum.
|
||||
*/
|
||||
|
||||
if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS))
|
||||
if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) {
|
||||
if (unlikely(origskb->len <= FCS_LEN)) {
|
||||
/* driver bug */
|
||||
WARN_ON(1);
|
||||
dev_kfree_skb(origskb);
|
||||
return NULL;
|
||||
}
|
||||
present_fcs_len = FCS_LEN;
|
||||
}
|
||||
|
||||
/* ensure hdr->frame_control and vendor radiotap data are in skb head */
|
||||
if (!pskb_may_pull(origskb, 2 + rtap_vendor_space)) {
|
||||
@@ -550,7 +546,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return remove_monitor_info(local, origskb, rtap_vendor_space);
|
||||
remove_monitor_info(origskb, present_fcs_len,
|
||||
rtap_vendor_space);
|
||||
return origskb;
|
||||
}
|
||||
|
||||
/* room for the radiotap header based on driver features */
|
||||
@@ -580,9 +578,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
||||
* and FCS from the original.
|
||||
*/
|
||||
skb = skb_copy_expand(origskb, needed_headroom, 0, GFP_ATOMIC);
|
||||
|
||||
origskb = remove_monitor_info(local, origskb,
|
||||
rtap_vendor_space);
|
||||
remove_monitor_info(origskb, present_fcs_len,
|
||||
rtap_vendor_space);
|
||||
|
||||
if (!skb)
|
||||
return origskb;
|
||||
@@ -596,16 +593,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
skb->protocol = htons(ETH_P_802_2);
|
||||
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
|
||||
continue;
|
||||
|
||||
if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES)
|
||||
continue;
|
||||
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
continue;
|
||||
|
||||
list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) {
|
||||
if (prev_dev) {
|
||||
skb2 = skb_clone(skb, GFP_ATOMIC);
|
||||
if (skb2) {
|
||||
|
@@ -132,9 +132,9 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_vht_operation vht_oper = {
|
||||
.chan_width =
|
||||
wide_bw_chansw_ie->new_channel_width,
|
||||
.center_freq_seg1_idx =
|
||||
.center_freq_seg0_idx =
|
||||
wide_bw_chansw_ie->new_center_freq_seg0,
|
||||
.center_freq_seg2_idx =
|
||||
.center_freq_seg1_idx =
|
||||
wide_bw_chansw_ie->new_center_freq_seg1,
|
||||
/* .basic_mcs_set doesn't matter */
|
||||
};
|
||||
|
@@ -1960,14 +1960,17 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate,
|
||||
rinfo->bw = (rate & STA_STATS_RATE_BW_MASK) >>
|
||||
STA_STATS_RATE_BW_SHIFT;
|
||||
|
||||
if (rate & STA_STATS_RATE_VHT) {
|
||||
switch (rate & STA_STATS_RATE_TYPE_MASK) {
|
||||
case STA_STATS_RATE_TYPE_VHT:
|
||||
rinfo->flags = RATE_INFO_FLAGS_VHT_MCS;
|
||||
rinfo->mcs = rate & 0xf;
|
||||
rinfo->nss = (rate & 0xf0) >> 4;
|
||||
} else if (rate & STA_STATS_RATE_HT) {
|
||||
break;
|
||||
case STA_STATS_RATE_TYPE_HT:
|
||||
rinfo->flags = RATE_INFO_FLAGS_MCS;
|
||||
rinfo->mcs = rate & 0xff;
|
||||
} else if (rate & STA_STATS_RATE_LEGACY) {
|
||||
break;
|
||||
case STA_STATS_RATE_TYPE_LEGACY: {
|
||||
struct ieee80211_supported_band *sband;
|
||||
u16 brate;
|
||||
unsigned int shift;
|
||||
@@ -1982,6 +1985,8 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate,
|
||||
else
|
||||
shift = 0;
|
||||
rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rate & STA_STATS_RATE_SGI)
|
||||
|
@@ -324,6 +324,9 @@ struct ieee80211_fast_rx {
|
||||
struct rcu_head rcu_head;
|
||||
};
|
||||
|
||||
/* we use only values in the range 0-100, so pick a large precision */
|
||||
DECLARE_EWMA(mesh_fail_avg, 20, 8)
|
||||
|
||||
/**
|
||||
* struct mesh_sta - mesh STA information
|
||||
* @plink_lock: serialize access to plink fields
|
||||
@@ -369,7 +372,7 @@ struct mesh_sta {
|
||||
enum nl80211_mesh_power_mode nonpeer_pm;
|
||||
|
||||
/* moving percentage of failed MSDUs */
|
||||
unsigned int fail_avg;
|
||||
struct ewma_mesh_fail_avg fail_avg;
|
||||
};
|
||||
|
||||
DECLARE_EWMA(signal, 10, 8)
|
||||
@@ -725,9 +728,10 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta);
|
||||
unsigned long ieee80211_sta_last_active(struct sta_info *sta);
|
||||
|
||||
#define STA_STATS_RATE_INVALID 0
|
||||
#define STA_STATS_RATE_VHT 0x8000
|
||||
#define STA_STATS_RATE_HT 0x4000
|
||||
#define STA_STATS_RATE_LEGACY 0x2000
|
||||
#define STA_STATS_RATE_TYPE_MASK 0xC000
|
||||
#define STA_STATS_RATE_TYPE_LEGACY 0x4000
|
||||
#define STA_STATS_RATE_TYPE_HT 0x8000
|
||||
#define STA_STATS_RATE_TYPE_VHT 0xC000
|
||||
#define STA_STATS_RATE_SGI 0x1000
|
||||
#define STA_STATS_RATE_BW_SHIFT 9
|
||||
#define STA_STATS_RATE_BW_MASK (0x7 << STA_STATS_RATE_BW_SHIFT)
|
||||
@@ -753,11 +757,11 @@ static inline u16 sta_stats_encode_rate(struct ieee80211_rx_status *s)
|
||||
r |= STA_STATS_RATE_SGI;
|
||||
|
||||
if (s->flag & RX_FLAG_VHT)
|
||||
r |= STA_STATS_RATE_VHT | (s->vht_nss << 4);
|
||||
r |= STA_STATS_RATE_TYPE_VHT | (s->vht_nss << 4);
|
||||
else if (s->flag & RX_FLAG_HT)
|
||||
r |= STA_STATS_RATE_HT;
|
||||
r |= STA_STATS_RATE_TYPE_HT;
|
||||
else
|
||||
r |= STA_STATS_RATE_LEGACY | (s->band << 4);
|
||||
r |= STA_STATS_RATE_TYPE_LEGACY | (s->band << 4);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@@ -682,10 +682,6 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
|
||||
txrc.skb = tx->skb;
|
||||
txrc.reported_rate.idx = -1;
|
||||
txrc.rate_idx_mask = tx->sdata->rc_rateidx_mask[info->band];
|
||||
if (txrc.rate_idx_mask == (1 << sband->n_bitrates) - 1)
|
||||
txrc.max_rate_idx = -1;
|
||||
else
|
||||
txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1;
|
||||
|
||||
if (tx->sdata->rc_has_mcs_mask[info->band])
|
||||
txrc.rate_idx_mcs_mask =
|
||||
@@ -4249,10 +4245,6 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
|
||||
txrc.skb = skb;
|
||||
txrc.reported_rate.idx = -1;
|
||||
txrc.rate_idx_mask = sdata->rc_rateidx_mask[band];
|
||||
if (txrc.rate_idx_mask == (1 << txrc.sband->n_bitrates) - 1)
|
||||
txrc.max_rate_idx = -1;
|
||||
else
|
||||
txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1;
|
||||
txrc.bss = true;
|
||||
rate_control_get_rate(sdata, NULL, &txrc);
|
||||
|
||||
|
@@ -2413,13 +2413,13 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
|
||||
*pos++ = WLAN_EID_VHT_OPERATION;
|
||||
*pos++ = sizeof(struct ieee80211_vht_operation);
|
||||
vht_oper = (struct ieee80211_vht_operation *)pos;
|
||||
vht_oper->center_freq_seg1_idx = ieee80211_frequency_to_channel(
|
||||
vht_oper->center_freq_seg0_idx = ieee80211_frequency_to_channel(
|
||||
chandef->center_freq1);
|
||||
if (chandef->center_freq2)
|
||||
vht_oper->center_freq_seg2_idx =
|
||||
vht_oper->center_freq_seg1_idx =
|
||||
ieee80211_frequency_to_channel(chandef->center_freq2);
|
||||
else
|
||||
vht_oper->center_freq_seg2_idx = 0x00;
|
||||
vht_oper->center_freq_seg1_idx = 0x00;
|
||||
|
||||
switch (chandef->width) {
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
@@ -2428,11 +2428,11 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
|
||||
* workaround.
|
||||
*/
|
||||
vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
|
||||
vht_oper->center_freq_seg2_idx = vht_oper->center_freq_seg1_idx;
|
||||
vht_oper->center_freq_seg1_idx = vht_oper->center_freq_seg0_idx;
|
||||
if (chandef->chan->center_freq < chandef->center_freq1)
|
||||
vht_oper->center_freq_seg1_idx -= 8;
|
||||
vht_oper->center_freq_seg0_idx -= 8;
|
||||
else
|
||||
vht_oper->center_freq_seg1_idx += 8;
|
||||
vht_oper->center_freq_seg0_idx += 8;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
/*
|
||||
@@ -2491,9 +2491,9 @@ bool ieee80211_chandef_vht_oper(const struct ieee80211_vht_operation *oper,
|
||||
if (!oper)
|
||||
return false;
|
||||
|
||||
cf1 = ieee80211_channel_to_frequency(oper->center_freq_seg1_idx,
|
||||
cf1 = ieee80211_channel_to_frequency(oper->center_freq_seg0_idx,
|
||||
chandef->chan->band);
|
||||
cf2 = ieee80211_channel_to_frequency(oper->center_freq_seg2_idx,
|
||||
cf2 = ieee80211_channel_to_frequency(oper->center_freq_seg1_idx,
|
||||
chandef->chan->band);
|
||||
|
||||
switch (oper->chan_width) {
|
||||
@@ -2503,11 +2503,11 @@ bool ieee80211_chandef_vht_oper(const struct ieee80211_vht_operation *oper,
|
||||
new.width = NL80211_CHAN_WIDTH_80;
|
||||
new.center_freq1 = cf1;
|
||||
/* If needed, adjust based on the newer interop workaround. */
|
||||
if (oper->center_freq_seg2_idx) {
|
||||
if (oper->center_freq_seg1_idx) {
|
||||
unsigned int diff;
|
||||
|
||||
diff = abs(oper->center_freq_seg2_idx -
|
||||
oper->center_freq_seg1_idx);
|
||||
diff = abs(oper->center_freq_seg1_idx -
|
||||
oper->center_freq_seg0_idx);
|
||||
if (diff == 8) {
|
||||
new.width = NL80211_CHAN_WIDTH_160;
|
||||
new.center_freq1 = cf2;
|
||||
|
Reference in New Issue
Block a user