162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * mac80211 TDLS handling code 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> 662306a36Sopenharmony_ci * Copyright 2014, Intel Corporation 762306a36Sopenharmony_ci * Copyright 2014 Intel Mobile Communications GmbH 862306a36Sopenharmony_ci * Copyright 2015 - 2016 Intel Deutschland GmbH 962306a36Sopenharmony_ci * Copyright (C) 2019, 2021-2023 Intel Corporation 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/ieee80211.h> 1362306a36Sopenharmony_ci#include <linux/log2.h> 1462306a36Sopenharmony_ci#include <net/cfg80211.h> 1562306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1662306a36Sopenharmony_ci#include "ieee80211_i.h" 1762306a36Sopenharmony_ci#include "driver-ops.h" 1862306a36Sopenharmony_ci#include "rate.h" 1962306a36Sopenharmony_ci#include "wme.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* give usermode some time for retries in setting up the TDLS session */ 2262306a36Sopenharmony_ci#define TDLS_PEER_SETUP_TIMEOUT (15 * HZ) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_civoid ieee80211_tdls_peer_del_work(struct work_struct *wk) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 2762306a36Sopenharmony_ci struct ieee80211_local *local; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci sdata = container_of(wk, struct ieee80211_sub_if_data, 3062306a36Sopenharmony_ci u.mgd.tdls_peer_del_work.work); 3162306a36Sopenharmony_ci local = sdata->local; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci mutex_lock(&local->mtx); 3462306a36Sopenharmony_ci if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer)) { 3562306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->u.mgd.tdls_peer); 3662306a36Sopenharmony_ci sta_info_destroy_addr(sdata, sdata->u.mgd.tdls_peer); 3762306a36Sopenharmony_ci eth_zero_addr(sdata->u.mgd.tdls_peer); 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci mutex_unlock(&local->mtx); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void ieee80211_tdls_add_ext_capab(struct ieee80211_link_data *link, 4362306a36Sopenharmony_ci struct sk_buff *skb) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 4662306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 4762306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 4862306a36Sopenharmony_ci bool chan_switch = local->hw.wiphy->features & 4962306a36Sopenharmony_ci NL80211_FEATURE_TDLS_CHANNEL_SWITCH; 5062306a36Sopenharmony_ci bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) && 5162306a36Sopenharmony_ci !ifmgd->tdls_wider_bw_prohibited; 5262306a36Sopenharmony_ci bool buffer_sta = ieee80211_hw_check(&local->hw, 5362306a36Sopenharmony_ci SUPPORTS_TDLS_BUFFER_STA); 5462306a36Sopenharmony_ci struct ieee80211_supported_band *sband = ieee80211_get_link_sband(link); 5562306a36Sopenharmony_ci bool vht = sband && sband->vht_cap.vht_supported; 5662306a36Sopenharmony_ci u8 *pos = skb_put(skb, 10); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci *pos++ = WLAN_EID_EXT_CAPABILITY; 5962306a36Sopenharmony_ci *pos++ = 8; /* len */ 6062306a36Sopenharmony_ci *pos++ = 0x0; 6162306a36Sopenharmony_ci *pos++ = 0x0; 6262306a36Sopenharmony_ci *pos++ = 0x0; 6362306a36Sopenharmony_ci *pos++ = (chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0) | 6462306a36Sopenharmony_ci (buffer_sta ? WLAN_EXT_CAPA4_TDLS_BUFFER_STA : 0); 6562306a36Sopenharmony_ci *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; 6662306a36Sopenharmony_ci *pos++ = 0; 6762306a36Sopenharmony_ci *pos++ = 0; 6862306a36Sopenharmony_ci *pos++ = (vht && wider_band) ? WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED : 0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic u8 7262306a36Sopenharmony_ciieee80211_tdls_add_subband(struct ieee80211_sub_if_data *sdata, 7362306a36Sopenharmony_ci struct sk_buff *skb, u16 start, u16 end, 7462306a36Sopenharmony_ci u16 spacing) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci u8 subband_cnt = 0, ch_cnt = 0; 7762306a36Sopenharmony_ci struct ieee80211_channel *ch; 7862306a36Sopenharmony_ci struct cfg80211_chan_def chandef; 7962306a36Sopenharmony_ci int i, subband_start; 8062306a36Sopenharmony_ci struct wiphy *wiphy = sdata->local->hw.wiphy; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci for (i = start; i <= end; i += spacing) { 8362306a36Sopenharmony_ci if (!ch_cnt) 8462306a36Sopenharmony_ci subband_start = i; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ch = ieee80211_get_channel(sdata->local->hw.wiphy, i); 8762306a36Sopenharmony_ci if (ch) { 8862306a36Sopenharmony_ci /* we will be active on the channel */ 8962306a36Sopenharmony_ci cfg80211_chandef_create(&chandef, ch, 9062306a36Sopenharmony_ci NL80211_CHAN_NO_HT); 9162306a36Sopenharmony_ci if (cfg80211_reg_can_beacon_relax(wiphy, &chandef, 9262306a36Sopenharmony_ci sdata->wdev.iftype)) { 9362306a36Sopenharmony_ci ch_cnt++; 9462306a36Sopenharmony_ci /* 9562306a36Sopenharmony_ci * check if the next channel is also part of 9662306a36Sopenharmony_ci * this allowed range 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci continue; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* 10362306a36Sopenharmony_ci * we've reached the end of a range, with allowed channels 10462306a36Sopenharmony_ci * found 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci if (ch_cnt) { 10762306a36Sopenharmony_ci u8 *pos = skb_put(skb, 2); 10862306a36Sopenharmony_ci *pos++ = ieee80211_frequency_to_channel(subband_start); 10962306a36Sopenharmony_ci *pos++ = ch_cnt; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci subband_cnt++; 11262306a36Sopenharmony_ci ch_cnt = 0; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* all channels in the requested range are allowed - add them here */ 11762306a36Sopenharmony_ci if (ch_cnt) { 11862306a36Sopenharmony_ci u8 *pos = skb_put(skb, 2); 11962306a36Sopenharmony_ci *pos++ = ieee80211_frequency_to_channel(subband_start); 12062306a36Sopenharmony_ci *pos++ = ch_cnt; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci subband_cnt++; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return subband_cnt; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void 12962306a36Sopenharmony_ciieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata, 13062306a36Sopenharmony_ci struct sk_buff *skb) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * Add possible channels for TDLS. These are channels that are allowed 13462306a36Sopenharmony_ci * to be active. 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci u8 subband_cnt; 13762306a36Sopenharmony_ci u8 *pos = skb_put(skb, 2); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci *pos++ = WLAN_EID_SUPPORTED_CHANNELS; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * 5GHz and 2GHz channels numbers can overlap. Ignore this for now, as 14362306a36Sopenharmony_ci * this doesn't happen in real world scenarios. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* 2GHz, with 5MHz spacing */ 14762306a36Sopenharmony_ci subband_cnt = ieee80211_tdls_add_subband(sdata, skb, 2412, 2472, 5); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* 5GHz, with 20MHz spacing */ 15062306a36Sopenharmony_ci subband_cnt += ieee80211_tdls_add_subband(sdata, skb, 5000, 5825, 20); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* length */ 15362306a36Sopenharmony_ci *pos = 2 * subband_cnt; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic void ieee80211_tdls_add_oper_classes(struct ieee80211_link_data *link, 15762306a36Sopenharmony_ci struct sk_buff *skb) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci u8 *pos; 16062306a36Sopenharmony_ci u8 op_class; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (!ieee80211_chandef_to_operating_class(&link->conf->chandef, 16362306a36Sopenharmony_ci &op_class)) 16462306a36Sopenharmony_ci return; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci pos = skb_put(skb, 4); 16762306a36Sopenharmony_ci *pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES; 16862306a36Sopenharmony_ci *pos++ = 2; /* len */ 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci *pos++ = op_class; 17162306a36Sopenharmony_ci *pos++ = op_class; /* give current operating class as alternate too */ 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci u8 *pos = skb_put(skb, 3); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci *pos++ = WLAN_EID_BSS_COEX_2040; 17962306a36Sopenharmony_ci *pos++ = 1; /* len */ 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci *pos++ = WLAN_BSS_COEX_INFORMATION_REQUEST; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic u16 ieee80211_get_tdls_sta_capab(struct ieee80211_link_data *link, 18562306a36Sopenharmony_ci u16 status_code) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* The capability will be 0 when sending a failure code */ 19062306a36Sopenharmony_ci if (status_code != 0) 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci sband = ieee80211_get_link_sband(link); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (sband && sband->band == NL80211_BAND_2GHZ) { 19662306a36Sopenharmony_ci return WLAN_CAPABILITY_SHORT_SLOT_TIME | 19762306a36Sopenharmony_ci WLAN_CAPABILITY_SHORT_PREAMBLE; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic void ieee80211_tdls_add_link_ie(struct ieee80211_link_data *link, 20462306a36Sopenharmony_ci struct sk_buff *skb, const u8 *peer, 20562306a36Sopenharmony_ci bool initiator) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 20862306a36Sopenharmony_ci struct ieee80211_tdls_lnkie *lnkid; 20962306a36Sopenharmony_ci const u8 *init_addr, *rsp_addr; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (initiator) { 21262306a36Sopenharmony_ci init_addr = sdata->vif.addr; 21362306a36Sopenharmony_ci rsp_addr = peer; 21462306a36Sopenharmony_ci } else { 21562306a36Sopenharmony_ci init_addr = peer; 21662306a36Sopenharmony_ci rsp_addr = sdata->vif.addr; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci lnkid = skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci lnkid->ie_type = WLAN_EID_LINK_ID; 22262306a36Sopenharmony_ci lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci memcpy(lnkid->bssid, link->u.mgd.bssid, ETH_ALEN); 22562306a36Sopenharmony_ci memcpy(lnkid->init_sta, init_addr, ETH_ALEN); 22662306a36Sopenharmony_ci memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic void 23062306a36Sopenharmony_ciieee80211_tdls_add_aid(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci u8 *pos = skb_put(skb, 4); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci *pos++ = WLAN_EID_AID; 23562306a36Sopenharmony_ci *pos++ = 2; /* len */ 23662306a36Sopenharmony_ci put_unaligned_le16(sdata->vif.cfg.aid, pos); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/* translate numbering in the WMM parameter IE to the mac80211 notation */ 24062306a36Sopenharmony_cistatic enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci switch (ac) { 24362306a36Sopenharmony_ci default: 24462306a36Sopenharmony_ci WARN_ON_ONCE(1); 24562306a36Sopenharmony_ci fallthrough; 24662306a36Sopenharmony_ci case 0: 24762306a36Sopenharmony_ci return IEEE80211_AC_BE; 24862306a36Sopenharmony_ci case 1: 24962306a36Sopenharmony_ci return IEEE80211_AC_BK; 25062306a36Sopenharmony_ci case 2: 25162306a36Sopenharmony_ci return IEEE80211_AC_VI; 25262306a36Sopenharmony_ci case 3: 25362306a36Sopenharmony_ci return IEEE80211_AC_VO; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic u8 ieee80211_wmm_aci_aifsn(int aifsn, bool acm, int aci) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci u8 ret; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci ret = aifsn & 0x0f; 26262306a36Sopenharmony_ci if (acm) 26362306a36Sopenharmony_ci ret |= 0x10; 26462306a36Sopenharmony_ci ret |= (aci << 5) & 0x60; 26562306a36Sopenharmony_ci return ret; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic u8 ieee80211_wmm_ecw(u16 cw_min, u16 cw_max) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci return ((ilog2(cw_min + 1) << 0x0) & 0x0f) | 27162306a36Sopenharmony_ci ((ilog2(cw_max + 1) << 0x4) & 0xf0); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata, 27562306a36Sopenharmony_ci struct sk_buff *skb) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct ieee80211_wmm_param_ie *wmm; 27862306a36Sopenharmony_ci struct ieee80211_tx_queue_params *txq; 27962306a36Sopenharmony_ci int i; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci wmm = skb_put_zero(skb, sizeof(*wmm)); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci wmm->element_id = WLAN_EID_VENDOR_SPECIFIC; 28462306a36Sopenharmony_ci wmm->len = sizeof(*wmm) - 2; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */ 28762306a36Sopenharmony_ci wmm->oui[1] = 0x50; 28862306a36Sopenharmony_ci wmm->oui[2] = 0xf2; 28962306a36Sopenharmony_ci wmm->oui_type = 2; /* WME */ 29062306a36Sopenharmony_ci wmm->oui_subtype = 1; /* WME param */ 29162306a36Sopenharmony_ci wmm->version = 1; /* WME ver */ 29262306a36Sopenharmony_ci wmm->qos_info = 0; /* U-APSD not in use */ 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* 29562306a36Sopenharmony_ci * Use the EDCA parameters defined for the BSS, or default if the AP 29662306a36Sopenharmony_ci * doesn't support it, as mandated by 802.11-2012 section 10.22.4 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_ACS; i++) { 29962306a36Sopenharmony_ci txq = &sdata->deflink.tx_conf[ieee80211_ac_from_wmm(i)]; 30062306a36Sopenharmony_ci wmm->ac[i].aci_aifsn = ieee80211_wmm_aci_aifsn(txq->aifs, 30162306a36Sopenharmony_ci txq->acm, i); 30262306a36Sopenharmony_ci wmm->ac[i].cw = ieee80211_wmm_ecw(txq->cw_min, txq->cw_max); 30362306a36Sopenharmony_ci wmm->ac[i].txop_limit = cpu_to_le16(txq->txop); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic void 30862306a36Sopenharmony_ciieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata, 30962306a36Sopenharmony_ci struct sta_info *sta) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci /* IEEE802.11ac-2013 Table E-4 */ 31262306a36Sopenharmony_ci u16 centers_80mhz[] = { 5210, 5290, 5530, 5610, 5690, 5775 }; 31362306a36Sopenharmony_ci struct cfg80211_chan_def uc = sta->tdls_chandef; 31462306a36Sopenharmony_ci enum nl80211_chan_width max_width = 31562306a36Sopenharmony_ci ieee80211_sta_cap_chan_bw(&sta->deflink); 31662306a36Sopenharmony_ci int i; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* only support upgrading non-narrow channels up to 80Mhz */ 31962306a36Sopenharmony_ci if (max_width == NL80211_CHAN_WIDTH_5 || 32062306a36Sopenharmony_ci max_width == NL80211_CHAN_WIDTH_10) 32162306a36Sopenharmony_ci return; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (max_width > NL80211_CHAN_WIDTH_80) 32462306a36Sopenharmony_ci max_width = NL80211_CHAN_WIDTH_80; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (uc.width >= max_width) 32762306a36Sopenharmony_ci return; 32862306a36Sopenharmony_ci /* 32962306a36Sopenharmony_ci * Channel usage constrains in the IEEE802.11ac-2013 specification only 33062306a36Sopenharmony_ci * allow expanding a 20MHz channel to 80MHz in a single way. In 33162306a36Sopenharmony_ci * addition, there are no 40MHz allowed channels that are not part of 33262306a36Sopenharmony_ci * the allowed 80MHz range in the 5GHz spectrum (the relevant one here). 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(centers_80mhz); i++) 33562306a36Sopenharmony_ci if (abs(uc.chan->center_freq - centers_80mhz[i]) <= 30) { 33662306a36Sopenharmony_ci uc.center_freq1 = centers_80mhz[i]; 33762306a36Sopenharmony_ci uc.center_freq2 = 0; 33862306a36Sopenharmony_ci uc.width = NL80211_CHAN_WIDTH_80; 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (!uc.center_freq1) 34362306a36Sopenharmony_ci return; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* proceed to downgrade the chandef until usable or the same as AP BW */ 34662306a36Sopenharmony_ci while (uc.width > max_width || 34762306a36Sopenharmony_ci (uc.width > sta->tdls_chandef.width && 34862306a36Sopenharmony_ci !cfg80211_reg_can_beacon_relax(sdata->local->hw.wiphy, &uc, 34962306a36Sopenharmony_ci sdata->wdev.iftype))) 35062306a36Sopenharmony_ci ieee80211_chandef_downgrade(&uc); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (!cfg80211_chandef_identical(&uc, &sta->tdls_chandef)) { 35362306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS ch width upgraded %d -> %d\n", 35462306a36Sopenharmony_ci sta->tdls_chandef.width, uc.width); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* 35762306a36Sopenharmony_ci * the station is not yet authorized when BW upgrade is done, 35862306a36Sopenharmony_ci * locking is not required 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_ci sta->tdls_chandef = uc; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic void 36562306a36Sopenharmony_ciieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link, 36662306a36Sopenharmony_ci struct sk_buff *skb, const u8 *peer, 36762306a36Sopenharmony_ci u8 action_code, bool initiator, 36862306a36Sopenharmony_ci const u8 *extra_ies, size_t extra_ies_len) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 37162306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 37262306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 37362306a36Sopenharmony_ci struct ieee80211_sta_ht_cap ht_cap; 37462306a36Sopenharmony_ci struct ieee80211_sta_vht_cap vht_cap; 37562306a36Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap; 37662306a36Sopenharmony_ci const struct ieee80211_sta_eht_cap *eht_cap; 37762306a36Sopenharmony_ci struct sta_info *sta = NULL; 37862306a36Sopenharmony_ci size_t offset = 0, noffset; 37962306a36Sopenharmony_ci u8 *pos; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci sband = ieee80211_get_link_sband(link); 38262306a36Sopenharmony_ci if (WARN_ON_ONCE(!sband)) 38362306a36Sopenharmony_ci return; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci ieee80211_add_srates_ie(sdata, skb, false, sband->band); 38662306a36Sopenharmony_ci ieee80211_add_ext_srates_ie(sdata, skb, false, sband->band); 38762306a36Sopenharmony_ci ieee80211_tdls_add_supp_channels(sdata, skb); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* add any custom IEs that go before Extended Capabilities */ 39062306a36Sopenharmony_ci if (extra_ies_len) { 39162306a36Sopenharmony_ci static const u8 before_ext_cap[] = { 39262306a36Sopenharmony_ci WLAN_EID_SUPP_RATES, 39362306a36Sopenharmony_ci WLAN_EID_COUNTRY, 39462306a36Sopenharmony_ci WLAN_EID_EXT_SUPP_RATES, 39562306a36Sopenharmony_ci WLAN_EID_SUPPORTED_CHANNELS, 39662306a36Sopenharmony_ci WLAN_EID_RSN, 39762306a36Sopenharmony_ci }; 39862306a36Sopenharmony_ci noffset = ieee80211_ie_split(extra_ies, extra_ies_len, 39962306a36Sopenharmony_ci before_ext_cap, 40062306a36Sopenharmony_ci ARRAY_SIZE(before_ext_cap), 40162306a36Sopenharmony_ci offset); 40262306a36Sopenharmony_ci skb_put_data(skb, extra_ies + offset, noffset - offset); 40362306a36Sopenharmony_ci offset = noffset; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci ieee80211_tdls_add_ext_capab(link, skb); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* add the QoS element if we support it */ 40962306a36Sopenharmony_ci if (local->hw.queues >= IEEE80211_NUM_ACS && 41062306a36Sopenharmony_ci action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES) 41162306a36Sopenharmony_ci ieee80211_add_wmm_info_ie(skb_put(skb, 9), 0); /* no U-APSD */ 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* add any custom IEs that go before HT capabilities */ 41462306a36Sopenharmony_ci if (extra_ies_len) { 41562306a36Sopenharmony_ci static const u8 before_ht_cap[] = { 41662306a36Sopenharmony_ci WLAN_EID_SUPP_RATES, 41762306a36Sopenharmony_ci WLAN_EID_COUNTRY, 41862306a36Sopenharmony_ci WLAN_EID_EXT_SUPP_RATES, 41962306a36Sopenharmony_ci WLAN_EID_SUPPORTED_CHANNELS, 42062306a36Sopenharmony_ci WLAN_EID_RSN, 42162306a36Sopenharmony_ci WLAN_EID_EXT_CAPABILITY, 42262306a36Sopenharmony_ci WLAN_EID_QOS_CAPA, 42362306a36Sopenharmony_ci WLAN_EID_FAST_BSS_TRANSITION, 42462306a36Sopenharmony_ci WLAN_EID_TIMEOUT_INTERVAL, 42562306a36Sopenharmony_ci WLAN_EID_SUPPORTED_REGULATORY_CLASSES, 42662306a36Sopenharmony_ci }; 42762306a36Sopenharmony_ci noffset = ieee80211_ie_split(extra_ies, extra_ies_len, 42862306a36Sopenharmony_ci before_ht_cap, 42962306a36Sopenharmony_ci ARRAY_SIZE(before_ht_cap), 43062306a36Sopenharmony_ci offset); 43162306a36Sopenharmony_ci skb_put_data(skb, extra_ies + offset, noffset - offset); 43262306a36Sopenharmony_ci offset = noffset; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* we should have the peer STA if we're already responding */ 43662306a36Sopenharmony_ci if (action_code == WLAN_TDLS_SETUP_RESPONSE) { 43762306a36Sopenharmony_ci sta = sta_info_get(sdata, peer); 43862306a36Sopenharmony_ci if (WARN_ON_ONCE(!sta)) 43962306a36Sopenharmony_ci return; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci sta->tdls_chandef = link->conf->chandef; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci ieee80211_tdls_add_oper_classes(link, skb); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* 44762306a36Sopenharmony_ci * with TDLS we can switch channels, and HT-caps are not necessarily 44862306a36Sopenharmony_ci * the same on all bands. The specification limits the setup to a 44962306a36Sopenharmony_ci * single HT-cap, so use the current band for now. 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if ((action_code == WLAN_TDLS_SETUP_REQUEST || 45462306a36Sopenharmony_ci action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) && 45562306a36Sopenharmony_ci ht_cap.ht_supported) { 45662306a36Sopenharmony_ci ieee80211_apply_htcap_overrides(sdata, &ht_cap); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* disable SMPS in TDLS initiator */ 45962306a36Sopenharmony_ci ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED 46062306a36Sopenharmony_ci << IEEE80211_HT_CAP_SM_PS_SHIFT; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); 46362306a36Sopenharmony_ci ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap); 46462306a36Sopenharmony_ci } else if (action_code == WLAN_TDLS_SETUP_RESPONSE && 46562306a36Sopenharmony_ci ht_cap.ht_supported && sta->sta.deflink.ht_cap.ht_supported) { 46662306a36Sopenharmony_ci /* the peer caps are already intersected with our own */ 46762306a36Sopenharmony_ci memcpy(&ht_cap, &sta->sta.deflink.ht_cap, sizeof(ht_cap)); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); 47062306a36Sopenharmony_ci ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (ht_cap.ht_supported && 47462306a36Sopenharmony_ci (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) 47562306a36Sopenharmony_ci ieee80211_tdls_add_bss_coex_ie(skb); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci ieee80211_tdls_add_link_ie(link, skb, peer, initiator); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* add any custom IEs that go before VHT capabilities */ 48062306a36Sopenharmony_ci if (extra_ies_len) { 48162306a36Sopenharmony_ci static const u8 before_vht_cap[] = { 48262306a36Sopenharmony_ci WLAN_EID_SUPP_RATES, 48362306a36Sopenharmony_ci WLAN_EID_COUNTRY, 48462306a36Sopenharmony_ci WLAN_EID_EXT_SUPP_RATES, 48562306a36Sopenharmony_ci WLAN_EID_SUPPORTED_CHANNELS, 48662306a36Sopenharmony_ci WLAN_EID_RSN, 48762306a36Sopenharmony_ci WLAN_EID_EXT_CAPABILITY, 48862306a36Sopenharmony_ci WLAN_EID_QOS_CAPA, 48962306a36Sopenharmony_ci WLAN_EID_FAST_BSS_TRANSITION, 49062306a36Sopenharmony_ci WLAN_EID_TIMEOUT_INTERVAL, 49162306a36Sopenharmony_ci WLAN_EID_SUPPORTED_REGULATORY_CLASSES, 49262306a36Sopenharmony_ci WLAN_EID_MULTI_BAND, 49362306a36Sopenharmony_ci }; 49462306a36Sopenharmony_ci noffset = ieee80211_ie_split(extra_ies, extra_ies_len, 49562306a36Sopenharmony_ci before_vht_cap, 49662306a36Sopenharmony_ci ARRAY_SIZE(before_vht_cap), 49762306a36Sopenharmony_ci offset); 49862306a36Sopenharmony_ci skb_put_data(skb, extra_ies + offset, noffset - offset); 49962306a36Sopenharmony_ci offset = noffset; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* add AID if VHT, HE or EHT capabilities supported */ 50362306a36Sopenharmony_ci memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap)); 50462306a36Sopenharmony_ci he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); 50562306a36Sopenharmony_ci eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif); 50662306a36Sopenharmony_ci if ((vht_cap.vht_supported || he_cap || eht_cap) && 50762306a36Sopenharmony_ci (action_code == WLAN_TDLS_SETUP_REQUEST || 50862306a36Sopenharmony_ci action_code == WLAN_TDLS_SETUP_RESPONSE)) 50962306a36Sopenharmony_ci ieee80211_tdls_add_aid(sdata, skb); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* build the VHT-cap similarly to the HT-cap */ 51262306a36Sopenharmony_ci if ((action_code == WLAN_TDLS_SETUP_REQUEST || 51362306a36Sopenharmony_ci action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) && 51462306a36Sopenharmony_ci vht_cap.vht_supported) { 51562306a36Sopenharmony_ci ieee80211_apply_vhtcap_overrides(sdata, &vht_cap); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); 51862306a36Sopenharmony_ci ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap); 51962306a36Sopenharmony_ci } else if (action_code == WLAN_TDLS_SETUP_RESPONSE && 52062306a36Sopenharmony_ci vht_cap.vht_supported && sta->sta.deflink.vht_cap.vht_supported) { 52162306a36Sopenharmony_ci /* the peer caps are already intersected with our own */ 52262306a36Sopenharmony_ci memcpy(&vht_cap, &sta->sta.deflink.vht_cap, sizeof(vht_cap)); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); 52562306a36Sopenharmony_ci ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* 52862306a36Sopenharmony_ci * if both peers support WIDER_BW, we can expand the chandef to 52962306a36Sopenharmony_ci * a wider compatible one, up to 80MHz 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW)) 53262306a36Sopenharmony_ci ieee80211_tdls_chandef_vht_upgrade(sdata, sta); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* add any custom IEs that go before HE capabilities */ 53662306a36Sopenharmony_ci if (extra_ies_len) { 53762306a36Sopenharmony_ci static const u8 before_he_cap[] = { 53862306a36Sopenharmony_ci WLAN_EID_EXTENSION, 53962306a36Sopenharmony_ci WLAN_EID_EXT_FILS_REQ_PARAMS, 54062306a36Sopenharmony_ci WLAN_EID_AP_CSN, 54162306a36Sopenharmony_ci }; 54262306a36Sopenharmony_ci noffset = ieee80211_ie_split(extra_ies, extra_ies_len, 54362306a36Sopenharmony_ci before_he_cap, 54462306a36Sopenharmony_ci ARRAY_SIZE(before_he_cap), 54562306a36Sopenharmony_ci offset); 54662306a36Sopenharmony_ci skb_put_data(skb, extra_ies + offset, noffset - offset); 54762306a36Sopenharmony_ci offset = noffset; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* build the HE-cap from sband */ 55162306a36Sopenharmony_ci if (he_cap && 55262306a36Sopenharmony_ci (action_code == WLAN_TDLS_SETUP_REQUEST || 55362306a36Sopenharmony_ci action_code == WLAN_TDLS_SETUP_RESPONSE || 55462306a36Sopenharmony_ci action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES)) { 55562306a36Sopenharmony_ci __le16 he_6ghz_capa; 55662306a36Sopenharmony_ci u8 cap_size; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci cap_size = 55962306a36Sopenharmony_ci 2 + 1 + sizeof(he_cap->he_cap_elem) + 56062306a36Sopenharmony_ci ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem) + 56162306a36Sopenharmony_ci ieee80211_he_ppe_size(he_cap->ppe_thres[0], 56262306a36Sopenharmony_ci he_cap->he_cap_elem.phy_cap_info); 56362306a36Sopenharmony_ci pos = skb_put(skb, cap_size); 56462306a36Sopenharmony_ci pos = ieee80211_ie_build_he_cap(0, pos, he_cap, pos + cap_size); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* Build HE 6Ghz capa IE from sband */ 56762306a36Sopenharmony_ci if (sband->band == NL80211_BAND_6GHZ) { 56862306a36Sopenharmony_ci cap_size = 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa); 56962306a36Sopenharmony_ci pos = skb_put(skb, cap_size); 57062306a36Sopenharmony_ci he_6ghz_capa = 57162306a36Sopenharmony_ci ieee80211_get_he_6ghz_capa_vif(sband, &sdata->vif); 57262306a36Sopenharmony_ci pos = ieee80211_write_he_6ghz_cap(pos, he_6ghz_capa, 57362306a36Sopenharmony_ci pos + cap_size); 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* add any custom IEs that go before EHT capabilities */ 57862306a36Sopenharmony_ci if (extra_ies_len) { 57962306a36Sopenharmony_ci static const u8 before_he_cap[] = { 58062306a36Sopenharmony_ci WLAN_EID_EXTENSION, 58162306a36Sopenharmony_ci WLAN_EID_EXT_FILS_REQ_PARAMS, 58262306a36Sopenharmony_ci WLAN_EID_AP_CSN, 58362306a36Sopenharmony_ci }; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci noffset = ieee80211_ie_split(extra_ies, extra_ies_len, 58662306a36Sopenharmony_ci before_he_cap, 58762306a36Sopenharmony_ci ARRAY_SIZE(before_he_cap), 58862306a36Sopenharmony_ci offset); 58962306a36Sopenharmony_ci skb_put_data(skb, extra_ies + offset, noffset - offset); 59062306a36Sopenharmony_ci offset = noffset; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* build the EHT-cap from sband */ 59462306a36Sopenharmony_ci if (he_cap && eht_cap && 59562306a36Sopenharmony_ci (action_code == WLAN_TDLS_SETUP_REQUEST || 59662306a36Sopenharmony_ci action_code == WLAN_TDLS_SETUP_RESPONSE || 59762306a36Sopenharmony_ci action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES)) { 59862306a36Sopenharmony_ci u8 cap_size; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci cap_size = 60162306a36Sopenharmony_ci 2 + 1 + sizeof(eht_cap->eht_cap_elem) + 60262306a36Sopenharmony_ci ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, 60362306a36Sopenharmony_ci &eht_cap->eht_cap_elem, false) + 60462306a36Sopenharmony_ci ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0], 60562306a36Sopenharmony_ci eht_cap->eht_cap_elem.phy_cap_info); 60662306a36Sopenharmony_ci pos = skb_put(skb, cap_size); 60762306a36Sopenharmony_ci ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + cap_size, false); 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci /* add any remaining IEs */ 61162306a36Sopenharmony_ci if (extra_ies_len) { 61262306a36Sopenharmony_ci noffset = extra_ies_len; 61362306a36Sopenharmony_ci skb_put_data(skb, extra_ies + offset, noffset - offset); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic void 61962306a36Sopenharmony_ciieee80211_tdls_add_setup_cfm_ies(struct ieee80211_link_data *link, 62062306a36Sopenharmony_ci struct sk_buff *skb, const u8 *peer, 62162306a36Sopenharmony_ci bool initiator, const u8 *extra_ies, 62262306a36Sopenharmony_ci size_t extra_ies_len) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 62562306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 62662306a36Sopenharmony_ci size_t offset = 0, noffset; 62762306a36Sopenharmony_ci struct sta_info *sta, *ap_sta; 62862306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 62962306a36Sopenharmony_ci u8 *pos; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci sband = ieee80211_get_link_sband(link); 63262306a36Sopenharmony_ci if (WARN_ON_ONCE(!sband)) 63362306a36Sopenharmony_ci return; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci sta = sta_info_get(sdata, peer); 63662306a36Sopenharmony_ci ap_sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (WARN_ON_ONCE(!sta || !ap_sta)) 63962306a36Sopenharmony_ci return; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci sta->tdls_chandef = link->conf->chandef; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* add any custom IEs that go before the QoS IE */ 64462306a36Sopenharmony_ci if (extra_ies_len) { 64562306a36Sopenharmony_ci static const u8 before_qos[] = { 64662306a36Sopenharmony_ci WLAN_EID_RSN, 64762306a36Sopenharmony_ci }; 64862306a36Sopenharmony_ci noffset = ieee80211_ie_split(extra_ies, extra_ies_len, 64962306a36Sopenharmony_ci before_qos, 65062306a36Sopenharmony_ci ARRAY_SIZE(before_qos), 65162306a36Sopenharmony_ci offset); 65262306a36Sopenharmony_ci skb_put_data(skb, extra_ies + offset, noffset - offset); 65362306a36Sopenharmony_ci offset = noffset; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* add the QoS param IE if both the peer and we support it */ 65762306a36Sopenharmony_ci if (local->hw.queues >= IEEE80211_NUM_ACS && sta->sta.wme) 65862306a36Sopenharmony_ci ieee80211_tdls_add_wmm_param_ie(sdata, skb); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci /* add any custom IEs that go before HT operation */ 66162306a36Sopenharmony_ci if (extra_ies_len) { 66262306a36Sopenharmony_ci static const u8 before_ht_op[] = { 66362306a36Sopenharmony_ci WLAN_EID_RSN, 66462306a36Sopenharmony_ci WLAN_EID_QOS_CAPA, 66562306a36Sopenharmony_ci WLAN_EID_FAST_BSS_TRANSITION, 66662306a36Sopenharmony_ci WLAN_EID_TIMEOUT_INTERVAL, 66762306a36Sopenharmony_ci }; 66862306a36Sopenharmony_ci noffset = ieee80211_ie_split(extra_ies, extra_ies_len, 66962306a36Sopenharmony_ci before_ht_op, 67062306a36Sopenharmony_ci ARRAY_SIZE(before_ht_op), 67162306a36Sopenharmony_ci offset); 67262306a36Sopenharmony_ci skb_put_data(skb, extra_ies + offset, noffset - offset); 67362306a36Sopenharmony_ci offset = noffset; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* 67762306a36Sopenharmony_ci * if HT support is only added in TDLS, we need an HT-operation IE. 67862306a36Sopenharmony_ci * add the IE as required by IEEE802.11-2012 9.23.3.2. 67962306a36Sopenharmony_ci */ 68062306a36Sopenharmony_ci if (!ap_sta->sta.deflink.ht_cap.ht_supported && sta->sta.deflink.ht_cap.ht_supported) { 68162306a36Sopenharmony_ci u16 prot = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED | 68262306a36Sopenharmony_ci IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT | 68362306a36Sopenharmony_ci IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation)); 68662306a36Sopenharmony_ci ieee80211_ie_build_ht_oper(pos, &sta->sta.deflink.ht_cap, 68762306a36Sopenharmony_ci &link->conf->chandef, prot, 68862306a36Sopenharmony_ci true); 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci ieee80211_tdls_add_link_ie(link, skb, peer, initiator); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* only include VHT-operation if not on the 2.4GHz band */ 69462306a36Sopenharmony_ci if (sband->band != NL80211_BAND_2GHZ && 69562306a36Sopenharmony_ci sta->sta.deflink.vht_cap.vht_supported) { 69662306a36Sopenharmony_ci /* 69762306a36Sopenharmony_ci * if both peers support WIDER_BW, we can expand the chandef to 69862306a36Sopenharmony_ci * a wider compatible one, up to 80MHz 69962306a36Sopenharmony_ci */ 70062306a36Sopenharmony_ci if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW)) 70162306a36Sopenharmony_ci ieee80211_tdls_chandef_vht_upgrade(sdata, sta); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation)); 70462306a36Sopenharmony_ci ieee80211_ie_build_vht_oper(pos, &sta->sta.deflink.vht_cap, 70562306a36Sopenharmony_ci &sta->tdls_chandef); 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* add any remaining IEs */ 70962306a36Sopenharmony_ci if (extra_ies_len) { 71062306a36Sopenharmony_ci noffset = extra_ies_len; 71162306a36Sopenharmony_ci skb_put_data(skb, extra_ies + offset, noffset - offset); 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic void 71662306a36Sopenharmony_ciieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_link_data *link, 71762306a36Sopenharmony_ci struct sk_buff *skb, const u8 *peer, 71862306a36Sopenharmony_ci bool initiator, const u8 *extra_ies, 71962306a36Sopenharmony_ci size_t extra_ies_len, u8 oper_class, 72062306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct ieee80211_tdls_data *tf; 72362306a36Sopenharmony_ci size_t offset = 0, noffset; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (WARN_ON_ONCE(!chandef)) 72662306a36Sopenharmony_ci return; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci tf = (void *)skb->data; 72962306a36Sopenharmony_ci tf->u.chan_switch_req.target_channel = 73062306a36Sopenharmony_ci ieee80211_frequency_to_channel(chandef->chan->center_freq); 73162306a36Sopenharmony_ci tf->u.chan_switch_req.oper_class = oper_class; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (extra_ies_len) { 73462306a36Sopenharmony_ci static const u8 before_lnkie[] = { 73562306a36Sopenharmony_ci WLAN_EID_SECONDARY_CHANNEL_OFFSET, 73662306a36Sopenharmony_ci }; 73762306a36Sopenharmony_ci noffset = ieee80211_ie_split(extra_ies, extra_ies_len, 73862306a36Sopenharmony_ci before_lnkie, 73962306a36Sopenharmony_ci ARRAY_SIZE(before_lnkie), 74062306a36Sopenharmony_ci offset); 74162306a36Sopenharmony_ci skb_put_data(skb, extra_ies + offset, noffset - offset); 74262306a36Sopenharmony_ci offset = noffset; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci ieee80211_tdls_add_link_ie(link, skb, peer, initiator); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* add any remaining IEs */ 74862306a36Sopenharmony_ci if (extra_ies_len) { 74962306a36Sopenharmony_ci noffset = extra_ies_len; 75062306a36Sopenharmony_ci skb_put_data(skb, extra_ies + offset, noffset - offset); 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic void 75562306a36Sopenharmony_ciieee80211_tdls_add_chan_switch_resp_ies(struct ieee80211_link_data *link, 75662306a36Sopenharmony_ci struct sk_buff *skb, const u8 *peer, 75762306a36Sopenharmony_ci u16 status_code, bool initiator, 75862306a36Sopenharmony_ci const u8 *extra_ies, 75962306a36Sopenharmony_ci size_t extra_ies_len) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci if (status_code == 0) 76262306a36Sopenharmony_ci ieee80211_tdls_add_link_ie(link, skb, peer, initiator); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (extra_ies_len) 76562306a36Sopenharmony_ci skb_put_data(skb, extra_ies, extra_ies_len); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic void ieee80211_tdls_add_ies(struct ieee80211_link_data *link, 76962306a36Sopenharmony_ci struct sk_buff *skb, const u8 *peer, 77062306a36Sopenharmony_ci u8 action_code, u16 status_code, 77162306a36Sopenharmony_ci bool initiator, const u8 *extra_ies, 77262306a36Sopenharmony_ci size_t extra_ies_len, u8 oper_class, 77362306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci switch (action_code) { 77662306a36Sopenharmony_ci case WLAN_TDLS_SETUP_REQUEST: 77762306a36Sopenharmony_ci case WLAN_TDLS_SETUP_RESPONSE: 77862306a36Sopenharmony_ci case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: 77962306a36Sopenharmony_ci if (status_code == 0) 78062306a36Sopenharmony_ci ieee80211_tdls_add_setup_start_ies(link, 78162306a36Sopenharmony_ci skb, peer, 78262306a36Sopenharmony_ci action_code, 78362306a36Sopenharmony_ci initiator, 78462306a36Sopenharmony_ci extra_ies, 78562306a36Sopenharmony_ci extra_ies_len); 78662306a36Sopenharmony_ci break; 78762306a36Sopenharmony_ci case WLAN_TDLS_SETUP_CONFIRM: 78862306a36Sopenharmony_ci if (status_code == 0) 78962306a36Sopenharmony_ci ieee80211_tdls_add_setup_cfm_ies(link, skb, peer, 79062306a36Sopenharmony_ci initiator, extra_ies, 79162306a36Sopenharmony_ci extra_ies_len); 79262306a36Sopenharmony_ci break; 79362306a36Sopenharmony_ci case WLAN_TDLS_TEARDOWN: 79462306a36Sopenharmony_ci case WLAN_TDLS_DISCOVERY_REQUEST: 79562306a36Sopenharmony_ci if (extra_ies_len) 79662306a36Sopenharmony_ci skb_put_data(skb, extra_ies, extra_ies_len); 79762306a36Sopenharmony_ci if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN) 79862306a36Sopenharmony_ci ieee80211_tdls_add_link_ie(link, skb, 79962306a36Sopenharmony_ci peer, initiator); 80062306a36Sopenharmony_ci break; 80162306a36Sopenharmony_ci case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: 80262306a36Sopenharmony_ci ieee80211_tdls_add_chan_switch_req_ies(link, skb, peer, 80362306a36Sopenharmony_ci initiator, extra_ies, 80462306a36Sopenharmony_ci extra_ies_len, 80562306a36Sopenharmony_ci oper_class, chandef); 80662306a36Sopenharmony_ci break; 80762306a36Sopenharmony_ci case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: 80862306a36Sopenharmony_ci ieee80211_tdls_add_chan_switch_resp_ies(link, skb, peer, 80962306a36Sopenharmony_ci status_code, 81062306a36Sopenharmony_ci initiator, extra_ies, 81162306a36Sopenharmony_ci extra_ies_len); 81262306a36Sopenharmony_ci break; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic int 81862306a36Sopenharmony_ciieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, 81962306a36Sopenharmony_ci struct ieee80211_link_data *link, 82062306a36Sopenharmony_ci const u8 *peer, u8 action_code, u8 dialog_token, 82162306a36Sopenharmony_ci u16 status_code, struct sk_buff *skb) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 82462306a36Sopenharmony_ci struct ieee80211_tdls_data *tf; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci tf = skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci memcpy(tf->da, peer, ETH_ALEN); 82962306a36Sopenharmony_ci memcpy(tf->sa, sdata->vif.addr, ETH_ALEN); 83062306a36Sopenharmony_ci tf->ether_type = cpu_to_be16(ETH_P_TDLS); 83162306a36Sopenharmony_ci tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* network header is after the ethernet header */ 83462306a36Sopenharmony_ci skb_set_network_header(skb, ETH_HLEN); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci switch (action_code) { 83762306a36Sopenharmony_ci case WLAN_TDLS_SETUP_REQUEST: 83862306a36Sopenharmony_ci tf->category = WLAN_CATEGORY_TDLS; 83962306a36Sopenharmony_ci tf->action_code = WLAN_TDLS_SETUP_REQUEST; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci skb_put(skb, sizeof(tf->u.setup_req)); 84262306a36Sopenharmony_ci tf->u.setup_req.dialog_token = dialog_token; 84362306a36Sopenharmony_ci tf->u.setup_req.capability = 84462306a36Sopenharmony_ci cpu_to_le16(ieee80211_get_tdls_sta_capab(link, 84562306a36Sopenharmony_ci status_code)); 84662306a36Sopenharmony_ci break; 84762306a36Sopenharmony_ci case WLAN_TDLS_SETUP_RESPONSE: 84862306a36Sopenharmony_ci tf->category = WLAN_CATEGORY_TDLS; 84962306a36Sopenharmony_ci tf->action_code = WLAN_TDLS_SETUP_RESPONSE; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci skb_put(skb, sizeof(tf->u.setup_resp)); 85262306a36Sopenharmony_ci tf->u.setup_resp.status_code = cpu_to_le16(status_code); 85362306a36Sopenharmony_ci tf->u.setup_resp.dialog_token = dialog_token; 85462306a36Sopenharmony_ci tf->u.setup_resp.capability = 85562306a36Sopenharmony_ci cpu_to_le16(ieee80211_get_tdls_sta_capab(link, 85662306a36Sopenharmony_ci status_code)); 85762306a36Sopenharmony_ci break; 85862306a36Sopenharmony_ci case WLAN_TDLS_SETUP_CONFIRM: 85962306a36Sopenharmony_ci tf->category = WLAN_CATEGORY_TDLS; 86062306a36Sopenharmony_ci tf->action_code = WLAN_TDLS_SETUP_CONFIRM; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci skb_put(skb, sizeof(tf->u.setup_cfm)); 86362306a36Sopenharmony_ci tf->u.setup_cfm.status_code = cpu_to_le16(status_code); 86462306a36Sopenharmony_ci tf->u.setup_cfm.dialog_token = dialog_token; 86562306a36Sopenharmony_ci break; 86662306a36Sopenharmony_ci case WLAN_TDLS_TEARDOWN: 86762306a36Sopenharmony_ci tf->category = WLAN_CATEGORY_TDLS; 86862306a36Sopenharmony_ci tf->action_code = WLAN_TDLS_TEARDOWN; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci skb_put(skb, sizeof(tf->u.teardown)); 87162306a36Sopenharmony_ci tf->u.teardown.reason_code = cpu_to_le16(status_code); 87262306a36Sopenharmony_ci break; 87362306a36Sopenharmony_ci case WLAN_TDLS_DISCOVERY_REQUEST: 87462306a36Sopenharmony_ci tf->category = WLAN_CATEGORY_TDLS; 87562306a36Sopenharmony_ci tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci skb_put(skb, sizeof(tf->u.discover_req)); 87862306a36Sopenharmony_ci tf->u.discover_req.dialog_token = dialog_token; 87962306a36Sopenharmony_ci break; 88062306a36Sopenharmony_ci case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: 88162306a36Sopenharmony_ci tf->category = WLAN_CATEGORY_TDLS; 88262306a36Sopenharmony_ci tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci skb_put(skb, sizeof(tf->u.chan_switch_req)); 88562306a36Sopenharmony_ci break; 88662306a36Sopenharmony_ci case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: 88762306a36Sopenharmony_ci tf->category = WLAN_CATEGORY_TDLS; 88862306a36Sopenharmony_ci tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci skb_put(skb, sizeof(tf->u.chan_switch_resp)); 89162306a36Sopenharmony_ci tf->u.chan_switch_resp.status_code = cpu_to_le16(status_code); 89262306a36Sopenharmony_ci break; 89362306a36Sopenharmony_ci default: 89462306a36Sopenharmony_ci return -EINVAL; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci return 0; 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cistatic int 90162306a36Sopenharmony_ciieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, 90262306a36Sopenharmony_ci const u8 *peer, struct ieee80211_link_data *link, 90362306a36Sopenharmony_ci u8 action_code, u8 dialog_token, 90462306a36Sopenharmony_ci u16 status_code, struct sk_buff *skb) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 90762306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci mgmt = skb_put_zero(skb, 24); 91062306a36Sopenharmony_ci memcpy(mgmt->da, peer, ETH_ALEN); 91162306a36Sopenharmony_ci memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 91262306a36Sopenharmony_ci memcpy(mgmt->bssid, link->u.mgd.bssid, ETH_ALEN); 91362306a36Sopenharmony_ci mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 91462306a36Sopenharmony_ci IEEE80211_STYPE_ACTION); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci switch (action_code) { 91762306a36Sopenharmony_ci case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: 91862306a36Sopenharmony_ci skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); 91962306a36Sopenharmony_ci mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; 92062306a36Sopenharmony_ci mgmt->u.action.u.tdls_discover_resp.action_code = 92162306a36Sopenharmony_ci WLAN_PUB_ACTION_TDLS_DISCOVER_RES; 92262306a36Sopenharmony_ci mgmt->u.action.u.tdls_discover_resp.dialog_token = 92362306a36Sopenharmony_ci dialog_token; 92462306a36Sopenharmony_ci mgmt->u.action.u.tdls_discover_resp.capability = 92562306a36Sopenharmony_ci cpu_to_le16(ieee80211_get_tdls_sta_capab(link, 92662306a36Sopenharmony_ci status_code)); 92762306a36Sopenharmony_ci break; 92862306a36Sopenharmony_ci default: 92962306a36Sopenharmony_ci return -EINVAL; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci return 0; 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic struct sk_buff * 93662306a36Sopenharmony_ciieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata, 93762306a36Sopenharmony_ci const u8 *peer, int link_id, 93862306a36Sopenharmony_ci u8 action_code, u8 dialog_token, 93962306a36Sopenharmony_ci u16 status_code, bool initiator, 94062306a36Sopenharmony_ci const u8 *extra_ies, size_t extra_ies_len, 94162306a36Sopenharmony_ci u8 oper_class, 94262306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 94562306a36Sopenharmony_ci struct sk_buff *skb; 94662306a36Sopenharmony_ci int ret; 94762306a36Sopenharmony_ci struct ieee80211_link_data *link; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci link_id = link_id >= 0 ? link_id : 0; 95062306a36Sopenharmony_ci rcu_read_lock(); 95162306a36Sopenharmony_ci link = rcu_dereference(sdata->link[link_id]); 95262306a36Sopenharmony_ci if (WARN_ON(!link)) 95362306a36Sopenharmony_ci goto unlock; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci skb = netdev_alloc_skb(sdata->dev, 95662306a36Sopenharmony_ci local->hw.extra_tx_headroom + 95762306a36Sopenharmony_ci max(sizeof(struct ieee80211_mgmt), 95862306a36Sopenharmony_ci sizeof(struct ieee80211_tdls_data)) + 95962306a36Sopenharmony_ci 50 + /* supported rates */ 96062306a36Sopenharmony_ci 10 + /* ext capab */ 96162306a36Sopenharmony_ci 26 + /* max(WMM-info, WMM-param) */ 96262306a36Sopenharmony_ci 2 + max(sizeof(struct ieee80211_ht_cap), 96362306a36Sopenharmony_ci sizeof(struct ieee80211_ht_operation)) + 96462306a36Sopenharmony_ci 2 + max(sizeof(struct ieee80211_vht_cap), 96562306a36Sopenharmony_ci sizeof(struct ieee80211_vht_operation)) + 96662306a36Sopenharmony_ci 2 + 1 + sizeof(struct ieee80211_he_cap_elem) + 96762306a36Sopenharmony_ci sizeof(struct ieee80211_he_mcs_nss_supp) + 96862306a36Sopenharmony_ci IEEE80211_HE_PPE_THRES_MAX_LEN + 96962306a36Sopenharmony_ci 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) + 97062306a36Sopenharmony_ci 2 + 1 + sizeof(struct ieee80211_eht_cap_elem) + 97162306a36Sopenharmony_ci sizeof(struct ieee80211_eht_mcs_nss_supp) + 97262306a36Sopenharmony_ci IEEE80211_EHT_PPE_THRES_MAX_LEN + 97362306a36Sopenharmony_ci 50 + /* supported channels */ 97462306a36Sopenharmony_ci 3 + /* 40/20 BSS coex */ 97562306a36Sopenharmony_ci 4 + /* AID */ 97662306a36Sopenharmony_ci 4 + /* oper classes */ 97762306a36Sopenharmony_ci extra_ies_len + 97862306a36Sopenharmony_ci sizeof(struct ieee80211_tdls_lnkie)); 97962306a36Sopenharmony_ci if (!skb) 98062306a36Sopenharmony_ci goto unlock; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci skb_reserve(skb, local->hw.extra_tx_headroom); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci switch (action_code) { 98562306a36Sopenharmony_ci case WLAN_TDLS_SETUP_REQUEST: 98662306a36Sopenharmony_ci case WLAN_TDLS_SETUP_RESPONSE: 98762306a36Sopenharmony_ci case WLAN_TDLS_SETUP_CONFIRM: 98862306a36Sopenharmony_ci case WLAN_TDLS_TEARDOWN: 98962306a36Sopenharmony_ci case WLAN_TDLS_DISCOVERY_REQUEST: 99062306a36Sopenharmony_ci case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: 99162306a36Sopenharmony_ci case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: 99262306a36Sopenharmony_ci ret = ieee80211_prep_tdls_encap_data(local->hw.wiphy, 99362306a36Sopenharmony_ci sdata->dev, link, peer, 99462306a36Sopenharmony_ci action_code, dialog_token, 99562306a36Sopenharmony_ci status_code, skb); 99662306a36Sopenharmony_ci break; 99762306a36Sopenharmony_ci case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: 99862306a36Sopenharmony_ci ret = ieee80211_prep_tdls_direct(local->hw.wiphy, sdata->dev, 99962306a36Sopenharmony_ci peer, link, action_code, 100062306a36Sopenharmony_ci dialog_token, status_code, 100162306a36Sopenharmony_ci skb); 100262306a36Sopenharmony_ci break; 100362306a36Sopenharmony_ci default: 100462306a36Sopenharmony_ci ret = -ENOTSUPP; 100562306a36Sopenharmony_ci break; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (ret < 0) 100962306a36Sopenharmony_ci goto fail; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci ieee80211_tdls_add_ies(link, skb, peer, action_code, status_code, 101262306a36Sopenharmony_ci initiator, extra_ies, extra_ies_len, oper_class, 101362306a36Sopenharmony_ci chandef); 101462306a36Sopenharmony_ci rcu_read_unlock(); 101562306a36Sopenharmony_ci return skb; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_cifail: 101862306a36Sopenharmony_ci dev_kfree_skb(skb); 101962306a36Sopenharmony_ciunlock: 102062306a36Sopenharmony_ci rcu_read_unlock(); 102162306a36Sopenharmony_ci return NULL; 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cistatic int 102562306a36Sopenharmony_ciieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, 102662306a36Sopenharmony_ci const u8 *peer, int link_id, 102762306a36Sopenharmony_ci u8 action_code, u8 dialog_token, 102862306a36Sopenharmony_ci u16 status_code, u32 peer_capability, 102962306a36Sopenharmony_ci bool initiator, const u8 *extra_ies, 103062306a36Sopenharmony_ci size_t extra_ies_len, u8 oper_class, 103162306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 103262306a36Sopenharmony_ci{ 103362306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 103462306a36Sopenharmony_ci struct sk_buff *skb = NULL; 103562306a36Sopenharmony_ci struct sta_info *sta; 103662306a36Sopenharmony_ci u32 flags = 0; 103762306a36Sopenharmony_ci int ret = 0; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci rcu_read_lock(); 104062306a36Sopenharmony_ci sta = sta_info_get(sdata, peer); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* infer the initiator if we can, to support old userspace */ 104362306a36Sopenharmony_ci switch (action_code) { 104462306a36Sopenharmony_ci case WLAN_TDLS_SETUP_REQUEST: 104562306a36Sopenharmony_ci if (sta) { 104662306a36Sopenharmony_ci set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR); 104762306a36Sopenharmony_ci sta->sta.tdls_initiator = false; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci fallthrough; 105062306a36Sopenharmony_ci case WLAN_TDLS_SETUP_CONFIRM: 105162306a36Sopenharmony_ci case WLAN_TDLS_DISCOVERY_REQUEST: 105262306a36Sopenharmony_ci initiator = true; 105362306a36Sopenharmony_ci break; 105462306a36Sopenharmony_ci case WLAN_TDLS_SETUP_RESPONSE: 105562306a36Sopenharmony_ci /* 105662306a36Sopenharmony_ci * In some testing scenarios, we send a request and response. 105762306a36Sopenharmony_ci * Make the last packet sent take effect for the initiator 105862306a36Sopenharmony_ci * value. 105962306a36Sopenharmony_ci */ 106062306a36Sopenharmony_ci if (sta) { 106162306a36Sopenharmony_ci clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR); 106262306a36Sopenharmony_ci sta->sta.tdls_initiator = true; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci fallthrough; 106562306a36Sopenharmony_ci case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: 106662306a36Sopenharmony_ci initiator = false; 106762306a36Sopenharmony_ci break; 106862306a36Sopenharmony_ci case WLAN_TDLS_TEARDOWN: 106962306a36Sopenharmony_ci case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: 107062306a36Sopenharmony_ci case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: 107162306a36Sopenharmony_ci /* any value is ok */ 107262306a36Sopenharmony_ci break; 107362306a36Sopenharmony_ci default: 107462306a36Sopenharmony_ci ret = -ENOTSUPP; 107562306a36Sopenharmony_ci break; 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci if (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR)) 107962306a36Sopenharmony_ci initiator = true; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci rcu_read_unlock(); 108262306a36Sopenharmony_ci if (ret < 0) 108362306a36Sopenharmony_ci goto fail; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer, 108662306a36Sopenharmony_ci link_id, action_code, 108762306a36Sopenharmony_ci dialog_token, status_code, 108862306a36Sopenharmony_ci initiator, extra_ies, 108962306a36Sopenharmony_ci extra_ies_len, oper_class, 109062306a36Sopenharmony_ci chandef); 109162306a36Sopenharmony_ci if (!skb) { 109262306a36Sopenharmony_ci ret = -EINVAL; 109362306a36Sopenharmony_ci goto fail; 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) { 109762306a36Sopenharmony_ci ieee80211_tx_skb_tid(sdata, skb, 7, link_id); 109862306a36Sopenharmony_ci return 0; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* 110262306a36Sopenharmony_ci * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise 110362306a36Sopenharmony_ci * we should default to AC_VI. 110462306a36Sopenharmony_ci */ 110562306a36Sopenharmony_ci switch (action_code) { 110662306a36Sopenharmony_ci case WLAN_TDLS_SETUP_REQUEST: 110762306a36Sopenharmony_ci case WLAN_TDLS_SETUP_RESPONSE: 110862306a36Sopenharmony_ci skb->priority = 256 + 2; 110962306a36Sopenharmony_ci break; 111062306a36Sopenharmony_ci default: 111162306a36Sopenharmony_ci skb->priority = 256 + 5; 111262306a36Sopenharmony_ci break; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci /* 111662306a36Sopenharmony_ci * Set the WLAN_TDLS_TEARDOWN flag to indicate a teardown in progress. 111762306a36Sopenharmony_ci * Later, if no ACK is returned from peer, we will re-send the teardown 111862306a36Sopenharmony_ci * packet through the AP. 111962306a36Sopenharmony_ci */ 112062306a36Sopenharmony_ci if ((action_code == WLAN_TDLS_TEARDOWN) && 112162306a36Sopenharmony_ci ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) { 112262306a36Sopenharmony_ci bool try_resend; /* Should we keep skb for possible resend */ 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci /* If not sending directly to peer - no point in keeping skb */ 112562306a36Sopenharmony_ci rcu_read_lock(); 112662306a36Sopenharmony_ci sta = sta_info_get(sdata, peer); 112762306a36Sopenharmony_ci try_resend = sta && test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); 112862306a36Sopenharmony_ci rcu_read_unlock(); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci spin_lock_bh(&sdata->u.mgd.teardown_lock); 113162306a36Sopenharmony_ci if (try_resend && !sdata->u.mgd.teardown_skb) { 113262306a36Sopenharmony_ci /* Mark it as requiring TX status callback */ 113362306a36Sopenharmony_ci flags |= IEEE80211_TX_CTL_REQ_TX_STATUS | 113462306a36Sopenharmony_ci IEEE80211_TX_INTFL_MLME_CONN_TX; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci /* 113762306a36Sopenharmony_ci * skb is copied since mac80211 will later set 113862306a36Sopenharmony_ci * properties that might not be the same as the AP, 113962306a36Sopenharmony_ci * such as encryption, QoS, addresses, etc. 114062306a36Sopenharmony_ci * 114162306a36Sopenharmony_ci * No problem if skb_copy() fails, so no need to check. 114262306a36Sopenharmony_ci */ 114362306a36Sopenharmony_ci sdata->u.mgd.teardown_skb = skb_copy(skb, GFP_ATOMIC); 114462306a36Sopenharmony_ci sdata->u.mgd.orig_teardown_skb = skb; 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci spin_unlock_bh(&sdata->u.mgd.teardown_lock); 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* disable bottom halves when entering the Tx path */ 115062306a36Sopenharmony_ci local_bh_disable(); 115162306a36Sopenharmony_ci __ieee80211_subif_start_xmit(skb, dev, flags, 115262306a36Sopenharmony_ci IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, NULL); 115362306a36Sopenharmony_ci local_bh_enable(); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci return ret; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_cifail: 115862306a36Sopenharmony_ci dev_kfree_skb(skb); 115962306a36Sopenharmony_ci return ret; 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_cistatic int 116362306a36Sopenharmony_ciieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, 116462306a36Sopenharmony_ci const u8 *peer, int link_id, 116562306a36Sopenharmony_ci u8 action_code, u8 dialog_token, 116662306a36Sopenharmony_ci u16 status_code, u32 peer_capability, bool initiator, 116762306a36Sopenharmony_ci const u8 *extra_ies, size_t extra_ies_len) 116862306a36Sopenharmony_ci{ 116962306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 117062306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 117162306a36Sopenharmony_ci enum ieee80211_smps_mode smps_mode = 117262306a36Sopenharmony_ci sdata->deflink.u.mgd.driver_smps_mode; 117362306a36Sopenharmony_ci int ret; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci /* don't support setup with forced SMPS mode that's not off */ 117662306a36Sopenharmony_ci if (smps_mode != IEEE80211_SMPS_AUTOMATIC && 117762306a36Sopenharmony_ci smps_mode != IEEE80211_SMPS_OFF) { 117862306a36Sopenharmony_ci tdls_dbg(sdata, "Aborting TDLS setup due to SMPS mode %d\n", 117962306a36Sopenharmony_ci smps_mode); 118062306a36Sopenharmony_ci return -ENOTSUPP; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci mutex_lock(&local->mtx); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci /* we don't support concurrent TDLS peer setups */ 118662306a36Sopenharmony_ci if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) && 118762306a36Sopenharmony_ci !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) { 118862306a36Sopenharmony_ci ret = -EBUSY; 118962306a36Sopenharmony_ci goto out_unlock; 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci /* 119362306a36Sopenharmony_ci * make sure we have a STA representing the peer so we drop or buffer 119462306a36Sopenharmony_ci * non-TDLS-setup frames to the peer. We can't send other packets 119562306a36Sopenharmony_ci * during setup through the AP path. 119662306a36Sopenharmony_ci * Allow error packets to be sent - sometimes we don't even add a STA 119762306a36Sopenharmony_ci * before failing the setup. 119862306a36Sopenharmony_ci */ 119962306a36Sopenharmony_ci if (status_code == 0) { 120062306a36Sopenharmony_ci rcu_read_lock(); 120162306a36Sopenharmony_ci if (!sta_info_get(sdata, peer)) { 120262306a36Sopenharmony_ci rcu_read_unlock(); 120362306a36Sopenharmony_ci ret = -ENOLINK; 120462306a36Sopenharmony_ci goto out_unlock; 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci rcu_read_unlock(); 120762306a36Sopenharmony_ci } 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci ieee80211_flush_queues(local, sdata, false); 121062306a36Sopenharmony_ci memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN); 121162306a36Sopenharmony_ci mutex_unlock(&local->mtx); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci /* we cannot take the mutex while preparing the setup packet */ 121462306a36Sopenharmony_ci ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, 121562306a36Sopenharmony_ci link_id, action_code, 121662306a36Sopenharmony_ci dialog_token, status_code, 121762306a36Sopenharmony_ci peer_capability, initiator, 121862306a36Sopenharmony_ci extra_ies, extra_ies_len, 0, 121962306a36Sopenharmony_ci NULL); 122062306a36Sopenharmony_ci if (ret < 0) { 122162306a36Sopenharmony_ci mutex_lock(&local->mtx); 122262306a36Sopenharmony_ci eth_zero_addr(sdata->u.mgd.tdls_peer); 122362306a36Sopenharmony_ci mutex_unlock(&local->mtx); 122462306a36Sopenharmony_ci return ret; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci ieee80211_queue_delayed_work(&sdata->local->hw, 122862306a36Sopenharmony_ci &sdata->u.mgd.tdls_peer_del_work, 122962306a36Sopenharmony_ci TDLS_PEER_SETUP_TIMEOUT); 123062306a36Sopenharmony_ci return 0; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ciout_unlock: 123362306a36Sopenharmony_ci mutex_unlock(&local->mtx); 123462306a36Sopenharmony_ci return ret; 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_cistatic int 123862306a36Sopenharmony_ciieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev, 123962306a36Sopenharmony_ci const u8 *peer, int link_id, 124062306a36Sopenharmony_ci u8 action_code, u8 dialog_token, 124162306a36Sopenharmony_ci u16 status_code, u32 peer_capability, 124262306a36Sopenharmony_ci bool initiator, const u8 *extra_ies, 124362306a36Sopenharmony_ci size_t extra_ies_len) 124462306a36Sopenharmony_ci{ 124562306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 124662306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 124762306a36Sopenharmony_ci struct sta_info *sta; 124862306a36Sopenharmony_ci int ret; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci /* 125162306a36Sopenharmony_ci * No packets can be transmitted to the peer via the AP during setup - 125262306a36Sopenharmony_ci * the STA is set as a TDLS peer, but is not authorized. 125362306a36Sopenharmony_ci * During teardown, we prevent direct transmissions by stopping the 125462306a36Sopenharmony_ci * queues and flushing all direct packets. 125562306a36Sopenharmony_ci */ 125662306a36Sopenharmony_ci ieee80211_stop_vif_queues(local, sdata, 125762306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN); 125862306a36Sopenharmony_ci ieee80211_flush_queues(local, sdata, false); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, 126162306a36Sopenharmony_ci link_id, action_code, 126262306a36Sopenharmony_ci dialog_token, status_code, 126362306a36Sopenharmony_ci peer_capability, initiator, 126462306a36Sopenharmony_ci extra_ies, extra_ies_len, 0, 126562306a36Sopenharmony_ci NULL); 126662306a36Sopenharmony_ci if (ret < 0) 126762306a36Sopenharmony_ci sdata_err(sdata, "Failed sending TDLS teardown packet %d\n", 126862306a36Sopenharmony_ci ret); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci /* 127162306a36Sopenharmony_ci * Remove the STA AUTH flag to force further traffic through the AP. If 127262306a36Sopenharmony_ci * the STA was unreachable, it was already removed. 127362306a36Sopenharmony_ci */ 127462306a36Sopenharmony_ci rcu_read_lock(); 127562306a36Sopenharmony_ci sta = sta_info_get(sdata, peer); 127662306a36Sopenharmony_ci if (sta) 127762306a36Sopenharmony_ci clear_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); 127862306a36Sopenharmony_ci rcu_read_unlock(); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci ieee80211_wake_vif_queues(local, sdata, 128162306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN); 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci return 0; 128462306a36Sopenharmony_ci} 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ciint ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, 128762306a36Sopenharmony_ci const u8 *peer, int link_id, 128862306a36Sopenharmony_ci u8 action_code, u8 dialog_token, u16 status_code, 128962306a36Sopenharmony_ci u32 peer_capability, bool initiator, 129062306a36Sopenharmony_ci const u8 *extra_ies, size_t extra_ies_len) 129162306a36Sopenharmony_ci{ 129262306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 129362306a36Sopenharmony_ci int ret; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) 129662306a36Sopenharmony_ci return -ENOTSUPP; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci /* make sure we are in managed mode, and associated */ 129962306a36Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_STATION || 130062306a36Sopenharmony_ci !sdata->u.mgd.associated) 130162306a36Sopenharmony_ci return -EINVAL; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci switch (action_code) { 130462306a36Sopenharmony_ci case WLAN_TDLS_SETUP_REQUEST: 130562306a36Sopenharmony_ci case WLAN_TDLS_SETUP_RESPONSE: 130662306a36Sopenharmony_ci ret = ieee80211_tdls_mgmt_setup(wiphy, dev, peer, 130762306a36Sopenharmony_ci link_id, action_code, 130862306a36Sopenharmony_ci dialog_token, status_code, 130962306a36Sopenharmony_ci peer_capability, initiator, 131062306a36Sopenharmony_ci extra_ies, extra_ies_len); 131162306a36Sopenharmony_ci break; 131262306a36Sopenharmony_ci case WLAN_TDLS_TEARDOWN: 131362306a36Sopenharmony_ci ret = ieee80211_tdls_mgmt_teardown(wiphy, dev, peer, link_id, 131462306a36Sopenharmony_ci action_code, dialog_token, 131562306a36Sopenharmony_ci status_code, 131662306a36Sopenharmony_ci peer_capability, initiator, 131762306a36Sopenharmony_ci extra_ies, extra_ies_len); 131862306a36Sopenharmony_ci break; 131962306a36Sopenharmony_ci case WLAN_TDLS_DISCOVERY_REQUEST: 132062306a36Sopenharmony_ci /* 132162306a36Sopenharmony_ci * Protect the discovery so we can hear the TDLS discovery 132262306a36Sopenharmony_ci * response frame. It is transmitted directly and not buffered 132362306a36Sopenharmony_ci * by the AP. 132462306a36Sopenharmony_ci */ 132562306a36Sopenharmony_ci drv_mgd_protect_tdls_discover(sdata->local, sdata); 132662306a36Sopenharmony_ci fallthrough; 132762306a36Sopenharmony_ci case WLAN_TDLS_SETUP_CONFIRM: 132862306a36Sopenharmony_ci case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: 132962306a36Sopenharmony_ci /* no special handling */ 133062306a36Sopenharmony_ci ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, 133162306a36Sopenharmony_ci link_id, action_code, 133262306a36Sopenharmony_ci dialog_token, 133362306a36Sopenharmony_ci status_code, 133462306a36Sopenharmony_ci peer_capability, 133562306a36Sopenharmony_ci initiator, extra_ies, 133662306a36Sopenharmony_ci extra_ies_len, 0, NULL); 133762306a36Sopenharmony_ci break; 133862306a36Sopenharmony_ci default: 133962306a36Sopenharmony_ci ret = -EOPNOTSUPP; 134062306a36Sopenharmony_ci break; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS mgmt action %d peer %pM link_id %d status %d\n", 134462306a36Sopenharmony_ci action_code, peer, link_id, ret); 134562306a36Sopenharmony_ci return ret; 134662306a36Sopenharmony_ci} 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_cistatic void iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data *sdata, 134962306a36Sopenharmony_ci struct sta_info *sta) 135062306a36Sopenharmony_ci{ 135162306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 135262306a36Sopenharmony_ci struct ieee80211_chanctx_conf *conf; 135362306a36Sopenharmony_ci struct ieee80211_chanctx *ctx; 135462306a36Sopenharmony_ci enum nl80211_chan_width width; 135562306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 135862306a36Sopenharmony_ci conf = rcu_dereference_protected(sdata->vif.bss_conf.chanctx_conf, 135962306a36Sopenharmony_ci lockdep_is_held(&local->chanctx_mtx)); 136062306a36Sopenharmony_ci if (conf) { 136162306a36Sopenharmony_ci width = conf->def.width; 136262306a36Sopenharmony_ci sband = local->hw.wiphy->bands[conf->def.chan->band]; 136362306a36Sopenharmony_ci ctx = container_of(conf, struct ieee80211_chanctx, conf); 136462306a36Sopenharmony_ci ieee80211_recalc_chanctx_chantype(local, ctx); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci /* if width changed and a peer is given, update its BW */ 136762306a36Sopenharmony_ci if (width != conf->def.width && sta && 136862306a36Sopenharmony_ci test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW)) { 136962306a36Sopenharmony_ci enum ieee80211_sta_rx_bandwidth bw; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci bw = ieee80211_chan_width_to_rx_bw(conf->def.width); 137262306a36Sopenharmony_ci bw = min(bw, ieee80211_sta_cap_rx_bw(&sta->deflink)); 137362306a36Sopenharmony_ci if (bw != sta->sta.deflink.bandwidth) { 137462306a36Sopenharmony_ci sta->sta.deflink.bandwidth = bw; 137562306a36Sopenharmony_ci rate_control_rate_update(local, sband, sta, 0, 137662306a36Sopenharmony_ci IEEE80211_RC_BW_CHANGED); 137762306a36Sopenharmony_ci /* 137862306a36Sopenharmony_ci * if a TDLS peer BW was updated, we need to 137962306a36Sopenharmony_ci * recalc the chandef width again, to get the 138062306a36Sopenharmony_ci * correct chanctx min_def 138162306a36Sopenharmony_ci */ 138262306a36Sopenharmony_ci ieee80211_recalc_chanctx_chantype(local, ctx); 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_cistatic int iee80211_tdls_have_ht_peers(struct ieee80211_sub_if_data *sdata) 139162306a36Sopenharmony_ci{ 139262306a36Sopenharmony_ci struct sta_info *sta; 139362306a36Sopenharmony_ci bool result = false; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci rcu_read_lock(); 139662306a36Sopenharmony_ci list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { 139762306a36Sopenharmony_ci if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded || 139862306a36Sopenharmony_ci !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || 139962306a36Sopenharmony_ci !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH) || 140062306a36Sopenharmony_ci !sta->sta.deflink.ht_cap.ht_supported) 140162306a36Sopenharmony_ci continue; 140262306a36Sopenharmony_ci result = true; 140362306a36Sopenharmony_ci break; 140462306a36Sopenharmony_ci } 140562306a36Sopenharmony_ci rcu_read_unlock(); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci return result; 140862306a36Sopenharmony_ci} 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_cistatic void 141162306a36Sopenharmony_ciiee80211_tdls_recalc_ht_protection(struct ieee80211_sub_if_data *sdata, 141262306a36Sopenharmony_ci struct sta_info *sta) 141362306a36Sopenharmony_ci{ 141462306a36Sopenharmony_ci bool tdls_ht; 141562306a36Sopenharmony_ci u16 protection = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED | 141662306a36Sopenharmony_ci IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT | 141762306a36Sopenharmony_ci IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT; 141862306a36Sopenharmony_ci u16 opmode; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci /* Nothing to do if the BSS connection uses HT */ 142162306a36Sopenharmony_ci if (!(sdata->deflink.u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) 142262306a36Sopenharmony_ci return; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci tdls_ht = (sta && sta->sta.deflink.ht_cap.ht_supported) || 142562306a36Sopenharmony_ci iee80211_tdls_have_ht_peers(sdata); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci opmode = sdata->vif.bss_conf.ht_operation_mode; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci if (tdls_ht) 143062306a36Sopenharmony_ci opmode |= protection; 143162306a36Sopenharmony_ci else 143262306a36Sopenharmony_ci opmode &= ~protection; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci if (opmode == sdata->vif.bss_conf.ht_operation_mode) 143562306a36Sopenharmony_ci return; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci sdata->vif.bss_conf.ht_operation_mode = opmode; 143862306a36Sopenharmony_ci ieee80211_link_info_change_notify(sdata, &sdata->deflink, 143962306a36Sopenharmony_ci BSS_CHANGED_HT); 144062306a36Sopenharmony_ci} 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ciint ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, 144362306a36Sopenharmony_ci const u8 *peer, enum nl80211_tdls_operation oper) 144462306a36Sopenharmony_ci{ 144562306a36Sopenharmony_ci struct sta_info *sta; 144662306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 144762306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 144862306a36Sopenharmony_ci int ret; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) 145162306a36Sopenharmony_ci return -ENOTSUPP; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_STATION) 145462306a36Sopenharmony_ci return -EINVAL; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci switch (oper) { 145762306a36Sopenharmony_ci case NL80211_TDLS_ENABLE_LINK: 145862306a36Sopenharmony_ci case NL80211_TDLS_DISABLE_LINK: 145962306a36Sopenharmony_ci break; 146062306a36Sopenharmony_ci case NL80211_TDLS_TEARDOWN: 146162306a36Sopenharmony_ci case NL80211_TDLS_SETUP: 146262306a36Sopenharmony_ci case NL80211_TDLS_DISCOVERY_REQ: 146362306a36Sopenharmony_ci /* We don't support in-driver setup/teardown/discovery */ 146462306a36Sopenharmony_ci return -ENOTSUPP; 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci /* protect possible bss_conf changes and avoid concurrency in 146862306a36Sopenharmony_ci * ieee80211_bss_info_change_notify() 146962306a36Sopenharmony_ci */ 147062306a36Sopenharmony_ci sdata_lock(sdata); 147162306a36Sopenharmony_ci mutex_lock(&local->mtx); 147262306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci switch (oper) { 147562306a36Sopenharmony_ci case NL80211_TDLS_ENABLE_LINK: 147662306a36Sopenharmony_ci if (sdata->vif.bss_conf.csa_active) { 147762306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS: disallow link during CSA\n"); 147862306a36Sopenharmony_ci ret = -EBUSY; 147962306a36Sopenharmony_ci break; 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci mutex_lock(&local->sta_mtx); 148362306a36Sopenharmony_ci sta = sta_info_get(sdata, peer); 148462306a36Sopenharmony_ci if (!sta) { 148562306a36Sopenharmony_ci mutex_unlock(&local->sta_mtx); 148662306a36Sopenharmony_ci ret = -ENOLINK; 148762306a36Sopenharmony_ci break; 148862306a36Sopenharmony_ci } 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci iee80211_tdls_recalc_chanctx(sdata, sta); 149162306a36Sopenharmony_ci iee80211_tdls_recalc_ht_protection(sdata, sta); 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); 149462306a36Sopenharmony_ci mutex_unlock(&local->sta_mtx); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci WARN_ON_ONCE(is_zero_ether_addr(sdata->u.mgd.tdls_peer) || 149762306a36Sopenharmony_ci !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)); 149862306a36Sopenharmony_ci ret = 0; 149962306a36Sopenharmony_ci break; 150062306a36Sopenharmony_ci case NL80211_TDLS_DISABLE_LINK: 150162306a36Sopenharmony_ci /* 150262306a36Sopenharmony_ci * The teardown message in ieee80211_tdls_mgmt_teardown() was 150362306a36Sopenharmony_ci * created while the queues were stopped, so it might still be 150462306a36Sopenharmony_ci * pending. Before flushing the queues we need to be sure the 150562306a36Sopenharmony_ci * message is handled by the tasklet handling pending messages, 150662306a36Sopenharmony_ci * otherwise we might start destroying the station before 150762306a36Sopenharmony_ci * sending the teardown packet. 150862306a36Sopenharmony_ci * Note that this only forces the tasklet to flush pendings - 150962306a36Sopenharmony_ci * not to stop the tasklet from rescheduling itself. 151062306a36Sopenharmony_ci */ 151162306a36Sopenharmony_ci tasklet_kill(&local->tx_pending_tasklet); 151262306a36Sopenharmony_ci /* flush a potentially queued teardown packet */ 151362306a36Sopenharmony_ci ieee80211_flush_queues(local, sdata, false); 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci ret = sta_info_destroy_addr(sdata, peer); 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci mutex_lock(&local->sta_mtx); 151862306a36Sopenharmony_ci iee80211_tdls_recalc_ht_protection(sdata, NULL); 151962306a36Sopenharmony_ci mutex_unlock(&local->sta_mtx); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci iee80211_tdls_recalc_chanctx(sdata, NULL); 152262306a36Sopenharmony_ci break; 152362306a36Sopenharmony_ci default: 152462306a36Sopenharmony_ci ret = -ENOTSUPP; 152562306a36Sopenharmony_ci break; 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci if (ret == 0 && ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) { 152962306a36Sopenharmony_ci cancel_delayed_work(&sdata->u.mgd.tdls_peer_del_work); 153062306a36Sopenharmony_ci eth_zero_addr(sdata->u.mgd.tdls_peer); 153162306a36Sopenharmony_ci } 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci if (ret == 0) 153462306a36Sopenharmony_ci wiphy_work_queue(sdata->local->hw.wiphy, 153562306a36Sopenharmony_ci &sdata->deflink.u.mgd.request_smps_work); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci mutex_unlock(&local->mtx); 153862306a36Sopenharmony_ci sdata_unlock(sdata); 153962306a36Sopenharmony_ci return ret; 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_civoid ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer, 154362306a36Sopenharmony_ci enum nl80211_tdls_operation oper, 154462306a36Sopenharmony_ci u16 reason_code, gfp_t gfp) 154562306a36Sopenharmony_ci{ 154662306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) { 154962306a36Sopenharmony_ci sdata_err(sdata, "Discarding TDLS oper %d - not STA or disconnected\n", 155062306a36Sopenharmony_ci oper); 155162306a36Sopenharmony_ci return; 155262306a36Sopenharmony_ci } 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp); 155562306a36Sopenharmony_ci} 155662306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_tdls_oper_request); 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_cistatic void 155962306a36Sopenharmony_ciiee80211_tdls_add_ch_switch_timing(u8 *buf, u16 switch_time, u16 switch_timeout) 156062306a36Sopenharmony_ci{ 156162306a36Sopenharmony_ci struct ieee80211_ch_switch_timing *ch_sw; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci *buf++ = WLAN_EID_CHAN_SWITCH_TIMING; 156462306a36Sopenharmony_ci *buf++ = sizeof(struct ieee80211_ch_switch_timing); 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci ch_sw = (void *)buf; 156762306a36Sopenharmony_ci ch_sw->switch_time = cpu_to_le16(switch_time); 156862306a36Sopenharmony_ci ch_sw->switch_timeout = cpu_to_le16(switch_timeout); 156962306a36Sopenharmony_ci} 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci/* find switch timing IE in SKB ready for Tx */ 157262306a36Sopenharmony_cistatic const u8 *ieee80211_tdls_find_sw_timing_ie(struct sk_buff *skb) 157362306a36Sopenharmony_ci{ 157462306a36Sopenharmony_ci struct ieee80211_tdls_data *tf; 157562306a36Sopenharmony_ci const u8 *ie_start; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci /* 157862306a36Sopenharmony_ci * Get the offset for the new location of the switch timing IE. 157962306a36Sopenharmony_ci * The SKB network header will now point to the "payload_type" 158062306a36Sopenharmony_ci * element of the TDLS data frame struct. 158162306a36Sopenharmony_ci */ 158262306a36Sopenharmony_ci tf = container_of(skb->data + skb_network_offset(skb), 158362306a36Sopenharmony_ci struct ieee80211_tdls_data, payload_type); 158462306a36Sopenharmony_ci ie_start = tf->u.chan_switch_req.variable; 158562306a36Sopenharmony_ci return cfg80211_find_ie(WLAN_EID_CHAN_SWITCH_TIMING, ie_start, 158662306a36Sopenharmony_ci skb->len - (ie_start - skb->data)); 158762306a36Sopenharmony_ci} 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_cistatic struct sk_buff * 159062306a36Sopenharmony_ciieee80211_tdls_ch_sw_tmpl_get(struct sta_info *sta, u8 oper_class, 159162306a36Sopenharmony_ci struct cfg80211_chan_def *chandef, 159262306a36Sopenharmony_ci u32 *ch_sw_tm_ie_offset) 159362306a36Sopenharmony_ci{ 159462306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = sta->sdata; 159562306a36Sopenharmony_ci u8 extra_ies[2 + sizeof(struct ieee80211_sec_chan_offs_ie) + 159662306a36Sopenharmony_ci 2 + sizeof(struct ieee80211_ch_switch_timing)]; 159762306a36Sopenharmony_ci int extra_ies_len = 2 + sizeof(struct ieee80211_ch_switch_timing); 159862306a36Sopenharmony_ci u8 *pos = extra_ies; 159962306a36Sopenharmony_ci struct sk_buff *skb; 160062306a36Sopenharmony_ci int link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci /* 160362306a36Sopenharmony_ci * if chandef points to a wide channel add a Secondary-Channel 160462306a36Sopenharmony_ci * Offset information element 160562306a36Sopenharmony_ci */ 160662306a36Sopenharmony_ci if (chandef->width == NL80211_CHAN_WIDTH_40) { 160762306a36Sopenharmony_ci struct ieee80211_sec_chan_offs_ie *sec_chan_ie; 160862306a36Sopenharmony_ci bool ht40plus; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; 161162306a36Sopenharmony_ci *pos++ = sizeof(*sec_chan_ie); 161262306a36Sopenharmony_ci sec_chan_ie = (void *)pos; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci ht40plus = cfg80211_get_chandef_type(chandef) == 161562306a36Sopenharmony_ci NL80211_CHAN_HT40PLUS; 161662306a36Sopenharmony_ci sec_chan_ie->sec_chan_offs = ht40plus ? 161762306a36Sopenharmony_ci IEEE80211_HT_PARAM_CHA_SEC_ABOVE : 161862306a36Sopenharmony_ci IEEE80211_HT_PARAM_CHA_SEC_BELOW; 161962306a36Sopenharmony_ci pos += sizeof(*sec_chan_ie); 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci extra_ies_len += 2 + sizeof(struct ieee80211_sec_chan_offs_ie); 162262306a36Sopenharmony_ci } 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci /* just set the values to 0, this is a template */ 162562306a36Sopenharmony_ci iee80211_tdls_add_ch_switch_timing(pos, 0, 0); 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr, 162862306a36Sopenharmony_ci link_id, 162962306a36Sopenharmony_ci WLAN_TDLS_CHANNEL_SWITCH_REQUEST, 163062306a36Sopenharmony_ci 0, 0, !sta->sta.tdls_initiator, 163162306a36Sopenharmony_ci extra_ies, extra_ies_len, 163262306a36Sopenharmony_ci oper_class, chandef); 163362306a36Sopenharmony_ci if (!skb) 163462306a36Sopenharmony_ci return NULL; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci skb = ieee80211_build_data_template(sdata, skb, 0); 163762306a36Sopenharmony_ci if (IS_ERR(skb)) { 163862306a36Sopenharmony_ci tdls_dbg(sdata, "Failed building TDLS channel switch frame\n"); 163962306a36Sopenharmony_ci return NULL; 164062306a36Sopenharmony_ci } 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci if (ch_sw_tm_ie_offset) { 164362306a36Sopenharmony_ci const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb); 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci if (!tm_ie) { 164662306a36Sopenharmony_ci tdls_dbg(sdata, "No switch timing IE in TDLS switch\n"); 164762306a36Sopenharmony_ci dev_kfree_skb_any(skb); 164862306a36Sopenharmony_ci return NULL; 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci *ch_sw_tm_ie_offset = tm_ie - skb->data; 165262306a36Sopenharmony_ci } 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci tdls_dbg(sdata, 165562306a36Sopenharmony_ci "TDLS channel switch request template for %pM ch %d width %d\n", 165662306a36Sopenharmony_ci sta->sta.addr, chandef->chan->center_freq, chandef->width); 165762306a36Sopenharmony_ci return skb; 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ciint 166162306a36Sopenharmony_ciieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev, 166262306a36Sopenharmony_ci const u8 *addr, u8 oper_class, 166362306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 166462306a36Sopenharmony_ci{ 166562306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 166662306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 166762306a36Sopenharmony_ci struct sta_info *sta; 166862306a36Sopenharmony_ci struct sk_buff *skb = NULL; 166962306a36Sopenharmony_ci u32 ch_sw_tm_ie; 167062306a36Sopenharmony_ci int ret; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci if (chandef->chan->freq_offset) 167362306a36Sopenharmony_ci /* this may work, but is untested */ 167462306a36Sopenharmony_ci return -EOPNOTSUPP; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci mutex_lock(&local->sta_mtx); 167762306a36Sopenharmony_ci sta = sta_info_get(sdata, addr); 167862306a36Sopenharmony_ci if (!sta) { 167962306a36Sopenharmony_ci tdls_dbg(sdata, 168062306a36Sopenharmony_ci "Invalid TDLS peer %pM for channel switch request\n", 168162306a36Sopenharmony_ci addr); 168262306a36Sopenharmony_ci ret = -ENOENT; 168362306a36Sopenharmony_ci goto out; 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci if (!test_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH)) { 168762306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS channel switch unsupported by %pM\n", 168862306a36Sopenharmony_ci addr); 168962306a36Sopenharmony_ci ret = -ENOTSUPP; 169062306a36Sopenharmony_ci goto out; 169162306a36Sopenharmony_ci } 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci skb = ieee80211_tdls_ch_sw_tmpl_get(sta, oper_class, chandef, 169462306a36Sopenharmony_ci &ch_sw_tm_ie); 169562306a36Sopenharmony_ci if (!skb) { 169662306a36Sopenharmony_ci ret = -ENOENT; 169762306a36Sopenharmony_ci goto out; 169862306a36Sopenharmony_ci } 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci ret = drv_tdls_channel_switch(local, sdata, &sta->sta, oper_class, 170162306a36Sopenharmony_ci chandef, skb, ch_sw_tm_ie); 170262306a36Sopenharmony_ci if (!ret) 170362306a36Sopenharmony_ci set_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL); 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ciout: 170662306a36Sopenharmony_ci mutex_unlock(&local->sta_mtx); 170762306a36Sopenharmony_ci dev_kfree_skb_any(skb); 170862306a36Sopenharmony_ci return ret; 170962306a36Sopenharmony_ci} 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_civoid 171262306a36Sopenharmony_ciieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy, 171362306a36Sopenharmony_ci struct net_device *dev, 171462306a36Sopenharmony_ci const u8 *addr) 171562306a36Sopenharmony_ci{ 171662306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 171762306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 171862306a36Sopenharmony_ci struct sta_info *sta; 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci mutex_lock(&local->sta_mtx); 172162306a36Sopenharmony_ci sta = sta_info_get(sdata, addr); 172262306a36Sopenharmony_ci if (!sta) { 172362306a36Sopenharmony_ci tdls_dbg(sdata, 172462306a36Sopenharmony_ci "Invalid TDLS peer %pM for channel switch cancel\n", 172562306a36Sopenharmony_ci addr); 172662306a36Sopenharmony_ci goto out; 172762306a36Sopenharmony_ci } 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci if (!test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) { 173062306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS channel switch not initiated by %pM\n", 173162306a36Sopenharmony_ci addr); 173262306a36Sopenharmony_ci goto out; 173362306a36Sopenharmony_ci } 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci drv_tdls_cancel_channel_switch(local, sdata, &sta->sta); 173662306a36Sopenharmony_ci clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL); 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ciout: 173962306a36Sopenharmony_ci mutex_unlock(&local->sta_mtx); 174062306a36Sopenharmony_ci} 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_cistatic struct sk_buff * 174362306a36Sopenharmony_ciieee80211_tdls_ch_sw_resp_tmpl_get(struct sta_info *sta, 174462306a36Sopenharmony_ci u32 *ch_sw_tm_ie_offset) 174562306a36Sopenharmony_ci{ 174662306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = sta->sdata; 174762306a36Sopenharmony_ci struct sk_buff *skb; 174862306a36Sopenharmony_ci u8 extra_ies[2 + sizeof(struct ieee80211_ch_switch_timing)]; 174962306a36Sopenharmony_ci int link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci /* initial timing are always zero in the template */ 175262306a36Sopenharmony_ci iee80211_tdls_add_ch_switch_timing(extra_ies, 0, 0); 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr, 175562306a36Sopenharmony_ci link_id, 175662306a36Sopenharmony_ci WLAN_TDLS_CHANNEL_SWITCH_RESPONSE, 175762306a36Sopenharmony_ci 0, 0, !sta->sta.tdls_initiator, 175862306a36Sopenharmony_ci extra_ies, sizeof(extra_ies), 0, NULL); 175962306a36Sopenharmony_ci if (!skb) 176062306a36Sopenharmony_ci return NULL; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci skb = ieee80211_build_data_template(sdata, skb, 0); 176362306a36Sopenharmony_ci if (IS_ERR(skb)) { 176462306a36Sopenharmony_ci tdls_dbg(sdata, 176562306a36Sopenharmony_ci "Failed building TDLS channel switch resp frame\n"); 176662306a36Sopenharmony_ci return NULL; 176762306a36Sopenharmony_ci } 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci if (ch_sw_tm_ie_offset) { 177062306a36Sopenharmony_ci const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb); 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci if (!tm_ie) { 177362306a36Sopenharmony_ci tdls_dbg(sdata, 177462306a36Sopenharmony_ci "No switch timing IE in TDLS switch resp\n"); 177562306a36Sopenharmony_ci dev_kfree_skb_any(skb); 177662306a36Sopenharmony_ci return NULL; 177762306a36Sopenharmony_ci } 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci *ch_sw_tm_ie_offset = tm_ie - skb->data; 178062306a36Sopenharmony_ci } 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS get channel switch response template for %pM\n", 178362306a36Sopenharmony_ci sta->sta.addr); 178462306a36Sopenharmony_ci return skb; 178562306a36Sopenharmony_ci} 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_cistatic int 178862306a36Sopenharmony_ciieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata, 178962306a36Sopenharmony_ci struct sk_buff *skb) 179062306a36Sopenharmony_ci{ 179162306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 179262306a36Sopenharmony_ci struct ieee802_11_elems *elems = NULL; 179362306a36Sopenharmony_ci struct sta_info *sta; 179462306a36Sopenharmony_ci struct ieee80211_tdls_data *tf = (void *)skb->data; 179562306a36Sopenharmony_ci bool local_initiator; 179662306a36Sopenharmony_ci struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); 179762306a36Sopenharmony_ci int baselen = offsetof(typeof(*tf), u.chan_switch_resp.variable); 179862306a36Sopenharmony_ci struct ieee80211_tdls_ch_sw_params params = {}; 179962306a36Sopenharmony_ci int ret; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci params.action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE; 180262306a36Sopenharmony_ci params.timestamp = rx_status->device_timestamp; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci if (skb->len < baselen) { 180562306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS channel switch resp too short: %d\n", 180662306a36Sopenharmony_ci skb->len); 180762306a36Sopenharmony_ci return -EINVAL; 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci mutex_lock(&local->sta_mtx); 181162306a36Sopenharmony_ci sta = sta_info_get(sdata, tf->sa); 181262306a36Sopenharmony_ci if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) { 181362306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n", 181462306a36Sopenharmony_ci tf->sa); 181562306a36Sopenharmony_ci ret = -EINVAL; 181662306a36Sopenharmony_ci goto out; 181762306a36Sopenharmony_ci } 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci params.sta = &sta->sta; 182062306a36Sopenharmony_ci params.status = le16_to_cpu(tf->u.chan_switch_resp.status_code); 182162306a36Sopenharmony_ci if (params.status != 0) { 182262306a36Sopenharmony_ci ret = 0; 182362306a36Sopenharmony_ci goto call_drv; 182462306a36Sopenharmony_ci } 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci elems = ieee802_11_parse_elems(tf->u.chan_switch_resp.variable, 182762306a36Sopenharmony_ci skb->len - baselen, false, NULL); 182862306a36Sopenharmony_ci if (!elems) { 182962306a36Sopenharmony_ci ret = -ENOMEM; 183062306a36Sopenharmony_ci goto out; 183162306a36Sopenharmony_ci } 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci if (elems->parse_error) { 183462306a36Sopenharmony_ci tdls_dbg(sdata, "Invalid IEs in TDLS channel switch resp\n"); 183562306a36Sopenharmony_ci ret = -EINVAL; 183662306a36Sopenharmony_ci goto out; 183762306a36Sopenharmony_ci } 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci if (!elems->ch_sw_timing || !elems->lnk_id) { 184062306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS channel switch resp - missing IEs\n"); 184162306a36Sopenharmony_ci ret = -EINVAL; 184262306a36Sopenharmony_ci goto out; 184362306a36Sopenharmony_ci } 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci /* validate the initiator is set correctly */ 184662306a36Sopenharmony_ci local_initiator = 184762306a36Sopenharmony_ci !memcmp(elems->lnk_id->init_sta, sdata->vif.addr, ETH_ALEN); 184862306a36Sopenharmony_ci if (local_initiator == sta->sta.tdls_initiator) { 184962306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n"); 185062306a36Sopenharmony_ci ret = -EINVAL; 185162306a36Sopenharmony_ci goto out; 185262306a36Sopenharmony_ci } 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci params.switch_time = le16_to_cpu(elems->ch_sw_timing->switch_time); 185562306a36Sopenharmony_ci params.switch_timeout = le16_to_cpu(elems->ch_sw_timing->switch_timeout); 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci params.tmpl_skb = 185862306a36Sopenharmony_ci ieee80211_tdls_ch_sw_resp_tmpl_get(sta, ¶ms.ch_sw_tm_ie); 185962306a36Sopenharmony_ci if (!params.tmpl_skb) { 186062306a36Sopenharmony_ci ret = -ENOENT; 186162306a36Sopenharmony_ci goto out; 186262306a36Sopenharmony_ci } 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci ret = 0; 186562306a36Sopenharmony_cicall_drv: 186662306a36Sopenharmony_ci drv_tdls_recv_channel_switch(sdata->local, sdata, ¶ms); 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci tdls_dbg(sdata, 186962306a36Sopenharmony_ci "TDLS channel switch response received from %pM status %d\n", 187062306a36Sopenharmony_ci tf->sa, params.status); 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ciout: 187362306a36Sopenharmony_ci mutex_unlock(&local->sta_mtx); 187462306a36Sopenharmony_ci dev_kfree_skb_any(params.tmpl_skb); 187562306a36Sopenharmony_ci kfree(elems); 187662306a36Sopenharmony_ci return ret; 187762306a36Sopenharmony_ci} 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_cistatic int 188062306a36Sopenharmony_ciieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata, 188162306a36Sopenharmony_ci struct sk_buff *skb) 188262306a36Sopenharmony_ci{ 188362306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 188462306a36Sopenharmony_ci struct ieee802_11_elems *elems; 188562306a36Sopenharmony_ci struct cfg80211_chan_def chandef; 188662306a36Sopenharmony_ci struct ieee80211_channel *chan; 188762306a36Sopenharmony_ci enum nl80211_channel_type chan_type; 188862306a36Sopenharmony_ci int freq; 188962306a36Sopenharmony_ci u8 target_channel, oper_class; 189062306a36Sopenharmony_ci bool local_initiator; 189162306a36Sopenharmony_ci struct sta_info *sta; 189262306a36Sopenharmony_ci enum nl80211_band band; 189362306a36Sopenharmony_ci struct ieee80211_tdls_data *tf = (void *)skb->data; 189462306a36Sopenharmony_ci struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); 189562306a36Sopenharmony_ci int baselen = offsetof(typeof(*tf), u.chan_switch_req.variable); 189662306a36Sopenharmony_ci struct ieee80211_tdls_ch_sw_params params = {}; 189762306a36Sopenharmony_ci int ret = 0; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci params.action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST; 190062306a36Sopenharmony_ci params.timestamp = rx_status->device_timestamp; 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci if (skb->len < baselen) { 190362306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS channel switch req too short: %d\n", 190462306a36Sopenharmony_ci skb->len); 190562306a36Sopenharmony_ci return -EINVAL; 190662306a36Sopenharmony_ci } 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci target_channel = tf->u.chan_switch_req.target_channel; 190962306a36Sopenharmony_ci oper_class = tf->u.chan_switch_req.oper_class; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci /* 191262306a36Sopenharmony_ci * We can't easily infer the channel band. The operating class is 191362306a36Sopenharmony_ci * ambiguous - there are multiple tables (US/Europe/JP/Global). The 191462306a36Sopenharmony_ci * solution here is to treat channels with number >14 as 5GHz ones, 191562306a36Sopenharmony_ci * and specifically check for the (oper_class, channel) combinations 191662306a36Sopenharmony_ci * where this doesn't hold. These are thankfully unique according to 191762306a36Sopenharmony_ci * IEEE802.11-2012. 191862306a36Sopenharmony_ci * We consider only the 2GHz and 5GHz bands and 20MHz+ channels as 191962306a36Sopenharmony_ci * valid here. 192062306a36Sopenharmony_ci */ 192162306a36Sopenharmony_ci if ((oper_class == 112 || oper_class == 2 || oper_class == 3 || 192262306a36Sopenharmony_ci oper_class == 4 || oper_class == 5 || oper_class == 6) && 192362306a36Sopenharmony_ci target_channel < 14) 192462306a36Sopenharmony_ci band = NL80211_BAND_5GHZ; 192562306a36Sopenharmony_ci else 192662306a36Sopenharmony_ci band = target_channel < 14 ? NL80211_BAND_2GHZ : 192762306a36Sopenharmony_ci NL80211_BAND_5GHZ; 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci freq = ieee80211_channel_to_frequency(target_channel, band); 193062306a36Sopenharmony_ci if (freq == 0) { 193162306a36Sopenharmony_ci tdls_dbg(sdata, "Invalid channel in TDLS chan switch: %d\n", 193262306a36Sopenharmony_ci target_channel); 193362306a36Sopenharmony_ci return -EINVAL; 193462306a36Sopenharmony_ci } 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq); 193762306a36Sopenharmony_ci if (!chan) { 193862306a36Sopenharmony_ci tdls_dbg(sdata, 193962306a36Sopenharmony_ci "Unsupported channel for TDLS chan switch: %d\n", 194062306a36Sopenharmony_ci target_channel); 194162306a36Sopenharmony_ci return -EINVAL; 194262306a36Sopenharmony_ci } 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci elems = ieee802_11_parse_elems(tf->u.chan_switch_req.variable, 194562306a36Sopenharmony_ci skb->len - baselen, false, NULL); 194662306a36Sopenharmony_ci if (!elems) 194762306a36Sopenharmony_ci return -ENOMEM; 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci if (elems->parse_error) { 195062306a36Sopenharmony_ci tdls_dbg(sdata, "Invalid IEs in TDLS channel switch req\n"); 195162306a36Sopenharmony_ci ret = -EINVAL; 195262306a36Sopenharmony_ci goto free; 195362306a36Sopenharmony_ci } 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci if (!elems->ch_sw_timing || !elems->lnk_id) { 195662306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS channel switch req - missing IEs\n"); 195762306a36Sopenharmony_ci ret = -EINVAL; 195862306a36Sopenharmony_ci goto free; 195962306a36Sopenharmony_ci } 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci if (!elems->sec_chan_offs) { 196262306a36Sopenharmony_ci chan_type = NL80211_CHAN_HT20; 196362306a36Sopenharmony_ci } else { 196462306a36Sopenharmony_ci switch (elems->sec_chan_offs->sec_chan_offs) { 196562306a36Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 196662306a36Sopenharmony_ci chan_type = NL80211_CHAN_HT40PLUS; 196762306a36Sopenharmony_ci break; 196862306a36Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 196962306a36Sopenharmony_ci chan_type = NL80211_CHAN_HT40MINUS; 197062306a36Sopenharmony_ci break; 197162306a36Sopenharmony_ci default: 197262306a36Sopenharmony_ci chan_type = NL80211_CHAN_HT20; 197362306a36Sopenharmony_ci break; 197462306a36Sopenharmony_ci } 197562306a36Sopenharmony_ci } 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci cfg80211_chandef_create(&chandef, chan, chan_type); 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci /* we will be active on the TDLS link */ 198062306a36Sopenharmony_ci if (!cfg80211_reg_can_beacon_relax(sdata->local->hw.wiphy, &chandef, 198162306a36Sopenharmony_ci sdata->wdev.iftype)) { 198262306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS chan switch to forbidden channel\n"); 198362306a36Sopenharmony_ci ret = -EINVAL; 198462306a36Sopenharmony_ci goto free; 198562306a36Sopenharmony_ci } 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci mutex_lock(&local->sta_mtx); 198862306a36Sopenharmony_ci sta = sta_info_get(sdata, tf->sa); 198962306a36Sopenharmony_ci if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) { 199062306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n", 199162306a36Sopenharmony_ci tf->sa); 199262306a36Sopenharmony_ci ret = -EINVAL; 199362306a36Sopenharmony_ci goto out; 199462306a36Sopenharmony_ci } 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci params.sta = &sta->sta; 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci /* validate the initiator is set correctly */ 199962306a36Sopenharmony_ci local_initiator = 200062306a36Sopenharmony_ci !memcmp(elems->lnk_id->init_sta, sdata->vif.addr, ETH_ALEN); 200162306a36Sopenharmony_ci if (local_initiator == sta->sta.tdls_initiator) { 200262306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n"); 200362306a36Sopenharmony_ci ret = -EINVAL; 200462306a36Sopenharmony_ci goto out; 200562306a36Sopenharmony_ci } 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci /* peer should have known better */ 200862306a36Sopenharmony_ci if (!sta->sta.deflink.ht_cap.ht_supported && elems->sec_chan_offs && 200962306a36Sopenharmony_ci elems->sec_chan_offs->sec_chan_offs) { 201062306a36Sopenharmony_ci tdls_dbg(sdata, "TDLS chan switch - wide chan unsupported\n"); 201162306a36Sopenharmony_ci ret = -ENOTSUPP; 201262306a36Sopenharmony_ci goto out; 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci params.chandef = &chandef; 201662306a36Sopenharmony_ci params.switch_time = le16_to_cpu(elems->ch_sw_timing->switch_time); 201762306a36Sopenharmony_ci params.switch_timeout = le16_to_cpu(elems->ch_sw_timing->switch_timeout); 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci params.tmpl_skb = 202062306a36Sopenharmony_ci ieee80211_tdls_ch_sw_resp_tmpl_get(sta, 202162306a36Sopenharmony_ci ¶ms.ch_sw_tm_ie); 202262306a36Sopenharmony_ci if (!params.tmpl_skb) { 202362306a36Sopenharmony_ci ret = -ENOENT; 202462306a36Sopenharmony_ci goto out; 202562306a36Sopenharmony_ci } 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci drv_tdls_recv_channel_switch(sdata->local, sdata, ¶ms); 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci tdls_dbg(sdata, 203062306a36Sopenharmony_ci "TDLS ch switch request received from %pM ch %d width %d\n", 203162306a36Sopenharmony_ci tf->sa, params.chandef->chan->center_freq, 203262306a36Sopenharmony_ci params.chandef->width); 203362306a36Sopenharmony_ciout: 203462306a36Sopenharmony_ci mutex_unlock(&local->sta_mtx); 203562306a36Sopenharmony_ci dev_kfree_skb_any(params.tmpl_skb); 203662306a36Sopenharmony_cifree: 203762306a36Sopenharmony_ci kfree(elems); 203862306a36Sopenharmony_ci return ret; 203962306a36Sopenharmony_ci} 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_civoid 204262306a36Sopenharmony_ciieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata, 204362306a36Sopenharmony_ci struct sk_buff *skb) 204462306a36Sopenharmony_ci{ 204562306a36Sopenharmony_ci struct ieee80211_tdls_data *tf = (void *)skb->data; 204662306a36Sopenharmony_ci struct wiphy *wiphy = sdata->local->hw.wiphy; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci lockdep_assert_wiphy(wiphy); 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci /* make sure the driver supports it */ 205162306a36Sopenharmony_ci if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)) 205262306a36Sopenharmony_ci return; 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci /* we want to access the entire packet */ 205562306a36Sopenharmony_ci if (skb_linearize(skb)) 205662306a36Sopenharmony_ci return; 205762306a36Sopenharmony_ci /* 205862306a36Sopenharmony_ci * The packet/size was already validated by mac80211 Rx path, only look 205962306a36Sopenharmony_ci * at the action type. 206062306a36Sopenharmony_ci */ 206162306a36Sopenharmony_ci switch (tf->action_code) { 206262306a36Sopenharmony_ci case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: 206362306a36Sopenharmony_ci ieee80211_process_tdls_channel_switch_req(sdata, skb); 206462306a36Sopenharmony_ci break; 206562306a36Sopenharmony_ci case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: 206662306a36Sopenharmony_ci ieee80211_process_tdls_channel_switch_resp(sdata, skb); 206762306a36Sopenharmony_ci break; 206862306a36Sopenharmony_ci default: 206962306a36Sopenharmony_ci WARN_ON_ONCE(1); 207062306a36Sopenharmony_ci return; 207162306a36Sopenharmony_ci } 207262306a36Sopenharmony_ci} 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_civoid ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata) 207562306a36Sopenharmony_ci{ 207662306a36Sopenharmony_ci struct sta_info *sta; 207762306a36Sopenharmony_ci u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED; 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci rcu_read_lock(); 208062306a36Sopenharmony_ci list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { 208162306a36Sopenharmony_ci if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded || 208262306a36Sopenharmony_ci !test_sta_flag(sta, WLAN_STA_AUTHORIZED)) 208362306a36Sopenharmony_ci continue; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci ieee80211_tdls_oper_request(&sdata->vif, sta->sta.addr, 208662306a36Sopenharmony_ci NL80211_TDLS_TEARDOWN, reason, 208762306a36Sopenharmony_ci GFP_ATOMIC); 208862306a36Sopenharmony_ci } 208962306a36Sopenharmony_ci rcu_read_unlock(); 209062306a36Sopenharmony_ci} 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_civoid ieee80211_tdls_handle_disconnect(struct ieee80211_sub_if_data *sdata, 209362306a36Sopenharmony_ci const u8 *peer, u16 reason) 209462306a36Sopenharmony_ci{ 209562306a36Sopenharmony_ci struct ieee80211_sta *sta; 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci rcu_read_lock(); 209862306a36Sopenharmony_ci sta = ieee80211_find_sta(&sdata->vif, peer); 209962306a36Sopenharmony_ci if (!sta || !sta->tdls) { 210062306a36Sopenharmony_ci rcu_read_unlock(); 210162306a36Sopenharmony_ci return; 210262306a36Sopenharmony_ci } 210362306a36Sopenharmony_ci rcu_read_unlock(); 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci tdls_dbg(sdata, "disconnected from TDLS peer %pM (Reason: %u=%s)\n", 210662306a36Sopenharmony_ci peer, reason, 210762306a36Sopenharmony_ci ieee80211_get_reason_code_string(reason)); 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci ieee80211_tdls_oper_request(&sdata->vif, peer, 211062306a36Sopenharmony_ci NL80211_TDLS_TEARDOWN, 211162306a36Sopenharmony_ci WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE, 211262306a36Sopenharmony_ci GFP_ATOMIC); 211362306a36Sopenharmony_ci} 2114