Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
This commit is contained in:
+55
-22
@@ -928,6 +928,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
||||
/* TODO: make hostapd tell us what it wants */
|
||||
sdata->smps_mode = IEEE80211_SMPS_OFF;
|
||||
sdata->needed_rx_chains = sdata->local->rx_chains;
|
||||
sdata->radar_required = params->radar_required;
|
||||
|
||||
err = ieee80211_vif_use_channel(sdata, ¶ms->chandef,
|
||||
IEEE80211_CHANCTX_SHARED);
|
||||
@@ -1251,19 +1252,16 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
||||
|
||||
if (params->ht_capa)
|
||||
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
|
||||
params->ht_capa,
|
||||
&sta->sta.ht_cap);
|
||||
params->ht_capa, sta);
|
||||
|
||||
if (params->vht_capa)
|
||||
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
|
||||
params->vht_capa,
|
||||
&sta->sta.vht_cap);
|
||||
params->vht_capa, sta);
|
||||
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
u32 changed = 0;
|
||||
if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) {
|
||||
u32 changed = 0;
|
||||
|
||||
switch (params->plink_state) {
|
||||
case NL80211_PLINK_ESTAB:
|
||||
if (sta->plink_state != NL80211_PLINK_ESTAB)
|
||||
@@ -1272,8 +1270,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
||||
sta->plink_state = params->plink_state;
|
||||
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
ieee80211_mps_set_sta_local_pm(sta,
|
||||
sdata->u.mesh.mshcfg.power_mode);
|
||||
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
||||
sdata->u.mesh.mshcfg.power_mode);
|
||||
break;
|
||||
case NL80211_PLINK_LISTEN:
|
||||
case NL80211_PLINK_BLOCKED:
|
||||
@@ -1287,26 +1285,29 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
||||
sta->plink_state = params->plink_state;
|
||||
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
changed |=
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
break;
|
||||
default:
|
||||
/* nothing */
|
||||
break;
|
||||
}
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
} else {
|
||||
switch (params->plink_action) {
|
||||
case PLINK_ACTION_OPEN:
|
||||
mesh_plink_open(sta);
|
||||
changed |= mesh_plink_open(sta);
|
||||
break;
|
||||
case PLINK_ACTION_BLOCK:
|
||||
mesh_plink_block(sta);
|
||||
changed |= mesh_plink_block(sta);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (params->local_pm)
|
||||
ieee80211_mps_set_sta_local_pm(sta, params->local_pm);
|
||||
changed |=
|
||||
ieee80211_mps_set_sta_local_pm(sta,
|
||||
params->local_pm);
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1411,9 +1412,11 @@ static int ieee80211_change_station(struct wiphy *wiphy,
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* in station mode, supported rates are only valid with TDLS */
|
||||
/* in station mode, some updates are only valid with TDLS */
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
|
||||
params->supported_rates &&
|
||||
(params->supported_rates || params->ht_capa || params->vht_capa ||
|
||||
params->sta_modify_mask ||
|
||||
(params->sta_flags_mask & BIT(NL80211_STA_FLAG_WME))) &&
|
||||
!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
return -EINVAL;
|
||||
@@ -1799,11 +1802,10 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
|
||||
conf->power_mode = nconf->power_mode;
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
}
|
||||
if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask)) {
|
||||
if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask))
|
||||
conf->dot11MeshAwakeWindowDuration =
|
||||
nconf->dot11MeshAwakeWindowDuration;
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
|
||||
}
|
||||
ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1829,9 +1831,7 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ieee80211_start_mesh(sdata);
|
||||
|
||||
return 0;
|
||||
return ieee80211_start_mesh(sdata);
|
||||
}
|
||||
|
||||
static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
|
||||
@@ -2396,7 +2396,8 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
|
||||
INIT_LIST_HEAD(&roc->dependents);
|
||||
|
||||
/* if there's one pending or we're scanning, queue this one */
|
||||
if (!list_empty(&local->roc_list) || local->scanning)
|
||||
if (!list_empty(&local->roc_list) ||
|
||||
local->scanning || local->radar_detect_enabled)
|
||||
goto out_check_combine;
|
||||
|
||||
/* if not HW assist, just queue & schedule work */
|
||||
@@ -2646,6 +2647,37 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
|
||||
return ieee80211_cancel_roc(local, cookie, false);
|
||||
}
|
||||
|
||||
static int ieee80211_start_radar_detection(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
unsigned long timeout;
|
||||
int err;
|
||||
|
||||
if (!list_empty(&local->roc_list) || local->scanning)
|
||||
return -EBUSY;
|
||||
|
||||
/* whatever, but channel contexts should not complain about that one */
|
||||
sdata->smps_mode = IEEE80211_SMPS_OFF;
|
||||
sdata->needed_rx_chains = local->rx_chains;
|
||||
sdata->radar_required = true;
|
||||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
err = ieee80211_vif_use_channel(sdata, chandef,
|
||||
IEEE80211_CHANCTX_SHARED);
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
|
||||
ieee80211_queue_delayed_work(&sdata->local->hw,
|
||||
&sdata->dfs_cac_timer_work, timeout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
struct ieee80211_channel *chan, bool offchan,
|
||||
unsigned int wait, const u8 *buf, size_t len,
|
||||
@@ -3351,4 +3383,5 @@ struct cfg80211_ops mac80211_config_ops = {
|
||||
.get_et_stats = ieee80211_get_et_stats,
|
||||
.get_et_strings = ieee80211_get_et_strings,
|
||||
.get_channel = ieee80211_cfg_get_channel,
|
||||
.start_radar_detection = ieee80211_start_radar_detection,
|
||||
};
|
||||
|
||||
+93
-5
@@ -9,7 +9,7 @@
|
||||
#include "ieee80211_i.h"
|
||||
#include "driver-ops.h"
|
||||
|
||||
static void ieee80211_change_chandef(struct ieee80211_local *local,
|
||||
static void ieee80211_change_chanctx(struct ieee80211_local *local,
|
||||
struct ieee80211_chanctx *ctx,
|
||||
const struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
@@ -49,7 +49,7 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
|
||||
if (!compat)
|
||||
continue;
|
||||
|
||||
ieee80211_change_chandef(local, ctx, compat);
|
||||
ieee80211_change_chanctx(local, ctx, compat);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
@@ -137,7 +137,10 @@ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
ieee80211_recalc_txpower(sdata);
|
||||
sdata->vif.bss_conf.idle = false;
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
|
||||
sdata->vif.type != NL80211_IFTYPE_MONITOR)
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -172,7 +175,7 @@ static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
|
||||
if (WARN_ON_ONCE(!compat))
|
||||
return;
|
||||
|
||||
ieee80211_change_chandef(local, ctx, compat);
|
||||
ieee80211_change_chanctx(local, ctx, compat);
|
||||
}
|
||||
|
||||
static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
|
||||
@@ -186,13 +189,17 @@ static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
|
||||
rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
|
||||
|
||||
sdata->vif.bss_conf.idle = true;
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
|
||||
sdata->vif.type != NL80211_IFTYPE_MONITOR)
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
|
||||
|
||||
drv_unassign_vif_chanctx(local, sdata, ctx);
|
||||
|
||||
if (ctx->refcount > 0) {
|
||||
ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
|
||||
ieee80211_recalc_smps_chanctx(local, ctx);
|
||||
ieee80211_recalc_radar_chanctx(local, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,6 +223,37 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
|
||||
ieee80211_free_chanctx(local, ctx);
|
||||
}
|
||||
|
||||
void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
|
||||
struct ieee80211_chanctx *chanctx)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
bool radar_enabled = false;
|
||||
|
||||
lockdep_assert_held(&local->chanctx_mtx);
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
if (sdata->radar_required) {
|
||||
radar_enabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (radar_enabled == chanctx->conf.radar_enabled)
|
||||
return;
|
||||
|
||||
chanctx->conf.radar_enabled = radar_enabled;
|
||||
local->radar_detect_enabled = chanctx->conf.radar_enabled;
|
||||
|
||||
if (!local->use_chanctx) {
|
||||
local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
|
||||
}
|
||||
|
||||
drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
|
||||
}
|
||||
|
||||
void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
|
||||
struct ieee80211_chanctx *chanctx)
|
||||
{
|
||||
@@ -331,6 +369,56 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
|
||||
ieee80211_recalc_smps_chanctx(local, ctx);
|
||||
ieee80211_recalc_radar_chanctx(local, ctx);
|
||||
out:
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
u32 *changed)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx_conf *conf;
|
||||
struct ieee80211_chanctx *ctx;
|
||||
int ret;
|
||||
|
||||
if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
|
||||
IEEE80211_CHAN_DISABLED))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT ||
|
||||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
lockdep_is_held(&local->chanctx_mtx));
|
||||
if (!conf) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx = container_of(conf, struct ieee80211_chanctx, conf);
|
||||
if (!cfg80211_chandef_compatible(&conf->def, chandef)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sdata->vif.bss_conf.chandef = *chandef;
|
||||
|
||||
ieee80211_recalc_chanctx_chantype(local, ctx);
|
||||
|
||||
*changed |= BSS_CHANGED_BANDWIDTH;
|
||||
ret = 0;
|
||||
out:
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
return ret;
|
||||
|
||||
@@ -207,13 +207,16 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON |
|
||||
BSS_CHANGED_BEACON_ENABLED) &&
|
||||
sdata->vif.type != NL80211_IFTYPE_AP &&
|
||||
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||
sdata->vif.type != NL80211_IFTYPE_MESH_POINT);
|
||||
WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE &&
|
||||
changed & ~BSS_CHANGED_IDLE);
|
||||
if (WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON |
|
||||
BSS_CHANGED_BEACON_ENABLED) &&
|
||||
sdata->vif.type != NL80211_IFTYPE_AP &&
|
||||
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||
sdata->vif.type != NL80211_IFTYPE_MESH_POINT))
|
||||
return;
|
||||
|
||||
if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
|
||||
sdata->vif.type == NL80211_IFTYPE_MONITOR))
|
||||
return;
|
||||
|
||||
check_sdata_in_driver(sdata);
|
||||
|
||||
|
||||
+77
-33
@@ -37,6 +37,9 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
|
||||
u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask);
|
||||
int i;
|
||||
|
||||
if (!ht_cap->ht_supported)
|
||||
return;
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION) {
|
||||
/* AP interfaces call this code when adding new stations,
|
||||
* so just silently ignore non station interfaces.
|
||||
@@ -89,22 +92,24 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
|
||||
|
||||
void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_ht_cap *ht_cap_ie,
|
||||
struct ieee80211_sta_ht_cap *ht_cap)
|
||||
const struct ieee80211_ht_cap *ht_cap_ie,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
struct ieee80211_sta_ht_cap ht_cap;
|
||||
u8 ampdu_info, tx_mcs_set_cap;
|
||||
int i, max_tx_streams;
|
||||
bool changed;
|
||||
enum ieee80211_sta_rx_bandwidth bw;
|
||||
enum ieee80211_smps_mode smps_mode;
|
||||
|
||||
BUG_ON(!ht_cap);
|
||||
|
||||
memset(ht_cap, 0, sizeof(*ht_cap));
|
||||
memset(&ht_cap, 0, sizeof(ht_cap));
|
||||
|
||||
if (!ht_cap_ie || !sband->ht_cap.ht_supported)
|
||||
return;
|
||||
goto apply;
|
||||
|
||||
ht_cap->ht_supported = true;
|
||||
ht_cap.ht_supported = true;
|
||||
|
||||
/*
|
||||
* The bits listed in this expression should be
|
||||
@@ -112,7 +117,7 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
* advertises more then we can't use those thus
|
||||
* we mask them out.
|
||||
*/
|
||||
ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) &
|
||||
ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) &
|
||||
(sband->ht_cap.cap |
|
||||
~(IEEE80211_HT_CAP_LDPC_CODING |
|
||||
IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
|
||||
@@ -121,44 +126,30 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
IEEE80211_HT_CAP_SGI_40 |
|
||||
IEEE80211_HT_CAP_DSSSCCK40));
|
||||
|
||||
/* Unset 40 MHz if we're not using a 40 MHz channel */
|
||||
switch (sdata->vif.bss_conf.chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
ht_cap->cap &= ~IEEE80211_HT_CAP_SGI_40;
|
||||
ht_cap->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* The STBC bits are asymmetric -- if we don't have
|
||||
* TX then mask out the peer's RX and vice versa.
|
||||
*/
|
||||
if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC))
|
||||
ht_cap->cap &= ~IEEE80211_HT_CAP_RX_STBC;
|
||||
ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC;
|
||||
if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC))
|
||||
ht_cap->cap &= ~IEEE80211_HT_CAP_TX_STBC;
|
||||
ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC;
|
||||
|
||||
ampdu_info = ht_cap_ie->ampdu_params_info;
|
||||
ht_cap->ampdu_factor =
|
||||
ht_cap.ampdu_factor =
|
||||
ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR;
|
||||
ht_cap->ampdu_density =
|
||||
ht_cap.ampdu_density =
|
||||
(ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
|
||||
|
||||
/* own MCS TX capabilities */
|
||||
tx_mcs_set_cap = sband->ht_cap.mcs.tx_params;
|
||||
|
||||
/* Copy peer MCS TX capabilities, the driver might need them. */
|
||||
ht_cap->mcs.tx_params = ht_cap_ie->mcs.tx_params;
|
||||
ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params;
|
||||
|
||||
/* can we TX with MCS rates? */
|
||||
if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED))
|
||||
return;
|
||||
goto apply;
|
||||
|
||||
/* Counting from 0, therefore +1 */
|
||||
if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF)
|
||||
@@ -176,25 +167,75 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
* - remainder are multiple spatial streams using unequal modulation
|
||||
*/
|
||||
for (i = 0; i < max_tx_streams; i++)
|
||||
ht_cap->mcs.rx_mask[i] =
|
||||
ht_cap.mcs.rx_mask[i] =
|
||||
sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
|
||||
|
||||
if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)
|
||||
for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;
|
||||
i < IEEE80211_HT_MCS_MASK_LEN; i++)
|
||||
ht_cap->mcs.rx_mask[i] =
|
||||
ht_cap.mcs.rx_mask[i] =
|
||||
sband->ht_cap.mcs.rx_mask[i] &
|
||||
ht_cap_ie->mcs.rx_mask[i];
|
||||
|
||||
/* handle MCS rate 32 too */
|
||||
if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
|
||||
ht_cap->mcs.rx_mask[32/8] |= 1;
|
||||
ht_cap.mcs.rx_mask[32/8] |= 1;
|
||||
|
||||
apply:
|
||||
/*
|
||||
* If user has specified capability over-rides, take care
|
||||
* of that here.
|
||||
*/
|
||||
ieee80211_apply_htcap_overrides(sdata, ht_cap);
|
||||
ieee80211_apply_htcap_overrides(sdata, &ht_cap);
|
||||
|
||||
changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
|
||||
|
||||
memcpy(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
|
||||
|
||||
switch (sdata->vif.bss_conf.chandef.width) {
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
/* fall through */
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
bw = IEEE80211_STA_RX_BW_20;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
|
||||
break;
|
||||
}
|
||||
|
||||
if (bw != sta->sta.bandwidth)
|
||||
changed = true;
|
||||
sta->sta.bandwidth = bw;
|
||||
|
||||
sta->cur_max_bandwidth =
|
||||
ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
|
||||
|
||||
switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS)
|
||||
>> IEEE80211_HT_CAP_SM_PS_SHIFT) {
|
||||
case WLAN_HT_CAP_SM_PS_INVALID:
|
||||
case WLAN_HT_CAP_SM_PS_STATIC:
|
||||
smps_mode = IEEE80211_SMPS_STATIC;
|
||||
break;
|
||||
case WLAN_HT_CAP_SM_PS_DYNAMIC:
|
||||
smps_mode = IEEE80211_SMPS_DYNAMIC;
|
||||
break;
|
||||
case WLAN_HT_CAP_SM_PS_DISABLED:
|
||||
smps_mode = IEEE80211_SMPS_OFF;
|
||||
break;
|
||||
}
|
||||
|
||||
if (smps_mode != sta->sta.smps_mode)
|
||||
changed = true;
|
||||
sta->sta.smps_mode = smps_mode;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
|
||||
@@ -406,6 +447,9 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,
|
||||
if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
|
||||
smps_mode = IEEE80211_SMPS_AUTOMATIC;
|
||||
|
||||
if (sdata->u.mgd.driver_smps_mode == smps_mode)
|
||||
return;
|
||||
|
||||
sdata->u.mgd.driver_smps_mode = smps_mode;
|
||||
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
|
||||
+7
-14
@@ -496,33 +496,26 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
||||
if (sta && elems->ht_operation && elems->ht_cap_elem &&
|
||||
sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) {
|
||||
/* we both use HT */
|
||||
struct ieee80211_sta_ht_cap sta_ht_cap_new;
|
||||
struct ieee80211_ht_cap htcap_ie;
|
||||
struct cfg80211_chan_def chandef;
|
||||
|
||||
ieee80211_ht_oper_to_chandef(channel,
|
||||
elems->ht_operation,
|
||||
&chandef);
|
||||
|
||||
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
|
||||
elems->ht_cap_elem,
|
||||
&sta_ht_cap_new);
|
||||
memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
|
||||
|
||||
/*
|
||||
* fall back to HT20 if we don't use or use
|
||||
* the other extension channel
|
||||
*/
|
||||
if (chandef.width != NL80211_CHAN_WIDTH_40 ||
|
||||
cfg80211_get_chandef_type(&chandef) !=
|
||||
if (cfg80211_get_chandef_type(&chandef) !=
|
||||
sdata->u.ibss.channel_type)
|
||||
sta_ht_cap_new.cap &=
|
||||
~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
||||
htcap_ie.cap_info &=
|
||||
cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
|
||||
|
||||
if (memcmp(&sta->sta.ht_cap, &sta_ht_cap_new,
|
||||
sizeof(sta_ht_cap_new))) {
|
||||
memcpy(&sta->sta.ht_cap, &sta_ht_cap_new,
|
||||
sizeof(sta_ht_cap_new));
|
||||
rates_updated = true;
|
||||
}
|
||||
rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(
|
||||
sdata, sband, &htcap_ie, sta);
|
||||
}
|
||||
|
||||
if (sta && rates_updated) {
|
||||
|
||||
+75
-46
@@ -343,6 +343,7 @@ struct ieee80211_mgd_auth_data {
|
||||
u8 key[WLAN_KEY_LEN_WEP104];
|
||||
u8 key_len, key_idx;
|
||||
bool done;
|
||||
bool timeout_started;
|
||||
|
||||
u16 sae_trans, sae_status;
|
||||
size_t data_len;
|
||||
@@ -364,6 +365,7 @@ struct ieee80211_mgd_assoc_data {
|
||||
bool wmm, uapsd;
|
||||
bool have_beacon, need_beacon;
|
||||
bool synced;
|
||||
bool timeout_started;
|
||||
|
||||
u8 ap_ht_param;
|
||||
|
||||
@@ -578,6 +580,9 @@ struct ieee80211_if_mesh {
|
||||
u32 mesh_seqnum;
|
||||
bool accepting_plinks;
|
||||
int num_gates;
|
||||
struct beacon_data __rcu *beacon;
|
||||
/* just protects beacon updates for now */
|
||||
struct mutex mtx;
|
||||
const u8 *ie;
|
||||
u8 ie_len;
|
||||
enum {
|
||||
@@ -722,6 +727,9 @@ struct ieee80211_sub_if_data {
|
||||
int user_power_level; /* in dBm */
|
||||
int ap_power_level; /* in dBm */
|
||||
|
||||
bool radar_required;
|
||||
struct delayed_work dfs_cac_timer_work;
|
||||
|
||||
/*
|
||||
* AP this belongs to: self in AP mode and
|
||||
* corresponding AP in VLAN mode, NULL for
|
||||
@@ -942,6 +950,10 @@ struct ieee80211_local {
|
||||
/* wowlan is enabled -- don't reconfig on resume */
|
||||
bool wowlan;
|
||||
|
||||
/* DFS/radar detection is enabled */
|
||||
bool radar_detect_enabled;
|
||||
struct work_struct radar_detected_work;
|
||||
|
||||
/* number of RX chains the hardware has */
|
||||
u8 rx_chains;
|
||||
|
||||
@@ -1154,41 +1166,41 @@ struct ieee80211_ra_tid {
|
||||
|
||||
/* Parsed Information Elements */
|
||||
struct ieee802_11_elems {
|
||||
u8 *ie_start;
|
||||
const u8 *ie_start;
|
||||
size_t total_len;
|
||||
|
||||
/* pointers to IEs */
|
||||
u8 *ssid;
|
||||
u8 *supp_rates;
|
||||
u8 *fh_params;
|
||||
u8 *ds_params;
|
||||
u8 *cf_params;
|
||||
struct ieee80211_tim_ie *tim;
|
||||
u8 *ibss_params;
|
||||
u8 *challenge;
|
||||
u8 *wpa;
|
||||
u8 *rsn;
|
||||
u8 *erp_info;
|
||||
u8 *ext_supp_rates;
|
||||
u8 *wmm_info;
|
||||
u8 *wmm_param;
|
||||
struct ieee80211_ht_cap *ht_cap_elem;
|
||||
struct ieee80211_ht_operation *ht_operation;
|
||||
struct ieee80211_vht_cap *vht_cap_elem;
|
||||
struct ieee80211_vht_operation *vht_operation;
|
||||
struct ieee80211_meshconf_ie *mesh_config;
|
||||
u8 *mesh_id;
|
||||
u8 *peering;
|
||||
__le16 *awake_window;
|
||||
u8 *preq;
|
||||
u8 *prep;
|
||||
u8 *perr;
|
||||
struct ieee80211_rann_ie *rann;
|
||||
struct ieee80211_channel_sw_ie *ch_switch_ie;
|
||||
u8 *country_elem;
|
||||
u8 *pwr_constr_elem;
|
||||
u8 *quiet_elem; /* first quite element */
|
||||
u8 *timeout_int;
|
||||
const u8 *ssid;
|
||||
const u8 *supp_rates;
|
||||
const u8 *fh_params;
|
||||
const u8 *ds_params;
|
||||
const u8 *cf_params;
|
||||
const struct ieee80211_tim_ie *tim;
|
||||
const u8 *ibss_params;
|
||||
const u8 *challenge;
|
||||
const u8 *rsn;
|
||||
const u8 *erp_info;
|
||||
const u8 *ext_supp_rates;
|
||||
const u8 *wmm_info;
|
||||
const u8 *wmm_param;
|
||||
const struct ieee80211_ht_cap *ht_cap_elem;
|
||||
const struct ieee80211_ht_operation *ht_operation;
|
||||
const struct ieee80211_vht_cap *vht_cap_elem;
|
||||
const struct ieee80211_vht_operation *vht_operation;
|
||||
const struct ieee80211_meshconf_ie *mesh_config;
|
||||
const u8 *mesh_id;
|
||||
const u8 *peering;
|
||||
const __le16 *awake_window;
|
||||
const u8 *preq;
|
||||
const u8 *prep;
|
||||
const u8 *perr;
|
||||
const struct ieee80211_rann_ie *rann;
|
||||
const struct ieee80211_channel_sw_ie *ch_switch_ie;
|
||||
const u8 *country_elem;
|
||||
const u8 *pwr_constr_elem;
|
||||
const u8 *quiet_elem; /* first quite element */
|
||||
const u8 *timeout_int;
|
||||
const u8 *opmode_notif;
|
||||
|
||||
/* length of them, respectively */
|
||||
u8 ssid_len;
|
||||
@@ -1199,7 +1211,6 @@ struct ieee802_11_elems {
|
||||
u8 tim_len;
|
||||
u8 ibss_params_len;
|
||||
u8 challenge_len;
|
||||
u8 wpa_len;
|
||||
u8 rsn_len;
|
||||
u8 erp_info_len;
|
||||
u8 ext_supp_rates_len;
|
||||
@@ -1268,10 +1279,10 @@ void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata);
|
||||
int ieee80211_max_network_latency(struct notifier_block *nb,
|
||||
unsigned long data, void *dummy);
|
||||
int ieee80211_set_arp_filter(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_channel_sw_ie *sw_elem,
|
||||
struct ieee80211_bss *bss,
|
||||
u64 timestamp);
|
||||
void
|
||||
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
const struct ieee80211_channel_sw_ie *sw_elem,
|
||||
struct ieee80211_bss *bss, u64 timestamp);
|
||||
void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata);
|
||||
@@ -1377,10 +1388,10 @@ void ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
|
||||
/* HT */
|
||||
void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_sta_ht_cap *ht_cap);
|
||||
void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_ht_cap *ht_cap_ie,
|
||||
struct ieee80211_sta_ht_cap *ht_cap);
|
||||
const struct ieee80211_ht_cap *ht_cap_ie,
|
||||
struct sta_info *sta);
|
||||
void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *da, u16 tid,
|
||||
u16 initiator, u16 reason_code);
|
||||
@@ -1420,10 +1431,17 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid);
|
||||
u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs);
|
||||
|
||||
/* VHT */
|
||||
void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_vht_cap *vht_cap_ie,
|
||||
struct ieee80211_sta_vht_cap *vht_cap);
|
||||
void
|
||||
ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_supported_band *sband,
|
||||
const struct ieee80211_vht_cap *vht_cap_ie,
|
||||
struct sta_info *sta);
|
||||
enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta);
|
||||
void ieee80211_sta_set_rx_nss(struct sta_info *sta);
|
||||
void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
|
||||
struct sta_info *sta, u8 opmode,
|
||||
enum ieee80211_band band, bool nss_only);
|
||||
|
||||
/* Spectrum management */
|
||||
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
@@ -1541,7 +1559,7 @@ static inline void ieee80211_add_pending_skbs(struct ieee80211_local *local,
|
||||
|
||||
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
|
||||
u16 transaction, u16 auth_alg, u16 status,
|
||||
u8 *extra, size_t extra_len, const u8 *bssid,
|
||||
const u8 *extra, size_t extra_len, const u8 *bssid,
|
||||
const u8 *da, const u8 *key, u8 key_len, u8 key_idx,
|
||||
u32 tx_flags);
|
||||
void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||
@@ -1592,13 +1610,17 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
/* channel management */
|
||||
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
|
||||
struct ieee80211_ht_operation *ht_oper,
|
||||
const struct ieee80211_ht_operation *ht_oper,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
|
||||
int __must_check
|
||||
ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
enum ieee80211_chanctx_mode mode);
|
||||
int __must_check
|
||||
ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
u32 *changed);
|
||||
void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
|
||||
@@ -1606,6 +1628,13 @@ void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
|
||||
struct ieee80211_chanctx *chanctx);
|
||||
void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
|
||||
struct ieee80211_chanctx *chanctx);
|
||||
|
||||
void ieee80211_dfs_cac_timer(unsigned long data);
|
||||
void ieee80211_dfs_cac_timer_work(struct work_struct *work);
|
||||
void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
|
||||
void ieee80211_dfs_radar_detected_work(struct work_struct *work);
|
||||
|
||||
#ifdef CONFIG_MAC80211_NOINLINE
|
||||
#define debug_noinline noinline
|
||||
|
||||
@@ -680,6 +680,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, *tmp;
|
||||
u32 hw_reconf_flags = 0;
|
||||
int i, flushed;
|
||||
struct ps_data *ps;
|
||||
|
||||
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
|
||||
|
||||
@@ -749,6 +750,16 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
cancel_work_sync(&sdata->recalc_smps);
|
||||
|
||||
cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
|
||||
|
||||
if (sdata->wdev.cac_started) {
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
|
||||
/* APs need special treatment */
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP) {
|
||||
struct ieee80211_sub_if_data *vlan, *tmpsdata;
|
||||
@@ -758,6 +769,19 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
||||
u.vlan.list)
|
||||
dev_close(vlan->dev);
|
||||
WARN_ON(!list_empty(&sdata->u.ap.vlans));
|
||||
} else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
|
||||
/* remove all packets in parent bc_buf pointing to this dev */
|
||||
ps = &sdata->bss->ps;
|
||||
|
||||
spin_lock_irqsave(&ps->bc_buf.lock, flags);
|
||||
skb_queue_walk_safe(&ps->bc_buf, skb, tmp) {
|
||||
if (skb->dev == sdata->dev) {
|
||||
__skb_unlink(skb, &ps->bc_buf);
|
||||
local->total_ps_buffered--;
|
||||
ieee80211_free_txskb(&local->hw, skb);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&ps->bc_buf.lock, flags);
|
||||
} else if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
||||
ieee80211_mgd_stop(sdata);
|
||||
}
|
||||
@@ -1513,6 +1537,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
|
||||
spin_lock_init(&sdata->cleanup_stations_lock);
|
||||
INIT_LIST_HEAD(&sdata->cleanup_stations);
|
||||
INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);
|
||||
INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work,
|
||||
ieee80211_dfs_cac_timer_work);
|
||||
|
||||
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
|
||||
struct ieee80211_supported_band *sband;
|
||||
|
||||
+23
-2
@@ -501,6 +501,11 @@ static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = {
|
||||
},
|
||||
};
|
||||
|
||||
static const u8 extended_capabilities[] = {
|
||||
0, 0, 0, 0, 0, 0, 0,
|
||||
WLAN_EXT_CAPA8_OPMODE_NOTIF,
|
||||
};
|
||||
|
||||
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
const struct ieee80211_ops *ops)
|
||||
{
|
||||
@@ -557,14 +562,17 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
WIPHY_FLAG_REPORTS_OBSS |
|
||||
WIPHY_FLAG_OFFCHAN_TX;
|
||||
|
||||
wiphy->extended_capabilities = extended_capabilities;
|
||||
wiphy->extended_capabilities_mask = extended_capabilities;
|
||||
wiphy->extended_capabilities_len = ARRAY_SIZE(extended_capabilities);
|
||||
|
||||
if (ops->remain_on_channel)
|
||||
wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
|
||||
|
||||
wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
|
||||
NL80211_FEATURE_SAE |
|
||||
NL80211_FEATURE_HT_IBSS |
|
||||
NL80211_FEATURE_VIF_TXPOWER |
|
||||
NL80211_FEATURE_FULL_AP_CLIENT_STATE;
|
||||
NL80211_FEATURE_VIF_TXPOWER;
|
||||
|
||||
if (!ops->hw_scan)
|
||||
wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
|
||||
@@ -621,6 +629,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
|
||||
INIT_WORK(&local->restart_work, ieee80211_restart_work);
|
||||
|
||||
INIT_WORK(&local->radar_detected_work,
|
||||
ieee80211_dfs_radar_detected_work);
|
||||
|
||||
INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
|
||||
local->smps_mode = IEEE80211_SMPS_OFF;
|
||||
|
||||
@@ -713,6 +724,16 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||
*/
|
||||
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))
|
||||
return -EINVAL;
|
||||
|
||||
/* DFS currently not supported with channel context drivers */
|
||||
for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
|
||||
const struct ieee80211_iface_combination *comb;
|
||||
|
||||
comb = &local->hw.wiphy->iface_combinations[i];
|
||||
|
||||
if (comb->radar_detect_widths)
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only HW csum features are currently compatible with mac80211 */
|
||||
|
||||
+212
-4
@@ -171,7 +171,7 @@ void mesh_sta_cleanup(struct sta_info *sta)
|
||||
}
|
||||
|
||||
if (changed)
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
ieee80211_mbss_info_change_notify(sdata, changed);
|
||||
}
|
||||
|
||||
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
|
||||
@@ -593,7 +593,7 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
|
||||
mesh_path_expire(sdata);
|
||||
|
||||
changed = mesh_accept_plinks_update(sdata);
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
ieee80211_mbss_info_change_notify(sdata, changed);
|
||||
|
||||
mod_timer(&ifmsh->housekeeping_timer,
|
||||
round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL));
|
||||
@@ -644,7 +644,140 @@ void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
|
||||
}
|
||||
#endif
|
||||
|
||||
void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
|
||||
static int
|
||||
ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
|
||||
{
|
||||
struct beacon_data *bcn;
|
||||
int head_len, tail_len;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
enum ieee80211_band band;
|
||||
u8 *pos;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) +
|
||||
sizeof(mgmt->u.beacon);
|
||||
|
||||
sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh);
|
||||
rcu_read_lock();
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
band = chanctx_conf->def.chan->band;
|
||||
rcu_read_unlock();
|
||||
|
||||
head_len = hdr_len +
|
||||
2 + /* NULL SSID */
|
||||
2 + 8 + /* supported rates */
|
||||
2 + 3; /* DS params */
|
||||
tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
|
||||
2 + sizeof(struct ieee80211_ht_cap) +
|
||||
2 + sizeof(struct ieee80211_ht_operation) +
|
||||
2 + ifmsh->mesh_id_len +
|
||||
2 + sizeof(struct ieee80211_meshconf_ie) +
|
||||
2 + sizeof(__le16) + /* awake window */
|
||||
ifmsh->ie_len;
|
||||
|
||||
bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL);
|
||||
/* need an skb for IE builders to operate on */
|
||||
skb = dev_alloc_skb(max(head_len, tail_len));
|
||||
|
||||
if (!bcn || !skb)
|
||||
goto out_free;
|
||||
|
||||
/*
|
||||
* pointers go into the block we allocated,
|
||||
* memory is | beacon_data | head | tail |
|
||||
*/
|
||||
bcn->head = ((u8 *) bcn) + sizeof(*bcn);
|
||||
|
||||
/* fill in the head */
|
||||
mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
|
||||
memset(mgmt, 0, hdr_len);
|
||||
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
||||
IEEE80211_STYPE_BEACON);
|
||||
eth_broadcast_addr(mgmt->da);
|
||||
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
||||
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
|
||||
ieee80211_mps_set_frame_flags(sdata, NULL, (void *) mgmt);
|
||||
mgmt->u.beacon.beacon_int =
|
||||
cpu_to_le16(sdata->vif.bss_conf.beacon_int);
|
||||
mgmt->u.beacon.capab_info |= cpu_to_le16(
|
||||
sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0);
|
||||
|
||||
pos = skb_put(skb, 2);
|
||||
*pos++ = WLAN_EID_SSID;
|
||||
*pos++ = 0x0;
|
||||
|
||||
if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
|
||||
mesh_add_ds_params_ie(skb, sdata))
|
||||
goto out_free;
|
||||
|
||||
bcn->head_len = skb->len;
|
||||
memcpy(bcn->head, skb->data, bcn->head_len);
|
||||
|
||||
/* now the tail */
|
||||
skb_trim(skb, 0);
|
||||
bcn->tail = bcn->head + bcn->head_len;
|
||||
|
||||
if (ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
|
||||
mesh_add_rsn_ie(skb, sdata) ||
|
||||
mesh_add_ht_cap_ie(skb, sdata) ||
|
||||
mesh_add_ht_oper_ie(skb, sdata) ||
|
||||
mesh_add_meshid_ie(skb, sdata) ||
|
||||
mesh_add_meshconf_ie(skb, sdata) ||
|
||||
mesh_add_awake_window_ie(skb, sdata) ||
|
||||
mesh_add_vendor_ies(skb, sdata))
|
||||
goto out_free;
|
||||
|
||||
bcn->tail_len = skb->len;
|
||||
memcpy(bcn->tail, skb->data, bcn->tail_len);
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
rcu_assign_pointer(ifmsh->beacon, bcn);
|
||||
return 0;
|
||||
out_free:
|
||||
kfree(bcn);
|
||||
dev_kfree_skb(skb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_mesh_rebuild_beacon(struct ieee80211_if_mesh *ifmsh)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct beacon_data *old_bcn;
|
||||
int ret;
|
||||
sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh);
|
||||
|
||||
mutex_lock(&ifmsh->mtx);
|
||||
|
||||
old_bcn = rcu_dereference_protected(ifmsh->beacon,
|
||||
lockdep_is_held(&ifmsh->mtx));
|
||||
ret = ieee80211_mesh_build_beacon(ifmsh);
|
||||
if (ret)
|
||||
/* just reuse old beacon */
|
||||
goto out;
|
||||
|
||||
if (old_bcn)
|
||||
kfree_rcu(old_bcn, rcu_head);
|
||||
out:
|
||||
mutex_unlock(&ifmsh->mtx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
|
||||
u32 changed)
|
||||
{
|
||||
if (sdata->vif.bss_conf.enable_beacon &&
|
||||
(changed & (BSS_CHANGED_BEACON |
|
||||
BSS_CHANGED_HT |
|
||||
BSS_CHANGED_BASIC_RATES |
|
||||
BSS_CHANGED_BEACON_INT)))
|
||||
if (ieee80211_mesh_rebuild_beacon(&sdata->u.mesh))
|
||||
return;
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
}
|
||||
|
||||
int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
@@ -675,17 +808,24 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
|
||||
sdata->vif.bss_conf.basic_rates =
|
||||
ieee80211_mandatory_rates(local, band);
|
||||
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
changed |= ieee80211_mps_local_status_update(sdata);
|
||||
|
||||
if (ieee80211_mesh_build_beacon(ifmsh)) {
|
||||
ieee80211_stop_mesh(sdata);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
|
||||
netif_carrier_on(sdata->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct beacon_data *bcn;
|
||||
|
||||
netif_carrier_off(sdata->dev);
|
||||
|
||||
@@ -694,6 +834,12 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
|
||||
sdata->vif.bss_conf.enable_beacon = false;
|
||||
clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
|
||||
mutex_lock(&ifmsh->mtx);
|
||||
bcn = rcu_dereference_protected(ifmsh->beacon,
|
||||
lockdep_is_held(&ifmsh->mtx));
|
||||
rcu_assign_pointer(ifmsh->beacon, NULL);
|
||||
kfree_rcu(bcn, rcu_head);
|
||||
mutex_unlock(&ifmsh->mtx);
|
||||
|
||||
/* flush STAs and mpaths on this iface */
|
||||
sta_info_flush(sdata);
|
||||
@@ -722,6 +868,63 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
|
||||
sdata->u.mesh.timers_running = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt, size_t len)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct sk_buff *presp;
|
||||
struct beacon_data *bcn;
|
||||
struct ieee80211_mgmt *hdr;
|
||||
struct ieee802_11_elems elems;
|
||||
size_t baselen;
|
||||
u8 *pos, *end;
|
||||
|
||||
end = ((u8 *) mgmt) + len;
|
||||
pos = mgmt->u.probe_req.variable;
|
||||
baselen = (u8 *) pos - (u8 *) mgmt;
|
||||
if (baselen > len)
|
||||
return;
|
||||
|
||||
ieee802_11_parse_elems(pos, len - baselen, &elems);
|
||||
|
||||
/* 802.11-2012 10.1.4.3.2 */
|
||||
if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
|
||||
!is_broadcast_ether_addr(mgmt->da)) ||
|
||||
elems.ssid_len != 0)
|
||||
return;
|
||||
|
||||
if (elems.mesh_id_len != 0 &&
|
||||
(elems.mesh_id_len != ifmsh->mesh_id_len ||
|
||||
memcmp(elems.mesh_id, ifmsh->mesh_id, ifmsh->mesh_id_len)))
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
bcn = rcu_dereference(ifmsh->beacon);
|
||||
|
||||
if (!bcn)
|
||||
goto out;
|
||||
|
||||
presp = dev_alloc_skb(local->tx_headroom +
|
||||
bcn->head_len + bcn->tail_len);
|
||||
if (!presp)
|
||||
goto out;
|
||||
|
||||
skb_reserve(presp, local->tx_headroom);
|
||||
memcpy(skb_put(presp, bcn->head_len), bcn->head, bcn->head_len);
|
||||
memcpy(skb_put(presp, bcn->tail_len), bcn->tail, bcn->tail_len);
|
||||
hdr = (struct ieee80211_mgmt *) presp->data;
|
||||
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
||||
IEEE80211_STYPE_PROBE_RESP);
|
||||
memcpy(hdr->da, mgmt->sa, ETH_ALEN);
|
||||
mpl_dbg(sdata, "sending probe resp. to %pM\n", hdr->da);
|
||||
IEEE80211_SKB_CB(presp)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
||||
ieee80211_tx_skb(sdata, presp);
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
|
||||
u16 stype,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
@@ -811,6 +1014,9 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
ieee80211_mesh_rx_bcn_presp(sdata, stype, mgmt, skb->len,
|
||||
rx_status);
|
||||
break;
|
||||
case IEEE80211_STYPE_PROBE_REQ:
|
||||
ieee80211_mesh_rx_probe_req(sdata, mgmt, skb->len);
|
||||
break;
|
||||
case IEEE80211_STYPE_ACTION:
|
||||
ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status);
|
||||
break;
|
||||
@@ -883,6 +1089,8 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
skb_queue_head_init(&ifmsh->ps.bc_buf);
|
||||
spin_lock_init(&ifmsh->mesh_preq_queue_lock);
|
||||
spin_lock_init(&ifmsh->sync_offset_lock);
|
||||
RCU_INIT_POINTER(ifmsh->beacon, NULL);
|
||||
mutex_init(&ifmsh->mtx);
|
||||
|
||||
sdata->vif.bss_conf.bssid = zero_addr;
|
||||
}
|
||||
|
||||
+15
-11
@@ -239,15 +239,18 @@ void ieee80211s_update_metric(struct ieee80211_local *local,
|
||||
struct sta_info *sta, struct sk_buff *skb);
|
||||
void ieee80211s_stop(void);
|
||||
void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
|
||||
int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh);
|
||||
const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method);
|
||||
/* wrapper for ieee80211_bss_info_change_notify() */
|
||||
void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
|
||||
u32 changed);
|
||||
|
||||
/* mesh power save */
|
||||
void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
|
||||
enum nl80211_mesh_power_mode pm);
|
||||
u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata);
|
||||
u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
|
||||
enum nl80211_mesh_power_mode pm);
|
||||
void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata,
|
||||
struct sta_info *sta,
|
||||
struct ieee80211_hdr *hdr);
|
||||
@@ -265,8 +268,8 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
|
||||
int mesh_nexthop_resolve(struct sk_buff *skb,
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata);
|
||||
struct mesh_path *mesh_path_lookup(u8 *dst,
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
struct mesh_path *mesh_path_lookup(const u8 *dst,
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
struct mesh_path *mpp_path_lookup(u8 *dst,
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata);
|
||||
@@ -276,7 +279,7 @@ void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop);
|
||||
void mesh_path_expire(struct ieee80211_sub_if_data *sdata);
|
||||
void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt, size_t len);
|
||||
int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata);
|
||||
int mesh_path_add(const u8 *dst, struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
int mesh_path_add_gate(struct mesh_path *mpath);
|
||||
int mesh_path_send_to_gates(struct mesh_path *mpath);
|
||||
@@ -289,8 +292,8 @@ bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie);
|
||||
u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata);
|
||||
void mesh_plink_broken(struct sta_info *sta);
|
||||
u32 mesh_plink_deactivate(struct sta_info *sta);
|
||||
int mesh_plink_open(struct sta_info *sta);
|
||||
void mesh_plink_block(struct sta_info *sta);
|
||||
u32 mesh_plink_open(struct sta_info *sta);
|
||||
u32 mesh_plink_block(struct sta_info *sta);
|
||||
void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt, size_t len,
|
||||
struct ieee80211_rx_status *rx_status);
|
||||
@@ -301,8 +304,9 @@ void mesh_sta_cleanup(struct sta_info *sta);
|
||||
void mesh_mpath_table_grow(void);
|
||||
void mesh_mpp_table_grow(void);
|
||||
/* Mesh paths */
|
||||
int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, __le16 target_rcode,
|
||||
const u8 *ra, struct ieee80211_sub_if_data *sdata);
|
||||
int mesh_path_error_tx(u8 ttl, const u8 *target, __le32 target_sn,
|
||||
__le16 target_rcode, const u8 *ra,
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta);
|
||||
void mesh_path_flush_pending(struct mesh_path *mpath);
|
||||
void mesh_path_tx_pending(struct mesh_path *mpath);
|
||||
|
||||
+23
-19
@@ -30,14 +30,14 @@
|
||||
|
||||
static void mesh_queue_preq(struct mesh_path *, u8);
|
||||
|
||||
static inline u32 u32_field_get(u8 *preq_elem, int offset, bool ae)
|
||||
static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae)
|
||||
{
|
||||
if (ae)
|
||||
offset += 6;
|
||||
return get_unaligned_le32(preq_elem + offset);
|
||||
}
|
||||
|
||||
static inline u32 u16_field_get(u8 *preq_elem, int offset, bool ae)
|
||||
static inline u32 u16_field_get(const u8 *preq_elem, int offset, bool ae)
|
||||
{
|
||||
if (ae)
|
||||
offset += 6;
|
||||
@@ -102,10 +102,13 @@ enum mpath_frame_type {
|
||||
static const u8 broadcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||
|
||||
static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
|
||||
u8 *orig_addr, __le32 orig_sn, u8 target_flags, u8 *target,
|
||||
__le32 target_sn, const u8 *da, u8 hop_count, u8 ttl,
|
||||
__le32 lifetime, __le32 metric, __le32 preq_id,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
const u8 *orig_addr, __le32 orig_sn,
|
||||
u8 target_flags, const u8 *target,
|
||||
__le32 target_sn, const u8 *da,
|
||||
u8 hop_count, u8 ttl,
|
||||
__le32 lifetime, __le32 metric,
|
||||
__le32 preq_id,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sk_buff *skb;
|
||||
@@ -235,7 +238,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,
|
||||
* also acquires in the TX path. To avoid a deadlock we don't transmit the
|
||||
* frame directly but add it to the pending queue instead.
|
||||
*/
|
||||
int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn,
|
||||
int mesh_path_error_tx(u8 ttl, const u8 *target, __le32 target_sn,
|
||||
__le16 target_rcode, const u8 *ra,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
@@ -369,14 +372,14 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
|
||||
* path routing information is updated.
|
||||
*/
|
||||
static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
u8 *hwmp_ie, enum mpath_frame_type action)
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
const u8 *hwmp_ie, enum mpath_frame_type action)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct mesh_path *mpath;
|
||||
struct sta_info *sta;
|
||||
bool fresh_info;
|
||||
u8 *orig_addr, *ta;
|
||||
const u8 *orig_addr, *ta;
|
||||
u32 orig_sn, orig_metric;
|
||||
unsigned long orig_lifetime, exp_time;
|
||||
u32 last_hop_metric, new_metric;
|
||||
@@ -511,11 +514,11 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
u8 *preq_elem, u32 metric)
|
||||
const u8 *preq_elem, u32 metric)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct mesh_path *mpath = NULL;
|
||||
u8 *target_addr, *orig_addr;
|
||||
const u8 *target_addr, *orig_addr;
|
||||
const u8 *da;
|
||||
u8 target_flags, ttl, flags;
|
||||
u32 orig_sn, target_sn, lifetime, orig_metric;
|
||||
@@ -648,11 +651,11 @@ next_hop_deref_protected(struct mesh_path *mpath)
|
||||
|
||||
static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
u8 *prep_elem, u32 metric)
|
||||
const u8 *prep_elem, u32 metric)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct mesh_path *mpath;
|
||||
u8 *target_addr, *orig_addr;
|
||||
const u8 *target_addr, *orig_addr;
|
||||
u8 ttl, hopcount, flags;
|
||||
u8 next_hop[ETH_ALEN];
|
||||
u32 target_sn, orig_sn, lifetime;
|
||||
@@ -711,12 +714,13 @@ fail:
|
||||
}
|
||||
|
||||
static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt, u8 *perr_elem)
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
const u8 *perr_elem)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct mesh_path *mpath;
|
||||
u8 ttl;
|
||||
u8 *ta, *target_addr;
|
||||
const u8 *ta, *target_addr;
|
||||
u32 target_sn;
|
||||
u16 target_rcode;
|
||||
|
||||
@@ -758,15 +762,15 @@ endperr:
|
||||
}
|
||||
|
||||
static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
struct ieee80211_rann_ie *rann)
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
const struct ieee80211_rann_ie *rann)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sta_info *sta;
|
||||
struct mesh_path *mpath;
|
||||
u8 ttl, flags, hopcount;
|
||||
u8 *orig_addr;
|
||||
const u8 *orig_addr;
|
||||
u32 orig_sn, metric, metric_txsta, interval;
|
||||
bool root_is_gate;
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ errcopy:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata,
|
||||
static u32 mesh_table_hash(const u8 *addr, struct ieee80211_sub_if_data *sdata,
|
||||
struct mesh_table *tbl)
|
||||
{
|
||||
/* Use last four bytes of hw addr and interface index as hash index */
|
||||
@@ -326,8 +326,8 @@ static void mesh_path_move_to_queue(struct mesh_path *gate_mpath,
|
||||
}
|
||||
|
||||
|
||||
static struct mesh_path *mpath_lookup(struct mesh_table *tbl, u8 *dst,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
static struct mesh_path *mpath_lookup(struct mesh_table *tbl, const u8 *dst,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct mesh_path *mpath;
|
||||
struct hlist_node *n;
|
||||
@@ -359,7 +359,8 @@ static struct mesh_path *mpath_lookup(struct mesh_table *tbl, u8 *dst,
|
||||
*
|
||||
* Locking: must be called within a read rcu section.
|
||||
*/
|
||||
struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata)
|
||||
struct mesh_path *mesh_path_lookup(const u8 *dst,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
return mpath_lookup(rcu_dereference(mesh_paths), dst, sdata);
|
||||
}
|
||||
@@ -494,7 +495,7 @@ int mesh_gate_num(struct ieee80211_sub_if_data *sdata)
|
||||
*
|
||||
* State: the initial state of the new path is set to 0
|
||||
*/
|
||||
int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
|
||||
int mesh_path_add(const u8 *dst, struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
||||
+21
-20
@@ -202,7 +202,7 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
|
||||
mesh_path_flush_by_nexthop(sta);
|
||||
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
changed |= ieee80211_mps_local_status_update(sdata);
|
||||
|
||||
return changed;
|
||||
}
|
||||
@@ -373,8 +373,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
|
||||
if (elems->ht_cap_elem &&
|
||||
sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
|
||||
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
|
||||
elems->ht_cap_elem,
|
||||
&sta->sta.ht_cap);
|
||||
elems->ht_cap_elem, sta);
|
||||
else
|
||||
memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap));
|
||||
|
||||
@@ -383,8 +382,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
if (!(elems->ht_operation->ht_param &
|
||||
IEEE80211_HT_PARAM_CHAN_WIDTH_ANY))
|
||||
sta->sta.ht_cap.cap &=
|
||||
~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
||||
sta->sta.bandwidth = IEEE80211_STA_RX_BW_20;
|
||||
ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
|
||||
elems->ht_operation, &chandef);
|
||||
if (sta->ch_width != chandef.width)
|
||||
@@ -494,6 +492,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
u32 changed = 0;
|
||||
|
||||
sta = mesh_sta_info_get(sdata, hw_addr, elems);
|
||||
if (!sta)
|
||||
@@ -504,11 +503,12 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
|
||||
sdata->u.mesh.accepting_plinks &&
|
||||
sdata->u.mesh.mshcfg.auto_open_plinks &&
|
||||
rssi_threshold_check(sta, sdata))
|
||||
mesh_plink_open(sta);
|
||||
changed = mesh_plink_open(sta);
|
||||
|
||||
ieee80211_mps_frame_release(sta, elems);
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
ieee80211_mbss_info_change_notify(sdata, changed);
|
||||
}
|
||||
|
||||
static void mesh_plink_timer(unsigned long data)
|
||||
@@ -621,13 +621,14 @@ static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
|
||||
add_timer(&sta->plink_timer);
|
||||
}
|
||||
|
||||
int mesh_plink_open(struct sta_info *sta)
|
||||
u32 mesh_plink_open(struct sta_info *sta)
|
||||
{
|
||||
__le16 llid;
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
u32 changed;
|
||||
|
||||
if (!test_sta_flag(sta, WLAN_STA_AUTH))
|
||||
return -EPERM;
|
||||
return 0;
|
||||
|
||||
spin_lock_bh(&sta->lock);
|
||||
get_random_bytes(&llid, 2);
|
||||
@@ -635,7 +636,7 @@ int mesh_plink_open(struct sta_info *sta)
|
||||
if (sta->plink_state != NL80211_PLINK_LISTEN &&
|
||||
sta->plink_state != NL80211_PLINK_BLOCKED) {
|
||||
spin_unlock_bh(&sta->lock);
|
||||
return -EBUSY;
|
||||
return 0;
|
||||
}
|
||||
sta->plink_state = NL80211_PLINK_OPN_SNT;
|
||||
mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout);
|
||||
@@ -645,15 +646,15 @@ int mesh_plink_open(struct sta_info *sta)
|
||||
sta->sta.addr);
|
||||
|
||||
/* set the non-peer mode to active during peering */
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
changed = ieee80211_mps_local_status_update(sdata);
|
||||
|
||||
return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
|
||||
sta->sta.addr, llid, 0, 0);
|
||||
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
|
||||
sta->sta.addr, llid, 0, 0);
|
||||
return changed;
|
||||
}
|
||||
|
||||
void mesh_plink_block(struct sta_info *sta)
|
||||
u32 mesh_plink_block(struct sta_info *sta)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
u32 changed;
|
||||
|
||||
spin_lock_bh(&sta->lock);
|
||||
@@ -661,7 +662,7 @@ void mesh_plink_block(struct sta_info *sta)
|
||||
sta->plink_state = NL80211_PLINK_BLOCKED;
|
||||
spin_unlock_bh(&sta->lock);
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
@@ -882,7 +883,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
|
||||
mshcfg->dot11MeshRetryTimeout);
|
||||
|
||||
/* set the non-peer mode to active during peering */
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
changed |= ieee80211_mps_local_status_update(sdata);
|
||||
|
||||
spin_unlock_bh(&sta->lock);
|
||||
mesh_plink_frame_tx(sdata,
|
||||
@@ -978,7 +979,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
|
||||
mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n",
|
||||
sta->sta.addr);
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
ieee80211_mps_set_sta_local_pm(sta,
|
||||
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
||||
mshcfg->power_mode);
|
||||
break;
|
||||
default:
|
||||
@@ -1020,8 +1021,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
|
||||
WLAN_SP_MESH_PEERING_CONFIRM,
|
||||
sta->sta.addr, llid, plid, 0);
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
ieee80211_mps_set_sta_local_pm(sta,
|
||||
mshcfg->power_mode);
|
||||
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
||||
mshcfg->power_mode);
|
||||
break;
|
||||
default:
|
||||
spin_unlock_bh(&sta->lock);
|
||||
@@ -1089,5 +1090,5 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
|
||||
rcu_read_unlock();
|
||||
|
||||
if (changed)
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
ieee80211_mbss_info_change_notify(sdata, changed);
|
||||
}
|
||||
|
||||
+20
-7
@@ -74,14 +74,17 @@ static void mps_qos_null_tx(struct sta_info *sta)
|
||||
* @sdata: local mesh subif
|
||||
*
|
||||
* sets the non-peer power mode and triggers the driver PS (re-)configuration
|
||||
* Return BSS_CHANGED_BEACON if a beacon update is necessary.
|
||||
*/
|
||||
void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata)
|
||||
u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct sta_info *sta;
|
||||
bool peering = false;
|
||||
int light_sleep_cnt = 0;
|
||||
int deep_sleep_cnt = 0;
|
||||
u32 changed = 0;
|
||||
enum nl80211_mesh_power_mode nonpeer_pm;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
|
||||
@@ -115,17 +118,26 @@ void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata)
|
||||
*/
|
||||
if (peering) {
|
||||
mps_dbg(sdata, "setting non-peer PM to active for peering\n");
|
||||
ifmsh->nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
|
||||
nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
|
||||
} else if (light_sleep_cnt || deep_sleep_cnt) {
|
||||
mps_dbg(sdata, "setting non-peer PM to deep sleep\n");
|
||||
ifmsh->nonpeer_pm = NL80211_MESH_POWER_DEEP_SLEEP;
|
||||
nonpeer_pm = NL80211_MESH_POWER_DEEP_SLEEP;
|
||||
} else {
|
||||
mps_dbg(sdata, "setting non-peer PM to user value\n");
|
||||
ifmsh->nonpeer_pm = ifmsh->mshcfg.power_mode;
|
||||
nonpeer_pm = ifmsh->mshcfg.power_mode;
|
||||
}
|
||||
|
||||
/* need update if sleep counts move between 0 and non-zero */
|
||||
if (ifmsh->nonpeer_pm != nonpeer_pm ||
|
||||
!ifmsh->ps_peers_light_sleep != !light_sleep_cnt ||
|
||||
!ifmsh->ps_peers_deep_sleep != !deep_sleep_cnt)
|
||||
changed = BSS_CHANGED_BEACON;
|
||||
|
||||
ifmsh->nonpeer_pm = nonpeer_pm;
|
||||
ifmsh->ps_peers_light_sleep = light_sleep_cnt;
|
||||
ifmsh->ps_peers_deep_sleep = deep_sleep_cnt;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,9 +145,10 @@ void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata)
|
||||
*
|
||||
* @sta: mesh STA
|
||||
* @pm: the power mode to set
|
||||
* Return BSS_CHANGED_BEACON if a beacon update is in order.
|
||||
*/
|
||||
void ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
|
||||
enum nl80211_mesh_power_mode pm)
|
||||
u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
|
||||
enum nl80211_mesh_power_mode pm)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
|
||||
@@ -151,7 +164,7 @@ void ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
|
||||
if (sta->plink_state == NL80211_PLINK_ESTAB)
|
||||
mps_qos_null_tx(sta);
|
||||
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
return ieee80211_mps_local_status_update(sdata);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+446
-292
File diff suppressed because it is too large
Load Diff
@@ -38,6 +38,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
|
||||
|
||||
ieee80211_scan_cancel(local);
|
||||
|
||||
ieee80211_dfs_cac_cancel(local);
|
||||
|
||||
if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
|
||||
mutex_lock(&local->sta_mtx);
|
||||
list_for_each_entry(sta, &local->sta_list, list) {
|
||||
|
||||
@@ -68,6 +68,8 @@ static inline void rate_control_rate_init(struct sta_info *sta)
|
||||
sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
|
||||
rcu_read_unlock();
|
||||
|
||||
ieee80211_sta_set_rx_nss(sta);
|
||||
|
||||
ref->ops->rate_init(ref->priv, sband, ista, priv_sta);
|
||||
set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
|
||||
}
|
||||
|
||||
@@ -494,6 +494,33 @@ minstrel_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta)
|
||||
kfree(mi);
|
||||
}
|
||||
|
||||
static void
|
||||
minstrel_init_cck_rates(struct minstrel_priv *mp)
|
||||
{
|
||||
static const int bitrates[4] = { 10, 20, 55, 110 };
|
||||
struct ieee80211_supported_band *sband;
|
||||
int i, j;
|
||||
|
||||
sband = mp->hw->wiphy->bands[IEEE80211_BAND_2GHZ];
|
||||
if (!sband)
|
||||
return;
|
||||
|
||||
for (i = 0, j = 0; i < sband->n_bitrates; i++) {
|
||||
struct ieee80211_rate *rate = &sband->bitrates[i];
|
||||
|
||||
if (rate->flags & IEEE80211_RATE_ERP_G)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(bitrates); j++) {
|
||||
if (rate->bitrate != bitrates[j])
|
||||
continue;
|
||||
|
||||
mp->cck_rates[j] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
|
||||
{
|
||||
@@ -539,6 +566,8 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
|
||||
S_IRUGO | S_IWUGO, debugfsdir, &mp->fixed_rate_idx);
|
||||
#endif
|
||||
|
||||
minstrel_init_cck_rates(mp);
|
||||
|
||||
return mp;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,8 @@ struct minstrel_priv {
|
||||
unsigned int lookaround_rate;
|
||||
unsigned int lookaround_rate_mrr;
|
||||
|
||||
u8 cck_rates[4];
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/*
|
||||
* enable fixed rate processing per RC
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
|
||||
* Copyright (C) 2010-2013 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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
|
||||
@@ -63,6 +63,30 @@
|
||||
} \
|
||||
}
|
||||
|
||||
#define CCK_DURATION(_bitrate, _short, _len) \
|
||||
(10 /* SIFS */ + \
|
||||
(_short ? 72 + 24 : 144 + 48 ) + \
|
||||
(8 * (_len + 4) * 10) / (_bitrate))
|
||||
|
||||
#define CCK_ACK_DURATION(_bitrate, _short) \
|
||||
(CCK_DURATION((_bitrate > 10 ? 20 : 10), false, 60) + \
|
||||
CCK_DURATION(_bitrate, _short, AVG_PKT_SIZE))
|
||||
|
||||
#define CCK_DURATION_LIST(_short) \
|
||||
CCK_ACK_DURATION(10, _short), \
|
||||
CCK_ACK_DURATION(20, _short), \
|
||||
CCK_ACK_DURATION(55, _short), \
|
||||
CCK_ACK_DURATION(110, _short)
|
||||
|
||||
#define CCK_GROUP \
|
||||
[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS] = { \
|
||||
.streams = 0, \
|
||||
.duration = { \
|
||||
CCK_DURATION_LIST(false), \
|
||||
CCK_DURATION_LIST(true) \
|
||||
} \
|
||||
}
|
||||
|
||||
/*
|
||||
* To enable sufficiently targeted rate sampling, MCS rates are divided into
|
||||
* groups, based on the number of streams and flags (HT40, SGI) that they
|
||||
@@ -95,8 +119,13 @@ const struct mcs_group minstrel_mcs_groups[] = {
|
||||
#if MINSTREL_MAX_STREAMS >= 3
|
||||
MCS_GROUP(3, 1, 1),
|
||||
#endif
|
||||
|
||||
/* must be last */
|
||||
CCK_GROUP
|
||||
};
|
||||
|
||||
#define MINSTREL_CCK_GROUP (ARRAY_SIZE(minstrel_mcs_groups) - 1)
|
||||
|
||||
static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES];
|
||||
|
||||
/*
|
||||
@@ -119,6 +148,29 @@ minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate)
|
||||
!!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH));
|
||||
}
|
||||
|
||||
static struct minstrel_rate_stats *
|
||||
minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
struct ieee80211_tx_rate *rate)
|
||||
{
|
||||
int group, idx;
|
||||
|
||||
if (rate->flags & IEEE80211_TX_RC_MCS) {
|
||||
group = minstrel_ht_get_group_idx(rate);
|
||||
idx = rate->idx % MCS_GROUP_RATES;
|
||||
} else {
|
||||
group = MINSTREL_CCK_GROUP;
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(mp->cck_rates); idx++)
|
||||
if (rate->idx == mp->cck_rates[idx])
|
||||
break;
|
||||
|
||||
/* short preamble */
|
||||
if (!(mi->groups[group].supported & BIT(idx)))
|
||||
idx += 4;
|
||||
}
|
||||
return &mi->groups[group].rates[idx];
|
||||
}
|
||||
|
||||
static inline struct minstrel_rate_stats *
|
||||
minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index)
|
||||
{
|
||||
@@ -159,7 +211,7 @@ static void
|
||||
minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
|
||||
{
|
||||
struct minstrel_rate_stats *mr;
|
||||
unsigned int usecs;
|
||||
unsigned int usecs = 0;
|
||||
|
||||
mr = &mi->groups[group].rates[rate];
|
||||
|
||||
@@ -168,7 +220,9 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
|
||||
return;
|
||||
}
|
||||
|
||||
usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
|
||||
if (group != MINSTREL_CCK_GROUP)
|
||||
usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
|
||||
|
||||
usecs += minstrel_mcs_groups[group].duration[rate];
|
||||
mr->cur_tp = MINSTREL_TRUNC((1000000 / usecs) * mr->probability);
|
||||
}
|
||||
@@ -293,7 +347,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
||||
}
|
||||
|
||||
static bool
|
||||
minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate)
|
||||
minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rate)
|
||||
{
|
||||
if (rate->idx < 0)
|
||||
return false;
|
||||
@@ -301,7 +355,13 @@ minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate)
|
||||
if (!rate->count)
|
||||
return false;
|
||||
|
||||
return !!(rate->flags & IEEE80211_TX_RC_MCS);
|
||||
if (rate->flags & IEEE80211_TX_RC_MCS)
|
||||
return true;
|
||||
|
||||
return rate->idx == mp->cck_rates[0] ||
|
||||
rate->idx == mp->cck_rates[1] ||
|
||||
rate->idx == mp->cck_rates[2] ||
|
||||
rate->idx == mp->cck_rates[3];
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -386,7 +446,6 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
|
||||
struct minstrel_rate_stats *rate, *rate2;
|
||||
struct minstrel_priv *mp = priv;
|
||||
bool last;
|
||||
int group;
|
||||
int i;
|
||||
|
||||
if (!msp->is_ht)
|
||||
@@ -415,13 +474,12 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
|
||||
if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
|
||||
mi->sample_packets += info->status.ampdu_len;
|
||||
|
||||
last = !minstrel_ht_txstat_valid(&ar[0]);
|
||||
last = !minstrel_ht_txstat_valid(mp, &ar[0]);
|
||||
for (i = 0; !last; i++) {
|
||||
last = (i == IEEE80211_TX_MAX_RATES - 1) ||
|
||||
!minstrel_ht_txstat_valid(&ar[i + 1]);
|
||||
!minstrel_ht_txstat_valid(mp, &ar[i + 1]);
|
||||
|
||||
group = minstrel_ht_get_group_idx(&ar[i]);
|
||||
rate = &mi->groups[group].rates[ar[i].idx % 8];
|
||||
rate = minstrel_ht_get_stats(mp, mi, &ar[i]);
|
||||
|
||||
if (last)
|
||||
rate->success += info->status.ampdu_ack_len;
|
||||
@@ -447,7 +505,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
|
||||
|
||||
if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) {
|
||||
minstrel_ht_update_stats(mp, mi);
|
||||
if (!(info->flags & IEEE80211_TX_CTL_AMPDU))
|
||||
if (!(info->flags & IEEE80211_TX_CTL_AMPDU) &&
|
||||
mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP)
|
||||
minstrel_aggr_check(sta, skb);
|
||||
}
|
||||
}
|
||||
@@ -463,6 +522,7 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
unsigned int ctime = 0;
|
||||
unsigned int t_slot = 9; /* FIXME */
|
||||
unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len);
|
||||
unsigned int overhead = 0, overhead_rtscts = 0;
|
||||
|
||||
mr = minstrel_get_ratestats(mi, index);
|
||||
if (mr->probability < MINSTREL_FRAC(1, 10)) {
|
||||
@@ -484,9 +544,14 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
ctime += (t_slot * cw) >> 1;
|
||||
cw = min((cw << 1) | 1, mp->cw_max);
|
||||
|
||||
if (index / MCS_GROUP_RATES != MINSTREL_CCK_GROUP) {
|
||||
overhead = mi->overhead;
|
||||
overhead_rtscts = mi->overhead_rtscts;
|
||||
}
|
||||
|
||||
/* Total TX time for data and Contention after first 2 tries */
|
||||
tx_time = ctime + 2 * (mi->overhead + tx_time_data);
|
||||
tx_time_rtscts = ctime + 2 * (mi->overhead_rtscts + tx_time_data);
|
||||
tx_time = ctime + 2 * (overhead + tx_time_data);
|
||||
tx_time_rtscts = ctime + 2 * (overhead_rtscts + tx_time_data);
|
||||
|
||||
/* See how many more tries we can fit inside segment size */
|
||||
do {
|
||||
@@ -495,8 +560,8 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
cw = min((cw << 1) | 1, mp->cw_max);
|
||||
|
||||
/* Total TX time after this try */
|
||||
tx_time += ctime + mi->overhead + tx_time_data;
|
||||
tx_time_rtscts += ctime + mi->overhead_rtscts + tx_time_data;
|
||||
tx_time += ctime + overhead + tx_time_data;
|
||||
tx_time_rtscts += ctime + overhead_rtscts + tx_time_data;
|
||||
|
||||
if (tx_time_rtscts < mp->segment_size)
|
||||
mr->retry_count_rtscts++;
|
||||
@@ -526,9 +591,16 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
else
|
||||
rate->count = mr->retry_count;
|
||||
|
||||
rate->flags = IEEE80211_TX_RC_MCS | group->flags;
|
||||
rate->flags = 0;
|
||||
if (rtscts)
|
||||
rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS;
|
||||
|
||||
if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
|
||||
rate->idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)];
|
||||
return;
|
||||
}
|
||||
|
||||
rate->flags |= IEEE80211_TX_RC_MCS | group->flags;
|
||||
rate->idx = index % MCS_GROUP_RATES + (group->streams - 1) * MCS_GROUP_RATES;
|
||||
}
|
||||
|
||||
@@ -591,6 +663,22 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
||||
return sample_idx;
|
||||
}
|
||||
|
||||
static void
|
||||
minstrel_ht_check_cck_shortpreamble(struct minstrel_priv *mp,
|
||||
struct minstrel_ht_sta *mi, bool val)
|
||||
{
|
||||
u8 supported = mi->groups[MINSTREL_CCK_GROUP].supported;
|
||||
|
||||
if (!supported || !mi->cck_supported_short)
|
||||
return;
|
||||
|
||||
if (supported & (mi->cck_supported_short << (val * 4)))
|
||||
return;
|
||||
|
||||
supported ^= mi->cck_supported_short | (mi->cck_supported_short << 4);
|
||||
mi->groups[MINSTREL_CCK_GROUP].supported = supported;
|
||||
}
|
||||
|
||||
static void
|
||||
minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
|
||||
struct ieee80211_tx_rate_control *txrc)
|
||||
@@ -610,6 +698,7 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
|
||||
return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc);
|
||||
|
||||
info->flags |= mi->tx_flags;
|
||||
minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble);
|
||||
|
||||
/* Don't use EAPOL frames for sampling on non-mrr hw */
|
||||
if (mp->hw->max_rates == 1 &&
|
||||
@@ -682,6 +771,30 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_sta *sta)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (sband->band != IEEE80211_BAND_2GHZ)
|
||||
return;
|
||||
|
||||
mi->cck_supported = 0;
|
||||
mi->cck_supported_short = 0;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (!rate_supported(sta, sband->band, mp->cck_rates[i]))
|
||||
continue;
|
||||
|
||||
mi->cck_supported |= BIT(i);
|
||||
if (sband->bitrates[i].flags & IEEE80211_RATE_SHORT_PREAMBLE)
|
||||
mi->cck_supported_short |= BIT(i);
|
||||
}
|
||||
|
||||
mi->groups[MINSTREL_CCK_GROUP].supported = mi->cck_supported;
|
||||
}
|
||||
|
||||
static void
|
||||
minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_sta *sta, void *priv_sta)
|
||||
@@ -695,14 +808,13 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
|
||||
int ack_dur;
|
||||
int stbc;
|
||||
int i;
|
||||
unsigned int smps;
|
||||
|
||||
/* fall back to the old minstrel for legacy stations */
|
||||
if (!sta->ht_cap.ht_supported)
|
||||
goto use_legacy;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) !=
|
||||
MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS);
|
||||
MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1);
|
||||
|
||||
msp->is_ht = true;
|
||||
memset(mi, 0, sizeof(*mi));
|
||||
@@ -731,28 +843,29 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
|
||||
if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
|
||||
mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
|
||||
|
||||
smps = (sta_cap & IEEE80211_HT_CAP_SM_PS) >>
|
||||
IEEE80211_HT_CAP_SM_PS_SHIFT;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mi->groups); i++) {
|
||||
u16 req = 0;
|
||||
|
||||
mi->groups[i].supported = 0;
|
||||
if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) {
|
||||
if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
|
||||
req |= IEEE80211_HT_CAP_SGI_40;
|
||||
else
|
||||
req |= IEEE80211_HT_CAP_SGI_20;
|
||||
if (i == MINSTREL_CCK_GROUP) {
|
||||
minstrel_ht_update_cck(mp, mi, sband, sta);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
|
||||
req |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
||||
if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) {
|
||||
if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
|
||||
if (!(sta_cap & IEEE80211_HT_CAP_SGI_40))
|
||||
continue;
|
||||
} else {
|
||||
if (!(sta_cap & IEEE80211_HT_CAP_SGI_20))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ((sta_cap & req) != req)
|
||||
if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH &&
|
||||
sta->bandwidth < IEEE80211_STA_RX_BW_40)
|
||||
continue;
|
||||
|
||||
/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
|
||||
if (smps == WLAN_HT_CAP_SM_PS_STATIC &&
|
||||
if (sta->smps_mode == IEEE80211_SMPS_STATIC &&
|
||||
minstrel_mcs_groups[i].streams > 1)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -107,8 +107,11 @@ struct minstrel_ht_sta {
|
||||
/* current MCS group to be sampled */
|
||||
u8 sample_group;
|
||||
|
||||
u8 cck_supported;
|
||||
u8 cck_supported_short;
|
||||
|
||||
/* MCS rate group info and statistics */
|
||||
struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS];
|
||||
struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1];
|
||||
};
|
||||
|
||||
struct minstrel_ht_sta_priv {
|
||||
|
||||
@@ -15,13 +15,76 @@
|
||||
#include "rc80211_minstrel.h"
|
||||
#include "rc80211_minstrel_ht.h"
|
||||
|
||||
static char *
|
||||
minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
|
||||
{
|
||||
unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS;
|
||||
const struct mcs_group *mg;
|
||||
unsigned int j, tp, prob, eprob;
|
||||
char htmode = '2';
|
||||
char gimode = 'L';
|
||||
|
||||
if (!mi->groups[i].supported)
|
||||
return p;
|
||||
|
||||
mg = &minstrel_mcs_groups[i];
|
||||
if (mg->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
|
||||
htmode = '4';
|
||||
if (mg->flags & IEEE80211_TX_RC_SHORT_GI)
|
||||
gimode = 'S';
|
||||
|
||||
for (j = 0; j < MCS_GROUP_RATES; j++) {
|
||||
struct minstrel_rate_stats *mr = &mi->groups[i].rates[j];
|
||||
static const int bitrates[4] = { 10, 20, 55, 110 };
|
||||
int idx = i * MCS_GROUP_RATES + j;
|
||||
|
||||
if (!(mi->groups[i].supported & BIT(j)))
|
||||
continue;
|
||||
|
||||
if (i == max_mcs)
|
||||
p += sprintf(p, "CCK/%cP ", j < 4 ? 'L' : 'S');
|
||||
else
|
||||
p += sprintf(p, "HT%c0/%cGI ", htmode, gimode);
|
||||
|
||||
*(p++) = (idx == mi->max_tp_rate) ? 'T' : ' ';
|
||||
*(p++) = (idx == mi->max_tp_rate2) ? 't' : ' ';
|
||||
*(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
|
||||
|
||||
if (i == max_mcs) {
|
||||
int r = bitrates[j % 4];
|
||||
p += sprintf(p, " %2u.%1uM", r / 10, r % 10);
|
||||
} else {
|
||||
p += sprintf(p, " MCS%-2u", (mg->streams - 1) *
|
||||
MCS_GROUP_RATES + j);
|
||||
}
|
||||
|
||||
tp = mr->cur_tp / 10;
|
||||
prob = MINSTREL_TRUNC(mr->cur_prob * 1000);
|
||||
eprob = MINSTREL_TRUNC(mr->probability * 1000);
|
||||
|
||||
p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u "
|
||||
"%3u %3u(%3u) %8llu %8llu\n",
|
||||
tp / 10, tp % 10,
|
||||
eprob / 10, eprob % 10,
|
||||
prob / 10, prob % 10,
|
||||
mr->retry_count,
|
||||
mr->last_success,
|
||||
mr->last_attempts,
|
||||
(unsigned long long)mr->succ_hist,
|
||||
(unsigned long long)mr->att_hist);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static int
|
||||
minstrel_ht_stats_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct minstrel_ht_sta_priv *msp = inode->i_private;
|
||||
struct minstrel_ht_sta *mi = &msp->ht;
|
||||
struct minstrel_debugfs_info *ms;
|
||||
unsigned int i, j, tp, prob, eprob;
|
||||
unsigned int i;
|
||||
unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS;
|
||||
char *p;
|
||||
int ret;
|
||||
|
||||
@@ -40,49 +103,11 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
|
||||
p = ms->buf;
|
||||
p += sprintf(p, "type rate throughput ewma prob this prob "
|
||||
"retry this succ/attempt success attempts\n");
|
||||
for (i = 0; i < MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; i++) {
|
||||
char htmode = '2';
|
||||
char gimode = 'L';
|
||||
|
||||
if (!mi->groups[i].supported)
|
||||
continue;
|
||||
p = minstrel_ht_stats_dump(mi, max_mcs, p);
|
||||
for (i = 0; i < max_mcs; i++)
|
||||
p = minstrel_ht_stats_dump(mi, i, p);
|
||||
|
||||
if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
|
||||
htmode = '4';
|
||||
if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI)
|
||||
gimode = 'S';
|
||||
|
||||
for (j = 0; j < MCS_GROUP_RATES; j++) {
|
||||
struct minstrel_rate_stats *mr = &mi->groups[i].rates[j];
|
||||
int idx = i * MCS_GROUP_RATES + j;
|
||||
|
||||
if (!(mi->groups[i].supported & BIT(j)))
|
||||
continue;
|
||||
|
||||
p += sprintf(p, "HT%c0/%cGI ", htmode, gimode);
|
||||
|
||||
*(p++) = (idx == mi->max_tp_rate) ? 'T' : ' ';
|
||||
*(p++) = (idx == mi->max_tp_rate2) ? 't' : ' ';
|
||||
*(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
|
||||
p += sprintf(p, " MCS%-2u", (minstrel_mcs_groups[i].streams - 1) *
|
||||
MCS_GROUP_RATES + j);
|
||||
|
||||
tp = mr->cur_tp / 10;
|
||||
prob = MINSTREL_TRUNC(mr->cur_prob * 1000);
|
||||
eprob = MINSTREL_TRUNC(mr->probability * 1000);
|
||||
|
||||
p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u "
|
||||
"%3u %3u(%3u) %8llu %8llu\n",
|
||||
tp / 10, tp % 10,
|
||||
eprob / 10, eprob % 10,
|
||||
prob / 10, prob % 10,
|
||||
mr->retry_count,
|
||||
mr->last_success,
|
||||
mr->last_attempts,
|
||||
(unsigned long long)mr->succ_hist,
|
||||
(unsigned long long)mr->att_hist);
|
||||
}
|
||||
}
|
||||
p += sprintf(p, "\nTotal packet count:: ideal %d "
|
||||
"lookaround %d\n",
|
||||
max(0, (int) mi->total_packets - (int) mi->sample_packets),
|
||||
|
||||
+49
-26
@@ -2375,31 +2375,27 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
|
||||
switch (mgmt->u.action.u.ht_smps.action) {
|
||||
case WLAN_HT_ACTION_SMPS: {
|
||||
struct ieee80211_supported_band *sband;
|
||||
u8 smps;
|
||||
enum ieee80211_smps_mode smps_mode;
|
||||
|
||||
/* convert to HT capability */
|
||||
switch (mgmt->u.action.u.ht_smps.smps_control) {
|
||||
case WLAN_HT_SMPS_CONTROL_DISABLED:
|
||||
smps = WLAN_HT_CAP_SM_PS_DISABLED;
|
||||
smps_mode = IEEE80211_SMPS_OFF;
|
||||
break;
|
||||
case WLAN_HT_SMPS_CONTROL_STATIC:
|
||||
smps = WLAN_HT_CAP_SM_PS_STATIC;
|
||||
smps_mode = IEEE80211_SMPS_STATIC;
|
||||
break;
|
||||
case WLAN_HT_SMPS_CONTROL_DYNAMIC:
|
||||
smps = WLAN_HT_CAP_SM_PS_DYNAMIC;
|
||||
smps_mode = IEEE80211_SMPS_DYNAMIC;
|
||||
break;
|
||||
default:
|
||||
goto invalid;
|
||||
}
|
||||
smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT;
|
||||
|
||||
/* if no change do nothing */
|
||||
if ((rx->sta->sta.ht_cap.cap &
|
||||
IEEE80211_HT_CAP_SM_PS) == smps)
|
||||
if (rx->sta->sta.smps_mode == smps_mode)
|
||||
goto handled;
|
||||
|
||||
rx->sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SM_PS;
|
||||
rx->sta->sta.ht_cap.cap |= smps;
|
||||
rx->sta->sta.smps_mode = smps_mode;
|
||||
|
||||
sband = rx->local->hw.wiphy->bands[status->band];
|
||||
|
||||
@@ -2410,25 +2406,20 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
|
||||
case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: {
|
||||
struct ieee80211_supported_band *sband;
|
||||
u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth;
|
||||
bool old_40mhz, new_40mhz;
|
||||
enum ieee80211_sta_rx_bandwidth new_bw;
|
||||
|
||||
/* If it doesn't support 40 MHz it can't change ... */
|
||||
if (!rx->sta->supports_40mhz)
|
||||
if (!(rx->sta->sta.ht_cap.cap &
|
||||
IEEE80211_HT_CAP_SUP_WIDTH_20_40))
|
||||
goto handled;
|
||||
|
||||
old_40mhz = rx->sta->sta.ht_cap.cap &
|
||||
IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
||||
new_40mhz = chanwidth == IEEE80211_HT_CHANWIDTH_ANY;
|
||||
|
||||
if (old_40mhz == new_40mhz)
|
||||
goto handled;
|
||||
|
||||
if (new_40mhz)
|
||||
rx->sta->sta.ht_cap.cap |=
|
||||
IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
||||
if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ)
|
||||
new_bw = IEEE80211_STA_RX_BW_20;
|
||||
else
|
||||
rx->sta->sta.ht_cap.cap &=
|
||||
~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
||||
new_bw = ieee80211_sta_cur_vht_bw(rx->sta);
|
||||
|
||||
if (rx->sta->sta.bandwidth == new_bw)
|
||||
goto handled;
|
||||
|
||||
sband = rx->local->hw.wiphy->bands[status->band];
|
||||
|
||||
@@ -2440,6 +2431,37 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
break;
|
||||
case WLAN_CATEGORY_VHT:
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
|
||||
sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
|
||||
sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
|
||||
sdata->vif.type != NL80211_IFTYPE_AP &&
|
||||
sdata->vif.type != NL80211_IFTYPE_ADHOC)
|
||||
break;
|
||||
|
||||
/* verify action code is present */
|
||||
if (len < IEEE80211_MIN_ACTION_SIZE + 1)
|
||||
goto invalid;
|
||||
|
||||
switch (mgmt->u.action.u.vht_opmode_notif.action_code) {
|
||||
case WLAN_VHT_ACTION_OPMODE_NOTIF: {
|
||||
u8 opmode;
|
||||
|
||||
/* verify opmode is present */
|
||||
if (len < IEEE80211_MIN_ACTION_SIZE + 2)
|
||||
goto invalid;
|
||||
|
||||
opmode = mgmt->u.action.u.vht_opmode_notif.operating_mode;
|
||||
|
||||
ieee80211_vht_handle_opmode(rx->sdata, rx->sta,
|
||||
opmode, status->band,
|
||||
false);
|
||||
goto handled;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WLAN_CATEGORY_BACK:
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
|
||||
@@ -2692,8 +2714,9 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
|
||||
return RX_DROP_MONITOR;
|
||||
break;
|
||||
case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ):
|
||||
/* process only for ibss */
|
||||
if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
|
||||
/* process only for ibss and mesh */
|
||||
if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||
sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
|
||||
return RX_DROP_MONITOR;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -351,6 +351,9 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
|
||||
static bool ieee80211_can_scan(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
if (local->radar_detect_enabled)
|
||||
return false;
|
||||
|
||||
if (!list_empty(&local->roc_list))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -375,6 +375,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
||||
for (i = 0; i < IEEE80211_NUM_TIDS; i++)
|
||||
sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
|
||||
|
||||
sta->sta.smps_mode = IEEE80211_SMPS_OFF;
|
||||
|
||||
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
|
||||
|
||||
return sta;
|
||||
@@ -571,7 +573,6 @@ void sta_info_recalc_tim(struct sta_info *sta)
|
||||
{
|
||||
struct ieee80211_local *local = sta->local;
|
||||
struct ps_data *ps;
|
||||
unsigned long flags;
|
||||
bool indicate_tim = false;
|
||||
u8 ignore_for_tim = sta->sta.uapsd_queues;
|
||||
int ac;
|
||||
@@ -628,7 +629,7 @@ void sta_info_recalc_tim(struct sta_info *sta)
|
||||
}
|
||||
|
||||
done:
|
||||
spin_lock_irqsave(&local->tim_lock, flags);
|
||||
spin_lock_bh(&local->tim_lock);
|
||||
|
||||
if (indicate_tim)
|
||||
__bss_tim_set(ps->tim, id);
|
||||
@@ -641,7 +642,7 @@ void sta_info_recalc_tim(struct sta_info *sta)
|
||||
local->tim_in_locked_section = false;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&local->tim_lock, flags);
|
||||
spin_unlock_bh(&local->tim_lock);
|
||||
}
|
||||
|
||||
static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb)
|
||||
@@ -1120,6 +1121,8 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false);
|
||||
|
||||
skb->dev = sdata->dev;
|
||||
|
||||
rcu_read_lock();
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (WARN_ON(!chanctx_conf)) {
|
||||
|
||||
@@ -296,9 +296,9 @@ struct sta_ampdu_mlme {
|
||||
* @sta: station information we share with the driver
|
||||
* @sta_state: duplicates information about station state (for debug)
|
||||
* @beacon_loss_count: number of times beacon loss has triggered
|
||||
* @supports_40mhz: tracks whether the station advertised 40 MHz support
|
||||
* as we overwrite its HT parameters with the currently used value
|
||||
* @rcu_head: RCU head used for freeing this station struct
|
||||
* @cur_max_bandwidth: maximum bandwidth to use for TX to the station,
|
||||
* taken from HT/VHT capabilities or VHT operating mode notification
|
||||
*/
|
||||
struct sta_info {
|
||||
/* General information, mostly static */
|
||||
@@ -400,11 +400,11 @@ struct sta_info {
|
||||
} debugfs;
|
||||
#endif
|
||||
|
||||
enum ieee80211_sta_rx_bandwidth cur_max_bandwidth;
|
||||
|
||||
unsigned int lost_packets;
|
||||
unsigned int beacon_loss_count;
|
||||
|
||||
bool supports_40mhz;
|
||||
|
||||
/* keep last! */
|
||||
struct ieee80211_sta sta;
|
||||
};
|
||||
|
||||
+4
-6
@@ -177,12 +177,11 @@ void ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *keyconf,
|
||||
struct ieee80211_key *key = (struct ieee80211_key *)
|
||||
container_of(keyconf, struct ieee80211_key, conf);
|
||||
struct tkip_ctx *ctx = &key->u.tkip.tx;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&key->u.tkip.txlock, flags);
|
||||
spin_lock_bh(&key->u.tkip.txlock);
|
||||
ieee80211_compute_tkip_p1k(key, iv32);
|
||||
memcpy(p1k, ctx->p1k, sizeof(ctx->p1k));
|
||||
spin_unlock_irqrestore(&key->u.tkip.txlock, flags);
|
||||
spin_unlock_bh(&key->u.tkip.txlock);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_get_tkip_p1k_iv);
|
||||
|
||||
@@ -208,12 +207,11 @@ void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
|
||||
const u8 *data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
|
||||
u32 iv32 = get_unaligned_le32(&data[4]);
|
||||
u16 iv16 = data[2] | (data[0] << 8);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&key->u.tkip.txlock, flags);
|
||||
spin_lock_bh(&key->u.tkip.txlock);
|
||||
ieee80211_compute_tkip_p1k(key, iv32);
|
||||
tkip_mixing_phase2(tk, ctx, iv16, p2k);
|
||||
spin_unlock_irqrestore(&key->u.tkip.txlock, flags);
|
||||
spin_unlock_bh(&key->u.tkip.txlock);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_get_tkip_p2k);
|
||||
|
||||
|
||||
@@ -1862,6 +1862,25 @@ TRACE_EVENT(drv_set_default_unicast_key,
|
||||
LOCAL_PR_ARG, VIF_PR_ARG, __entry->key_idx)
|
||||
);
|
||||
|
||||
TRACE_EVENT(api_radar_detected,
|
||||
TP_PROTO(struct ieee80211_local *local),
|
||||
|
||||
TP_ARGS(local),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
LOCAL_PR_FMT " radar detected",
|
||||
LOCAL_PR_ARG
|
||||
)
|
||||
);
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESSAGE_TRACING
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM mac80211_msg
|
||||
|
||||
+13
-57
@@ -2364,11 +2364,9 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
|
||||
if (local->tim_in_locked_section) {
|
||||
__ieee80211_beacon_add_tim(sdata, ps, skb);
|
||||
} else {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&local->tim_lock, flags);
|
||||
spin_lock(&local->tim_lock);
|
||||
__ieee80211_beacon_add_tim(sdata, ps, skb);
|
||||
spin_unlock_irqrestore(&local->tim_lock, flags);
|
||||
spin_unlock(&local->tim_lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -2446,71 +2444,26 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
|
||||
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
||||
IEEE80211_STYPE_BEACON);
|
||||
} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
u8 *pos;
|
||||
int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) +
|
||||
sizeof(mgmt->u.beacon);
|
||||
struct beacon_data *bcn = rcu_dereference(ifmsh->beacon);
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
if (!sdata->u.mesh.mesh_id_len)
|
||||
if (!bcn)
|
||||
goto out;
|
||||
#endif
|
||||
|
||||
if (ifmsh->sync_ops)
|
||||
ifmsh->sync_ops->adjust_tbtt(
|
||||
sdata);
|
||||
|
||||
skb = dev_alloc_skb(local->tx_headroom +
|
||||
hdr_len +
|
||||
2 + /* NULL SSID */
|
||||
2 + 8 + /* supported rates */
|
||||
2 + 3 + /* DS params */
|
||||
bcn->head_len +
|
||||
256 + /* TIM IE */
|
||||
2 + (IEEE80211_MAX_SUPP_RATES - 8) +
|
||||
2 + sizeof(struct ieee80211_ht_cap) +
|
||||
2 + sizeof(struct ieee80211_ht_operation) +
|
||||
2 + sdata->u.mesh.mesh_id_len +
|
||||
2 + sizeof(struct ieee80211_meshconf_ie) +
|
||||
sdata->u.mesh.ie_len +
|
||||
2 + sizeof(__le16)); /* awake window */
|
||||
bcn->tail_len);
|
||||
if (!skb)
|
||||
goto out;
|
||||
|
||||
skb_reserve(skb, local->hw.extra_tx_headroom);
|
||||
mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
|
||||
memset(mgmt, 0, hdr_len);
|
||||
mgmt->frame_control =
|
||||
cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON);
|
||||
eth_broadcast_addr(mgmt->da);
|
||||
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
||||
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
|
||||
ieee80211_mps_set_frame_flags(sdata, NULL, (void *) mgmt);
|
||||
mgmt->u.beacon.beacon_int =
|
||||
cpu_to_le16(sdata->vif.bss_conf.beacon_int);
|
||||
mgmt->u.beacon.capab_info |= cpu_to_le16(
|
||||
sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0);
|
||||
|
||||
pos = skb_put(skb, 2);
|
||||
*pos++ = WLAN_EID_SSID;
|
||||
*pos++ = 0x0;
|
||||
|
||||
band = chanctx_conf->def.chan->band;
|
||||
|
||||
if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
|
||||
mesh_add_ds_params_ie(skb, sdata) ||
|
||||
ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb) ||
|
||||
ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
|
||||
mesh_add_rsn_ie(skb, sdata) ||
|
||||
mesh_add_ht_cap_ie(skb, sdata) ||
|
||||
mesh_add_ht_oper_ie(skb, sdata) ||
|
||||
mesh_add_meshid_ie(skb, sdata) ||
|
||||
mesh_add_meshconf_ie(skb, sdata) ||
|
||||
mesh_add_awake_window_ie(skb, sdata) ||
|
||||
mesh_add_vendor_ies(skb, sdata)) {
|
||||
pr_err("o11s: couldn't add ies!\n");
|
||||
goto out;
|
||||
}
|
||||
skb_reserve(skb, local->tx_headroom);
|
||||
memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len);
|
||||
ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb);
|
||||
memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len);
|
||||
} else {
|
||||
WARN_ON(1);
|
||||
goto out;
|
||||
@@ -2785,6 +2738,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
|
||||
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
|
||||
}
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
|
||||
if (!ieee80211_tx_prepare(sdata, &tx, skb))
|
||||
break;
|
||||
dev_kfree_skb_any(skb);
|
||||
@@ -2817,6 +2771,8 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
|
||||
skb_set_queue_mapping(skb, ac);
|
||||
skb->priority = tid;
|
||||
|
||||
skb->dev = sdata->dev;
|
||||
|
||||
/*
|
||||
* The other path calling ieee80211_xmit is from the tasklet,
|
||||
* and while we can handle concurrent transmissions locking
|
||||
|
||||
+55
-7
@@ -739,11 +739,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
|
||||
if (calc_crc)
|
||||
crc = crc32_be(crc, pos - 2, elen + 2);
|
||||
|
||||
if (pos[3] == 1) {
|
||||
/* OUI Type 1 - WPA IE */
|
||||
elems->wpa = pos;
|
||||
elems->wpa_len = elen;
|
||||
} else if (elen >= 5 && pos[3] == 2) {
|
||||
if (elen >= 5 && pos[3] == 2) {
|
||||
/* OUI Type 2 - WMM IE */
|
||||
if (pos[4] == 0) {
|
||||
elems->wmm_info = pos;
|
||||
@@ -791,6 +787,12 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
|
||||
else
|
||||
elem_parse_failed = true;
|
||||
break;
|
||||
case WLAN_EID_OPMODE_NOTIF:
|
||||
if (elen > 0)
|
||||
elems->opmode_notif = pos;
|
||||
else
|
||||
elem_parse_failed = true;
|
||||
break;
|
||||
case WLAN_EID_MESH_ID:
|
||||
elems->mesh_id = pos;
|
||||
elems->mesh_id_len = elen;
|
||||
@@ -1033,7 +1035,7 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
|
||||
|
||||
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
|
||||
u16 transaction, u16 auth_alg, u16 status,
|
||||
u8 *extra, size_t extra_len, const u8 *da,
|
||||
const u8 *extra, size_t extra_len, const u8 *da,
|
||||
const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx,
|
||||
u32 tx_flags)
|
||||
{
|
||||
@@ -1945,7 +1947,7 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
|
||||
}
|
||||
|
||||
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
|
||||
struct ieee80211_ht_operation *ht_oper,
|
||||
const struct ieee80211_ht_operation *ht_oper,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
enum nl80211_channel_type channel_type;
|
||||
@@ -2133,3 +2135,49 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
|
||||
|
||||
if (sdata->wdev.cac_started) {
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
cfg80211_cac_event(sdata->dev,
|
||||
NL80211_RADAR_CAC_ABORTED,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
}
|
||||
|
||||
void ieee80211_dfs_radar_detected_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_local *local =
|
||||
container_of(work, struct ieee80211_local, radar_detected_work);
|
||||
struct cfg80211_chan_def chandef;
|
||||
|
||||
ieee80211_dfs_cac_cancel(local);
|
||||
|
||||
if (local->use_chanctx)
|
||||
/* currently not handled */
|
||||
WARN_ON(1);
|
||||
else {
|
||||
cfg80211_chandef_create(&chandef, local->hw.conf.channel,
|
||||
local->hw.conf.channel_type);
|
||||
cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
|
||||
}
|
||||
}
|
||||
|
||||
void ieee80211_radar_detected(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
|
||||
trace_api_radar_detected(local);
|
||||
|
||||
ieee80211_queue_work(hw, &local->radar_detected_work);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_radar_detected);
|
||||
|
||||
+166
-6
@@ -10,21 +10,29 @@
|
||||
#include <linux/export.h>
|
||||
#include <net/mac80211.h>
|
||||
#include "ieee80211_i.h"
|
||||
#include "rate.h"
|
||||
|
||||
|
||||
void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_vht_cap *vht_cap_ie,
|
||||
struct ieee80211_sta_vht_cap *vht_cap)
|
||||
void
|
||||
ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_supported_band *sband,
|
||||
const struct ieee80211_vht_cap *vht_cap_ie,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
if (WARN_ON_ONCE(!vht_cap))
|
||||
return;
|
||||
struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap;
|
||||
|
||||
memset(vht_cap, 0, sizeof(*vht_cap));
|
||||
|
||||
if (!sta->sta.ht_cap.ht_supported)
|
||||
return;
|
||||
|
||||
if (!vht_cap_ie || !sband->vht_cap.vht_supported)
|
||||
return;
|
||||
|
||||
/* A VHT STA must support 40 MHz */
|
||||
if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
|
||||
return;
|
||||
|
||||
vht_cap->vht_supported = true;
|
||||
|
||||
vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info);
|
||||
@@ -32,4 +40,156 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
/* Copy peer MCS info, the driver might need them. */
|
||||
memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs,
|
||||
sizeof(struct ieee80211_vht_mcs_info));
|
||||
|
||||
switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
|
||||
case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
|
||||
case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
|
||||
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
|
||||
break;
|
||||
default:
|
||||
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
|
||||
}
|
||||
|
||||
sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta);
|
||||
}
|
||||
|
||||
enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
u32 cap = sta->sta.vht_cap.cap;
|
||||
enum ieee80211_sta_rx_bandwidth bw;
|
||||
|
||||
if (!sta->sta.vht_cap.vht_supported) {
|
||||
bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
|
||||
goto check_max;
|
||||
}
|
||||
|
||||
switch (sdata->vif.bss_conf.chandef.width) {
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
/* fall through */
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) ==
|
||||
IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) {
|
||||
bw = IEEE80211_STA_RX_BW_160;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) ==
|
||||
IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
|
||||
bw = IEEE80211_STA_RX_BW_160;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
bw = IEEE80211_STA_RX_BW_80;
|
||||
}
|
||||
|
||||
check_max:
|
||||
if (bw > sta->cur_max_bandwidth)
|
||||
bw = sta->cur_max_bandwidth;
|
||||
return bw;
|
||||
}
|
||||
|
||||
void ieee80211_sta_set_rx_nss(struct sta_info *sta)
|
||||
{
|
||||
u8 ht_rx_nss = 0, vht_rx_nss = 0;
|
||||
|
||||
/* if we received a notification already don't overwrite it */
|
||||
if (sta->sta.rx_nss)
|
||||
return;
|
||||
|
||||
if (sta->sta.ht_cap.ht_supported) {
|
||||
if (sta->sta.ht_cap.mcs.rx_mask[0])
|
||||
ht_rx_nss++;
|
||||
if (sta->sta.ht_cap.mcs.rx_mask[1])
|
||||
ht_rx_nss++;
|
||||
if (sta->sta.ht_cap.mcs.rx_mask[2])
|
||||
ht_rx_nss++;
|
||||
if (sta->sta.ht_cap.mcs.rx_mask[3])
|
||||
ht_rx_nss++;
|
||||
/* FIXME: consider rx_highest? */
|
||||
}
|
||||
|
||||
if (sta->sta.vht_cap.vht_supported) {
|
||||
int i;
|
||||
u16 rx_mcs_map;
|
||||
|
||||
rx_mcs_map = le16_to_cpu(sta->sta.vht_cap.vht_mcs.rx_mcs_map);
|
||||
|
||||
for (i = 7; i >= 0; i--) {
|
||||
u8 mcs = (rx_mcs_map >> (2 * i)) & 3;
|
||||
|
||||
if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
|
||||
vht_rx_nss = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* FIXME: consider rx_highest? */
|
||||
}
|
||||
|
||||
ht_rx_nss = max(ht_rx_nss, vht_rx_nss);
|
||||
sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss);
|
||||
}
|
||||
|
||||
void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
|
||||
struct sta_info *sta, u8 opmode,
|
||||
enum ieee80211_band band, bool nss_only)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_supported_band *sband;
|
||||
enum ieee80211_sta_rx_bandwidth new_bw;
|
||||
u32 changed = 0;
|
||||
u8 nss;
|
||||
|
||||
sband = local->hw.wiphy->bands[band];
|
||||
|
||||
/* ignore - no support for BF yet */
|
||||
if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)
|
||||
return;
|
||||
|
||||
nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
|
||||
nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
|
||||
nss += 1;
|
||||
|
||||
if (sta->sta.rx_nss != nss) {
|
||||
sta->sta.rx_nss = nss;
|
||||
changed |= IEEE80211_RC_NSS_CHANGED;
|
||||
}
|
||||
|
||||
if (nss_only)
|
||||
goto change;
|
||||
|
||||
switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) {
|
||||
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ:
|
||||
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20;
|
||||
break;
|
||||
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ:
|
||||
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40;
|
||||
break;
|
||||
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ:
|
||||
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
|
||||
break;
|
||||
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ:
|
||||
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
|
||||
break;
|
||||
}
|
||||
|
||||
new_bw = ieee80211_sta_cur_vht_bw(sta);
|
||||
if (new_bw != sta->sta.bandwidth) {
|
||||
sta->sta.bandwidth = new_bw;
|
||||
changed |= IEEE80211_RC_NSS_CHANGED;
|
||||
}
|
||||
|
||||
change:
|
||||
if (changed)
|
||||
rate_control_rate_update(local, sband, sta, changed);
|
||||
}
|
||||
|
||||
+2
-3
@@ -181,7 +181,6 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
struct ieee80211_key *key = tx->key;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
unsigned long flags;
|
||||
unsigned int hdrlen;
|
||||
int len, tail;
|
||||
u8 *pos;
|
||||
@@ -216,12 +215,12 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
|
||||
return 0;
|
||||
|
||||
/* Increase IV for the frame */
|
||||
spin_lock_irqsave(&key->u.tkip.txlock, flags);
|
||||
spin_lock(&key->u.tkip.txlock);
|
||||
key->u.tkip.tx.iv16++;
|
||||
if (key->u.tkip.tx.iv16 == 0)
|
||||
key->u.tkip.tx.iv32++;
|
||||
pos = ieee80211_tkip_add_iv(pos, key);
|
||||
spin_unlock_irqrestore(&key->u.tkip.txlock, flags);
|
||||
spin_unlock(&key->u.tkip.txlock);
|
||||
|
||||
/* hwaccel - with software IV */
|
||||
if (info->control.hw_key)
|
||||
|
||||
+139
-3
@@ -147,6 +147,32 @@ static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
|
||||
}
|
||||
}
|
||||
|
||||
static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
|
||||
{
|
||||
int width;
|
||||
|
||||
switch (c->width) {
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
width = 20;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
width = 40;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
width = 80;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
width = 160;
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return -1;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
const struct cfg80211_chan_def *
|
||||
cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
|
||||
const struct cfg80211_chan_def *c2)
|
||||
@@ -192,6 +218,93 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_chandef_compatible);
|
||||
|
||||
static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
|
||||
u32 bandwidth,
|
||||
enum nl80211_dfs_state dfs_state)
|
||||
{
|
||||
struct ieee80211_channel *c;
|
||||
u32 freq;
|
||||
|
||||
for (freq = center_freq - bandwidth/2 + 10;
|
||||
freq <= center_freq + bandwidth/2 - 10;
|
||||
freq += 20) {
|
||||
c = ieee80211_get_channel(wiphy, freq);
|
||||
if (!c || !(c->flags & IEEE80211_CHAN_RADAR))
|
||||
continue;
|
||||
|
||||
c->dfs_state = dfs_state;
|
||||
c->dfs_state_entered = jiffies;
|
||||
}
|
||||
}
|
||||
|
||||
void cfg80211_set_dfs_state(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
enum nl80211_dfs_state dfs_state)
|
||||
{
|
||||
int width;
|
||||
|
||||
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
||||
return;
|
||||
|
||||
width = cfg80211_chandef_get_width(chandef);
|
||||
if (width < 0)
|
||||
return;
|
||||
|
||||
cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1,
|
||||
width, dfs_state);
|
||||
|
||||
if (!chandef->center_freq2)
|
||||
return;
|
||||
cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2,
|
||||
width, dfs_state);
|
||||
}
|
||||
|
||||
static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
|
||||
u32 center_freq,
|
||||
u32 bandwidth)
|
||||
{
|
||||
struct ieee80211_channel *c;
|
||||
u32 freq;
|
||||
|
||||
for (freq = center_freq - bandwidth/2 + 10;
|
||||
freq <= center_freq + bandwidth/2 - 10;
|
||||
freq += 20) {
|
||||
c = ieee80211_get_channel(wiphy, freq);
|
||||
if (!c)
|
||||
return -EINVAL;
|
||||
|
||||
if (c->flags & IEEE80211_CHAN_RADAR)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
int width;
|
||||
int r;
|
||||
|
||||
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
||||
return -EINVAL;
|
||||
|
||||
width = cfg80211_chandef_get_width(chandef);
|
||||
if (width < 0)
|
||||
return -EINVAL;
|
||||
|
||||
r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1,
|
||||
width);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (!chandef->center_freq2)
|
||||
return 0;
|
||||
|
||||
return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
|
||||
width);
|
||||
}
|
||||
|
||||
static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
|
||||
u32 center_freq, u32 bandwidth,
|
||||
u32 prohibited_flags)
|
||||
@@ -203,7 +316,16 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
|
||||
freq <= center_freq + bandwidth/2 - 10;
|
||||
freq += 20) {
|
||||
c = ieee80211_get_channel(wiphy, freq);
|
||||
if (!c || c->flags & prohibited_flags)
|
||||
if (!c)
|
||||
return false;
|
||||
|
||||
/* check for radar flags */
|
||||
if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) &&
|
||||
(c->dfs_state != NL80211_DFS_AVAILABLE))
|
||||
return false;
|
||||
|
||||
/* check for the other flags */
|
||||
if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -253,6 +375,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
if (!vht_cap->vht_supported)
|
||||
return false;
|
||||
prohibited_flags |= IEEE80211_CHAN_NO_80MHZ;
|
||||
width = 80;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
@@ -260,6 +383,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
|
||||
return false;
|
||||
if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
|
||||
return false;
|
||||
prohibited_flags |= IEEE80211_CHAN_NO_160MHZ;
|
||||
width = 160;
|
||||
break;
|
||||
default:
|
||||
@@ -267,7 +391,16 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
|
||||
return false;
|
||||
}
|
||||
|
||||
/* TODO: missing regulatory check on 80/160 bandwidth */
|
||||
/*
|
||||
* TODO: What if there are only certain 80/160/80+80 MHz channels
|
||||
* allowed by the driver, or only certain combinations?
|
||||
* For 40 MHz the driver can set the NO_HT40 flags, but for
|
||||
* 80/160 MHz and in particular 80+80 MHz this isn't really
|
||||
* feasible and we only have NO_80MHZ/NO_160MHZ so far but
|
||||
* no way to cover 80+80 MHz or more complex restrictions.
|
||||
* Note that such restrictions also need to be advertised to
|
||||
* userspace, for example for P2P channel selection.
|
||||
*/
|
||||
|
||||
if (width > 20)
|
||||
prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
|
||||
@@ -344,7 +477,10 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
|
||||
break;
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
if (wdev->beacon_interval) {
|
||||
if (wdev->cac_started) {
|
||||
*chan = wdev->channel;
|
||||
*chanmode = CHAN_MODE_SHARED;
|
||||
} else if (wdev->beacon_interval) {
|
||||
*chan = wdev->channel;
|
||||
*chanmode = CHAN_MODE_SHARED;
|
||||
}
|
||||
|
||||
+5
-1
@@ -324,6 +324,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
|
||||
INIT_LIST_HEAD(&rdev->bss_list);
|
||||
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
|
||||
INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
|
||||
INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk,
|
||||
cfg80211_dfs_channels_update_work);
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
rdev->wiphy.wext = &cfg80211_wext_handler;
|
||||
#endif
|
||||
@@ -365,7 +367,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
|
||||
rdev->wiphy.rts_threshold = (u32) -1;
|
||||
rdev->wiphy.coverage_class = 0;
|
||||
|
||||
rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH;
|
||||
rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH |
|
||||
NL80211_FEATURE_ADVERTISE_CHAN_LIMITS;
|
||||
|
||||
return &rdev->wiphy;
|
||||
}
|
||||
@@ -695,6 +698,7 @@ void wiphy_unregister(struct wiphy *wiphy)
|
||||
flush_work(&rdev->scan_done_wk);
|
||||
cancel_work_sync(&rdev->conn_work);
|
||||
flush_work(&rdev->event_work);
|
||||
cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
|
||||
|
||||
if (rdev->wowlan && rdev->ops->set_wakeup)
|
||||
rdev_set_wakeup(rdev, false);
|
||||
|
||||
@@ -86,6 +86,8 @@ struct cfg80211_registered_device {
|
||||
|
||||
struct cfg80211_wowlan *wowlan;
|
||||
|
||||
struct delayed_work dfs_update_channels_wk;
|
||||
|
||||
/* must be last because of the way we do wiphy_priv(),
|
||||
* and it should at least be aligned to NETDEV_ALIGN */
|
||||
struct wiphy wiphy __aligned(NETDEV_ALIGN);
|
||||
@@ -108,6 +110,9 @@ cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)
|
||||
for (i = 0; i < rdev->wowlan->n_patterns; i++)
|
||||
kfree(rdev->wowlan->patterns[i].mask);
|
||||
kfree(rdev->wowlan->patterns);
|
||||
if (rdev->wowlan->tcp && rdev->wowlan->tcp->sock)
|
||||
sock_release(rdev->wowlan->tcp->sock);
|
||||
kfree(rdev->wowlan->tcp);
|
||||
kfree(rdev->wowlan);
|
||||
}
|
||||
|
||||
@@ -428,6 +433,22 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
|
||||
enum cfg80211_chan_mode chanmode,
|
||||
u8 radar_detect);
|
||||
|
||||
/**
|
||||
* cfg80211_chandef_dfs_required - checks if radar detection is required
|
||||
* @wiphy: the wiphy to validate against
|
||||
* @chandef: the channel definition to check
|
||||
* Return: 1 if radar detection is required, 0 if it is not, < 0 on error
|
||||
*/
|
||||
int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *c);
|
||||
|
||||
void cfg80211_set_dfs_state(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
enum nl80211_dfs_state dfs_state);
|
||||
|
||||
void cfg80211_dfs_channels_update_work(struct work_struct *work);
|
||||
|
||||
|
||||
static inline int
|
||||
cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
@@ -454,6 +475,16 @@ cfg80211_can_use_chan(struct cfg80211_registered_device *rdev,
|
||||
chan, chanmode, 0);
|
||||
}
|
||||
|
||||
static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
|
||||
{
|
||||
unsigned long end = jiffies;
|
||||
|
||||
if (end >= start)
|
||||
return jiffies_to_msecs(end - start);
|
||||
|
||||
return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
|
||||
}
|
||||
|
||||
void
|
||||
cfg80211_get_chan_state(struct wireless_dev *wdev,
|
||||
struct ieee80211_channel **chan,
|
||||
|
||||
@@ -987,3 +987,123 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
|
||||
nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
|
||||
|
||||
void cfg80211_dfs_channels_update_work(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *delayed_work;
|
||||
struct cfg80211_registered_device *rdev;
|
||||
struct cfg80211_chan_def chandef;
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct ieee80211_channel *c;
|
||||
struct wiphy *wiphy;
|
||||
bool check_again = false;
|
||||
unsigned long timeout, next_time = 0;
|
||||
int bandid, i;
|
||||
|
||||
delayed_work = container_of(work, struct delayed_work, work);
|
||||
rdev = container_of(delayed_work, struct cfg80211_registered_device,
|
||||
dfs_update_channels_wk);
|
||||
wiphy = &rdev->wiphy;
|
||||
|
||||
mutex_lock(&cfg80211_mutex);
|
||||
for (bandid = 0; bandid < IEEE80211_NUM_BANDS; bandid++) {
|
||||
sband = wiphy->bands[bandid];
|
||||
if (!sband)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < sband->n_channels; i++) {
|
||||
c = &sband->channels[i];
|
||||
|
||||
if (c->dfs_state != NL80211_DFS_UNAVAILABLE)
|
||||
continue;
|
||||
|
||||
timeout = c->dfs_state_entered +
|
||||
IEEE80211_DFS_MIN_NOP_TIME_MS;
|
||||
|
||||
if (time_after_eq(jiffies, timeout)) {
|
||||
c->dfs_state = NL80211_DFS_USABLE;
|
||||
cfg80211_chandef_create(&chandef, c,
|
||||
NL80211_CHAN_NO_HT);
|
||||
|
||||
nl80211_radar_notify(rdev, &chandef,
|
||||
NL80211_RADAR_NOP_FINISHED,
|
||||
NULL, GFP_ATOMIC);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!check_again)
|
||||
next_time = timeout - jiffies;
|
||||
else
|
||||
next_time = min(next_time, timeout - jiffies);
|
||||
check_again = true;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&cfg80211_mutex);
|
||||
|
||||
/* reschedule if there are other channels waiting to be cleared again */
|
||||
if (check_again)
|
||||
queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
|
||||
next_time);
|
||||
}
|
||||
|
||||
|
||||
void cfg80211_radar_event(struct wiphy *wiphy,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
||||
unsigned long timeout;
|
||||
|
||||
trace_cfg80211_radar_event(wiphy, chandef);
|
||||
|
||||
/* only set the chandef supplied channel to unavailable, in
|
||||
* case the radar is detected on only one of multiple channels
|
||||
* spanned by the chandef.
|
||||
*/
|
||||
cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE);
|
||||
|
||||
timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS);
|
||||
queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
|
||||
timeout);
|
||||
|
||||
nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_radar_event);
|
||||
|
||||
void cfg80211_cac_event(struct net_device *netdev,
|
||||
enum nl80211_radar_event event, gfp_t gfp)
|
||||
{
|
||||
struct wireless_dev *wdev = netdev->ieee80211_ptr;
|
||||
struct wiphy *wiphy = wdev->wiphy;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
||||
struct cfg80211_chan_def chandef;
|
||||
unsigned long timeout;
|
||||
|
||||
trace_cfg80211_cac_event(netdev, event);
|
||||
|
||||
if (WARN_ON(!wdev->cac_started))
|
||||
return;
|
||||
|
||||
if (WARN_ON(!wdev->channel))
|
||||
return;
|
||||
|
||||
cfg80211_chandef_create(&chandef, wdev->channel, NL80211_CHAN_NO_HT);
|
||||
|
||||
switch (event) {
|
||||
case NL80211_RADAR_CAC_FINISHED:
|
||||
timeout = wdev->cac_start_time +
|
||||
msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
|
||||
WARN_ON(!time_after_eq(jiffies, timeout));
|
||||
cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_AVAILABLE);
|
||||
break;
|
||||
case NL80211_RADAR_CAC_ABORTED:
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
wdev->cac_started = false;
|
||||
|
||||
nl80211_radar_notify(rdev, &chandef, event, netdev, gfp);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_cac_event);
|
||||
|
||||
+603
-40
@@ -19,6 +19,7 @@
|
||||
#include <net/genetlink.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/inet_connection_sock.h>
|
||||
#include "core.h"
|
||||
#include "nl80211.h"
|
||||
#include "reg.h"
|
||||
@@ -367,6 +368,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
|
||||
[NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
|
||||
[NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 },
|
||||
[NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
|
||||
[NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
|
||||
[NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
@@ -399,6 +402,26 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
|
||||
[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
|
||||
[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
|
||||
[NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
|
||||
[NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
|
||||
};
|
||||
|
||||
static const struct nla_policy
|
||||
nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = {
|
||||
[NL80211_WOWLAN_TCP_SRC_IPV4] = { .type = NLA_U32 },
|
||||
[NL80211_WOWLAN_TCP_DST_IPV4] = { .type = NLA_U32 },
|
||||
[NL80211_WOWLAN_TCP_DST_MAC] = { .len = ETH_ALEN },
|
||||
[NL80211_WOWLAN_TCP_SRC_PORT] = { .type = NLA_U16 },
|
||||
[NL80211_WOWLAN_TCP_DST_PORT] = { .type = NLA_U16 },
|
||||
[NL80211_WOWLAN_TCP_DATA_PAYLOAD] = { .len = 1 },
|
||||
[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ] = {
|
||||
.len = sizeof(struct nl80211_wowlan_tcp_data_seq)
|
||||
},
|
||||
[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN] = {
|
||||
.len = sizeof(struct nl80211_wowlan_tcp_data_token)
|
||||
},
|
||||
[NL80211_WOWLAN_TCP_DATA_INTERVAL] = { .type = NLA_U32 },
|
||||
[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] = { .len = 1 },
|
||||
[NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 },
|
||||
};
|
||||
|
||||
/* policy for GTK rekey offload attributes */
|
||||
@@ -531,8 +554,27 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
|
||||
if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
|
||||
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
|
||||
goto nla_put_failure;
|
||||
if ((chan->flags & IEEE80211_CHAN_RADAR) &&
|
||||
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
|
||||
if (chan->flags & IEEE80211_CHAN_RADAR) {
|
||||
u32 time = elapsed_jiffies_msecs(chan->dfs_state_entered);
|
||||
if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE,
|
||||
chan->dfs_state))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME, time))
|
||||
goto nla_put_failure;
|
||||
}
|
||||
if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) &&
|
||||
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS))
|
||||
goto nla_put_failure;
|
||||
if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) &&
|
||||
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS))
|
||||
goto nla_put_failure;
|
||||
if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) &&
|
||||
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ))
|
||||
goto nla_put_failure;
|
||||
if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
|
||||
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
|
||||
@@ -872,6 +914,48 @@ nla_put_failure:
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
|
||||
struct sk_buff *msg)
|
||||
{
|
||||
const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan.tcp;
|
||||
struct nlattr *nl_tcp;
|
||||
|
||||
if (!tcp)
|
||||
return 0;
|
||||
|
||||
nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
|
||||
if (!nl_tcp)
|
||||
return -ENOBUFS;
|
||||
|
||||
if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
|
||||
tcp->data_payload_max))
|
||||
return -ENOBUFS;
|
||||
|
||||
if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
|
||||
tcp->data_payload_max))
|
||||
return -ENOBUFS;
|
||||
|
||||
if (tcp->seq && nla_put_flag(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ))
|
||||
return -ENOBUFS;
|
||||
|
||||
if (tcp->tok && nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
|
||||
sizeof(*tcp->tok), tcp->tok))
|
||||
return -ENOBUFS;
|
||||
|
||||
if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
|
||||
tcp->data_interval_max))
|
||||
return -ENOBUFS;
|
||||
|
||||
if (nla_put_u32(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
|
||||
tcp->wake_payload_max))
|
||||
return -ENOBUFS;
|
||||
|
||||
nla_nest_end(msg, nl_tcp);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flags,
|
||||
struct cfg80211_registered_device *dev)
|
||||
{
|
||||
@@ -1238,12 +1322,17 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
|
||||
dev->wiphy.wowlan.pattern_min_len,
|
||||
.max_pattern_len =
|
||||
dev->wiphy.wowlan.pattern_max_len,
|
||||
.max_pkt_offset =
|
||||
dev->wiphy.wowlan.max_pkt_offset,
|
||||
};
|
||||
if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
|
||||
sizeof(pat), &pat))
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
if (nl80211_send_wowlan_tcp_caps(dev, msg))
|
||||
goto nla_put_failure;
|
||||
|
||||
nla_nest_end(msg, nl_wowlan);
|
||||
}
|
||||
#endif
|
||||
@@ -1276,6 +1365,15 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
|
||||
dev->wiphy.max_acl_mac_addrs))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (dev->wiphy.extended_capabilities &&
|
||||
(nla_put(msg, NL80211_ATTR_EXT_CAPA,
|
||||
dev->wiphy.extended_capabilities_len,
|
||||
dev->wiphy.extended_capabilities) ||
|
||||
nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
|
||||
dev->wiphy.extended_capabilities_len,
|
||||
dev->wiphy.extended_capabilities_mask)))
|
||||
goto nla_put_failure;
|
||||
|
||||
return genlmsg_end(msg, hdr);
|
||||
|
||||
nla_put_failure:
|
||||
@@ -2707,6 +2805,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_ap_settings params;
|
||||
int err;
|
||||
u8 radar_detect_width = 0;
|
||||
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
||||
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
||||
@@ -2825,9 +2924,19 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef))
|
||||
return -EINVAL;
|
||||
|
||||
err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (err) {
|
||||
radar_detect_width = BIT(params.chandef.width);
|
||||
params.radar_required = true;
|
||||
}
|
||||
|
||||
mutex_lock(&rdev->devlist_mtx);
|
||||
err = cfg80211_can_use_chan(rdev, wdev, params.chandef.chan,
|
||||
CHAN_MODE_SHARED);
|
||||
err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
|
||||
params.chandef.chan,
|
||||
CHAN_MODE_SHARED,
|
||||
radar_detect_width);
|
||||
mutex_unlock(&rdev->devlist_mtx);
|
||||
|
||||
if (err)
|
||||
@@ -3300,6 +3409,63 @@ static struct net_device *get_vlan(struct genl_info *info,
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static struct nla_policy
|
||||
nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
|
||||
[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
|
||||
[NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
|
||||
};
|
||||
|
||||
static int nl80211_set_station_tdls(struct genl_info *info,
|
||||
struct station_parameters *params)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct nlattr *tb[NL80211_STA_WME_MAX + 1];
|
||||
struct nlattr *nla;
|
||||
int err;
|
||||
|
||||
/* Can only set if TDLS ... */
|
||||
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* ... with external setup is supported */
|
||||
if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Dummy STA entry gets updated once the peer capabilities are known */
|
||||
if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
|
||||
params->ht_capa =
|
||||
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
|
||||
if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
||||
params->vht_capa =
|
||||
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
|
||||
|
||||
/* parse WME attributes if present */
|
||||
if (!info->attrs[NL80211_ATTR_STA_WME])
|
||||
return 0;
|
||||
|
||||
nla = info->attrs[NL80211_ATTR_STA_WME];
|
||||
err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
|
||||
nl80211_sta_wme_policy);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (tb[NL80211_STA_WME_UAPSD_QUEUES])
|
||||
params->uapsd_queues = nla_get_u8(
|
||||
tb[NL80211_STA_WME_UAPSD_QUEUES]);
|
||||
if (params->uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
if (tb[NL80211_STA_WME_MAX_SP])
|
||||
params->max_sp = nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
|
||||
|
||||
if (params->max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
params->sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
@@ -3328,8 +3494,20 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
||||
nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL] ||
|
||||
info->attrs[NL80211_ATTR_HT_CAPABILITY])
|
||||
if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) {
|
||||
params.capability =
|
||||
nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]);
|
||||
params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) {
|
||||
params.ext_capab =
|
||||
nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
|
||||
params.ext_capab_len =
|
||||
nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
|
||||
return -EINVAL;
|
||||
|
||||
if (!rdev->ops->change_station)
|
||||
@@ -3398,6 +3576,13 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
||||
/* reject other things that can't change */
|
||||
if (params.supported_rates)
|
||||
return -EINVAL;
|
||||
if (info->attrs[NL80211_ATTR_STA_CAPABILITY])
|
||||
return -EINVAL;
|
||||
if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
|
||||
return -EINVAL;
|
||||
if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
|
||||
info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
||||
return -EINVAL;
|
||||
|
||||
/* must be last in here for error handling */
|
||||
params.vlan = get_vlan(info, rdev);
|
||||
@@ -3413,13 +3598,29 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
||||
* to change the flag.
|
||||
*/
|
||||
params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
|
||||
/* fall through */
|
||||
/* Include parameters for TDLS peer (driver will check) */
|
||||
err = nl80211_set_station_tdls(info, ¶ms);
|
||||
if (err)
|
||||
return err;
|
||||
/* disallow things sta doesn't support */
|
||||
if (params.plink_action)
|
||||
return -EINVAL;
|
||||
if (params.local_pm)
|
||||
return -EINVAL;
|
||||
/* reject any changes other than AUTHORIZED or WME (for TDLS) */
|
||||
if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
|
||||
BIT(NL80211_STA_FLAG_WME)))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
/* disallow things sta doesn't support */
|
||||
if (params.plink_action)
|
||||
return -EINVAL;
|
||||
if (params.local_pm)
|
||||
return -EINVAL;
|
||||
if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
|
||||
info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
||||
return -EINVAL;
|
||||
/* reject any changes other than AUTHORIZED */
|
||||
if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
|
||||
return -EINVAL;
|
||||
@@ -3430,6 +3631,13 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
||||
return -EINVAL;
|
||||
if (params.supported_rates)
|
||||
return -EINVAL;
|
||||
if (info->attrs[NL80211_ATTR_STA_CAPABILITY])
|
||||
return -EINVAL;
|
||||
if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
|
||||
return -EINVAL;
|
||||
if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
|
||||
info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
||||
return -EINVAL;
|
||||
/*
|
||||
* No special handling for TDLS here -- the userspace
|
||||
* mesh code doesn't have this bug.
|
||||
@@ -3454,12 +3662,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct nla_policy
|
||||
nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
|
||||
[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
|
||||
[NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
|
||||
};
|
||||
|
||||
static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
@@ -3494,6 +3696,19 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!params.aid || params.aid > IEEE80211_MAX_AID)
|
||||
return -EINVAL;
|
||||
|
||||
if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) {
|
||||
params.capability =
|
||||
nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]);
|
||||
params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) {
|
||||
params.ext_capab =
|
||||
nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
|
||||
params.ext_capab_len =
|
||||
nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
|
||||
params.ht_capa =
|
||||
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
|
||||
@@ -4987,6 +5202,54 @@ static int nl80211_stop_sched_scan(struct sk_buff *skb,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nl80211_start_radar_detection(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_chan_def chandef;
|
||||
int err;
|
||||
|
||||
err = nl80211_parse_chandef(rdev, info, &chandef);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (wdev->cac_started)
|
||||
return -EBUSY;
|
||||
|
||||
err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (err == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (chandef.chan->dfs_state != NL80211_DFS_USABLE)
|
||||
return -EINVAL;
|
||||
|
||||
if (!rdev->ops->start_radar_detection)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mutex_lock(&rdev->devlist_mtx);
|
||||
err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
|
||||
chandef.chan, CHAN_MODE_SHARED,
|
||||
BIT(chandef.width));
|
||||
if (err)
|
||||
goto err_locked;
|
||||
|
||||
err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef);
|
||||
if (!err) {
|
||||
wdev->channel = chandef.chan;
|
||||
wdev->cac_started = true;
|
||||
wdev->cac_start_time = jiffies;
|
||||
}
|
||||
err_locked:
|
||||
mutex_unlock(&rdev->devlist_mtx);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
|
||||
u32 seq, int flags,
|
||||
struct cfg80211_registered_device *rdev,
|
||||
@@ -6895,16 +7158,100 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int nl80211_send_wowlan_patterns(struct sk_buff *msg,
|
||||
struct cfg80211_registered_device *rdev)
|
||||
{
|
||||
struct nlattr *nl_pats, *nl_pat;
|
||||
int i, pat_len;
|
||||
|
||||
if (!rdev->wowlan->n_patterns)
|
||||
return 0;
|
||||
|
||||
nl_pats = nla_nest_start(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN);
|
||||
if (!nl_pats)
|
||||
return -ENOBUFS;
|
||||
|
||||
for (i = 0; i < rdev->wowlan->n_patterns; i++) {
|
||||
nl_pat = nla_nest_start(msg, i + 1);
|
||||
if (!nl_pat)
|
||||
return -ENOBUFS;
|
||||
pat_len = rdev->wowlan->patterns[i].pattern_len;
|
||||
if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
|
||||
DIV_ROUND_UP(pat_len, 8),
|
||||
rdev->wowlan->patterns[i].mask) ||
|
||||
nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
|
||||
pat_len, rdev->wowlan->patterns[i].pattern) ||
|
||||
nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET,
|
||||
rdev->wowlan->patterns[i].pkt_offset))
|
||||
return -ENOBUFS;
|
||||
nla_nest_end(msg, nl_pat);
|
||||
}
|
||||
nla_nest_end(msg, nl_pats);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_send_wowlan_tcp(struct sk_buff *msg,
|
||||
struct cfg80211_wowlan_tcp *tcp)
|
||||
{
|
||||
struct nlattr *nl_tcp;
|
||||
|
||||
if (!tcp)
|
||||
return 0;
|
||||
|
||||
nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
|
||||
if (!nl_tcp)
|
||||
return -ENOBUFS;
|
||||
|
||||
if (nla_put_be32(msg, NL80211_WOWLAN_TCP_SRC_IPV4, tcp->src) ||
|
||||
nla_put_be32(msg, NL80211_WOWLAN_TCP_DST_IPV4, tcp->dst) ||
|
||||
nla_put(msg, NL80211_WOWLAN_TCP_DST_MAC, ETH_ALEN, tcp->dst_mac) ||
|
||||
nla_put_u16(msg, NL80211_WOWLAN_TCP_SRC_PORT, tcp->src_port) ||
|
||||
nla_put_u16(msg, NL80211_WOWLAN_TCP_DST_PORT, tcp->dst_port) ||
|
||||
nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
|
||||
tcp->payload_len, tcp->payload) ||
|
||||
nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
|
||||
tcp->data_interval) ||
|
||||
nla_put(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
|
||||
tcp->wake_len, tcp->wake_data) ||
|
||||
nla_put(msg, NL80211_WOWLAN_TCP_WAKE_MASK,
|
||||
DIV_ROUND_UP(tcp->wake_len, 8), tcp->wake_mask))
|
||||
return -ENOBUFS;
|
||||
|
||||
if (tcp->payload_seq.len &&
|
||||
nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ,
|
||||
sizeof(tcp->payload_seq), &tcp->payload_seq))
|
||||
return -ENOBUFS;
|
||||
|
||||
if (tcp->payload_tok.len &&
|
||||
nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
|
||||
sizeof(tcp->payload_tok) + tcp->tokens_size,
|
||||
&tcp->payload_tok))
|
||||
return -ENOBUFS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct sk_buff *msg;
|
||||
void *hdr;
|
||||
u32 size = NLMSG_DEFAULT_SIZE;
|
||||
|
||||
if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
|
||||
if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
|
||||
!rdev->wiphy.wowlan.tcp)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (rdev->wowlan && rdev->wowlan->tcp) {
|
||||
/* adjust size to have room for all the data */
|
||||
size += rdev->wowlan->tcp->tokens_size +
|
||||
rdev->wowlan->tcp->payload_len +
|
||||
rdev->wowlan->tcp->wake_len +
|
||||
rdev->wowlan->tcp->wake_len / 8;
|
||||
}
|
||||
|
||||
msg = nlmsg_new(size, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -6935,31 +7282,12 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
|
||||
(rdev->wowlan->rfkill_release &&
|
||||
nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
|
||||
goto nla_put_failure;
|
||||
if (rdev->wowlan->n_patterns) {
|
||||
struct nlattr *nl_pats, *nl_pat;
|
||||
int i, pat_len;
|
||||
|
||||
nl_pats = nla_nest_start(msg,
|
||||
NL80211_WOWLAN_TRIG_PKT_PATTERN);
|
||||
if (!nl_pats)
|
||||
goto nla_put_failure;
|
||||
if (nl80211_send_wowlan_patterns(msg, rdev))
|
||||
goto nla_put_failure;
|
||||
|
||||
for (i = 0; i < rdev->wowlan->n_patterns; i++) {
|
||||
nl_pat = nla_nest_start(msg, i + 1);
|
||||
if (!nl_pat)
|
||||
goto nla_put_failure;
|
||||
pat_len = rdev->wowlan->patterns[i].pattern_len;
|
||||
if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
|
||||
DIV_ROUND_UP(pat_len, 8),
|
||||
rdev->wowlan->patterns[i].mask) ||
|
||||
nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
|
||||
pat_len,
|
||||
rdev->wowlan->patterns[i].pattern))
|
||||
goto nla_put_failure;
|
||||
nla_nest_end(msg, nl_pat);
|
||||
}
|
||||
nla_nest_end(msg, nl_pats);
|
||||
}
|
||||
if (nl80211_send_wowlan_tcp(msg, rdev->wowlan->tcp))
|
||||
goto nla_put_failure;
|
||||
|
||||
nla_nest_end(msg, nl_wowlan);
|
||||
}
|
||||
@@ -6972,6 +7300,150 @@ nla_put_failure:
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
|
||||
struct nlattr *attr,
|
||||
struct cfg80211_wowlan *trig)
|
||||
{
|
||||
struct nlattr *tb[NUM_NL80211_WOWLAN_TCP];
|
||||
struct cfg80211_wowlan_tcp *cfg;
|
||||
struct nl80211_wowlan_tcp_data_token *tok = NULL;
|
||||
struct nl80211_wowlan_tcp_data_seq *seq = NULL;
|
||||
u32 size;
|
||||
u32 data_size, wake_size, tokens_size = 0, wake_mask_size;
|
||||
int err, port;
|
||||
|
||||
if (!rdev->wiphy.wowlan.tcp)
|
||||
return -EINVAL;
|
||||
|
||||
err = nla_parse(tb, MAX_NL80211_WOWLAN_TCP,
|
||||
nla_data(attr), nla_len(attr),
|
||||
nl80211_wowlan_tcp_policy);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!tb[NL80211_WOWLAN_TCP_SRC_IPV4] ||
|
||||
!tb[NL80211_WOWLAN_TCP_DST_IPV4] ||
|
||||
!tb[NL80211_WOWLAN_TCP_DST_MAC] ||
|
||||
!tb[NL80211_WOWLAN_TCP_DST_PORT] ||
|
||||
!tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD] ||
|
||||
!tb[NL80211_WOWLAN_TCP_DATA_INTERVAL] ||
|
||||
!tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] ||
|
||||
!tb[NL80211_WOWLAN_TCP_WAKE_MASK])
|
||||
return -EINVAL;
|
||||
|
||||
data_size = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]);
|
||||
if (data_size > rdev->wiphy.wowlan.tcp->data_payload_max)
|
||||
return -EINVAL;
|
||||
|
||||
if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
|
||||
rdev->wiphy.wowlan.tcp->data_interval_max)
|
||||
return -EINVAL;
|
||||
|
||||
wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
|
||||
if (wake_size > rdev->wiphy.wowlan.tcp->wake_payload_max)
|
||||
return -EINVAL;
|
||||
|
||||
wake_mask_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_MASK]);
|
||||
if (wake_mask_size != DIV_ROUND_UP(wake_size, 8))
|
||||
return -EINVAL;
|
||||
|
||||
if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]) {
|
||||
u32 tokln = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]);
|
||||
|
||||
tok = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]);
|
||||
tokens_size = tokln - sizeof(*tok);
|
||||
|
||||
if (!tok->len || tokens_size % tok->len)
|
||||
return -EINVAL;
|
||||
if (!rdev->wiphy.wowlan.tcp->tok)
|
||||
return -EINVAL;
|
||||
if (tok->len > rdev->wiphy.wowlan.tcp->tok->max_len)
|
||||
return -EINVAL;
|
||||
if (tok->len < rdev->wiphy.wowlan.tcp->tok->min_len)
|
||||
return -EINVAL;
|
||||
if (tokens_size > rdev->wiphy.wowlan.tcp->tok->bufsize)
|
||||
return -EINVAL;
|
||||
if (tok->offset + tok->len > data_size)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) {
|
||||
seq = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]);
|
||||
if (!rdev->wiphy.wowlan.tcp->seq)
|
||||
return -EINVAL;
|
||||
if (seq->len == 0 || seq->len > 4)
|
||||
return -EINVAL;
|
||||
if (seq->len + seq->offset > data_size)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size = sizeof(*cfg);
|
||||
size += data_size;
|
||||
size += wake_size + wake_mask_size;
|
||||
size += tokens_size;
|
||||
|
||||
cfg = kzalloc(size, GFP_KERNEL);
|
||||
if (!cfg)
|
||||
return -ENOMEM;
|
||||
cfg->src = nla_get_be32(tb[NL80211_WOWLAN_TCP_SRC_IPV4]);
|
||||
cfg->dst = nla_get_be32(tb[NL80211_WOWLAN_TCP_DST_IPV4]);
|
||||
memcpy(cfg->dst_mac, nla_data(tb[NL80211_WOWLAN_TCP_DST_MAC]),
|
||||
ETH_ALEN);
|
||||
if (tb[NL80211_WOWLAN_TCP_SRC_PORT])
|
||||
port = nla_get_u16(tb[NL80211_WOWLAN_TCP_SRC_PORT]);
|
||||
else
|
||||
port = 0;
|
||||
#ifdef CONFIG_INET
|
||||
/* allocate a socket and port for it and use it */
|
||||
err = __sock_create(wiphy_net(&rdev->wiphy), PF_INET, SOCK_STREAM,
|
||||
IPPROTO_TCP, &cfg->sock, 1);
|
||||
if (err) {
|
||||
kfree(cfg);
|
||||
return err;
|
||||
}
|
||||
if (inet_csk_get_port(cfg->sock->sk, port)) {
|
||||
sock_release(cfg->sock);
|
||||
kfree(cfg);
|
||||
return -EADDRINUSE;
|
||||
}
|
||||
cfg->src_port = inet_sk(cfg->sock->sk)->inet_num;
|
||||
#else
|
||||
if (!port) {
|
||||
kfree(cfg);
|
||||
return -EINVAL;
|
||||
}
|
||||
cfg->src_port = port;
|
||||
#endif
|
||||
|
||||
cfg->dst_port = nla_get_u16(tb[NL80211_WOWLAN_TCP_DST_PORT]);
|
||||
cfg->payload_len = data_size;
|
||||
cfg->payload = (u8 *)cfg + sizeof(*cfg) + tokens_size;
|
||||
memcpy((void *)cfg->payload,
|
||||
nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]),
|
||||
data_size);
|
||||
if (seq)
|
||||
cfg->payload_seq = *seq;
|
||||
cfg->data_interval = nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]);
|
||||
cfg->wake_len = wake_size;
|
||||
cfg->wake_data = (u8 *)cfg + sizeof(*cfg) + tokens_size + data_size;
|
||||
memcpy((void *)cfg->wake_data,
|
||||
nla_data(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]),
|
||||
wake_size);
|
||||
cfg->wake_mask = (u8 *)cfg + sizeof(*cfg) + tokens_size +
|
||||
data_size + wake_size;
|
||||
memcpy((void *)cfg->wake_mask,
|
||||
nla_data(tb[NL80211_WOWLAN_TCP_WAKE_MASK]),
|
||||
wake_mask_size);
|
||||
if (tok) {
|
||||
cfg->tokens_size = tokens_size;
|
||||
memcpy(&cfg->payload_tok, tok, sizeof(*tok) + tokens_size);
|
||||
}
|
||||
|
||||
trig->tcp = cfg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
@@ -6982,7 +7454,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
|
||||
int err, i;
|
||||
bool prev_enabled = rdev->wowlan;
|
||||
|
||||
if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
|
||||
if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
|
||||
!rdev->wiphy.wowlan.tcp)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
|
||||
@@ -7046,7 +7519,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
|
||||
if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
|
||||
struct nlattr *pat;
|
||||
int n_patterns = 0;
|
||||
int rem, pat_len, mask_len;
|
||||
int rem, pat_len, mask_len, pkt_offset;
|
||||
struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
|
||||
|
||||
nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
|
||||
@@ -7081,6 +7554,15 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
|
||||
pat_len < wowlan->pattern_min_len)
|
||||
goto error;
|
||||
|
||||
if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET])
|
||||
pkt_offset = 0;
|
||||
else
|
||||
pkt_offset = nla_get_u32(
|
||||
pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]);
|
||||
if (pkt_offset > wowlan->max_pkt_offset)
|
||||
goto error;
|
||||
new_triggers.patterns[i].pkt_offset = pkt_offset;
|
||||
|
||||
new_triggers.patterns[i].mask =
|
||||
kmalloc(mask_len + pat_len, GFP_KERNEL);
|
||||
if (!new_triggers.patterns[i].mask) {
|
||||
@@ -7100,6 +7582,14 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
|
||||
}
|
||||
}
|
||||
|
||||
if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) {
|
||||
err = nl80211_parse_wowlan_tcp(
|
||||
rdev, tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION],
|
||||
&new_triggers);
|
||||
if (err)
|
||||
goto error;
|
||||
}
|
||||
|
||||
ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
|
||||
if (!ntrig) {
|
||||
err = -ENOMEM;
|
||||
@@ -7117,6 +7607,9 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
|
||||
for (i = 0; i < new_triggers.n_patterns; i++)
|
||||
kfree(new_triggers.patterns[i].mask);
|
||||
kfree(new_triggers.patterns);
|
||||
if (new_triggers.tcp && new_triggers.tcp->sock)
|
||||
sock_release(new_triggers.tcp->sock);
|
||||
kfree(new_triggers.tcp);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
@@ -8007,6 +8500,14 @@ static struct genl_ops nl80211_ops[] = {
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_RADAR_DETECT,
|
||||
.doit = nl80211_start_radar_detection,
|
||||
.policy = nl80211_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_multicast_group nl80211_mlme_mcgrp = {
|
||||
@@ -9203,6 +9704,57 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
|
||||
nlmsg_free(msg);
|
||||
}
|
||||
|
||||
void
|
||||
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
enum nl80211_radar_event event,
|
||||
struct net_device *netdev, gfp_t gfp)
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
void *hdr;
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_RADAR_DETECT);
|
||||
if (!hdr) {
|
||||
nlmsg_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
|
||||
goto nla_put_failure;
|
||||
|
||||
/* NOP and radar events don't need a netdev parameter */
|
||||
if (netdev) {
|
||||
struct wireless_dev *wdev = netdev->ieee80211_ptr;
|
||||
|
||||
if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
|
||||
nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
if (nla_put_u32(msg, NL80211_ATTR_RADAR_EVENT, event))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nl80211_send_chandef(msg, chandef))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (genlmsg_end(msg, hdr) < 0) {
|
||||
nlmsg_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
||||
nl80211_mlme_mcgrp.id, gfp);
|
||||
return;
|
||||
|
||||
nla_put_failure:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
nlmsg_free(msg);
|
||||
}
|
||||
|
||||
void
|
||||
nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev, const u8 *peer,
|
||||
@@ -9398,6 +9950,17 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
|
||||
wakeup->pattern_idx))
|
||||
goto free_msg;
|
||||
|
||||
if (wakeup->tcp_match)
|
||||
nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH);
|
||||
|
||||
if (wakeup->tcp_connlost)
|
||||
nla_put_flag(msg,
|
||||
NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST);
|
||||
|
||||
if (wakeup->tcp_nomoretokens)
|
||||
nla_put_flag(msg,
|
||||
NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS);
|
||||
|
||||
if (wakeup->packet) {
|
||||
u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211;
|
||||
u32 len_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN;
|
||||
|
||||
@@ -108,6 +108,13 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev,
|
||||
enum nl80211_cqm_rssi_threshold_event rssi_event,
|
||||
gfp_t gfp);
|
||||
|
||||
void
|
||||
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
enum nl80211_radar_event event,
|
||||
struct net_device *netdev, gfp_t gfp);
|
||||
|
||||
void
|
||||
nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev, const u8 *peer,
|
||||
|
||||
@@ -866,6 +866,10 @@ static void handle_channel(struct wiphy *wiphy,
|
||||
|
||||
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
|
||||
bw_flags = IEEE80211_CHAN_NO_HT40;
|
||||
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80))
|
||||
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
|
||||
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160))
|
||||
bw_flags |= IEEE80211_CHAN_NO_160MHZ;
|
||||
|
||||
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
|
||||
request_wiphy && request_wiphy == wiphy &&
|
||||
@@ -884,6 +888,9 @@ static void handle_channel(struct wiphy *wiphy,
|
||||
return;
|
||||
}
|
||||
|
||||
chan->dfs_state = NL80211_DFS_USABLE;
|
||||
chan->dfs_state_entered = jiffies;
|
||||
|
||||
chan->beacon_found = false;
|
||||
chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags);
|
||||
chan->max_antenna_gain =
|
||||
@@ -1261,6 +1268,10 @@ static void handle_channel_custom(struct wiphy *wiphy,
|
||||
|
||||
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
|
||||
bw_flags = IEEE80211_CHAN_NO_HT40;
|
||||
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80))
|
||||
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
|
||||
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160))
|
||||
bw_flags |= IEEE80211_CHAN_NO_160MHZ;
|
||||
|
||||
chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
|
||||
chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
|
||||
|
||||
+8
-14
@@ -365,14 +365,18 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type,
|
||||
if (!pos)
|
||||
return NULL;
|
||||
|
||||
if (end - pos < sizeof(*ie))
|
||||
return NULL;
|
||||
|
||||
ie = (struct ieee80211_vendor_ie *)pos;
|
||||
|
||||
/* make sure we can access ie->len */
|
||||
BUILD_BUG_ON(offsetof(struct ieee80211_vendor_ie, len) != 1);
|
||||
|
||||
if (ie->len < sizeof(*ie))
|
||||
goto cont;
|
||||
|
||||
ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2];
|
||||
if (ie_oui == oui && ie->oui_type == oui_type)
|
||||
return pos;
|
||||
|
||||
cont:
|
||||
pos += 2 + ie->len;
|
||||
}
|
||||
return NULL;
|
||||
@@ -1206,16 +1210,6 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
|
||||
{
|
||||
unsigned long end = jiffies;
|
||||
|
||||
if (end >= start)
|
||||
return jiffies_to_msecs(end - start);
|
||||
|
||||
return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
|
||||
}
|
||||
|
||||
static char *
|
||||
ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
|
||||
struct cfg80211_internal_bss *bss, char *current_ev,
|
||||
|
||||
@@ -2051,6 +2051,21 @@ TRACE_EVENT(cfg80211_reg_can_beacon,
|
||||
WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cfg80211_chandef_dfs_required,
|
||||
TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
|
||||
TP_ARGS(wiphy, chandef),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
CHAN_DEF_ENTRY
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
CHAN_DEF_ASSIGN(chandef);
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
|
||||
WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cfg80211_ch_switch_notify,
|
||||
TP_PROTO(struct net_device *netdev,
|
||||
struct cfg80211_chan_def *chandef),
|
||||
@@ -2067,6 +2082,36 @@ TRACE_EVENT(cfg80211_ch_switch_notify,
|
||||
NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cfg80211_radar_event,
|
||||
TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
|
||||
TP_ARGS(wiphy, chandef),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
CHAN_DEF_ENTRY
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
CHAN_DEF_ASSIGN(chandef);
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
|
||||
WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cfg80211_cac_event,
|
||||
TP_PROTO(struct net_device *netdev, enum nl80211_radar_event evt),
|
||||
TP_ARGS(netdev, evt),
|
||||
TP_STRUCT__entry(
|
||||
NETDEV_ENTRY
|
||||
__field(enum nl80211_radar_event, evt)
|
||||
),
|
||||
TP_fast_assign(
|
||||
NETDEV_ASSIGN;
|
||||
__entry->evt = evt;
|
||||
),
|
||||
TP_printk(NETDEV_PR_FMT ", event: %d",
|
||||
NETDEV_PR_ARG, __entry->evt)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cfg80211_rx_evt,
|
||||
TP_PROTO(struct net_device *netdev, const u8 *addr),
|
||||
TP_ARGS(netdev, addr),
|
||||
|
||||
Reference in New Issue
Block a user