Release 4.11 net/mac80211/tdls.c
/*
* mac80211 TDLS handling code
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2014, Intel Corporation
* Copyright 2014 Intel Mobile Communications GmbH
* Copyright 2015 - 2016 Intel Deutschland GmbH
*
* This file is GPLv2 as found in COPYING.
*/
#include <linux/ieee80211.h>
#include <linux/log2.h>
#include <net/cfg80211.h>
#include <linux/rtnetlink.h>
#include "ieee80211_i.h"
#include "driver-ops.h"
#include "rate.h"
/* give usermode some time for retries in setting up the TDLS session */
#define TDLS_PEER_SETUP_TIMEOUT (15 * HZ)
void ieee80211_tdls_peer_del_work(struct work_struct *wk)
{
struct ieee80211_sub_if_data *sdata;
struct ieee80211_local *local;
sdata = container_of(wk, struct ieee80211_sub_if_data,
u.mgd.tdls_peer_del_work.work);
local = sdata->local;
mutex_lock(&local->mtx);
if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer)) {
tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->u.mgd.tdls_peer);
sta_info_destroy_addr(sdata, sdata->u.mgd.tdls_peer);
eth_zero_addr(sdata->u.mgd.tdls_peer);
}
mutex_unlock(&local->mtx);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 115 | 100.00% | 2 | 100.00% |
Total | 115 | 100.00% | 2 | 100.00% |
static void ieee80211_tdls_add_ext_capab(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
bool chan_switch = local->hw.wiphy->features &
NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) &&
!ifmgd->tdls_wider_bw_prohibited;
enum nl80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
bool vht = sband && sband->vht_cap.vht_supported;
u8 *pos = (void *)skb_put(skb, 10);
*pos++ = WLAN_EID_EXT_CAPABILITY;
*pos++ = 8; /* len */
*pos++ = 0x0;
*pos++ = 0x0;
*pos++ = 0x0;
*pos++ = chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0;
*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
*pos++ = 0;
*pos++ = 0;
*pos++ = (vht && wider_band) ? WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED : 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 191 | 99.48% | 4 | 80.00% |
Johannes Berg | 1 | 0.52% | 1 | 20.00% |
Total | 192 | 100.00% | 5 | 100.00% |
static u8
ieee80211_tdls_add_subband(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, u16 start, u16 end,
u16 spacing)
{
u8 subband_cnt = 0, ch_cnt = 0;
struct ieee80211_channel *ch;
struct cfg80211_chan_def chandef;
int i, subband_start;
struct wiphy *wiphy = sdata->local->hw.wiphy;
for (i = start; i <= end; i += spacing) {
if (!ch_cnt)
subband_start = i;
ch = ieee80211_get_channel(sdata->local->hw.wiphy, i);
if (ch) {
/* we will be active on the channel */
cfg80211_chandef_create(&chandef, ch,
NL80211_CHAN_NO_HT);
if (cfg80211_reg_can_beacon_relax(wiphy, &chandef,
sdata->wdev.iftype)) {
ch_cnt++;
/*
* check if the next channel is also part of
* this allowed range
*/
continue;
}
}
/*
* we've reached the end of a range, with allowed channels
* found
*/
if (ch_cnt) {
u8 *pos = skb_put(skb, 2);
*pos++ = ieee80211_frequency_to_channel(subband_start);
*pos++ = ch_cnt;
subband_cnt++;
ch_cnt = 0;
}
}
/* all channels in the requested range are allowed - add them here */
if (ch_cnt) {
u8 *pos = skb_put(skb, 2);
*pos++ = ieee80211_frequency_to_channel(subband_start);
*pos++ = ch_cnt;
subband_cnt++;
}
return subband_cnt;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 220 | 100.00% | 3 | 100.00% |
Total | 220 | 100.00% | 3 | 100.00% |
static void
ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
/*
* Add possible channels for TDLS. These are channels that are allowed
* to be active.
*/
u8 subband_cnt;
u8 *pos = skb_put(skb, 2);
*pos++ = WLAN_EID_SUPPORTED_CHANNELS;
/*
* 5GHz and 2GHz channels numbers can overlap. Ignore this for now, as
* this doesn't happen in real world scenarios.
*/
/* 2GHz, with 5MHz spacing */
subband_cnt = ieee80211_tdls_add_subband(sdata, skb, 2412, 2472, 5);
/* 5GHz, with 20MHz spacing */
subband_cnt += ieee80211_tdls_add_subband(sdata, skb, 5000, 5825, 20);
/* length */
*pos = 2 * subband_cnt;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 78 | 100.00% | 1 | 100.00% |
Total | 78 | 100.00% | 1 | 100.00% |
static void ieee80211_tdls_add_oper_classes(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
u8 *pos;
u8 op_class;
if (!ieee80211_chandef_to_operating_class(&sdata->vif.bss_conf.chandef,
&op_class))
return;
pos = skb_put(skb, 4);
*pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES;
*pos++ = 2; /* len */
*pos++ = op_class;
*pos++ = op_class; /* give current operating class as alternate too */
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 77 | 100.00% | 1 | 100.00% |
Total | 77 | 100.00% | 1 | 100.00% |
static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb)
{
u8 *pos = (void *)skb_put(skb, 3);
*pos++ = WLAN_EID_BSS_COEX_2040;
*pos++ = 1; /* len */
*pos++ = WLAN_BSS_COEX_INFORMATION_REQUEST;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 45 | 100.00% | 1 | 100.00% |
Total | 45 | 100.00% | 1 | 100.00% |
static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
u16 status_code)
{
/* The capability will be 0 when sending a failure code */
if (status_code != 0)
return 0;
if (ieee80211_get_sdata_band(sdata) == NL80211_BAND_2GHZ) {
return WLAN_CAPABILITY_SHORT_SLOT_TIME |
WLAN_CAPABILITY_SHORT_PREAMBLE;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 36 | 83.72% | 2 | 50.00% |
Johannes Berg | 7 | 16.28% | 2 | 50.00% |
Total | 43 | 100.00% | 4 | 100.00% |
static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, const u8 *peer,
bool initiator)
{
struct ieee80211_tdls_lnkie *lnkid;
const u8 *init_addr, *rsp_addr;
if (initiator) {
init_addr = sdata->vif.addr;
rsp_addr = peer;
} else {
init_addr = peer;
rsp_addr = sdata->vif.addr;
}
lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
lnkid->ie_type = WLAN_EID_LINK_ID;
lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
memcpy(lnkid->bssid, sdata->u.mgd.bssid, ETH_ALEN);
memcpy(lnkid->init_sta, init_addr, ETH_ALEN);
memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 143 | 99.31% | 2 | 66.67% |
Johannes Berg | 1 | 0.69% | 1 | 33.33% |
Total | 144 | 100.00% | 3 | 100.00% |
static void
ieee80211_tdls_add_aid(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 *pos = (void *)skb_put(skb, 4);
*pos++ = WLAN_EID_AID;
*pos++ = 2; /* len */
put_unaligned_le16(ifmgd->aid, pos);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 65 | 100.00% | 1 | 100.00% |
Total | 65 | 100.00% | 1 | 100.00% |
/* translate numbering in the WMM parameter IE to the mac80211 notation */
static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac)
{
switch (ac) {
default:
WARN_ON_ONCE(1);
case 0:
return IEEE80211_AC_BE;
case 1:
return IEEE80211_AC_BK;
case 2:
return IEEE80211_AC_VI;
case 3:
return IEEE80211_AC_VO;
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 46 | 100.00% | 1 | 100.00% |
Total | 46 | 100.00% | 1 | 100.00% |
static u8 ieee80211_wmm_aci_aifsn(int aifsn, bool acm, int aci)
{
u8 ret;
ret = aifsn & 0x0f;
if (acm)
ret |= 0x10;
ret |= (aci << 5) & 0x60;
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 45 | 100.00% | 1 | 100.00% |
Total | 45 | 100.00% | 1 | 100.00% |
static u8 ieee80211_wmm_ecw(u16 cw_min, u16 cw_max)
{
return ((ilog2(cw_min + 1) << 0x0) & 0x0f) |
((ilog2(cw_max + 1) << 0x4) & 0xf0);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 43 | 100.00% | 1 | 100.00% |
Total | 43 | 100.00% | 1 | 100.00% |
static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
struct ieee80211_wmm_param_ie *wmm;
struct ieee80211_tx_queue_params *txq;
int i;
wmm = (void *)skb_put(skb, sizeof(*wmm));
memset(wmm, 0, sizeof(*wmm));
wmm->element_id = WLAN_EID_VENDOR_SPECIFIC;
wmm->len = sizeof(*wmm) - 2;
wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */
wmm->oui[1] = 0x50;
wmm->oui[2] = 0xf2;
wmm->oui_type = 2; /* WME */
wmm->oui_subtype = 1; /* WME param */
wmm->version = 1; /* WME ver */
wmm->qos_info = 0; /* U-APSD not in use */
/*
* Use the EDCA parameters defined for the BSS, or default if the AP
* doesn't support it, as mandated by 802.11-2012 section 10.22.4
*/
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
txq = &sdata->tx_conf[ieee80211_ac_from_wmm(i)];
wmm->ac[i].aci_aifsn = ieee80211_wmm_aci_aifsn(txq->aifs,
txq->acm, i);
wmm->ac[i].cw = ieee80211_wmm_ecw(txq->cw_min, txq->cw_max);
wmm->ac[i].txop_limit = cpu_to_le16(txq->txop);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 220 | 100.00% | 1 | 100.00% |
Total | 220 | 100.00% | 1 | 100.00% |
static void
ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta)
{
/* IEEE802.11ac-2013 Table E-4 */
u16 centers_80mhz[] = { 5210, 5290, 5530, 5610, 5690, 5775 };
struct cfg80211_chan_def uc = sta->tdls_chandef;
enum nl80211_chan_width max_width = ieee80211_sta_cap_chan_bw(sta);
int i;
/* only support upgrading non-narrow channels up to 80Mhz */
if (max_width == NL80211_CHAN_WIDTH_5 ||
max_width == NL80211_CHAN_WIDTH_10)
return;
if (max_width > NL80211_CHAN_WIDTH_80)
max_width = NL80211_CHAN_WIDTH_80;
if (uc.width >= max_width)
return;
/*
* Channel usage constrains in the IEEE802.11ac-2013 specification only
* allow expanding a 20MHz channel to 80MHz in a single way. In
* addition, there are no 40MHz allowed channels that are not part of
* the allowed 80MHz range in the 5GHz spectrum (the relevant one here).
*/
for (i = 0; i < ARRAY_SIZE(centers_80mhz); i++)
if (abs(uc.chan->center_freq - centers_80mhz[i]) <= 30) {
uc.center_freq1 = centers_80mhz[i];
uc.center_freq2 = 0;
uc.width = NL80211_CHAN_WIDTH_80;
break;
}
if (!uc.center_freq1)
return;
/* proceed to downgrade the chandef until usable or the same as AP BW */
while (uc.width > max_width ||
(uc.width > sta->tdls_chandef.width &&
!cfg80211_reg_can_beacon_relax(sdata->local->hw.wiphy, &uc,
sdata->wdev.iftype)))
ieee80211_chandef_downgrade(&uc);
if (!cfg80211_chandef_identical(&uc, &sta->tdls_chandef)) {
tdls_dbg(sdata, "TDLS ch width upgraded %d -> %d\n",
sta->tdls_chandef.width, uc.width);
/*
* the station is not yet authorized when BW upgrade is done,
* locking is not required
*/
sta->tdls_chandef = uc;
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 234 | 97.10% | 6 | 85.71% |
Ilan Peer | 7 | 2.90% | 1 | 14.29% |
Total | 241 | 100.00% | 7 | 100.00% |
static void
ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, const u8 *peer,
u8 action_code, bool initiator,
const u8 *extra_ies, size_t extra_ies_len)
{
enum nl80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
struct ieee80211_sta_ht_cap ht_cap;
struct ieee80211_sta_vht_cap vht_cap;
struct sta_info *sta = NULL;
size_t offset = 0, noffset;
u8 *pos;
ieee80211_add_srates_ie(sdata, skb, false, band);
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
ieee80211_tdls_add_supp_channels(sdata, skb);
/* add any custom IEs that go before Extended Capabilities */
if (extra_ies_len) {
static const u8 before_ext_cap[] = {
WLAN_EID_SUPP_RATES,
WLAN_EID_COUNTRY,
WLAN_EID_EXT_SUPP_RATES,
WLAN_EID_SUPPORTED_CHANNELS,
WLAN_EID_RSN,
};
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
before_ext_cap,
ARRAY_SIZE(before_ext_cap),
offset);
pos = skb_put(skb, noffset - offset);
memcpy(pos, extra_ies + offset, noffset - offset);
offset = noffset;
}
ieee80211_tdls_add_ext_capab(sdata, skb);
/* add the QoS element if we support it */
if (local->hw.queues >= IEEE80211_NUM_ACS &&
action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES)
ieee80211_add_wmm_info_ie(skb_put(skb, 9), 0); /* no U-APSD */
/* add any custom IEs that go before HT capabilities */
if (extra_ies_len) {
static const u8 before_ht_cap[] = {
WLAN_EID_SUPP_RATES,
WLAN_EID_COUNTRY,
WLAN_EID_EXT_SUPP_RATES,
WLAN_EID_SUPPORTED_CHANNELS,
WLAN_EID_RSN,
WLAN_EID_EXT_CAPABILITY,
WLAN_EID_QOS_CAPA,
WLAN_EID_FAST_BSS_TRANSITION,
WLAN_EID_TIMEOUT_INTERVAL,
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
};
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
before_ht_cap,
ARRAY_SIZE(before_ht_cap),
offset);
pos = skb_put(skb, noffset - offset);
memcpy(pos, extra_ies + offset, noffset - offset);
offset = noffset;
}
mutex_lock(&local->sta_mtx);
/* we should have the peer STA if we're already responding */
if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
sta = sta_info_get(sdata, peer);
if (WARN_ON_ONCE(!sta)) {
mutex_unlock(&local->sta_mtx);
return;
}
sta->tdls_chandef = sdata->vif.bss_conf.chandef;
}
ieee80211_tdls_add_oper_classes(sdata, skb);
/*
* with TDLS we can switch channels, and HT-caps are not necessarily
* the same on all bands. The specification limits the setup to a
* single HT-cap, so use the current band for now.
*/
sband = local->hw.wiphy->bands[band];
memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) &&
ht_cap.ht_supported) {
ieee80211_apply_htcap_overrides(sdata, &ht_cap);
/* disable SMPS in TDLS initiator */
ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED
<< IEEE80211_HT_CAP_SM_PS_SHIFT;
pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
} else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
/* the peer caps are already intersected with our own */
memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
}
if (ht_cap.ht_supported &&
(ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
ieee80211_tdls_add_bss_coex_ie(skb);
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
/* add any custom IEs that go before VHT capabilities */
if (extra_ies_len) {
static const u8 before_vht_cap[] = {
WLAN_EID_SUPP_RATES,
WLAN_EID_COUNTRY,
WLAN_EID_EXT_SUPP_RATES,
WLAN_EID_SUPPORTED_CHANNELS,
WLAN_EID_RSN,
WLAN_EID_EXT_CAPABILITY,
WLAN_EID_QOS_CAPA,
WLAN_EID_FAST_BSS_TRANSITION,
WLAN_EID_TIMEOUT_INTERVAL,
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
WLAN_EID_MULTI_BAND,
};
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
before_vht_cap,
ARRAY_SIZE(before_vht_cap),
offset);
pos = skb_put(skb, noffset - offset);
memcpy(pos, extra_ies + offset, noffset - offset);
offset = noffset;
}
/* build the VHT-cap similarly to the HT-cap */
memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) &&
vht_cap.vht_supported) {
ieee80211_apply_vhtcap_overrides(sdata, &vht_cap);
/* the AID is present only when VHT is implemented */
if (action_code == WLAN_TDLS_SETUP_REQUEST)
ieee80211_tdls_add_aid(sdata, skb);
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
} else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
vht_cap.vht_supported && sta->sta.vht_cap.vht_supported) {
/* the peer caps are already intersected with our own */
memcpy(&vht_cap, &sta->sta.vht_cap, sizeof(vht_cap));
/* the AID is present only when VHT is implemented */
ieee80211_tdls_add_aid(sdata, skb);
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
/*
* if both peers support WIDER_BW, we can expand the chandef to
* a wider compatible one, up to 80MHz
*/
if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
}
mutex_unlock(&local->sta_mtx);
/* add any remaining IEs */
if (extra_ies_len) {
noffset = extra_ies_len;
pos = skb_put(skb, noffset - offset);
memcpy(pos, extra_ies + offset, noffset - offset);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 808 | 94.50% | 13 | 86.67% |
Johannes Berg | 47 | 5.50% | 2 | 13.33% |
Total | 855 | 100.00% | 15 | 100.00% |
static void
ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, const u8 *peer,
bool initiator, const u8 *extra_ies,
size_t extra_ies_len)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
size_t offset = 0, noffset;
struct sta_info *sta, *ap_sta;
enum nl80211_band band = ieee80211_get_sdata_band(sdata);
u8 *pos;
mutex_lock(&local->sta_mtx);
sta = sta_info_get(sdata, peer);
ap_sta = sta_info_get(sdata, ifmgd->bssid);
if (WARN_ON_ONCE(!sta || !ap_sta)) {
mutex_unlock(&local->sta_mtx);
return;
}
sta->tdls_chandef = sdata->vif.bss_conf.chandef;
/* add any custom IEs that go before the QoS IE */
if (extra_ies_len) {
static const u8 before_qos[] = {
WLAN_EID_RSN,
};
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
before_qos,
ARRAY_SIZE(before_qos),
offset);
pos = skb_put(skb, noffset - offset);
memcpy(pos, extra_ies + offset, noffset - offset);
offset = noffset;
}
/* add the QoS param IE if both the peer and we support it */
if (local->hw.queues >= IEEE80211_NUM_ACS && sta->sta.wme)
ieee80211_tdls_add_wmm_param_ie(sdata, skb);
/* add any custom IEs that go before HT operation */
if (extra_ies_len) {
static const u8 before_ht_op[] = {
WLAN_EID_RSN,
WLAN_EID_QOS_CAPA,
WLAN_EID_FAST_BSS_TRANSITION,
WLAN_EID_TIMEOUT_INTERVAL,
};
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
before_ht_op,
ARRAY_SIZE(before_ht_op),
offset);
pos = skb_put(skb, noffset - offset);
memcpy(pos, extra_ies + offset, noffset - offset);
offset = noffset;
}
/*
* if HT support is only added in TDLS, we need an HT-operation IE.
* add the IE as required by IEEE802.11-2012 9.23.3.2.
*/
if (!ap_sta->sta.ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
u16 prot = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED |
IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT |
IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT;
pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
ieee80211_ie_build_ht_oper(pos, &sta->sta.ht_cap,
&sdata->vif.bss_conf.chandef, prot,
true);
}
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
/* only include VHT-operation if not on the 2.4GHz band */
if (band != NL80211_BAND_2GHZ && sta->sta.vht_cap.vht_supported) {
/*
* if both peers support WIDER_BW, we can expand the chandef to
* a wider compatible one, up to 80MHz
*/
if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation));
ieee80211_ie_build_vht_oper(pos, &sta->sta.vht_cap,
&sta->tdls_chandef);
}
mutex_unlock(&local->sta_mtx);
/* add any remaining IEs */
if (extra_ies_len) {
noffset = extra_ies_len;
pos = skb_put(skb, noffset - offset);
memcpy(pos, extra_ies + offset, noffset - offset);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 484 | 98.78% | 6 | 75.00% |
Johannes Berg | 6 | 1.22% | 2 | 25.00% |
Total | 490 | 100.00% | 8 | 100.00% |
static void
ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, const u8 *peer,
bool initiator, const u8 *extra_ies,
size_t extra_ies_len, u8 oper_class,
struct cfg80211_chan_def *chandef)
{
struct ieee80211_tdls_data *tf;
size_t offset = 0, noffset;
u8 *pos;
if (WARN_ON_ONCE(!chandef))
return;
tf = (void *)skb->data;
tf->u.chan_switch_req.target_channel =
ieee80211_frequency_to_channel(chandef->chan->center_freq);
tf->u.chan_switch_req.oper_class = oper_class;
if (extra_ies_len