128c61a66SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
295224fe8SArik Nemtsov /*
395224fe8SArik Nemtsov * mac80211 TDLS handling code
495224fe8SArik Nemtsov *
595224fe8SArik Nemtsov * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
695224fe8SArik Nemtsov * Copyright 2014, Intel Corporation
7d98ad83eSJohannes Berg * Copyright 2014 Intel Mobile Communications GmbH
859021c67SArik Nemtsov * Copyright 2015 - 2016 Intel Deutschland GmbH
9c6112046SMukesh Sisodiya * Copyright (C) 2019, 2021-2023 Intel Corporation
1095224fe8SArik Nemtsov */
1195224fe8SArik Nemtsov
1295224fe8SArik Nemtsov #include <linux/ieee80211.h>
136f7eaa47SArik Nemtsov #include <linux/log2.h>
14c887f0d3SArik Nemtsov #include <net/cfg80211.h>
15c8ff71e6SArik Nemtsov #include <linux/rtnetlink.h>
1695224fe8SArik Nemtsov #include "ieee80211_i.h"
17ee10f2c7SArik Nemtsov #include "driver-ops.h"
1859021c67SArik Nemtsov #include "rate.h"
19cb59bc14SJohannes Berg #include "wme.h"
2095224fe8SArik Nemtsov
2117e6a59aSArik Nemtsov /* give usermode some time for retries in setting up the TDLS session */
2217e6a59aSArik Nemtsov #define TDLS_PEER_SETUP_TIMEOUT (15 * HZ)
2317e6a59aSArik Nemtsov
ieee80211_tdls_peer_del_work(struct work_struct * wk)2417e6a59aSArik Nemtsov void ieee80211_tdls_peer_del_work(struct work_struct *wk)
2517e6a59aSArik Nemtsov {
2617e6a59aSArik Nemtsov struct ieee80211_sub_if_data *sdata;
2717e6a59aSArik Nemtsov struct ieee80211_local *local;
2817e6a59aSArik Nemtsov
2917e6a59aSArik Nemtsov sdata = container_of(wk, struct ieee80211_sub_if_data,
3081dd2b88SArik Nemtsov u.mgd.tdls_peer_del_work.work);
3117e6a59aSArik Nemtsov local = sdata->local;
3217e6a59aSArik Nemtsov
3317e6a59aSArik Nemtsov mutex_lock(&local->mtx);
3481dd2b88SArik Nemtsov if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer)) {
3581dd2b88SArik Nemtsov tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->u.mgd.tdls_peer);
3681dd2b88SArik Nemtsov sta_info_destroy_addr(sdata, sdata->u.mgd.tdls_peer);
3781dd2b88SArik Nemtsov eth_zero_addr(sdata->u.mgd.tdls_peer);
3817e6a59aSArik Nemtsov }
3917e6a59aSArik Nemtsov mutex_unlock(&local->mtx);
4017e6a59aSArik Nemtsov }
4117e6a59aSArik Nemtsov
ieee80211_tdls_add_ext_capab(struct ieee80211_link_data * link,struct sk_buff * skb)4278a7ea37SMukesh Sisodiya static void ieee80211_tdls_add_ext_capab(struct ieee80211_link_data *link,
4378632a17SArik Nemtsov struct sk_buff *skb)
4495224fe8SArik Nemtsov {
4578a7ea37SMukesh Sisodiya struct ieee80211_sub_if_data *sdata = link->sdata;
46b98fb44fSArik Nemtsov struct ieee80211_local *local = sdata->local;
4782c0cc90SArik Nemtsov struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
4878632a17SArik Nemtsov bool chan_switch = local->hw.wiphy->features &
4978632a17SArik Nemtsov NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
5082c0cc90SArik Nemtsov bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) &&
5182c0cc90SArik Nemtsov !ifmgd->tdls_wider_bw_prohibited;
52e2fb1b83SYingying Tang bool buffer_sta = ieee80211_hw_check(&local->hw,
53e2fb1b83SYingying Tang SUPPORTS_TDLS_BUFFER_STA);
5478a7ea37SMukesh Sisodiya struct ieee80211_supported_band *sband = ieee80211_get_link_sband(link);
55b98fb44fSArik Nemtsov bool vht = sband && sband->vht_cap.vht_supported;
564df864c1SJohannes Berg u8 *pos = skb_put(skb, 10);
5795224fe8SArik Nemtsov
5895224fe8SArik Nemtsov *pos++ = WLAN_EID_EXT_CAPABILITY;
59b98fb44fSArik Nemtsov *pos++ = 8; /* len */
6095224fe8SArik Nemtsov *pos++ = 0x0;
6195224fe8SArik Nemtsov *pos++ = 0x0;
6295224fe8SArik Nemtsov *pos++ = 0x0;
63e2fb1b83SYingying Tang *pos++ = (chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0) |
64e2fb1b83SYingying Tang (buffer_sta ? WLAN_EXT_CAPA4_TDLS_BUFFER_STA : 0);
6595224fe8SArik Nemtsov *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
66b98fb44fSArik Nemtsov *pos++ = 0;
67b98fb44fSArik Nemtsov *pos++ = 0;
68b98fb44fSArik Nemtsov *pos++ = (vht && wider_band) ? WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED : 0;
6995224fe8SArik Nemtsov }
7095224fe8SArik Nemtsov
71f0d29cb9SArik Nemtsov static u8
ieee80211_tdls_add_subband(struct ieee80211_sub_if_data * sdata,struct sk_buff * skb,u16 start,u16 end,u16 spacing)72f0d29cb9SArik Nemtsov ieee80211_tdls_add_subband(struct ieee80211_sub_if_data *sdata,
73f0d29cb9SArik Nemtsov struct sk_buff *skb, u16 start, u16 end,
74f0d29cb9SArik Nemtsov u16 spacing)
75f0d29cb9SArik Nemtsov {
76f0d29cb9SArik Nemtsov u8 subband_cnt = 0, ch_cnt = 0;
77f0d29cb9SArik Nemtsov struct ieee80211_channel *ch;
78f0d29cb9SArik Nemtsov struct cfg80211_chan_def chandef;
79f0d29cb9SArik Nemtsov int i, subband_start;
80923b352fSArik Nemtsov struct wiphy *wiphy = sdata->local->hw.wiphy;
81f0d29cb9SArik Nemtsov
82f0d29cb9SArik Nemtsov for (i = start; i <= end; i += spacing) {
83f0d29cb9SArik Nemtsov if (!ch_cnt)
84f0d29cb9SArik Nemtsov subband_start = i;
85f0d29cb9SArik Nemtsov
86f0d29cb9SArik Nemtsov ch = ieee80211_get_channel(sdata->local->hw.wiphy, i);
87f0d29cb9SArik Nemtsov if (ch) {
88f0d29cb9SArik Nemtsov /* we will be active on the channel */
89f0d29cb9SArik Nemtsov cfg80211_chandef_create(&chandef, ch,
9050075892SArik Nemtsov NL80211_CHAN_NO_HT);
91923b352fSArik Nemtsov if (cfg80211_reg_can_beacon_relax(wiphy, &chandef,
9250075892SArik Nemtsov sdata->wdev.iftype)) {
93f0d29cb9SArik Nemtsov ch_cnt++;
9450075892SArik Nemtsov /*
9550075892SArik Nemtsov * check if the next channel is also part of
9650075892SArik Nemtsov * this allowed range
9750075892SArik Nemtsov */
98f0d29cb9SArik Nemtsov continue;
99f0d29cb9SArik Nemtsov }
100f0d29cb9SArik Nemtsov }
101f0d29cb9SArik Nemtsov
10250075892SArik Nemtsov /*
10350075892SArik Nemtsov * we've reached the end of a range, with allowed channels
10450075892SArik Nemtsov * found
10550075892SArik Nemtsov */
106f0d29cb9SArik Nemtsov if (ch_cnt) {
107f0d29cb9SArik Nemtsov u8 *pos = skb_put(skb, 2);
108f0d29cb9SArik Nemtsov *pos++ = ieee80211_frequency_to_channel(subband_start);
109f0d29cb9SArik Nemtsov *pos++ = ch_cnt;
110f0d29cb9SArik Nemtsov
111f0d29cb9SArik Nemtsov subband_cnt++;
112f0d29cb9SArik Nemtsov ch_cnt = 0;
113f0d29cb9SArik Nemtsov }
114f0d29cb9SArik Nemtsov }
115f0d29cb9SArik Nemtsov
11650075892SArik Nemtsov /* all channels in the requested range are allowed - add them here */
11750075892SArik Nemtsov if (ch_cnt) {
11850075892SArik Nemtsov u8 *pos = skb_put(skb, 2);
11950075892SArik Nemtsov *pos++ = ieee80211_frequency_to_channel(subband_start);
12050075892SArik Nemtsov *pos++ = ch_cnt;
12150075892SArik Nemtsov
12250075892SArik Nemtsov subband_cnt++;
12350075892SArik Nemtsov }
12450075892SArik Nemtsov
125f0d29cb9SArik Nemtsov return subband_cnt;
126f0d29cb9SArik Nemtsov }
127f0d29cb9SArik Nemtsov
128f0d29cb9SArik Nemtsov static void
ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data * sdata,struct sk_buff * skb)129f0d29cb9SArik Nemtsov ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata,
130f0d29cb9SArik Nemtsov struct sk_buff *skb)
131f0d29cb9SArik Nemtsov {
132f0d29cb9SArik Nemtsov /*
133f0d29cb9SArik Nemtsov * Add possible channels for TDLS. These are channels that are allowed
134f0d29cb9SArik Nemtsov * to be active.
135f0d29cb9SArik Nemtsov */
136f0d29cb9SArik Nemtsov u8 subband_cnt;
137f0d29cb9SArik Nemtsov u8 *pos = skb_put(skb, 2);
138f0d29cb9SArik Nemtsov
139f0d29cb9SArik Nemtsov *pos++ = WLAN_EID_SUPPORTED_CHANNELS;
140f0d29cb9SArik Nemtsov
141f0d29cb9SArik Nemtsov /*
142f0d29cb9SArik Nemtsov * 5GHz and 2GHz channels numbers can overlap. Ignore this for now, as
143f0d29cb9SArik Nemtsov * this doesn't happen in real world scenarios.
144f0d29cb9SArik Nemtsov */
145f0d29cb9SArik Nemtsov
146f0d29cb9SArik Nemtsov /* 2GHz, with 5MHz spacing */
147f0d29cb9SArik Nemtsov subband_cnt = ieee80211_tdls_add_subband(sdata, skb, 2412, 2472, 5);
148f0d29cb9SArik Nemtsov
149f0d29cb9SArik Nemtsov /* 5GHz, with 20MHz spacing */
150f0d29cb9SArik Nemtsov subband_cnt += ieee80211_tdls_add_subband(sdata, skb, 5000, 5825, 20);
151f0d29cb9SArik Nemtsov
152f0d29cb9SArik Nemtsov /* length */
153f0d29cb9SArik Nemtsov *pos = 2 * subband_cnt;
154f0d29cb9SArik Nemtsov }
155f0d29cb9SArik Nemtsov
ieee80211_tdls_add_oper_classes(struct ieee80211_link_data * link,struct sk_buff * skb)15678a7ea37SMukesh Sisodiya static void ieee80211_tdls_add_oper_classes(struct ieee80211_link_data *link,
157a38700ddSArik Nemtsov struct sk_buff *skb)
158a38700ddSArik Nemtsov {
159a38700ddSArik Nemtsov u8 *pos;
160a38700ddSArik Nemtsov u8 op_class;
161a38700ddSArik Nemtsov
16278a7ea37SMukesh Sisodiya if (!ieee80211_chandef_to_operating_class(&link->conf->chandef,
163a38700ddSArik Nemtsov &op_class))
164a38700ddSArik Nemtsov return;
165a38700ddSArik Nemtsov
166a38700ddSArik Nemtsov pos = skb_put(skb, 4);
167a38700ddSArik Nemtsov *pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES;
168a38700ddSArik Nemtsov *pos++ = 2; /* len */
169a38700ddSArik Nemtsov
170a38700ddSArik Nemtsov *pos++ = op_class;
171a38700ddSArik Nemtsov *pos++ = op_class; /* give current operating class as alternate too */
172a38700ddSArik Nemtsov }
173a38700ddSArik Nemtsov
ieee80211_tdls_add_bss_coex_ie(struct sk_buff * skb)1742cedd879SArik Nemtsov static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb)
1752cedd879SArik Nemtsov {
1764df864c1SJohannes Berg u8 *pos = skb_put(skb, 3);
1772cedd879SArik Nemtsov
1782cedd879SArik Nemtsov *pos++ = WLAN_EID_BSS_COEX_2040;
1792cedd879SArik Nemtsov *pos++ = 1; /* len */
1802cedd879SArik Nemtsov
1812cedd879SArik Nemtsov *pos++ = WLAN_BSS_COEX_INFORMATION_REQUEST;
1822cedd879SArik Nemtsov }
1832cedd879SArik Nemtsov
ieee80211_get_tdls_sta_capab(struct ieee80211_link_data * link,u16 status_code)18478a7ea37SMukesh Sisodiya static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_link_data *link,
185dd8c0b03SArik Nemtsov u16 status_code)
18695224fe8SArik Nemtsov {
18721a8e9ddSMohammed Shafi Shajakhan struct ieee80211_supported_band *sband;
18821a8e9ddSMohammed Shafi Shajakhan
189dd8c0b03SArik Nemtsov /* The capability will be 0 when sending a failure code */
190dd8c0b03SArik Nemtsov if (status_code != 0)
191dd8c0b03SArik Nemtsov return 0;
192dd8c0b03SArik Nemtsov
19378a7ea37SMukesh Sisodiya sband = ieee80211_get_link_sband(link);
19478a7ea37SMukesh Sisodiya
19521a8e9ddSMohammed Shafi Shajakhan if (sband && sband->band == NL80211_BAND_2GHZ) {
196ea1b2b45SJohannes Berg return WLAN_CAPABILITY_SHORT_SLOT_TIME |
197ea1b2b45SJohannes Berg WLAN_CAPABILITY_SHORT_PREAMBLE;
198ea1b2b45SJohannes Berg }
19995224fe8SArik Nemtsov
200ea1b2b45SJohannes Berg return 0;
20195224fe8SArik Nemtsov }
20295224fe8SArik Nemtsov
ieee80211_tdls_add_link_ie(struct ieee80211_link_data * link,struct sk_buff * skb,const u8 * peer,bool initiator)20378a7ea37SMukesh Sisodiya static void ieee80211_tdls_add_link_ie(struct ieee80211_link_data *link,
2041606ef4aSArik Nemtsov struct sk_buff *skb, const u8 *peer,
2051606ef4aSArik Nemtsov bool initiator)
20695224fe8SArik Nemtsov {
20778a7ea37SMukesh Sisodiya struct ieee80211_sub_if_data *sdata = link->sdata;
20895224fe8SArik Nemtsov struct ieee80211_tdls_lnkie *lnkid;
2091606ef4aSArik Nemtsov const u8 *init_addr, *rsp_addr;
2101606ef4aSArik Nemtsov
2111606ef4aSArik Nemtsov if (initiator) {
2121606ef4aSArik Nemtsov init_addr = sdata->vif.addr;
2131606ef4aSArik Nemtsov rsp_addr = peer;
2141606ef4aSArik Nemtsov } else {
2151606ef4aSArik Nemtsov init_addr = peer;
2161606ef4aSArik Nemtsov rsp_addr = sdata->vif.addr;
2171606ef4aSArik Nemtsov }
21895224fe8SArik Nemtsov
2194df864c1SJohannes Berg lnkid = skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
22095224fe8SArik Nemtsov
22195224fe8SArik Nemtsov lnkid->ie_type = WLAN_EID_LINK_ID;
22295224fe8SArik Nemtsov lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
22395224fe8SArik Nemtsov
22478a7ea37SMukesh Sisodiya memcpy(lnkid->bssid, link->u.mgd.bssid, ETH_ALEN);
2251606ef4aSArik Nemtsov memcpy(lnkid->init_sta, init_addr, ETH_ALEN);
2261606ef4aSArik Nemtsov memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
22795224fe8SArik Nemtsov }
22895224fe8SArik Nemtsov
229fb28ec0cSArik Nemtsov static void
ieee80211_tdls_add_aid(struct ieee80211_sub_if_data * sdata,struct sk_buff * skb)230fb28ec0cSArik Nemtsov ieee80211_tdls_add_aid(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
231fb28ec0cSArik Nemtsov {
2324df864c1SJohannes Berg u8 *pos = skb_put(skb, 4);
233fb28ec0cSArik Nemtsov
234fb28ec0cSArik Nemtsov *pos++ = WLAN_EID_AID;
235fb28ec0cSArik Nemtsov *pos++ = 2; /* len */
236f276e20bSJohannes Berg put_unaligned_le16(sdata->vif.cfg.aid, pos);
237fb28ec0cSArik Nemtsov }
238fb28ec0cSArik Nemtsov
2396f7eaa47SArik Nemtsov /* translate numbering in the WMM parameter IE to the mac80211 notation */
ieee80211_ac_from_wmm(int ac)2406f7eaa47SArik Nemtsov static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac)
2416f7eaa47SArik Nemtsov {
2426f7eaa47SArik Nemtsov switch (ac) {
2436f7eaa47SArik Nemtsov default:
2446f7eaa47SArik Nemtsov WARN_ON_ONCE(1);
245fc0561dcSGustavo A. R. Silva fallthrough;
2466f7eaa47SArik Nemtsov case 0:
2476f7eaa47SArik Nemtsov return IEEE80211_AC_BE;
2486f7eaa47SArik Nemtsov case 1:
2496f7eaa47SArik Nemtsov return IEEE80211_AC_BK;
2506f7eaa47SArik Nemtsov case 2:
2516f7eaa47SArik Nemtsov return IEEE80211_AC_VI;
2526f7eaa47SArik Nemtsov case 3:
2536f7eaa47SArik Nemtsov return IEEE80211_AC_VO;
2546f7eaa47SArik Nemtsov }
2556f7eaa47SArik Nemtsov }
2566f7eaa47SArik Nemtsov
ieee80211_wmm_aci_aifsn(int aifsn,bool acm,int aci)2576f7eaa47SArik Nemtsov static u8 ieee80211_wmm_aci_aifsn(int aifsn, bool acm, int aci)
2586f7eaa47SArik Nemtsov {
2596f7eaa47SArik Nemtsov u8 ret;
2606f7eaa47SArik Nemtsov
2616f7eaa47SArik Nemtsov ret = aifsn & 0x0f;
2626f7eaa47SArik Nemtsov if (acm)
2636f7eaa47SArik Nemtsov ret |= 0x10;
2646f7eaa47SArik Nemtsov ret |= (aci << 5) & 0x60;
2656f7eaa47SArik Nemtsov return ret;
2666f7eaa47SArik Nemtsov }
2676f7eaa47SArik Nemtsov
ieee80211_wmm_ecw(u16 cw_min,u16 cw_max)2686f7eaa47SArik Nemtsov static u8 ieee80211_wmm_ecw(u16 cw_min, u16 cw_max)
2696f7eaa47SArik Nemtsov {
2706f7eaa47SArik Nemtsov return ((ilog2(cw_min + 1) << 0x0) & 0x0f) |
2716f7eaa47SArik Nemtsov ((ilog2(cw_max + 1) << 0x4) & 0xf0);
2726f7eaa47SArik Nemtsov }
2736f7eaa47SArik Nemtsov
ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data * sdata,struct sk_buff * skb)2746f7eaa47SArik Nemtsov static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
2756f7eaa47SArik Nemtsov struct sk_buff *skb)
2766f7eaa47SArik Nemtsov {
2776f7eaa47SArik Nemtsov struct ieee80211_wmm_param_ie *wmm;
2786f7eaa47SArik Nemtsov struct ieee80211_tx_queue_params *txq;
2796f7eaa47SArik Nemtsov int i;
2806f7eaa47SArik Nemtsov
281b080db58SJohannes Berg wmm = skb_put_zero(skb, sizeof(*wmm));
2826f7eaa47SArik Nemtsov
2836f7eaa47SArik Nemtsov wmm->element_id = WLAN_EID_VENDOR_SPECIFIC;
2846f7eaa47SArik Nemtsov wmm->len = sizeof(*wmm) - 2;
2856f7eaa47SArik Nemtsov
2866f7eaa47SArik Nemtsov wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */
2876f7eaa47SArik Nemtsov wmm->oui[1] = 0x50;
2886f7eaa47SArik Nemtsov wmm->oui[2] = 0xf2;
2896f7eaa47SArik Nemtsov wmm->oui_type = 2; /* WME */
2906f7eaa47SArik Nemtsov wmm->oui_subtype = 1; /* WME param */
2916f7eaa47SArik Nemtsov wmm->version = 1; /* WME ver */
2926f7eaa47SArik Nemtsov wmm->qos_info = 0; /* U-APSD not in use */
2936f7eaa47SArik Nemtsov
2946f7eaa47SArik Nemtsov /*
2956f7eaa47SArik Nemtsov * Use the EDCA parameters defined for the BSS, or default if the AP
2966f7eaa47SArik Nemtsov * doesn't support it, as mandated by 802.11-2012 section 10.22.4
2976f7eaa47SArik Nemtsov */
2986f7eaa47SArik Nemtsov for (i = 0; i < IEEE80211_NUM_ACS; i++) {
299b3e2130bSJohannes Berg txq = &sdata->deflink.tx_conf[ieee80211_ac_from_wmm(i)];
3006f7eaa47SArik Nemtsov wmm->ac[i].aci_aifsn = ieee80211_wmm_aci_aifsn(txq->aifs,
3016f7eaa47SArik Nemtsov txq->acm, i);
3026f7eaa47SArik Nemtsov wmm->ac[i].cw = ieee80211_wmm_ecw(txq->cw_min, txq->cw_max);
3036f7eaa47SArik Nemtsov wmm->ac[i].txop_limit = cpu_to_le16(txq->txop);
3046f7eaa47SArik Nemtsov }
3056f7eaa47SArik Nemtsov }
3066f7eaa47SArik Nemtsov
307f09a87d2SArik Nemtsov static void
ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data * sdata,struct sta_info * sta)3080fabfaafSArik Nemtsov ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata,
3090fabfaafSArik Nemtsov struct sta_info *sta)
3100fabfaafSArik Nemtsov {
3110fabfaafSArik Nemtsov /* IEEE802.11ac-2013 Table E-4 */
3120fabfaafSArik Nemtsov u16 centers_80mhz[] = { 5210, 5290, 5530, 5610, 5690, 5775 };
3130fabfaafSArik Nemtsov struct cfg80211_chan_def uc = sta->tdls_chandef;
314c71420dbSJohannes Berg enum nl80211_chan_width max_width =
315c71420dbSJohannes Berg ieee80211_sta_cap_chan_bw(&sta->deflink);
3160fabfaafSArik Nemtsov int i;
3170fabfaafSArik Nemtsov
3180fabfaafSArik Nemtsov /* only support upgrading non-narrow channels up to 80Mhz */
3190fabfaafSArik Nemtsov if (max_width == NL80211_CHAN_WIDTH_5 ||
3200fabfaafSArik Nemtsov max_width == NL80211_CHAN_WIDTH_10)
3210fabfaafSArik Nemtsov return;
3220fabfaafSArik Nemtsov
3230fabfaafSArik Nemtsov if (max_width > NL80211_CHAN_WIDTH_80)
3240fabfaafSArik Nemtsov max_width = NL80211_CHAN_WIDTH_80;
3250fabfaafSArik Nemtsov
3264b559ec0SIlan Peer if (uc.width >= max_width)
3270fabfaafSArik Nemtsov return;
3280fabfaafSArik Nemtsov /*
3290fabfaafSArik Nemtsov * Channel usage constrains in the IEEE802.11ac-2013 specification only
3300fabfaafSArik Nemtsov * allow expanding a 20MHz channel to 80MHz in a single way. In
3310fabfaafSArik Nemtsov * addition, there are no 40MHz allowed channels that are not part of
3320fabfaafSArik Nemtsov * the allowed 80MHz range in the 5GHz spectrum (the relevant one here).
3330fabfaafSArik Nemtsov */
3340fabfaafSArik Nemtsov for (i = 0; i < ARRAY_SIZE(centers_80mhz); i++)
3350fabfaafSArik Nemtsov if (abs(uc.chan->center_freq - centers_80mhz[i]) <= 30) {
3360fabfaafSArik Nemtsov uc.center_freq1 = centers_80mhz[i];
3374b559ec0SIlan Peer uc.center_freq2 = 0;
3380fabfaafSArik Nemtsov uc.width = NL80211_CHAN_WIDTH_80;
3390fabfaafSArik Nemtsov break;
3400fabfaafSArik Nemtsov }
3410fabfaafSArik Nemtsov
3420fabfaafSArik Nemtsov if (!uc.center_freq1)
3430fabfaafSArik Nemtsov return;
3440fabfaafSArik Nemtsov
345554d072eSArik Nemtsov /* proceed to downgrade the chandef until usable or the same as AP BW */
346db8d9977SArik Nemtsov while (uc.width > max_width ||
347554d072eSArik Nemtsov (uc.width > sta->tdls_chandef.width &&
348dd55ab59SArik Nemtsov !cfg80211_reg_can_beacon_relax(sdata->local->hw.wiphy, &uc,
349554d072eSArik Nemtsov sdata->wdev.iftype)))
3500fabfaafSArik Nemtsov ieee80211_chandef_downgrade(&uc);
3510fabfaafSArik Nemtsov
3520fabfaafSArik Nemtsov if (!cfg80211_chandef_identical(&uc, &sta->tdls_chandef)) {
3530fabfaafSArik Nemtsov tdls_dbg(sdata, "TDLS ch width upgraded %d -> %d\n",
3540fabfaafSArik Nemtsov sta->tdls_chandef.width, uc.width);
3550fabfaafSArik Nemtsov
3560fabfaafSArik Nemtsov /*
3570fabfaafSArik Nemtsov * the station is not yet authorized when BW upgrade is done,
3580fabfaafSArik Nemtsov * locking is not required
3590fabfaafSArik Nemtsov */
3600fabfaafSArik Nemtsov sta->tdls_chandef = uc;
3610fabfaafSArik Nemtsov }
3620fabfaafSArik Nemtsov }
3630fabfaafSArik Nemtsov
3640fabfaafSArik Nemtsov static void
ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data * link,struct sk_buff * skb,const u8 * peer,u8 action_code,bool initiator,const u8 * extra_ies,size_t extra_ies_len)36578a7ea37SMukesh Sisodiya ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link,
366f09a87d2SArik Nemtsov struct sk_buff *skb, const u8 *peer,
3671606ef4aSArik Nemtsov u8 action_code, bool initiator,
3681606ef4aSArik Nemtsov const u8 *extra_ies, size_t extra_ies_len)
369f09a87d2SArik Nemtsov {
37078a7ea37SMukesh Sisodiya struct ieee80211_sub_if_data *sdata = link->sdata;
37113cc8a4aSArik Nemtsov struct ieee80211_supported_band *sband;
37221a8e9ddSMohammed Shafi Shajakhan struct ieee80211_local *local = sdata->local;
37313cc8a4aSArik Nemtsov struct ieee80211_sta_ht_cap ht_cap;
374fb28ec0cSArik Nemtsov struct ieee80211_sta_vht_cap vht_cap;
37571b3b7acSAbhishek Naik const struct ieee80211_sta_he_cap *he_cap;
37671b3b7acSAbhishek Naik const struct ieee80211_sta_eht_cap *eht_cap;
37713cc8a4aSArik Nemtsov struct sta_info *sta = NULL;
378f09a87d2SArik Nemtsov size_t offset = 0, noffset;
379f09a87d2SArik Nemtsov u8 *pos;
380f09a87d2SArik Nemtsov
38178a7ea37SMukesh Sisodiya sband = ieee80211_get_link_sband(link);
38278a7ea37SMukesh Sisodiya if (WARN_ON_ONCE(!sband))
38321a8e9ddSMohammed Shafi Shajakhan return;
38421a8e9ddSMohammed Shafi Shajakhan
38521a8e9ddSMohammed Shafi Shajakhan ieee80211_add_srates_ie(sdata, skb, false, sband->band);
38621a8e9ddSMohammed Shafi Shajakhan ieee80211_add_ext_srates_ie(sdata, skb, false, sband->band);
387f0d29cb9SArik Nemtsov ieee80211_tdls_add_supp_channels(sdata, skb);
388f09a87d2SArik Nemtsov
389f09a87d2SArik Nemtsov /* add any custom IEs that go before Extended Capabilities */
390f09a87d2SArik Nemtsov if (extra_ies_len) {
391f09a87d2SArik Nemtsov static const u8 before_ext_cap[] = {
392f09a87d2SArik Nemtsov WLAN_EID_SUPP_RATES,
393f09a87d2SArik Nemtsov WLAN_EID_COUNTRY,
394f09a87d2SArik Nemtsov WLAN_EID_EXT_SUPP_RATES,
395f09a87d2SArik Nemtsov WLAN_EID_SUPPORTED_CHANNELS,
396f09a87d2SArik Nemtsov WLAN_EID_RSN,
397f09a87d2SArik Nemtsov };
398f09a87d2SArik Nemtsov noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
399f09a87d2SArik Nemtsov before_ext_cap,
400f09a87d2SArik Nemtsov ARRAY_SIZE(before_ext_cap),
401f09a87d2SArik Nemtsov offset);
402b952f4dfSyuan linyu skb_put_data(skb, extra_ies + offset, noffset - offset);
403f09a87d2SArik Nemtsov offset = noffset;
404f09a87d2SArik Nemtsov }
405f09a87d2SArik Nemtsov
40678a7ea37SMukesh Sisodiya ieee80211_tdls_add_ext_capab(link, skb);
407f09a87d2SArik Nemtsov
40840b861a0SArik Nemtsov /* add the QoS element if we support it */
40940b861a0SArik Nemtsov if (local->hw.queues >= IEEE80211_NUM_ACS &&
41040b861a0SArik Nemtsov action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES)
41140b861a0SArik Nemtsov ieee80211_add_wmm_info_ie(skb_put(skb, 9), 0); /* no U-APSD */
41240b861a0SArik Nemtsov
413f09a87d2SArik Nemtsov /* add any custom IEs that go before HT capabilities */
414f09a87d2SArik Nemtsov if (extra_ies_len) {
415f09a87d2SArik Nemtsov static const u8 before_ht_cap[] = {
416f09a87d2SArik Nemtsov WLAN_EID_SUPP_RATES,
417f09a87d2SArik Nemtsov WLAN_EID_COUNTRY,
418f09a87d2SArik Nemtsov WLAN_EID_EXT_SUPP_RATES,
419f09a87d2SArik Nemtsov WLAN_EID_SUPPORTED_CHANNELS,
420f09a87d2SArik Nemtsov WLAN_EID_RSN,
421f09a87d2SArik Nemtsov WLAN_EID_EXT_CAPABILITY,
422f09a87d2SArik Nemtsov WLAN_EID_QOS_CAPA,
423f09a87d2SArik Nemtsov WLAN_EID_FAST_BSS_TRANSITION,
424f09a87d2SArik Nemtsov WLAN_EID_TIMEOUT_INTERVAL,
425f09a87d2SArik Nemtsov WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
426f09a87d2SArik Nemtsov };
427f09a87d2SArik Nemtsov noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
428f09a87d2SArik Nemtsov before_ht_cap,
429f09a87d2SArik Nemtsov ARRAY_SIZE(before_ht_cap),
430f09a87d2SArik Nemtsov offset);
431b952f4dfSyuan linyu skb_put_data(skb, extra_ies + offset, noffset - offset);
432f09a87d2SArik Nemtsov offset = noffset;
433f09a87d2SArik Nemtsov }
434f09a87d2SArik Nemtsov
435ae2e9fbaSArik Nemtsov /* we should have the peer STA if we're already responding */
436ae2e9fbaSArik Nemtsov if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
437ae2e9fbaSArik Nemtsov sta = sta_info_get(sdata, peer);
43878a7ea37SMukesh Sisodiya if (WARN_ON_ONCE(!sta))
439ae2e9fbaSArik Nemtsov return;
44078a7ea37SMukesh Sisodiya
44178a7ea37SMukesh Sisodiya sta->tdls_chandef = link->conf->chandef;
442ae2e9fbaSArik Nemtsov }
4430fabfaafSArik Nemtsov
44478a7ea37SMukesh Sisodiya ieee80211_tdls_add_oper_classes(link, skb);
445a38700ddSArik Nemtsov
44613cc8a4aSArik Nemtsov /*
44713cc8a4aSArik Nemtsov * with TDLS we can switch channels, and HT-caps are not necessarily
44813cc8a4aSArik Nemtsov * the same on all bands. The specification limits the setup to a
44913cc8a4aSArik Nemtsov * single HT-cap, so use the current band for now.
45013cc8a4aSArik Nemtsov */
45113cc8a4aSArik Nemtsov memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
452c5309ba7SJohannes Berg
453070e176aSArik Nemtsov if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
454070e176aSArik Nemtsov action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) &&
455070e176aSArik Nemtsov ht_cap.ht_supported) {
45613cc8a4aSArik Nemtsov ieee80211_apply_htcap_overrides(sdata, &ht_cap);
45713cc8a4aSArik Nemtsov
45813cc8a4aSArik Nemtsov /* disable SMPS in TDLS initiator */
459c5309ba7SJohannes Berg ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED
460c5309ba7SJohannes Berg << IEEE80211_HT_CAP_SM_PS_SHIFT;
461c5309ba7SJohannes Berg
462c5309ba7SJohannes Berg pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
463c5309ba7SJohannes Berg ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
464c5309ba7SJohannes Berg } else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
465046d2e7cSSriram R ht_cap.ht_supported && sta->sta.deflink.ht_cap.ht_supported) {
46613cc8a4aSArik Nemtsov /* the peer caps are already intersected with our own */
467046d2e7cSSriram R memcpy(&ht_cap, &sta->sta.deflink.ht_cap, sizeof(ht_cap));
46813cc8a4aSArik Nemtsov
46913cc8a4aSArik Nemtsov pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
47013cc8a4aSArik Nemtsov ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
47113cc8a4aSArik Nemtsov }
47213cc8a4aSArik Nemtsov
4732cedd879SArik Nemtsov if (ht_cap.ht_supported &&
4742cedd879SArik Nemtsov (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
4752cedd879SArik Nemtsov ieee80211_tdls_add_bss_coex_ie(skb);
4762cedd879SArik Nemtsov
47778a7ea37SMukesh Sisodiya ieee80211_tdls_add_link_ie(link, skb, peer, initiator);
478fb28ec0cSArik Nemtsov
479fb28ec0cSArik Nemtsov /* add any custom IEs that go before VHT capabilities */
480fb28ec0cSArik Nemtsov if (extra_ies_len) {
481fb28ec0cSArik Nemtsov static const u8 before_vht_cap[] = {
482fb28ec0cSArik Nemtsov WLAN_EID_SUPP_RATES,
483fb28ec0cSArik Nemtsov WLAN_EID_COUNTRY,
484fb28ec0cSArik Nemtsov WLAN_EID_EXT_SUPP_RATES,
485fb28ec0cSArik Nemtsov WLAN_EID_SUPPORTED_CHANNELS,
486fb28ec0cSArik Nemtsov WLAN_EID_RSN,
487fb28ec0cSArik Nemtsov WLAN_EID_EXT_CAPABILITY,
488fb28ec0cSArik Nemtsov WLAN_EID_QOS_CAPA,
489fb28ec0cSArik Nemtsov WLAN_EID_FAST_BSS_TRANSITION,
490fb28ec0cSArik Nemtsov WLAN_EID_TIMEOUT_INTERVAL,
491fb28ec0cSArik Nemtsov WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
492fb28ec0cSArik Nemtsov WLAN_EID_MULTI_BAND,
493fb28ec0cSArik Nemtsov };
494fb28ec0cSArik Nemtsov noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
495fb28ec0cSArik Nemtsov before_vht_cap,
496fb28ec0cSArik Nemtsov ARRAY_SIZE(before_vht_cap),
497fb28ec0cSArik Nemtsov offset);
498b952f4dfSyuan linyu skb_put_data(skb, extra_ies + offset, noffset - offset);
499fb28ec0cSArik Nemtsov offset = noffset;
500fb28ec0cSArik Nemtsov }
501fb28ec0cSArik Nemtsov
502*05995d05SMukesh Sisodiya /* add AID if VHT, HE or EHT capabilities supported */
503fb28ec0cSArik Nemtsov memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
504*05995d05SMukesh Sisodiya he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
505*05995d05SMukesh Sisodiya eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif);
506*05995d05SMukesh Sisodiya if ((vht_cap.vht_supported || he_cap || eht_cap) &&
507*05995d05SMukesh Sisodiya (action_code == WLAN_TDLS_SETUP_REQUEST ||
508*05995d05SMukesh Sisodiya action_code == WLAN_TDLS_SETUP_RESPONSE))
509*05995d05SMukesh Sisodiya ieee80211_tdls_add_aid(sdata, skb);
510*05995d05SMukesh Sisodiya
511*05995d05SMukesh Sisodiya /* build the VHT-cap similarly to the HT-cap */
512070e176aSArik Nemtsov if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
513070e176aSArik Nemtsov action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) &&
514070e176aSArik Nemtsov vht_cap.vht_supported) {
515fb28ec0cSArik Nemtsov ieee80211_apply_vhtcap_overrides(sdata, &vht_cap);
516fb28ec0cSArik Nemtsov
517fb28ec0cSArik Nemtsov pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
518fb28ec0cSArik Nemtsov ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
519fb28ec0cSArik Nemtsov } else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
520046d2e7cSSriram R vht_cap.vht_supported && sta->sta.deflink.vht_cap.vht_supported) {
521fb28ec0cSArik Nemtsov /* the peer caps are already intersected with our own */
522046d2e7cSSriram R memcpy(&vht_cap, &sta->sta.deflink.vht_cap, sizeof(vht_cap));
523fb28ec0cSArik Nemtsov
524fb28ec0cSArik Nemtsov pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
525fb28ec0cSArik Nemtsov ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
5260fabfaafSArik Nemtsov
5270fabfaafSArik Nemtsov /*
5280fabfaafSArik Nemtsov * if both peers support WIDER_BW, we can expand the chandef to
5290fabfaafSArik Nemtsov * a wider compatible one, up to 80MHz
5300fabfaafSArik Nemtsov */
5310fabfaafSArik Nemtsov if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
5320fabfaafSArik Nemtsov ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
533fb28ec0cSArik Nemtsov }
534fb28ec0cSArik Nemtsov
53571b3b7acSAbhishek Naik /* add any custom IEs that go before HE capabilities */
53671b3b7acSAbhishek Naik if (extra_ies_len) {
53771b3b7acSAbhishek Naik static const u8 before_he_cap[] = {
53871b3b7acSAbhishek Naik WLAN_EID_EXTENSION,
53971b3b7acSAbhishek Naik WLAN_EID_EXT_FILS_REQ_PARAMS,
54071b3b7acSAbhishek Naik WLAN_EID_AP_CSN,
54171b3b7acSAbhishek Naik };
54271b3b7acSAbhishek Naik noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
54371b3b7acSAbhishek Naik before_he_cap,
54471b3b7acSAbhishek Naik ARRAY_SIZE(before_he_cap),
54571b3b7acSAbhishek Naik offset);
54671b3b7acSAbhishek Naik skb_put_data(skb, extra_ies + offset, noffset - offset);
54771b3b7acSAbhishek Naik offset = noffset;
54871b3b7acSAbhishek Naik }
54971b3b7acSAbhishek Naik
55071b3b7acSAbhishek Naik /* build the HE-cap from sband */
55171b3b7acSAbhishek Naik if (he_cap &&
55271b3b7acSAbhishek Naik (action_code == WLAN_TDLS_SETUP_REQUEST ||
55371b3b7acSAbhishek Naik action_code == WLAN_TDLS_SETUP_RESPONSE ||
55471b3b7acSAbhishek Naik action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES)) {
55571b3b7acSAbhishek Naik __le16 he_6ghz_capa;
55671b3b7acSAbhishek Naik u8 cap_size;
55771b3b7acSAbhishek Naik
55871b3b7acSAbhishek Naik cap_size =
55971b3b7acSAbhishek Naik 2 + 1 + sizeof(he_cap->he_cap_elem) +
56071b3b7acSAbhishek Naik ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem) +
56171b3b7acSAbhishek Naik ieee80211_he_ppe_size(he_cap->ppe_thres[0],
56271b3b7acSAbhishek Naik he_cap->he_cap_elem.phy_cap_info);
56371b3b7acSAbhishek Naik pos = skb_put(skb, cap_size);
56471b3b7acSAbhishek Naik pos = ieee80211_ie_build_he_cap(0, pos, he_cap, pos + cap_size);
56571b3b7acSAbhishek Naik
56671b3b7acSAbhishek Naik /* Build HE 6Ghz capa IE from sband */
56771b3b7acSAbhishek Naik if (sband->band == NL80211_BAND_6GHZ) {
56871b3b7acSAbhishek Naik cap_size = 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa);
56971b3b7acSAbhishek Naik pos = skb_put(skb, cap_size);
57071b3b7acSAbhishek Naik he_6ghz_capa =
57171b3b7acSAbhishek Naik ieee80211_get_he_6ghz_capa_vif(sband, &sdata->vif);
57271b3b7acSAbhishek Naik pos = ieee80211_write_he_6ghz_cap(pos, he_6ghz_capa,
57371b3b7acSAbhishek Naik pos + cap_size);
57471b3b7acSAbhishek Naik }
57571b3b7acSAbhishek Naik }
57671b3b7acSAbhishek Naik
57771b3b7acSAbhishek Naik /* add any custom IEs that go before EHT capabilities */
57871b3b7acSAbhishek Naik if (extra_ies_len) {
57971b3b7acSAbhishek Naik static const u8 before_he_cap[] = {
58071b3b7acSAbhishek Naik WLAN_EID_EXTENSION,
58171b3b7acSAbhishek Naik WLAN_EID_EXT_FILS_REQ_PARAMS,
58271b3b7acSAbhishek Naik WLAN_EID_AP_CSN,
58371b3b7acSAbhishek Naik };
58471b3b7acSAbhishek Naik
58571b3b7acSAbhishek Naik noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
58671b3b7acSAbhishek Naik before_he_cap,
58771b3b7acSAbhishek Naik ARRAY_SIZE(before_he_cap),
58871b3b7acSAbhishek Naik offset);
58971b3b7acSAbhishek Naik skb_put_data(skb, extra_ies + offset, noffset - offset);
59071b3b7acSAbhishek Naik offset = noffset;
59171b3b7acSAbhishek Naik }
59271b3b7acSAbhishek Naik
59371b3b7acSAbhishek Naik /* build the EHT-cap from sband */
59471b3b7acSAbhishek Naik if (he_cap && eht_cap &&
59571b3b7acSAbhishek Naik (action_code == WLAN_TDLS_SETUP_REQUEST ||
59671b3b7acSAbhishek Naik action_code == WLAN_TDLS_SETUP_RESPONSE ||
59771b3b7acSAbhishek Naik action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES)) {
59871b3b7acSAbhishek Naik u8 cap_size;
59971b3b7acSAbhishek Naik
60071b3b7acSAbhishek Naik cap_size =
60171b3b7acSAbhishek Naik 2 + 1 + sizeof(eht_cap->eht_cap_elem) +
60271b3b7acSAbhishek Naik ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
60371b3b7acSAbhishek Naik &eht_cap->eht_cap_elem, false) +
60471b3b7acSAbhishek Naik ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0],
60571b3b7acSAbhishek Naik eht_cap->eht_cap_elem.phy_cap_info);
60671b3b7acSAbhishek Naik pos = skb_put(skb, cap_size);
60771b3b7acSAbhishek Naik ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + cap_size, false);
60871b3b7acSAbhishek Naik }
60971b3b7acSAbhishek Naik
610f09a87d2SArik Nemtsov /* add any remaining IEs */
611f09a87d2SArik Nemtsov if (extra_ies_len) {
612f09a87d2SArik Nemtsov noffset = extra_ies_len;
613b952f4dfSyuan linyu skb_put_data(skb, extra_ies + offset, noffset - offset);
614f09a87d2SArik Nemtsov }
6151606ef4aSArik Nemtsov
616f09a87d2SArik Nemtsov }
617f09a87d2SArik Nemtsov
6186f7eaa47SArik Nemtsov static void
ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_link_data * link,struct sk_buff * skb,const u8 * peer,bool initiator,const u8 * extra_ies,size_t extra_ies_len)61978a7ea37SMukesh Sisodiya ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_link_data *link,
6206f7eaa47SArik Nemtsov struct sk_buff *skb, const u8 *peer,
6216f7eaa47SArik Nemtsov bool initiator, const u8 *extra_ies,
6226f7eaa47SArik Nemtsov size_t extra_ies_len)
6236f7eaa47SArik Nemtsov {
62478a7ea37SMukesh Sisodiya struct ieee80211_sub_if_data *sdata = link->sdata;
6256f7eaa47SArik Nemtsov struct ieee80211_local *local = sdata->local;
6266f7eaa47SArik Nemtsov size_t offset = 0, noffset;
62713cc8a4aSArik Nemtsov struct sta_info *sta, *ap_sta;
62821a8e9ddSMohammed Shafi Shajakhan struct ieee80211_supported_band *sband;
6296f7eaa47SArik Nemtsov u8 *pos;
6306f7eaa47SArik Nemtsov
63178a7ea37SMukesh Sisodiya sband = ieee80211_get_link_sband(link);
63278a7ea37SMukesh Sisodiya if (WARN_ON_ONCE(!sband))
63321a8e9ddSMohammed Shafi Shajakhan return;
63421a8e9ddSMohammed Shafi Shajakhan
6356f7eaa47SArik Nemtsov sta = sta_info_get(sdata, peer);
63678a7ea37SMukesh Sisodiya ap_sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr);
6376f7eaa47SArik Nemtsov
63878a7ea37SMukesh Sisodiya if (WARN_ON_ONCE(!sta || !ap_sta))
63978a7ea37SMukesh Sisodiya return;
64078a7ea37SMukesh Sisodiya
64178a7ea37SMukesh Sisodiya sta->tdls_chandef = link->conf->chandef;
6420fabfaafSArik Nemtsov
6436f7eaa47SArik Nemtsov /* add any custom IEs that go before the QoS IE */
6446f7eaa47SArik Nemtsov if (extra_ies_len) {
6456f7eaa47SArik Nemtsov static const u8 before_qos[] = {
6466f7eaa47SArik Nemtsov WLAN_EID_RSN,
6476f7eaa47SArik Nemtsov };
6486f7eaa47SArik Nemtsov noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
6496f7eaa47SArik Nemtsov before_qos,
6506f7eaa47SArik Nemtsov ARRAY_SIZE(before_qos),
6516f7eaa47SArik Nemtsov offset);
652b952f4dfSyuan linyu skb_put_data(skb, extra_ies + offset, noffset - offset);
6536f7eaa47SArik Nemtsov offset = noffset;
6546f7eaa47SArik Nemtsov }
6556f7eaa47SArik Nemtsov
6566f7eaa47SArik Nemtsov /* add the QoS param IE if both the peer and we support it */
657a74a8c84SJohannes Berg if (local->hw.queues >= IEEE80211_NUM_ACS && sta->sta.wme)
6586f7eaa47SArik Nemtsov ieee80211_tdls_add_wmm_param_ie(sdata, skb);
6596f7eaa47SArik Nemtsov
66013cc8a4aSArik Nemtsov /* add any custom IEs that go before HT operation */
66113cc8a4aSArik Nemtsov if (extra_ies_len) {
66213cc8a4aSArik Nemtsov static const u8 before_ht_op[] = {
66313cc8a4aSArik Nemtsov WLAN_EID_RSN,
66413cc8a4aSArik Nemtsov WLAN_EID_QOS_CAPA,
66513cc8a4aSArik Nemtsov WLAN_EID_FAST_BSS_TRANSITION,
66613cc8a4aSArik Nemtsov WLAN_EID_TIMEOUT_INTERVAL,
66713cc8a4aSArik Nemtsov };
66813cc8a4aSArik Nemtsov noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
66913cc8a4aSArik Nemtsov before_ht_op,
67013cc8a4aSArik Nemtsov ARRAY_SIZE(before_ht_op),
67113cc8a4aSArik Nemtsov offset);
672b952f4dfSyuan linyu skb_put_data(skb, extra_ies + offset, noffset - offset);
67313cc8a4aSArik Nemtsov offset = noffset;
67413cc8a4aSArik Nemtsov }
67513cc8a4aSArik Nemtsov
67657f255f5SArik Nemtsov /*
67757f255f5SArik Nemtsov * if HT support is only added in TDLS, we need an HT-operation IE.
67857f255f5SArik Nemtsov * add the IE as required by IEEE802.11-2012 9.23.3.2.
67957f255f5SArik Nemtsov */
680046d2e7cSSriram R if (!ap_sta->sta.deflink.ht_cap.ht_supported && sta->sta.deflink.ht_cap.ht_supported) {
68157f255f5SArik Nemtsov u16 prot = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED |
68257f255f5SArik Nemtsov IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT |
68357f255f5SArik Nemtsov IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT;
68457f255f5SArik Nemtsov
685890b7878SArik Nemtsov pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
686046d2e7cSSriram R ieee80211_ie_build_ht_oper(pos, &sta->sta.deflink.ht_cap,
68778a7ea37SMukesh Sisodiya &link->conf->chandef, prot,
68857f255f5SArik Nemtsov true);
68913cc8a4aSArik Nemtsov }
69013cc8a4aSArik Nemtsov
69178a7ea37SMukesh Sisodiya ieee80211_tdls_add_link_ie(link, skb, peer, initiator);
692fb28ec0cSArik Nemtsov
693fb28ec0cSArik Nemtsov /* only include VHT-operation if not on the 2.4GHz band */
69421a8e9ddSMohammed Shafi Shajakhan if (sband->band != NL80211_BAND_2GHZ &&
695046d2e7cSSriram R sta->sta.deflink.vht_cap.vht_supported) {
6960fabfaafSArik Nemtsov /*
6970fabfaafSArik Nemtsov * if both peers support WIDER_BW, we can expand the chandef to
6980fabfaafSArik Nemtsov * a wider compatible one, up to 80MHz
6990fabfaafSArik Nemtsov */
7000fabfaafSArik Nemtsov if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
7010fabfaafSArik Nemtsov ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
7020fabfaafSArik Nemtsov
703890b7878SArik Nemtsov pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation));
704046d2e7cSSriram R ieee80211_ie_build_vht_oper(pos, &sta->sta.deflink.vht_cap,
7050fabfaafSArik Nemtsov &sta->tdls_chandef);
706fb28ec0cSArik Nemtsov }
707fb28ec0cSArik Nemtsov
7086f7eaa47SArik Nemtsov /* add any remaining IEs */
7096f7eaa47SArik Nemtsov if (extra_ies_len) {
7106f7eaa47SArik Nemtsov noffset = extra_ies_len;
711b952f4dfSyuan linyu skb_put_data(skb, extra_ies + offset, noffset - offset);
7126f7eaa47SArik Nemtsov }
7136f7eaa47SArik Nemtsov }
7146f7eaa47SArik Nemtsov
715a7a6bdd0SArik Nemtsov static void
ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_link_data * link,struct sk_buff * skb,const u8 * peer,bool initiator,const u8 * extra_ies,size_t extra_ies_len,u8 oper_class,struct cfg80211_chan_def * chandef)71678a7ea37SMukesh Sisodiya ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_link_data *link,
717a7a6bdd0SArik Nemtsov struct sk_buff *skb, const u8 *peer,
718a7a6bdd0SArik Nemtsov bool initiator, const u8 *extra_ies,
719a7a6bdd0SArik Nemtsov size_t extra_ies_len, u8 oper_class,
720a7a6bdd0SArik Nemtsov struct cfg80211_chan_def *chandef)
721a7a6bdd0SArik Nemtsov {
722a7a6bdd0SArik Nemtsov struct ieee80211_tdls_data *tf;
723a7a6bdd0SArik Nemtsov size_t offset = 0, noffset;
724a7a6bdd0SArik Nemtsov
725a7a6bdd0SArik Nemtsov if (WARN_ON_ONCE(!chandef))
726a7a6bdd0SArik Nemtsov return;
727a7a6bdd0SArik Nemtsov
728a7a6bdd0SArik Nemtsov tf = (void *)skb->data;
729a7a6bdd0SArik Nemtsov tf->u.chan_switch_req.target_channel =
730a7a6bdd0SArik Nemtsov ieee80211_frequency_to_channel(chandef->chan->center_freq);
731a7a6bdd0SArik Nemtsov tf->u.chan_switch_req.oper_class = oper_class;
732a7a6bdd0SArik Nemtsov
733a7a6bdd0SArik Nemtsov if (extra_ies_len) {
734a7a6bdd0SArik Nemtsov static const u8 before_lnkie[] = {
735a7a6bdd0SArik Nemtsov WLAN_EID_SECONDARY_CHANNEL_OFFSET,
736a7a6bdd0SArik Nemtsov };
737a7a6bdd0SArik Nemtsov noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
738a7a6bdd0SArik Nemtsov before_lnkie,
739a7a6bdd0SArik Nemtsov ARRAY_SIZE(before_lnkie),
740a7a6bdd0SArik Nemtsov offset);
741b952f4dfSyuan linyu skb_put_data(skb, extra_ies + offset, noffset - offset);
742a7a6bdd0SArik Nemtsov offset = noffset;
743a7a6bdd0SArik Nemtsov }
744a7a6bdd0SArik Nemtsov
74578a7ea37SMukesh Sisodiya ieee80211_tdls_add_link_ie(link, skb, peer, initiator);
746a7a6bdd0SArik Nemtsov
747a7a6bdd0SArik Nemtsov /* add any remaining IEs */
748a7a6bdd0SArik Nemtsov if (extra_ies_len) {
749a7a6bdd0SArik Nemtsov noffset = extra_ies_len;
750b952f4dfSyuan linyu skb_put_data(skb, extra_ies + offset, noffset - offset);
751a7a6bdd0SArik Nemtsov }
752a7a6bdd0SArik Nemtsov }
753a7a6bdd0SArik Nemtsov
7548a4d32f3SArik Nemtsov static void
ieee80211_tdls_add_chan_switch_resp_ies(struct ieee80211_link_data * link,struct sk_buff * skb,const u8 * peer,u16 status_code,bool initiator,const u8 * extra_ies,size_t extra_ies_len)75578a7ea37SMukesh Sisodiya ieee80211_tdls_add_chan_switch_resp_ies(struct ieee80211_link_data *link,
7568a4d32f3SArik Nemtsov struct sk_buff *skb, const u8 *peer,
7578a4d32f3SArik Nemtsov u16 status_code, bool initiator,
7588a4d32f3SArik Nemtsov const u8 *extra_ies,
7598a4d32f3SArik Nemtsov size_t extra_ies_len)
7608a4d32f3SArik Nemtsov {
7618a4d32f3SArik Nemtsov if (status_code == 0)
76278a7ea37SMukesh Sisodiya ieee80211_tdls_add_link_ie(link, skb, peer, initiator);
7638a4d32f3SArik Nemtsov
7648a4d32f3SArik Nemtsov if (extra_ies_len)
76559ae1d12SJohannes Berg skb_put_data(skb, extra_ies, extra_ies_len);
7668a4d32f3SArik Nemtsov }
7678a4d32f3SArik Nemtsov
ieee80211_tdls_add_ies(struct ieee80211_link_data * link,struct sk_buff * skb,const u8 * peer,u8 action_code,u16 status_code,bool initiator,const u8 * extra_ies,size_t extra_ies_len,u8 oper_class,struct cfg80211_chan_def * chandef)76878a7ea37SMukesh Sisodiya static void ieee80211_tdls_add_ies(struct ieee80211_link_data *link,
76946792a2dSArik Nemtsov struct sk_buff *skb, const u8 *peer,
7701606ef4aSArik Nemtsov u8 action_code, u16 status_code,
7711606ef4aSArik Nemtsov bool initiator, const u8 *extra_ies,
772c2733905SArik Nemtsov size_t extra_ies_len, u8 oper_class,
773c2733905SArik Nemtsov struct cfg80211_chan_def *chandef)
77446792a2dSArik Nemtsov {
77546792a2dSArik Nemtsov switch (action_code) {
77646792a2dSArik Nemtsov case WLAN_TDLS_SETUP_REQUEST:
77746792a2dSArik Nemtsov case WLAN_TDLS_SETUP_RESPONSE:
77846792a2dSArik Nemtsov case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
7791606ef4aSArik Nemtsov if (status_code == 0)
78078a7ea37SMukesh Sisodiya ieee80211_tdls_add_setup_start_ies(link,
78178a7ea37SMukesh Sisodiya skb, peer,
7821606ef4aSArik Nemtsov action_code,
7831606ef4aSArik Nemtsov initiator,
7841606ef4aSArik Nemtsov extra_ies,
785f09a87d2SArik Nemtsov extra_ies_len);
78646792a2dSArik Nemtsov break;
78746792a2dSArik Nemtsov case WLAN_TDLS_SETUP_CONFIRM:
7886f7eaa47SArik Nemtsov if (status_code == 0)
78978a7ea37SMukesh Sisodiya ieee80211_tdls_add_setup_cfm_ies(link, skb, peer,
7906f7eaa47SArik Nemtsov initiator, extra_ies,
7916f7eaa47SArik Nemtsov extra_ies_len);
7926f7eaa47SArik Nemtsov break;
79346792a2dSArik Nemtsov case WLAN_TDLS_TEARDOWN:
79446792a2dSArik Nemtsov case WLAN_TDLS_DISCOVERY_REQUEST:
795f09a87d2SArik Nemtsov if (extra_ies_len)
79659ae1d12SJohannes Berg skb_put_data(skb, extra_ies, extra_ies_len);
7971606ef4aSArik Nemtsov if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
79878a7ea37SMukesh Sisodiya ieee80211_tdls_add_link_ie(link, skb,
79978a7ea37SMukesh Sisodiya peer, initiator);
80046792a2dSArik Nemtsov break;
801a7a6bdd0SArik Nemtsov case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
80278a7ea37SMukesh Sisodiya ieee80211_tdls_add_chan_switch_req_ies(link, skb, peer,
803a7a6bdd0SArik Nemtsov initiator, extra_ies,
804a7a6bdd0SArik Nemtsov extra_ies_len,
805a7a6bdd0SArik Nemtsov oper_class, chandef);
806a7a6bdd0SArik Nemtsov break;
8078a4d32f3SArik Nemtsov case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
80878a7ea37SMukesh Sisodiya ieee80211_tdls_add_chan_switch_resp_ies(link, skb, peer,
8098a4d32f3SArik Nemtsov status_code,
8108a4d32f3SArik Nemtsov initiator, extra_ies,
8118a4d32f3SArik Nemtsov extra_ies_len);
8128a4d32f3SArik Nemtsov break;
81346792a2dSArik Nemtsov }
81446792a2dSArik Nemtsov
81546792a2dSArik Nemtsov }
81646792a2dSArik Nemtsov
81795224fe8SArik Nemtsov static int
ieee80211_prep_tdls_encap_data(struct wiphy * wiphy,struct net_device * dev,struct ieee80211_link_data * link,const u8 * peer,u8 action_code,u8 dialog_token,u16 status_code,struct sk_buff * skb)81895224fe8SArik Nemtsov ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
81978a7ea37SMukesh Sisodiya struct ieee80211_link_data *link,
8203b3a0162SJohannes Berg const u8 *peer, u8 action_code, u8 dialog_token,
82195224fe8SArik Nemtsov u16 status_code, struct sk_buff *skb)
82295224fe8SArik Nemtsov {
82395224fe8SArik Nemtsov struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
82495224fe8SArik Nemtsov struct ieee80211_tdls_data *tf;
82595224fe8SArik Nemtsov
8264df864c1SJohannes Berg tf = skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
82795224fe8SArik Nemtsov
82895224fe8SArik Nemtsov memcpy(tf->da, peer, ETH_ALEN);
82995224fe8SArik Nemtsov memcpy(tf->sa, sdata->vif.addr, ETH_ALEN);
83095224fe8SArik Nemtsov tf->ether_type = cpu_to_be16(ETH_P_TDLS);
83195224fe8SArik Nemtsov tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
83295224fe8SArik Nemtsov
83359cd85cbSArik Nemtsov /* network header is after the ethernet header */
83459cd85cbSArik Nemtsov skb_set_network_header(skb, ETH_HLEN);
83559cd85cbSArik Nemtsov
83695224fe8SArik Nemtsov switch (action_code) {
83795224fe8SArik Nemtsov case WLAN_TDLS_SETUP_REQUEST:
83895224fe8SArik Nemtsov tf->category = WLAN_CATEGORY_TDLS;
83995224fe8SArik Nemtsov tf->action_code = WLAN_TDLS_SETUP_REQUEST;
84095224fe8SArik Nemtsov
84195224fe8SArik Nemtsov skb_put(skb, sizeof(tf->u.setup_req));
84295224fe8SArik Nemtsov tf->u.setup_req.dialog_token = dialog_token;
84395224fe8SArik Nemtsov tf->u.setup_req.capability =
84478a7ea37SMukesh Sisodiya cpu_to_le16(ieee80211_get_tdls_sta_capab(link,
845dd8c0b03SArik Nemtsov status_code));
84695224fe8SArik Nemtsov break;
84795224fe8SArik Nemtsov case WLAN_TDLS_SETUP_RESPONSE:
84895224fe8SArik Nemtsov tf->category = WLAN_CATEGORY_TDLS;
84995224fe8SArik Nemtsov tf->action_code = WLAN_TDLS_SETUP_RESPONSE;
85095224fe8SArik Nemtsov
85195224fe8SArik Nemtsov skb_put(skb, sizeof(tf->u.setup_resp));
85295224fe8SArik Nemtsov tf->u.setup_resp.status_code = cpu_to_le16(status_code);
85395224fe8SArik Nemtsov tf->u.setup_resp.dialog_token = dialog_token;
85495224fe8SArik Nemtsov tf->u.setup_resp.capability =
85578a7ea37SMukesh Sisodiya cpu_to_le16(ieee80211_get_tdls_sta_capab(link,
856dd8c0b03SArik Nemtsov status_code));
85795224fe8SArik Nemtsov break;
85895224fe8SArik Nemtsov case WLAN_TDLS_SETUP_CONFIRM:
85995224fe8SArik Nemtsov tf->category = WLAN_CATEGORY_TDLS;
86095224fe8SArik Nemtsov tf->action_code = WLAN_TDLS_SETUP_CONFIRM;
86195224fe8SArik Nemtsov
86295224fe8SArik Nemtsov skb_put(skb, sizeof(tf->u.setup_cfm));
86395224fe8SArik Nemtsov tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
86495224fe8SArik Nemtsov tf->u.setup_cfm.dialog_token = dialog_token;
86595224fe8SArik Nemtsov break;
86695224fe8SArik Nemtsov case WLAN_TDLS_TEARDOWN:
86795224fe8SArik Nemtsov tf->category = WLAN_CATEGORY_TDLS;
86895224fe8SArik Nemtsov tf->action_code = WLAN_TDLS_TEARDOWN;
86995224fe8SArik Nemtsov
87095224fe8SArik Nemtsov skb_put(skb, sizeof(tf->u.teardown));
87195224fe8SArik Nemtsov tf->u.teardown.reason_code = cpu_to_le16(status_code);
87295224fe8SArik Nemtsov break;
87395224fe8SArik Nemtsov case WLAN_TDLS_DISCOVERY_REQUEST:
87495224fe8SArik Nemtsov tf->category = WLAN_CATEGORY_TDLS;
87595224fe8SArik Nemtsov tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST;
87695224fe8SArik Nemtsov
87795224fe8SArik Nemtsov skb_put(skb, sizeof(tf->u.discover_req));
87895224fe8SArik Nemtsov tf->u.discover_req.dialog_token = dialog_token;
87995224fe8SArik Nemtsov break;
880a7a6bdd0SArik Nemtsov case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
881a7a6bdd0SArik Nemtsov tf->category = WLAN_CATEGORY_TDLS;
882a7a6bdd0SArik Nemtsov tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST;
883a7a6bdd0SArik Nemtsov
884a7a6bdd0SArik Nemtsov skb_put(skb, sizeof(tf->u.chan_switch_req));
885a7a6bdd0SArik Nemtsov break;
8868a4d32f3SArik Nemtsov case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
8878a4d32f3SArik Nemtsov tf->category = WLAN_CATEGORY_TDLS;
8888a4d32f3SArik Nemtsov tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE;
8898a4d32f3SArik Nemtsov
8908a4d32f3SArik Nemtsov skb_put(skb, sizeof(tf->u.chan_switch_resp));
8918a4d32f3SArik Nemtsov tf->u.chan_switch_resp.status_code = cpu_to_le16(status_code);
8928a4d32f3SArik Nemtsov break;
89395224fe8SArik Nemtsov default:
89495224fe8SArik Nemtsov return -EINVAL;
89595224fe8SArik Nemtsov }
89695224fe8SArik Nemtsov
89795224fe8SArik Nemtsov return 0;
89895224fe8SArik Nemtsov }
89995224fe8SArik Nemtsov
90095224fe8SArik Nemtsov static int
ieee80211_prep_tdls_direct(struct wiphy * wiphy,struct net_device * dev,const u8 * peer,struct ieee80211_link_data * link,u8 action_code,u8 dialog_token,u16 status_code,struct sk_buff * skb)90195224fe8SArik Nemtsov ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
90278a7ea37SMukesh Sisodiya const u8 *peer, struct ieee80211_link_data *link,
90378a7ea37SMukesh Sisodiya u8 action_code, u8 dialog_token,
90495224fe8SArik Nemtsov u16 status_code, struct sk_buff *skb)
90595224fe8SArik Nemtsov {
90695224fe8SArik Nemtsov struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
90795224fe8SArik Nemtsov struct ieee80211_mgmt *mgmt;
90895224fe8SArik Nemtsov
909b080db58SJohannes Berg mgmt = skb_put_zero(skb, 24);
91095224fe8SArik Nemtsov memcpy(mgmt->da, peer, ETH_ALEN);
91195224fe8SArik Nemtsov memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
91278a7ea37SMukesh Sisodiya memcpy(mgmt->bssid, link->u.mgd.bssid, ETH_ALEN);
91395224fe8SArik Nemtsov mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
91495224fe8SArik Nemtsov IEEE80211_STYPE_ACTION);
91595224fe8SArik Nemtsov
91695224fe8SArik Nemtsov switch (action_code) {
91795224fe8SArik Nemtsov case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
91895224fe8SArik Nemtsov skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
91995224fe8SArik Nemtsov mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
92095224fe8SArik Nemtsov mgmt->u.action.u.tdls_discover_resp.action_code =
92195224fe8SArik Nemtsov WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
92295224fe8SArik Nemtsov mgmt->u.action.u.tdls_discover_resp.dialog_token =
92395224fe8SArik Nemtsov dialog_token;
92495224fe8SArik Nemtsov mgmt->u.action.u.tdls_discover_resp.capability =
92578a7ea37SMukesh Sisodiya cpu_to_le16(ieee80211_get_tdls_sta_capab(link,
926dd8c0b03SArik Nemtsov status_code));
92795224fe8SArik Nemtsov break;
92895224fe8SArik Nemtsov default:
92995224fe8SArik Nemtsov return -EINVAL;
93095224fe8SArik Nemtsov }
93195224fe8SArik Nemtsov
93295224fe8SArik Nemtsov return 0;
93395224fe8SArik Nemtsov }
93495224fe8SArik Nemtsov
935c2733905SArik Nemtsov static struct sk_buff *
ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data * sdata,const u8 * peer,int link_id,u8 action_code,u8 dialog_token,u16 status_code,bool initiator,const u8 * extra_ies,size_t extra_ies_len,u8 oper_class,struct cfg80211_chan_def * chandef)936c2733905SArik Nemtsov ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
93778a7ea37SMukesh Sisodiya const u8 *peer, int link_id,
93878a7ea37SMukesh Sisodiya u8 action_code, u8 dialog_token,
93978a7ea37SMukesh Sisodiya u16 status_code, bool initiator,
94078a7ea37SMukesh Sisodiya const u8 *extra_ies, size_t extra_ies_len,
94178a7ea37SMukesh Sisodiya u8 oper_class,
942c2733905SArik Nemtsov struct cfg80211_chan_def *chandef)
94395224fe8SArik Nemtsov {
94495224fe8SArik Nemtsov struct ieee80211_local *local = sdata->local;
945c2733905SArik Nemtsov struct sk_buff *skb;
94695224fe8SArik Nemtsov int ret;
94778a7ea37SMukesh Sisodiya struct ieee80211_link_data *link;
94878a7ea37SMukesh Sisodiya
94978a7ea37SMukesh Sisodiya link_id = link_id >= 0 ? link_id : 0;
95078a7ea37SMukesh Sisodiya rcu_read_lock();
95178a7ea37SMukesh Sisodiya link = rcu_dereference(sdata->link[link_id]);
95278a7ea37SMukesh Sisodiya if (WARN_ON(!link))
95378a7ea37SMukesh Sisodiya goto unlock;
95495224fe8SArik Nemtsov
955c2733905SArik Nemtsov skb = netdev_alloc_skb(sdata->dev,
9561277b4a9SLiad Kaufman local->hw.extra_tx_headroom +
95795224fe8SArik Nemtsov max(sizeof(struct ieee80211_mgmt),
95895224fe8SArik Nemtsov sizeof(struct ieee80211_tdls_data)) +
95995224fe8SArik Nemtsov 50 + /* supported rates */
960b98fb44fSArik Nemtsov 10 + /* ext capab */
96140b861a0SArik Nemtsov 26 + /* max(WMM-info, WMM-param) */
96213cc8a4aSArik Nemtsov 2 + max(sizeof(struct ieee80211_ht_cap),
96313cc8a4aSArik Nemtsov sizeof(struct ieee80211_ht_operation)) +
964fb28ec0cSArik Nemtsov 2 + max(sizeof(struct ieee80211_vht_cap),
965fb28ec0cSArik Nemtsov sizeof(struct ieee80211_vht_operation)) +
96671b3b7acSAbhishek Naik 2 + 1 + sizeof(struct ieee80211_he_cap_elem) +
96771b3b7acSAbhishek Naik sizeof(struct ieee80211_he_mcs_nss_supp) +
96871b3b7acSAbhishek Naik IEEE80211_HE_PPE_THRES_MAX_LEN +
96971b3b7acSAbhishek Naik 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) +
97071b3b7acSAbhishek Naik 2 + 1 + sizeof(struct ieee80211_eht_cap_elem) +
97171b3b7acSAbhishek Naik sizeof(struct ieee80211_eht_mcs_nss_supp) +
97271b3b7acSAbhishek Naik IEEE80211_EHT_PPE_THRES_MAX_LEN +
973f0d29cb9SArik Nemtsov 50 + /* supported channels */
9742cedd879SArik Nemtsov 3 + /* 40/20 BSS coex */
975fb28ec0cSArik Nemtsov 4 + /* AID */
976a38700ddSArik Nemtsov 4 + /* oper classes */
97795224fe8SArik Nemtsov extra_ies_len +
97895224fe8SArik Nemtsov sizeof(struct ieee80211_tdls_lnkie));
97995224fe8SArik Nemtsov if (!skb)
98078a7ea37SMukesh Sisodiya goto unlock;
98195224fe8SArik Nemtsov
98295224fe8SArik Nemtsov skb_reserve(skb, local->hw.extra_tx_headroom);
98395224fe8SArik Nemtsov
98495224fe8SArik Nemtsov switch (action_code) {
98595224fe8SArik Nemtsov case WLAN_TDLS_SETUP_REQUEST:
98695224fe8SArik Nemtsov case WLAN_TDLS_SETUP_RESPONSE:
98795224fe8SArik Nemtsov case WLAN_TDLS_SETUP_CONFIRM:
98895224fe8SArik Nemtsov case WLAN_TDLS_TEARDOWN:
98995224fe8SArik Nemtsov case WLAN_TDLS_DISCOVERY_REQUEST:
990a7a6bdd0SArik Nemtsov case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
9918a4d32f3SArik Nemtsov case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
992c2733905SArik Nemtsov ret = ieee80211_prep_tdls_encap_data(local->hw.wiphy,
99378a7ea37SMukesh Sisodiya sdata->dev, link, peer,
99495224fe8SArik Nemtsov action_code, dialog_token,
99595224fe8SArik Nemtsov status_code, skb);
99695224fe8SArik Nemtsov break;
99795224fe8SArik Nemtsov case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
998c2733905SArik Nemtsov ret = ieee80211_prep_tdls_direct(local->hw.wiphy, sdata->dev,
99978a7ea37SMukesh Sisodiya peer, link, action_code,
100095224fe8SArik Nemtsov dialog_token, status_code,
100195224fe8SArik Nemtsov skb);
100295224fe8SArik Nemtsov break;
100395224fe8SArik Nemtsov default:
100495224fe8SArik Nemtsov ret = -ENOTSUPP;
100595224fe8SArik Nemtsov break;
100695224fe8SArik Nemtsov }
100795224fe8SArik Nemtsov
100895224fe8SArik Nemtsov if (ret < 0)
100995224fe8SArik Nemtsov goto fail;
101095224fe8SArik Nemtsov
101178a7ea37SMukesh Sisodiya ieee80211_tdls_add_ies(link, skb, peer, action_code, status_code,
1012c2733905SArik Nemtsov initiator, extra_ies, extra_ies_len, oper_class,
1013c2733905SArik Nemtsov chandef);
101478a7ea37SMukesh Sisodiya rcu_read_unlock();
1015c2733905SArik Nemtsov return skb;
1016c2733905SArik Nemtsov
1017c2733905SArik Nemtsov fail:
1018c2733905SArik Nemtsov dev_kfree_skb(skb);
101978a7ea37SMukesh Sisodiya unlock:
102078a7ea37SMukesh Sisodiya rcu_read_unlock();
1021c2733905SArik Nemtsov return NULL;
1022c2733905SArik Nemtsov }
1023c2733905SArik Nemtsov
1024c2733905SArik Nemtsov static int
ieee80211_tdls_prep_mgmt_packet(struct wiphy * wiphy,struct net_device * dev,const u8 * peer,int link_id,u8 action_code,u8 dialog_token,u16 status_code,u32 peer_capability,bool initiator,const u8 * extra_ies,size_t extra_ies_len,u8 oper_class,struct cfg80211_chan_def * chandef)1025c2733905SArik Nemtsov ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
102678a7ea37SMukesh Sisodiya const u8 *peer, int link_id,
102778a7ea37SMukesh Sisodiya u8 action_code, u8 dialog_token,
1028c2733905SArik Nemtsov u16 status_code, u32 peer_capability,
1029c2733905SArik Nemtsov bool initiator, const u8 *extra_ies,
1030c2733905SArik Nemtsov size_t extra_ies_len, u8 oper_class,
1031c2733905SArik Nemtsov struct cfg80211_chan_def *chandef)
1032c2733905SArik Nemtsov {
1033c2733905SArik Nemtsov struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1034c2733905SArik Nemtsov struct sk_buff *skb = NULL;
1035c2733905SArik Nemtsov struct sta_info *sta;
1036c2733905SArik Nemtsov u32 flags = 0;
1037c2733905SArik Nemtsov int ret = 0;
1038c2733905SArik Nemtsov
1039626911ccSArik Nemtsov rcu_read_lock();
1040626911ccSArik Nemtsov sta = sta_info_get(sdata, peer);
1041626911ccSArik Nemtsov
1042626911ccSArik Nemtsov /* infer the initiator if we can, to support old userspace */
104395224fe8SArik Nemtsov switch (action_code) {
104495224fe8SArik Nemtsov case WLAN_TDLS_SETUP_REQUEST:
10458b94148cSArik Nemtsov if (sta) {
1046626911ccSArik Nemtsov set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
10478b94148cSArik Nemtsov sta->sta.tdls_initiator = false;
10488b94148cSArik Nemtsov }
1049fc0561dcSGustavo A. R. Silva fallthrough;
105095224fe8SArik Nemtsov case WLAN_TDLS_SETUP_CONFIRM:
105195224fe8SArik Nemtsov case WLAN_TDLS_DISCOVERY_REQUEST:
1052626911ccSArik Nemtsov initiator = true;
105395224fe8SArik Nemtsov break;
105495224fe8SArik Nemtsov case WLAN_TDLS_SETUP_RESPONSE:
1055626911ccSArik Nemtsov /*
1056626911ccSArik Nemtsov * In some testing scenarios, we send a request and response.
1057626911ccSArik Nemtsov * Make the last packet sent take effect for the initiator
1058626911ccSArik Nemtsov * value.
1059626911ccSArik Nemtsov */
10608b94148cSArik Nemtsov if (sta) {
1061626911ccSArik Nemtsov clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
10628b94148cSArik Nemtsov sta->sta.tdls_initiator = true;
10638b94148cSArik Nemtsov }
1064fc0561dcSGustavo A. R. Silva fallthrough;
106595224fe8SArik Nemtsov case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
1066626911ccSArik Nemtsov initiator = false;
10672fb6b9b8SArik Nemtsov break;
10682fb6b9b8SArik Nemtsov case WLAN_TDLS_TEARDOWN:
1069a7a6bdd0SArik Nemtsov case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
10708a4d32f3SArik Nemtsov case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
10712fb6b9b8SArik Nemtsov /* any value is ok */
107295224fe8SArik Nemtsov break;
107395224fe8SArik Nemtsov default:
107495224fe8SArik Nemtsov ret = -ENOTSUPP;
1075626911ccSArik Nemtsov break;
107695224fe8SArik Nemtsov }
107795224fe8SArik Nemtsov
107846792a2dSArik Nemtsov if (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR))
107946792a2dSArik Nemtsov initiator = true;
10802fb6b9b8SArik Nemtsov
1081626911ccSArik Nemtsov rcu_read_unlock();
1082626911ccSArik Nemtsov if (ret < 0)
1083626911ccSArik Nemtsov goto fail;
1084626911ccSArik Nemtsov
108578a7ea37SMukesh Sisodiya skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer,
108678a7ea37SMukesh Sisodiya link_id, action_code,
1087c2733905SArik Nemtsov dialog_token, status_code,
1088c2733905SArik Nemtsov initiator, extra_ies,
1089c2733905SArik Nemtsov extra_ies_len, oper_class,
1090c2733905SArik Nemtsov chandef);
1091c2733905SArik Nemtsov if (!skb) {
1092c2733905SArik Nemtsov ret = -EINVAL;
1093c2733905SArik Nemtsov goto fail;
1094c2733905SArik Nemtsov }
1095c2733905SArik Nemtsov
1096c2733905SArik Nemtsov if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
109778a7ea37SMukesh Sisodiya ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
109895224fe8SArik Nemtsov return 0;
109995224fe8SArik Nemtsov }
110095224fe8SArik Nemtsov
110195224fe8SArik Nemtsov /*
110295224fe8SArik Nemtsov * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
110395224fe8SArik Nemtsov * we should default to AC_VI.
110495224fe8SArik Nemtsov */
110595224fe8SArik Nemtsov switch (action_code) {
110695224fe8SArik Nemtsov case WLAN_TDLS_SETUP_REQUEST:
110795224fe8SArik Nemtsov case WLAN_TDLS_SETUP_RESPONSE:
1108cb59bc14SJohannes Berg skb->priority = 256 + 2;
110995224fe8SArik Nemtsov break;
111095224fe8SArik Nemtsov default:
1111cb59bc14SJohannes Berg skb->priority = 256 + 5;
111295224fe8SArik Nemtsov break;
111395224fe8SArik Nemtsov }
111495224fe8SArik Nemtsov
11151277b4a9SLiad Kaufman /*
11161277b4a9SLiad Kaufman * Set the WLAN_TDLS_TEARDOWN flag to indicate a teardown in progress.
11171277b4a9SLiad Kaufman * Later, if no ACK is returned from peer, we will re-send the teardown
11181277b4a9SLiad Kaufman * packet through the AP.
11191277b4a9SLiad Kaufman */
11201277b4a9SLiad Kaufman if ((action_code == WLAN_TDLS_TEARDOWN) &&
112130686bf7SJohannes Berg ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) {
11221277b4a9SLiad Kaufman bool try_resend; /* Should we keep skb for possible resend */
11231277b4a9SLiad Kaufman
11241277b4a9SLiad Kaufman /* If not sending directly to peer - no point in keeping skb */
11251277b4a9SLiad Kaufman rcu_read_lock();
11261277b4a9SLiad Kaufman sta = sta_info_get(sdata, peer);
11271277b4a9SLiad Kaufman try_resend = sta && test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
11281277b4a9SLiad Kaufman rcu_read_unlock();
11291277b4a9SLiad Kaufman
11301277b4a9SLiad Kaufman spin_lock_bh(&sdata->u.mgd.teardown_lock);
11311277b4a9SLiad Kaufman if (try_resend && !sdata->u.mgd.teardown_skb) {
11321277b4a9SLiad Kaufman /* Mark it as requiring TX status callback */
11331277b4a9SLiad Kaufman flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
11341277b4a9SLiad Kaufman IEEE80211_TX_INTFL_MLME_CONN_TX;
11351277b4a9SLiad Kaufman
11361277b4a9SLiad Kaufman /*
11371277b4a9SLiad Kaufman * skb is copied since mac80211 will later set
11381277b4a9SLiad Kaufman * properties that might not be the same as the AP,
11391277b4a9SLiad Kaufman * such as encryption, QoS, addresses, etc.
11401277b4a9SLiad Kaufman *
11411277b4a9SLiad Kaufman * No problem if skb_copy() fails, so no need to check.
11421277b4a9SLiad Kaufman */
11431277b4a9SLiad Kaufman sdata->u.mgd.teardown_skb = skb_copy(skb, GFP_ATOMIC);
11441277b4a9SLiad Kaufman sdata->u.mgd.orig_teardown_skb = skb;
11451277b4a9SLiad Kaufman }
11461277b4a9SLiad Kaufman spin_unlock_bh(&sdata->u.mgd.teardown_lock);
11471277b4a9SLiad Kaufman }
11481277b4a9SLiad Kaufman
114995224fe8SArik Nemtsov /* disable bottom halves when entering the Tx path */
115095224fe8SArik Nemtsov local_bh_disable();
1151963d0e8dSJohannes Berg __ieee80211_subif_start_xmit(skb, dev, flags,
1152963d0e8dSJohannes Berg IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, NULL);
115395224fe8SArik Nemtsov local_bh_enable();
115495224fe8SArik Nemtsov
115595224fe8SArik Nemtsov return ret;
115695224fe8SArik Nemtsov
115795224fe8SArik Nemtsov fail:
115895224fe8SArik Nemtsov dev_kfree_skb(skb);
115995224fe8SArik Nemtsov return ret;
116095224fe8SArik Nemtsov }
116195224fe8SArik Nemtsov
1162191dd469SArik Nemtsov static int
ieee80211_tdls_mgmt_setup(struct wiphy * wiphy,struct net_device * dev,const u8 * peer,int link_id,u8 action_code,u8 dialog_token,u16 status_code,u32 peer_capability,bool initiator,const u8 * extra_ies,size_t extra_ies_len)1163191dd469SArik Nemtsov ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
116478a7ea37SMukesh Sisodiya const u8 *peer, int link_id,
116578a7ea37SMukesh Sisodiya u8 action_code, u8 dialog_token,
1166191dd469SArik Nemtsov u16 status_code, u32 peer_capability, bool initiator,
1167191dd469SArik Nemtsov const u8 *extra_ies, size_t extra_ies_len)
116817e6a59aSArik Nemtsov {
116917e6a59aSArik Nemtsov struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
117017e6a59aSArik Nemtsov struct ieee80211_local *local = sdata->local;
1171bfd8403aSJohannes Berg enum ieee80211_smps_mode smps_mode =
1172bfd8403aSJohannes Berg sdata->deflink.u.mgd.driver_smps_mode;
117317e6a59aSArik Nemtsov int ret;
117417e6a59aSArik Nemtsov
1175d51c2ea3SArik Nemtsov /* don't support setup with forced SMPS mode that's not off */
1176d51c2ea3SArik Nemtsov if (smps_mode != IEEE80211_SMPS_AUTOMATIC &&
1177d51c2ea3SArik Nemtsov smps_mode != IEEE80211_SMPS_OFF) {
1178d51c2ea3SArik Nemtsov tdls_dbg(sdata, "Aborting TDLS setup due to SMPS mode %d\n",
1179d51c2ea3SArik Nemtsov smps_mode);
1180d51c2ea3SArik Nemtsov return -ENOTSUPP;
1181d51c2ea3SArik Nemtsov }
1182d51c2ea3SArik Nemtsov
118317e6a59aSArik Nemtsov mutex_lock(&local->mtx);
118417e6a59aSArik Nemtsov
118517e6a59aSArik Nemtsov /* we don't support concurrent TDLS peer setups */
118681dd2b88SArik Nemtsov if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) &&
118781dd2b88SArik Nemtsov !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
118817e6a59aSArik Nemtsov ret = -EBUSY;
1189ae2e9fbaSArik Nemtsov goto out_unlock;
119017e6a59aSArik Nemtsov }
119117e6a59aSArik Nemtsov
11927adc3e46SArik Nemtsov /*
11937adc3e46SArik Nemtsov * make sure we have a STA representing the peer so we drop or buffer
11947adc3e46SArik Nemtsov * non-TDLS-setup frames to the peer. We can't send other packets
11956ae32e5dSArik Nemtsov * during setup through the AP path.
11966ae32e5dSArik Nemtsov * Allow error packets to be sent - sometimes we don't even add a STA
11976ae32e5dSArik Nemtsov * before failing the setup.
11987adc3e46SArik Nemtsov */
11996ae32e5dSArik Nemtsov if (status_code == 0) {
12007adc3e46SArik Nemtsov rcu_read_lock();
12017adc3e46SArik Nemtsov if (!sta_info_get(sdata, peer)) {
12027adc3e46SArik Nemtsov rcu_read_unlock();
12037adc3e46SArik Nemtsov ret = -ENOLINK;
1204ae2e9fbaSArik Nemtsov goto out_unlock;
12057adc3e46SArik Nemtsov }
12067adc3e46SArik Nemtsov rcu_read_unlock();
12076ae32e5dSArik Nemtsov }
12087adc3e46SArik Nemtsov
12093b24f4c6SEmmanuel Grumbach ieee80211_flush_queues(local, sdata, false);
1210ae2e9fbaSArik Nemtsov memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
1211ae2e9fbaSArik Nemtsov mutex_unlock(&local->mtx);
1212db67d661SArik Nemtsov
1213ae2e9fbaSArik Nemtsov /* we cannot take the mutex while preparing the setup packet */
121478a7ea37SMukesh Sisodiya ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
121578a7ea37SMukesh Sisodiya link_id, action_code,
121617e6a59aSArik Nemtsov dialog_token, status_code,
12172fb6b9b8SArik Nemtsov peer_capability, initiator,
1218c2733905SArik Nemtsov extra_ies, extra_ies_len, 0,
1219c2733905SArik Nemtsov NULL);
1220ae2e9fbaSArik Nemtsov if (ret < 0) {
1221ae2e9fbaSArik Nemtsov mutex_lock(&local->mtx);
1222ae2e9fbaSArik Nemtsov eth_zero_addr(sdata->u.mgd.tdls_peer);
1223ae2e9fbaSArik Nemtsov mutex_unlock(&local->mtx);
1224ae2e9fbaSArik Nemtsov return ret;
1225ae2e9fbaSArik Nemtsov }
122617e6a59aSArik Nemtsov
122717e6a59aSArik Nemtsov ieee80211_queue_delayed_work(&sdata->local->hw,
122881dd2b88SArik Nemtsov &sdata->u.mgd.tdls_peer_del_work,
122917e6a59aSArik Nemtsov TDLS_PEER_SETUP_TIMEOUT);
1230ae2e9fbaSArik Nemtsov return 0;
123117e6a59aSArik Nemtsov
1232ae2e9fbaSArik Nemtsov out_unlock:
123317e6a59aSArik Nemtsov mutex_unlock(&local->mtx);
1234191dd469SArik Nemtsov return ret;
1235191dd469SArik Nemtsov }
1236191dd469SArik Nemtsov
1237db67d661SArik Nemtsov static int
ieee80211_tdls_mgmt_teardown(struct wiphy * wiphy,struct net_device * dev,const u8 * peer,int link_id,u8 action_code,u8 dialog_token,u16 status_code,u32 peer_capability,bool initiator,const u8 * extra_ies,size_t extra_ies_len)1238db67d661SArik Nemtsov ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev,
123978a7ea37SMukesh Sisodiya const u8 *peer, int link_id,
124078a7ea37SMukesh Sisodiya u8 action_code, u8 dialog_token,
1241db67d661SArik Nemtsov u16 status_code, u32 peer_capability,
1242db67d661SArik Nemtsov bool initiator, const u8 *extra_ies,
1243db67d661SArik Nemtsov size_t extra_ies_len)
1244db67d661SArik Nemtsov {
1245db67d661SArik Nemtsov struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1246db67d661SArik Nemtsov struct ieee80211_local *local = sdata->local;
1247db67d661SArik Nemtsov struct sta_info *sta;
1248db67d661SArik Nemtsov int ret;
1249db67d661SArik Nemtsov
1250db67d661SArik Nemtsov /*
1251db67d661SArik Nemtsov * No packets can be transmitted to the peer via the AP during setup -
1252db67d661SArik Nemtsov * the STA is set as a TDLS peer, but is not authorized.
1253db67d661SArik Nemtsov * During teardown, we prevent direct transmissions by stopping the
1254db67d661SArik Nemtsov * queues and flushing all direct packets.
1255db67d661SArik Nemtsov */
1256db67d661SArik Nemtsov ieee80211_stop_vif_queues(local, sdata,
1257db67d661SArik Nemtsov IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
12583b24f4c6SEmmanuel Grumbach ieee80211_flush_queues(local, sdata, false);
1259db67d661SArik Nemtsov
126078a7ea37SMukesh Sisodiya ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
126178a7ea37SMukesh Sisodiya link_id, action_code,
1262db67d661SArik Nemtsov dialog_token, status_code,
1263db67d661SArik Nemtsov peer_capability, initiator,
1264c2733905SArik Nemtsov extra_ies, extra_ies_len, 0,
1265c2733905SArik Nemtsov NULL);
1266db67d661SArik Nemtsov if (ret < 0)
1267db67d661SArik Nemtsov sdata_err(sdata, "Failed sending TDLS teardown packet %d\n",
1268db67d661SArik Nemtsov ret);
1269db67d661SArik Nemtsov
1270db67d661SArik Nemtsov /*
1271db67d661SArik Nemtsov * Remove the STA AUTH flag to force further traffic through the AP. If
1272db67d661SArik Nemtsov * the STA was unreachable, it was already removed.
1273db67d661SArik Nemtsov */
1274db67d661SArik Nemtsov rcu_read_lock();
1275db67d661SArik Nemtsov sta = sta_info_get(sdata, peer);
1276db67d661SArik Nemtsov if (sta)
1277db67d661SArik Nemtsov clear_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
1278db67d661SArik Nemtsov rcu_read_unlock();
1279db67d661SArik Nemtsov
1280db67d661SArik Nemtsov ieee80211_wake_vif_queues(local, sdata,
1281db67d661SArik Nemtsov IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
1282db67d661SArik Nemtsov
1283db67d661SArik Nemtsov return 0;
1284db67d661SArik Nemtsov }
1285db67d661SArik Nemtsov
ieee80211_tdls_mgmt(struct wiphy * wiphy,struct net_device * dev,const u8 * peer,int link_id,u8 action_code,u8 dialog_token,u16 status_code,u32 peer_capability,bool initiator,const u8 * extra_ies,size_t extra_ies_len)1286191dd469SArik Nemtsov int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
1287c6112046SMukesh Sisodiya const u8 *peer, int link_id,
1288c6112046SMukesh Sisodiya u8 action_code, u8 dialog_token, u16 status_code,
1289c6112046SMukesh Sisodiya u32 peer_capability, bool initiator,
1290c6112046SMukesh Sisodiya const u8 *extra_ies, size_t extra_ies_len)
1291191dd469SArik Nemtsov {
1292191dd469SArik Nemtsov struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1293191dd469SArik Nemtsov int ret;
1294191dd469SArik Nemtsov
1295191dd469SArik Nemtsov if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
1296191dd469SArik Nemtsov return -ENOTSUPP;
1297191dd469SArik Nemtsov
1298191dd469SArik Nemtsov /* make sure we are in managed mode, and associated */
1299191dd469SArik Nemtsov if (sdata->vif.type != NL80211_IFTYPE_STATION ||
1300191dd469SArik Nemtsov !sdata->u.mgd.associated)
1301191dd469SArik Nemtsov return -EINVAL;
1302191dd469SArik Nemtsov
1303191dd469SArik Nemtsov switch (action_code) {
1304191dd469SArik Nemtsov case WLAN_TDLS_SETUP_REQUEST:
1305191dd469SArik Nemtsov case WLAN_TDLS_SETUP_RESPONSE:
130678a7ea37SMukesh Sisodiya ret = ieee80211_tdls_mgmt_setup(wiphy, dev, peer,
130778a7ea37SMukesh Sisodiya link_id, action_code,
1308191dd469SArik Nemtsov dialog_token, status_code,
1309191dd469SArik Nemtsov peer_capability, initiator,
1310191dd469SArik Nemtsov extra_ies, extra_ies_len);
1311191dd469SArik Nemtsov break;
1312191dd469SArik Nemtsov case WLAN_TDLS_TEARDOWN:
131378a7ea37SMukesh Sisodiya ret = ieee80211_tdls_mgmt_teardown(wiphy, dev, peer, link_id,
1314db67d661SArik Nemtsov action_code, dialog_token,
1315db67d661SArik Nemtsov status_code,
1316db67d661SArik Nemtsov peer_capability, initiator,
1317db67d661SArik Nemtsov extra_ies, extra_ies_len);
1318db67d661SArik Nemtsov break;
1319191dd469SArik Nemtsov case WLAN_TDLS_DISCOVERY_REQUEST:
1320ee10f2c7SArik Nemtsov /*
1321ee10f2c7SArik Nemtsov * Protect the discovery so we can hear the TDLS discovery
1322ee10f2c7SArik Nemtsov * response frame. It is transmitted directly and not buffered
1323ee10f2c7SArik Nemtsov * by the AP.
1324ee10f2c7SArik Nemtsov */
1325ee10f2c7SArik Nemtsov drv_mgd_protect_tdls_discover(sdata->local, sdata);
1326fc0561dcSGustavo A. R. Silva fallthrough;
1327ee10f2c7SArik Nemtsov case WLAN_TDLS_SETUP_CONFIRM:
1328191dd469SArik Nemtsov case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
1329191dd469SArik Nemtsov /* no special handling */
1330191dd469SArik Nemtsov ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
133178a7ea37SMukesh Sisodiya link_id, action_code,
1332191dd469SArik Nemtsov dialog_token,
1333191dd469SArik Nemtsov status_code,
1334191dd469SArik Nemtsov peer_capability,
1335191dd469SArik Nemtsov initiator, extra_ies,
1336c2733905SArik Nemtsov extra_ies_len, 0, NULL);
1337191dd469SArik Nemtsov break;
1338191dd469SArik Nemtsov default:
1339191dd469SArik Nemtsov ret = -EOPNOTSUPP;
1340191dd469SArik Nemtsov break;
1341191dd469SArik Nemtsov }
134217e6a59aSArik Nemtsov
134378a7ea37SMukesh Sisodiya tdls_dbg(sdata, "TDLS mgmt action %d peer %pM link_id %d status %d\n",
134478a7ea37SMukesh Sisodiya action_code, peer, link_id, ret);
134517e6a59aSArik Nemtsov return ret;
134617e6a59aSArik Nemtsov }
134717e6a59aSArik Nemtsov
iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data * sdata,struct sta_info * sta)134859021c67SArik Nemtsov static void iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data *sdata,
134959021c67SArik Nemtsov struct sta_info *sta)
13500fabfaafSArik Nemtsov {
13510fabfaafSArik Nemtsov struct ieee80211_local *local = sdata->local;
13520fabfaafSArik Nemtsov struct ieee80211_chanctx_conf *conf;
13530fabfaafSArik Nemtsov struct ieee80211_chanctx *ctx;
135459021c67SArik Nemtsov enum nl80211_chan_width width;
135559021c67SArik Nemtsov struct ieee80211_supported_band *sband;
13560fabfaafSArik Nemtsov
13570fabfaafSArik Nemtsov mutex_lock(&local->chanctx_mtx);
1358d0a9123eSJohannes Berg conf = rcu_dereference_protected(sdata->vif.bss_conf.chanctx_conf,
13590fabfaafSArik Nemtsov lockdep_is_held(&local->chanctx_mtx));
13600fabfaafSArik Nemtsov if (conf) {
136159021c67SArik Nemtsov width = conf->def.width;
136259021c67SArik Nemtsov sband = local->hw.wiphy->bands[conf->def.chan->band];
13630fabfaafSArik Nemtsov ctx = container_of(conf, struct ieee80211_chanctx, conf);
13640fabfaafSArik Nemtsov ieee80211_recalc_chanctx_chantype(local, ctx);
136559021c67SArik Nemtsov
136659021c67SArik Nemtsov /* if width changed and a peer is given, update its BW */
136759021c67SArik Nemtsov if (width != conf->def.width && sta &&
136859021c67SArik Nemtsov test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW)) {
136959021c67SArik Nemtsov enum ieee80211_sta_rx_bandwidth bw;
137059021c67SArik Nemtsov
137159021c67SArik Nemtsov bw = ieee80211_chan_width_to_rx_bw(conf->def.width);
1372c71420dbSJohannes Berg bw = min(bw, ieee80211_sta_cap_rx_bw(&sta->deflink));
1373046d2e7cSSriram R if (bw != sta->sta.deflink.bandwidth) {
1374046d2e7cSSriram R sta->sta.deflink.bandwidth = bw;
1375b4f85443SJohannes Berg rate_control_rate_update(local, sband, sta, 0,
137659021c67SArik Nemtsov IEEE80211_RC_BW_CHANGED);
137759021c67SArik Nemtsov /*
137859021c67SArik Nemtsov * if a TDLS peer BW was updated, we need to
137959021c67SArik Nemtsov * recalc the chandef width again, to get the
138059021c67SArik Nemtsov * correct chanctx min_def
138159021c67SArik Nemtsov */
138259021c67SArik Nemtsov ieee80211_recalc_chanctx_chantype(local, ctx);
138359021c67SArik Nemtsov }
138459021c67SArik Nemtsov }
138559021c67SArik Nemtsov
13860fabfaafSArik Nemtsov }
13870fabfaafSArik Nemtsov mutex_unlock(&local->chanctx_mtx);
13880fabfaafSArik Nemtsov }
13890fabfaafSArik Nemtsov
iee80211_tdls_have_ht_peers(struct ieee80211_sub_if_data * sdata)139022f66895SAvri Altman static int iee80211_tdls_have_ht_peers(struct ieee80211_sub_if_data *sdata)
139122f66895SAvri Altman {
139222f66895SAvri Altman struct sta_info *sta;
139322f66895SAvri Altman bool result = false;
139422f66895SAvri Altman
139522f66895SAvri Altman rcu_read_lock();
139622f66895SAvri Altman list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
139722f66895SAvri Altman if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
139822f66895SAvri Altman !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
139922f66895SAvri Altman !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH) ||
1400046d2e7cSSriram R !sta->sta.deflink.ht_cap.ht_supported)
140122f66895SAvri Altman continue;
140222f66895SAvri Altman result = true;
140322f66895SAvri Altman break;
140422f66895SAvri Altman }
140522f66895SAvri Altman rcu_read_unlock();
140622f66895SAvri Altman
140722f66895SAvri Altman return result;
140822f66895SAvri Altman }
140922f66895SAvri Altman
141022f66895SAvri Altman static void
iee80211_tdls_recalc_ht_protection(struct ieee80211_sub_if_data * sdata,struct sta_info * sta)141122f66895SAvri Altman iee80211_tdls_recalc_ht_protection(struct ieee80211_sub_if_data *sdata,
141222f66895SAvri Altman struct sta_info *sta)
141322f66895SAvri Altman {
141422f66895SAvri Altman bool tdls_ht;
141522f66895SAvri Altman u16 protection = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED |
141622f66895SAvri Altman IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT |
141722f66895SAvri Altman IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT;
141822f66895SAvri Altman u16 opmode;
141922f66895SAvri Altman
142022f66895SAvri Altman /* Nothing to do if the BSS connection uses HT */
1421ba323e29SJohannes Berg if (!(sdata->deflink.u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT))
142222f66895SAvri Altman return;
142322f66895SAvri Altman
1424046d2e7cSSriram R tdls_ht = (sta && sta->sta.deflink.ht_cap.ht_supported) ||
142522f66895SAvri Altman iee80211_tdls_have_ht_peers(sdata);
142622f66895SAvri Altman
142722f66895SAvri Altman opmode = sdata->vif.bss_conf.ht_operation_mode;
142822f66895SAvri Altman
142922f66895SAvri Altman if (tdls_ht)
143022f66895SAvri Altman opmode |= protection;
143122f66895SAvri Altman else
143222f66895SAvri Altman opmode &= ~protection;
143322f66895SAvri Altman
143422f66895SAvri Altman if (opmode == sdata->vif.bss_conf.ht_operation_mode)
143522f66895SAvri Altman return;
143622f66895SAvri Altman
143722f66895SAvri Altman sdata->vif.bss_conf.ht_operation_mode = opmode;
1438d8675a63SJohannes Berg ieee80211_link_info_change_notify(sdata, &sdata->deflink,
1439d8675a63SJohannes Berg BSS_CHANGED_HT);
144022f66895SAvri Altman }
144122f66895SAvri Altman
ieee80211_tdls_oper(struct wiphy * wiphy,struct net_device * dev,const u8 * peer,enum nl80211_tdls_operation oper)144295224fe8SArik Nemtsov int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
14433b3a0162SJohannes Berg const u8 *peer, enum nl80211_tdls_operation oper)
144495224fe8SArik Nemtsov {
144595224fe8SArik Nemtsov struct sta_info *sta;
144695224fe8SArik Nemtsov struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
144717e6a59aSArik Nemtsov struct ieee80211_local *local = sdata->local;
144817e6a59aSArik Nemtsov int ret;
144995224fe8SArik Nemtsov
145095224fe8SArik Nemtsov if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
145195224fe8SArik Nemtsov return -ENOTSUPP;
145295224fe8SArik Nemtsov
145395224fe8SArik Nemtsov if (sdata->vif.type != NL80211_IFTYPE_STATION)
145495224fe8SArik Nemtsov return -EINVAL;
145595224fe8SArik Nemtsov
145617e6a59aSArik Nemtsov switch (oper) {
145717e6a59aSArik Nemtsov case NL80211_TDLS_ENABLE_LINK:
145817e6a59aSArik Nemtsov case NL80211_TDLS_DISABLE_LINK:
145917e6a59aSArik Nemtsov break;
146017e6a59aSArik Nemtsov case NL80211_TDLS_TEARDOWN:
146117e6a59aSArik Nemtsov case NL80211_TDLS_SETUP:
146217e6a59aSArik Nemtsov case NL80211_TDLS_DISCOVERY_REQ:
146317e6a59aSArik Nemtsov /* We don't support in-driver setup/teardown/discovery */
146417e6a59aSArik Nemtsov return -ENOTSUPP;
146517e6a59aSArik Nemtsov }
146617e6a59aSArik Nemtsov
146722f66895SAvri Altman /* protect possible bss_conf changes and avoid concurrency in
146822f66895SAvri Altman * ieee80211_bss_info_change_notify()
146922f66895SAvri Altman */
147022f66895SAvri Altman sdata_lock(sdata);
147117e6a59aSArik Nemtsov mutex_lock(&local->mtx);
147295224fe8SArik Nemtsov tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
147395224fe8SArik Nemtsov
147495224fe8SArik Nemtsov switch (oper) {
147595224fe8SArik Nemtsov case NL80211_TDLS_ENABLE_LINK:
1476d0a9123eSJohannes Berg if (sdata->vif.bss_conf.csa_active) {
1477c5a71688SArik Nemtsov tdls_dbg(sdata, "TDLS: disallow link during CSA\n");
1478c5a71688SArik Nemtsov ret = -EBUSY;
1479c5a71688SArik Nemtsov break;
1480c5a71688SArik Nemtsov }
1481c5a71688SArik Nemtsov
148222f66895SAvri Altman mutex_lock(&local->sta_mtx);
148395224fe8SArik Nemtsov sta = sta_info_get(sdata, peer);
148495224fe8SArik Nemtsov if (!sta) {
148522f66895SAvri Altman mutex_unlock(&local->sta_mtx);
148617e6a59aSArik Nemtsov ret = -ENOLINK;
148717e6a59aSArik Nemtsov break;
148895224fe8SArik Nemtsov }
148995224fe8SArik Nemtsov
149059021c67SArik Nemtsov iee80211_tdls_recalc_chanctx(sdata, sta);
149122f66895SAvri Altman iee80211_tdls_recalc_ht_protection(sdata, sta);
149222f66895SAvri Altman
149395224fe8SArik Nemtsov set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
149422f66895SAvri Altman mutex_unlock(&local->sta_mtx);
149517e6a59aSArik Nemtsov
149681dd2b88SArik Nemtsov WARN_ON_ONCE(is_zero_ether_addr(sdata->u.mgd.tdls_peer) ||
149781dd2b88SArik Nemtsov !ether_addr_equal(sdata->u.mgd.tdls_peer, peer));
149817e6a59aSArik Nemtsov ret = 0;
149995224fe8SArik Nemtsov break;
150095224fe8SArik Nemtsov case NL80211_TDLS_DISABLE_LINK:
1501bb3f8486SLiad Kaufman /*
1502bb3f8486SLiad Kaufman * The teardown message in ieee80211_tdls_mgmt_teardown() was
1503bb3f8486SLiad Kaufman * created while the queues were stopped, so it might still be
1504bb3f8486SLiad Kaufman * pending. Before flushing the queues we need to be sure the
1505bb3f8486SLiad Kaufman * message is handled by the tasklet handling pending messages,
1506bb3f8486SLiad Kaufman * otherwise we might start destroying the station before
1507bb3f8486SLiad Kaufman * sending the teardown packet.
1508bb3f8486SLiad Kaufman * Note that this only forces the tasklet to flush pendings -
1509bb3f8486SLiad Kaufman * not to stop the tasklet from rescheduling itself.
1510bb3f8486SLiad Kaufman */
1511bb3f8486SLiad Kaufman tasklet_kill(&local->tx_pending_tasklet);
1512db67d661SArik Nemtsov /* flush a potentially queued teardown packet */
15133b24f4c6SEmmanuel Grumbach ieee80211_flush_queues(local, sdata, false);
1514db67d661SArik Nemtsov
151517e6a59aSArik Nemtsov ret = sta_info_destroy_addr(sdata, peer);
151622f66895SAvri Altman
151722f66895SAvri Altman mutex_lock(&local->sta_mtx);
151822f66895SAvri Altman iee80211_tdls_recalc_ht_protection(sdata, NULL);
151922f66895SAvri Altman mutex_unlock(&local->sta_mtx);
152022f66895SAvri Altman
152159021c67SArik Nemtsov iee80211_tdls_recalc_chanctx(sdata, NULL);
152217e6a59aSArik Nemtsov break;
152395224fe8SArik Nemtsov default:
152417e6a59aSArik Nemtsov ret = -ENOTSUPP;
152517e6a59aSArik Nemtsov break;
152695224fe8SArik Nemtsov }
152795224fe8SArik Nemtsov
152881dd2b88SArik Nemtsov if (ret == 0 && ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
152981dd2b88SArik Nemtsov cancel_delayed_work(&sdata->u.mgd.tdls_peer_del_work);
153081dd2b88SArik Nemtsov eth_zero_addr(sdata->u.mgd.tdls_peer);
153117e6a59aSArik Nemtsov }
153217e6a59aSArik Nemtsov
1533d51c2ea3SArik Nemtsov if (ret == 0)
15341444f589SJohannes Berg wiphy_work_queue(sdata->local->hw.wiphy,
1535bfd8403aSJohannes Berg &sdata->deflink.u.mgd.request_smps_work);
1536d51c2ea3SArik Nemtsov
153717e6a59aSArik Nemtsov mutex_unlock(&local->mtx);
153822f66895SAvri Altman sdata_unlock(sdata);
153917e6a59aSArik Nemtsov return ret;
154095224fe8SArik Nemtsov }
1541c887f0d3SArik Nemtsov
ieee80211_tdls_oper_request(struct ieee80211_vif * vif,const u8 * peer,enum nl80211_tdls_operation oper,u16 reason_code,gfp_t gfp)1542c887f0d3SArik Nemtsov void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
1543c887f0d3SArik Nemtsov enum nl80211_tdls_operation oper,
1544c887f0d3SArik Nemtsov u16 reason_code, gfp_t gfp)
1545c887f0d3SArik Nemtsov {
1546c887f0d3SArik Nemtsov struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
1547c887f0d3SArik Nemtsov
1548f276e20bSJohannes Berg if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) {
1549c887f0d3SArik Nemtsov sdata_err(sdata, "Discarding TDLS oper %d - not STA or disconnected\n",
1550c887f0d3SArik Nemtsov oper);
1551c887f0d3SArik Nemtsov return;
1552c887f0d3SArik Nemtsov }
1553c887f0d3SArik Nemtsov
1554c887f0d3SArik Nemtsov cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp);
1555c887f0d3SArik Nemtsov }
1556c887f0d3SArik Nemtsov EXPORT_SYMBOL(ieee80211_tdls_oper_request);
1557a7a6bdd0SArik Nemtsov
1558a7a6bdd0SArik Nemtsov static void
iee80211_tdls_add_ch_switch_timing(u8 * buf,u16 switch_time,u16 switch_timeout)1559a7a6bdd0SArik Nemtsov iee80211_tdls_add_ch_switch_timing(u8 *buf, u16 switch_time, u16 switch_timeout)
1560a7a6bdd0SArik Nemtsov {
1561a7a6bdd0SArik Nemtsov struct ieee80211_ch_switch_timing *ch_sw;
1562a7a6bdd0SArik Nemtsov
1563a7a6bdd0SArik Nemtsov *buf++ = WLAN_EID_CHAN_SWITCH_TIMING;
1564a7a6bdd0SArik Nemtsov *buf++ = sizeof(struct ieee80211_ch_switch_timing);
1565a7a6bdd0SArik Nemtsov
1566a7a6bdd0SArik Nemtsov ch_sw = (void *)buf;
1567a7a6bdd0SArik Nemtsov ch_sw->switch_time = cpu_to_le16(switch_time);
1568a7a6bdd0SArik Nemtsov ch_sw->switch_timeout = cpu_to_le16(switch_timeout);
1569a7a6bdd0SArik Nemtsov }
1570a7a6bdd0SArik Nemtsov
1571a7a6bdd0SArik Nemtsov /* find switch timing IE in SKB ready for Tx */
ieee80211_tdls_find_sw_timing_ie(struct sk_buff * skb)1572a7a6bdd0SArik Nemtsov static const u8 *ieee80211_tdls_find_sw_timing_ie(struct sk_buff *skb)
1573a7a6bdd0SArik Nemtsov {
1574a7a6bdd0SArik Nemtsov struct ieee80211_tdls_data *tf;
1575a7a6bdd0SArik Nemtsov const u8 *ie_start;
1576a7a6bdd0SArik Nemtsov
1577a7a6bdd0SArik Nemtsov /*
1578a7a6bdd0SArik Nemtsov * Get the offset for the new location of the switch timing IE.
1579a7a6bdd0SArik Nemtsov * The SKB network header will now point to the "payload_type"
1580a7a6bdd0SArik Nemtsov * element of the TDLS data frame struct.
1581a7a6bdd0SArik Nemtsov */
1582a7a6bdd0SArik Nemtsov tf = container_of(skb->data + skb_network_offset(skb),
1583a7a6bdd0SArik Nemtsov struct ieee80211_tdls_data, payload_type);
1584a7a6bdd0SArik Nemtsov ie_start = tf->u.chan_switch_req.variable;
1585a7a6bdd0SArik Nemtsov return cfg80211_find_ie(WLAN_EID_CHAN_SWITCH_TIMING, ie_start,
1586a7a6bdd0SArik Nemtsov skb->len - (ie_start - skb->data));
1587a7a6bdd0SArik Nemtsov }
1588a7a6bdd0SArik Nemtsov
1589a7a6bdd0SArik Nemtsov static struct sk_buff *
ieee80211_tdls_ch_sw_tmpl_get(struct sta_info * sta,u8 oper_class,struct cfg80211_chan_def * chandef,u32 * ch_sw_tm_ie_offset)1590a7a6bdd0SArik Nemtsov ieee80211_tdls_ch_sw_tmpl_get(struct sta_info *sta, u8 oper_class,
1591a7a6bdd0SArik Nemtsov struct cfg80211_chan_def *chandef,
1592a7a6bdd0SArik Nemtsov u32 *ch_sw_tm_ie_offset)
1593a7a6bdd0SArik Nemtsov {
1594a7a6bdd0SArik Nemtsov struct ieee80211_sub_if_data *sdata = sta->sdata;
1595a7a6bdd0SArik Nemtsov u8 extra_ies[2 + sizeof(struct ieee80211_sec_chan_offs_ie) +
1596a7a6bdd0SArik Nemtsov 2 + sizeof(struct ieee80211_ch_switch_timing)];
1597a7a6bdd0SArik Nemtsov int extra_ies_len = 2 + sizeof(struct ieee80211_ch_switch_timing);
1598a7a6bdd0SArik Nemtsov u8 *pos = extra_ies;
1599a7a6bdd0SArik Nemtsov struct sk_buff *skb;
160078a7ea37SMukesh Sisodiya int link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0;
1601a7a6bdd0SArik Nemtsov
1602a7a6bdd0SArik Nemtsov /*
1603a7a6bdd0SArik Nemtsov * if chandef points to a wide channel add a Secondary-Channel
1604a7a6bdd0SArik Nemtsov * Offset information element
1605a7a6bdd0SArik Nemtsov */
1606a7a6bdd0SArik Nemtsov if (chandef->width == NL80211_CHAN_WIDTH_40) {
1607a7a6bdd0SArik Nemtsov struct ieee80211_sec_chan_offs_ie *sec_chan_ie;
1608a7a6bdd0SArik Nemtsov bool ht40plus;
1609a7a6bdd0SArik Nemtsov
1610a7a6bdd0SArik Nemtsov *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
1611a7a6bdd0SArik Nemtsov *pos++ = sizeof(*sec_chan_ie);
1612a7a6bdd0SArik Nemtsov sec_chan_ie = (void *)pos;
1613a7a6bdd0SArik Nemtsov
1614a7a6bdd0SArik Nemtsov ht40plus = cfg80211_get_chandef_type(chandef) ==
1615a7a6bdd0SArik Nemtsov NL80211_CHAN_HT40PLUS;
1616a7a6bdd0SArik Nemtsov sec_chan_ie->sec_chan_offs = ht40plus ?
1617a7a6bdd0SArik Nemtsov IEEE80211_HT_PARAM_CHA_SEC_ABOVE :
1618a7a6bdd0SArik Nemtsov IEEE80211_HT_PARAM_CHA_SEC_BELOW;
1619a7a6bdd0SArik Nemtsov pos += sizeof(*sec_chan_ie);
1620a7a6bdd0SArik Nemtsov
1621a7a6bdd0SArik Nemtsov extra_ies_len += 2 + sizeof(struct ieee80211_sec_chan_offs_ie);
1622a7a6bdd0SArik Nemtsov }
1623a7a6bdd0SArik Nemtsov
1624a7a6bdd0SArik Nemtsov /* just set the values to 0, this is a template */
1625a7a6bdd0SArik Nemtsov iee80211_tdls_add_ch_switch_timing(pos, 0, 0);
1626a7a6bdd0SArik Nemtsov
1627a7a6bdd0SArik Nemtsov skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr,
162878a7ea37SMukesh Sisodiya link_id,
1629a7a6bdd0SArik Nemtsov WLAN_TDLS_CHANNEL_SWITCH_REQUEST,
1630a7a6bdd0SArik Nemtsov 0, 0, !sta->sta.tdls_initiator,
1631a7a6bdd0SArik Nemtsov extra_ies, extra_ies_len,
1632a7a6bdd0SArik Nemtsov oper_class, chandef);
1633a7a6bdd0SArik Nemtsov if (!skb)
1634a7a6bdd0SArik Nemtsov return NULL;
1635a7a6bdd0SArik Nemtsov
1636a7a6bdd0SArik Nemtsov skb = ieee80211_build_data_template(sdata, skb, 0);
1637a7a6bdd0SArik Nemtsov if (IS_ERR(skb)) {
1638a7a6bdd0SArik Nemtsov tdls_dbg(sdata, "Failed building TDLS channel switch frame\n");
1639a7a6bdd0SArik Nemtsov return NULL;
1640a7a6bdd0SArik Nemtsov }
1641a7a6bdd0SArik Nemtsov
1642a7a6bdd0SArik Nemtsov if (ch_sw_tm_ie_offset) {
1643a7a6bdd0SArik Nemtsov const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb);
1644a7a6bdd0SArik Nemtsov
1645a7a6bdd0SArik Nemtsov if (!tm_ie) {
1646a7a6bdd0SArik Nemtsov tdls_dbg(sdata, "No switch timing IE in TDLS switch\n");
1647a7a6bdd0SArik Nemtsov dev_kfree_skb_any(skb);
1648a7a6bdd0SArik Nemtsov return NULL;
1649a7a6bdd0SArik Nemtsov }
1650a7a6bdd0SArik Nemtsov
1651a7a6bdd0SArik Nemtsov *ch_sw_tm_ie_offset = tm_ie - skb->data;
1652a7a6bdd0SArik Nemtsov }
1653a7a6bdd0SArik Nemtsov
1654a7a6bdd0SArik Nemtsov tdls_dbg(sdata,
1655a7a6bdd0SArik Nemtsov "TDLS channel switch request template for %pM ch %d width %d\n",
1656a7a6bdd0SArik Nemtsov sta->sta.addr, chandef->chan->center_freq, chandef->width);
1657a7a6bdd0SArik Nemtsov return skb;
1658a7a6bdd0SArik Nemtsov }
1659a7a6bdd0SArik Nemtsov
1660a7a6bdd0SArik Nemtsov int
ieee80211_tdls_channel_switch(struct wiphy * wiphy,struct net_device * dev,const u8 * addr,u8 oper_class,struct cfg80211_chan_def * chandef)1661a7a6bdd0SArik Nemtsov ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
1662a7a6bdd0SArik Nemtsov const u8 *addr, u8 oper_class,
1663a7a6bdd0SArik Nemtsov struct cfg80211_chan_def *chandef)
1664a7a6bdd0SArik Nemtsov {
1665a7a6bdd0SArik Nemtsov struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1666a7a6bdd0SArik Nemtsov struct ieee80211_local *local = sdata->local;
1667a7a6bdd0SArik Nemtsov struct sta_info *sta;
1668a7a6bdd0SArik Nemtsov struct sk_buff *skb = NULL;
1669a7a6bdd0SArik Nemtsov u32 ch_sw_tm_ie;
1670a7a6bdd0SArik Nemtsov int ret;
1671a7a6bdd0SArik Nemtsov
1672b6011960SThomas Pedersen if (chandef->chan->freq_offset)
1673b6011960SThomas Pedersen /* this may work, but is untested */
1674b6011960SThomas Pedersen return -EOPNOTSUPP;
1675b6011960SThomas Pedersen
1676a7a6bdd0SArik Nemtsov mutex_lock(&local->sta_mtx);
1677a7a6bdd0SArik Nemtsov sta = sta_info_get(sdata, addr);
1678a7a6bdd0SArik Nemtsov if (!sta) {
1679a7a6bdd0SArik Nemtsov tdls_dbg(sdata,
1680a7a6bdd0SArik Nemtsov "Invalid TDLS peer %pM for channel switch request\n",
1681a7a6bdd0SArik Nemtsov addr);
1682a7a6bdd0SArik Nemtsov ret = -ENOENT;
1683a7a6bdd0SArik Nemtsov goto out;
1684a7a6bdd0SArik Nemtsov }
1685a7a6bdd0SArik Nemtsov
1686a7a6bdd0SArik Nemtsov if (!test_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH)) {
1687a7a6bdd0SArik Nemtsov tdls_dbg(sdata, "TDLS channel switch unsupported by %pM\n",
1688a7a6bdd0SArik Nemtsov addr);
1689a7a6bdd0SArik Nemtsov ret = -ENOTSUPP;
1690a7a6bdd0SArik Nemtsov goto out;
1691a7a6bdd0SArik Nemtsov }
1692a7a6bdd0SArik Nemtsov
1693a7a6bdd0SArik Nemtsov skb = ieee80211_tdls_ch_sw_tmpl_get(sta, oper_class, chandef,
1694a7a6bdd0SArik Nemtsov &ch_sw_tm_ie);
1695a7a6bdd0SArik Nemtsov if (!skb) {
1696a7a6bdd0SArik Nemtsov ret = -ENOENT;
1697a7a6bdd0SArik Nemtsov goto out;
1698a7a6bdd0SArik Nemtsov }
1699a7a6bdd0SArik Nemtsov
1700a7a6bdd0SArik Nemtsov ret = drv_tdls_channel_switch(local, sdata, &sta->sta, oper_class,
1701a7a6bdd0SArik Nemtsov chandef, skb, ch_sw_tm_ie);
1702a7a6bdd0SArik Nemtsov if (!ret)
1703a7a6bdd0SArik Nemtsov set_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
1704a7a6bdd0SArik Nemtsov
1705a7a6bdd0SArik Nemtsov out:
1706a7a6bdd0SArik Nemtsov mutex_unlock(&local->sta_mtx);
1707a7a6bdd0SArik Nemtsov dev_kfree_skb_any(skb);
1708a7a6bdd0SArik Nemtsov return ret;
1709a7a6bdd0SArik Nemtsov }
1710a7a6bdd0SArik Nemtsov
1711a7a6bdd0SArik Nemtsov void
ieee80211_tdls_cancel_channel_switch(struct wiphy * wiphy,struct net_device * dev,const u8 * addr)1712a7a6bdd0SArik Nemtsov ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
1713a7a6bdd0SArik Nemtsov struct net_device *dev,
1714a7a6bdd0SArik Nemtsov const u8 *addr)
1715a7a6bdd0SArik Nemtsov {
1716a7a6bdd0SArik Nemtsov struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1717a7a6bdd0SArik Nemtsov struct ieee80211_local *local = sdata->local;
1718a7a6bdd0SArik Nemtsov struct sta_info *sta;
1719a7a6bdd0SArik Nemtsov
1720a7a6bdd0SArik Nemtsov mutex_lock(&local->sta_mtx);
1721a7a6bdd0SArik Nemtsov sta = sta_info_get(sdata, addr);
1722a7a6bdd0SArik Nemtsov if (!sta) {
1723a7a6bdd0SArik Nemtsov tdls_dbg(sdata,
1724a7a6bdd0SArik Nemtsov "Invalid TDLS peer %pM for channel switch cancel\n",
1725a7a6bdd0SArik Nemtsov addr);
1726a7a6bdd0SArik Nemtsov goto out;
1727a7a6bdd0SArik Nemtsov }
1728a7a6bdd0SArik Nemtsov
1729a7a6bdd0SArik Nemtsov if (!test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) {
1730a7a6bdd0SArik Nemtsov tdls_dbg(sdata, "TDLS channel switch not initiated by %pM\n",
1731a7a6bdd0SArik Nemtsov addr);
1732a7a6bdd0SArik Nemtsov goto out;
1733a7a6bdd0SArik Nemtsov }
1734a7a6bdd0SArik Nemtsov
1735a7a6bdd0SArik Nemtsov drv_tdls_cancel_channel_switch(local, sdata, &sta->sta);
1736a7a6bdd0SArik Nemtsov clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
1737a7a6bdd0SArik Nemtsov
1738a7a6bdd0SArik Nemtsov out:
1739a7a6bdd0SArik Nemtsov mutex_unlock(&local->sta_mtx);
1740a7a6bdd0SArik Nemtsov }
17418a4d32f3SArik Nemtsov
17428a4d32f3SArik Nemtsov static struct sk_buff *
ieee80211_tdls_ch_sw_resp_tmpl_get(struct sta_info * sta,u32 * ch_sw_tm_ie_offset)17438a4d32f3SArik Nemtsov ieee80211_tdls_ch_sw_resp_tmpl_get(struct sta_info *sta,
17448a4d32f3SArik Nemtsov u32 *ch_sw_tm_ie_offset)
17458a4d32f3SArik Nemtsov {
17468a4d32f3SArik Nemtsov struct ieee80211_sub_if_data *sdata = sta->sdata;
17478a4d32f3SArik Nemtsov struct sk_buff *skb;
17488a4d32f3SArik Nemtsov u8 extra_ies[2 + sizeof(struct ieee80211_ch_switch_timing)];
174978a7ea37SMukesh Sisodiya int link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0;
17508a4d32f3SArik Nemtsov
17518a4d32f3SArik Nemtsov /* initial timing are always zero in the template */
17528a4d32f3SArik Nemtsov iee80211_tdls_add_ch_switch_timing(extra_ies, 0, 0);
17538a4d32f3SArik Nemtsov
17548a4d32f3SArik Nemtsov skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr,
175578a7ea37SMukesh Sisodiya link_id,
17568a4d32f3SArik Nemtsov WLAN_TDLS_CHANNEL_SWITCH_RESPONSE,
17578a4d32f3SArik Nemtsov 0, 0, !sta->sta.tdls_initiator,
17588a4d32f3SArik Nemtsov extra_ies, sizeof(extra_ies), 0, NULL);
17598a4d32f3SArik Nemtsov if (!skb)
17608a4d32f3SArik Nemtsov return NULL;
17618a4d32f3SArik Nemtsov
17628a4d32f3SArik Nemtsov skb = ieee80211_build_data_template(sdata, skb, 0);
17638a4d32f3SArik Nemtsov if (IS_ERR(skb)) {
17648a4d32f3SArik Nemtsov tdls_dbg(sdata,
17658a4d32f3SArik Nemtsov "Failed building TDLS channel switch resp frame\n");
17668a4d32f3SArik Nemtsov return NULL;
17678a4d32f3SArik Nemtsov }
17688a4d32f3SArik Nemtsov
17698a4d32f3SArik Nemtsov if (ch_sw_tm_ie_offset) {
17708a4d32f3SArik Nemtsov const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb);
17718a4d32f3SArik Nemtsov
17728a4d32f3SArik Nemtsov if (!tm_ie) {
17738a4d32f3SArik Nemtsov tdls_dbg(sdata,
17748a4d32f3SArik Nemtsov "No switch timing IE in TDLS switch resp\n");
17758a4d32f3SArik Nemtsov dev_kfree_skb_any(skb);
17768a4d32f3SArik Nemtsov return NULL;
17778a4d32f3SArik Nemtsov }
17788a4d32f3SArik Nemtsov
17798a4d32f3SArik Nemtsov *ch_sw_tm_ie_offset = tm_ie - skb->data;
17808a4d32f3SArik Nemtsov }
17818a4d32f3SArik Nemtsov
17828a4d32f3SArik Nemtsov tdls_dbg(sdata, "TDLS get channel switch response template for %pM\n",
17838a4d32f3SArik Nemtsov sta->sta.addr);
17848a4d32f3SArik Nemtsov return skb;
17858a4d32f3SArik Nemtsov }
17868a4d32f3SArik Nemtsov
17878a4d32f3SArik Nemtsov static int
ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data * sdata,struct sk_buff * skb)17888a4d32f3SArik Nemtsov ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata,
17898a4d32f3SArik Nemtsov struct sk_buff *skb)
17908a4d32f3SArik Nemtsov {
17918a4d32f3SArik Nemtsov struct ieee80211_local *local = sdata->local;
17925d24828dSJohannes Berg struct ieee802_11_elems *elems = NULL;
17938a4d32f3SArik Nemtsov struct sta_info *sta;
17948a4d32f3SArik Nemtsov struct ieee80211_tdls_data *tf = (void *)skb->data;
17958a4d32f3SArik Nemtsov bool local_initiator;
17968a4d32f3SArik Nemtsov struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
17978a4d32f3SArik Nemtsov int baselen = offsetof(typeof(*tf), u.chan_switch_resp.variable);
17988a4d32f3SArik Nemtsov struct ieee80211_tdls_ch_sw_params params = {};
17998a4d32f3SArik Nemtsov int ret;
18008a4d32f3SArik Nemtsov
18018a4d32f3SArik Nemtsov params.action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE;
18028a4d32f3SArik Nemtsov params.timestamp = rx_status->device_timestamp;
18038a4d32f3SArik Nemtsov
18048a4d32f3SArik Nemtsov if (skb->len < baselen) {
18058a4d32f3SArik Nemtsov tdls_dbg(sdata, "TDLS channel switch resp too short: %d\n",
18068a4d32f3SArik Nemtsov skb->len);
18078a4d32f3SArik Nemtsov return -EINVAL;
18088a4d32f3SArik Nemtsov }
18098a4d32f3SArik Nemtsov
18108a4d32f3SArik Nemtsov mutex_lock(&local->sta_mtx);
18118a4d32f3SArik Nemtsov sta = sta_info_get(sdata, tf->sa);
18128a4d32f3SArik Nemtsov if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) {
18138a4d32f3SArik Nemtsov tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n",
18148a4d32f3SArik Nemtsov tf->sa);
18158a4d32f3SArik Nemtsov ret = -EINVAL;
18168a4d32f3SArik Nemtsov goto out;
18178a4d32f3SArik Nemtsov }
18188a4d32f3SArik Nemtsov
18198a4d32f3SArik Nemtsov params.sta = &sta->sta;
18208a4d32f3SArik Nemtsov params.status = le16_to_cpu(tf->u.chan_switch_resp.status_code);
18218a4d32f3SArik Nemtsov if (params.status != 0) {
18228a4d32f3SArik Nemtsov ret = 0;
18238a4d32f3SArik Nemtsov goto call_drv;
18248a4d32f3SArik Nemtsov }
18258a4d32f3SArik Nemtsov
18265d24828dSJohannes Berg elems = ieee802_11_parse_elems(tf->u.chan_switch_resp.variable,
182738c6aa29SJohannes Berg skb->len - baselen, false, NULL);
18285d24828dSJohannes Berg if (!elems) {
18295d24828dSJohannes Berg ret = -ENOMEM;
18305d24828dSJohannes Berg goto out;
18315d24828dSJohannes Berg }
18325d24828dSJohannes Berg
18335d24828dSJohannes Berg if (elems->parse_error) {
18348a4d32f3SArik Nemtsov tdls_dbg(sdata, "Invalid IEs in TDLS channel switch resp\n");
18358a4d32f3SArik Nemtsov ret = -EINVAL;
18368a4d32f3SArik Nemtsov goto out;
18378a4d32f3SArik Nemtsov }
18388a4d32f3SArik Nemtsov
18395d24828dSJohannes Berg if (!elems->ch_sw_timing || !elems->lnk_id) {
18408a4d32f3SArik Nemtsov tdls_dbg(sdata, "TDLS channel switch resp - missing IEs\n");
18418a4d32f3SArik Nemtsov ret = -EINVAL;
18428a4d32f3SArik Nemtsov goto out;
18438a4d32f3SArik Nemtsov }
18448a4d32f3SArik Nemtsov
18458a4d32f3SArik Nemtsov /* validate the initiator is set correctly */
18468a4d32f3SArik Nemtsov local_initiator =
18475d24828dSJohannes Berg !memcmp(elems->lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
18488a4d32f3SArik Nemtsov if (local_initiator == sta->sta.tdls_initiator) {
18498a4d32f3SArik Nemtsov tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n");
18508a4d32f3SArik Nemtsov ret = -EINVAL;
18518a4d32f3SArik Nemtsov goto out;
18528a4d32f3SArik Nemtsov }
18538a4d32f3SArik Nemtsov
18545d24828dSJohannes Berg params.switch_time = le16_to_cpu(elems->ch_sw_timing->switch_time);
18555d24828dSJohannes Berg params.switch_timeout = le16_to_cpu(elems->ch_sw_timing->switch_timeout);
18568a4d32f3SArik Nemtsov
18578a4d32f3SArik Nemtsov params.tmpl_skb =
18588a4d32f3SArik Nemtsov ieee80211_tdls_ch_sw_resp_tmpl_get(sta, ¶ms.ch_sw_tm_ie);
18598a4d32f3SArik Nemtsov if (!params.tmpl_skb) {
18608a4d32f3SArik Nemtsov ret = -ENOENT;
18618a4d32f3SArik Nemtsov goto out;
18628a4d32f3SArik Nemtsov }
18638a4d32f3SArik Nemtsov
186449708e37SDan Carpenter ret = 0;
18658a4d32f3SArik Nemtsov call_drv:
18668a4d32f3SArik Nemtsov drv_tdls_recv_channel_switch(sdata->local, sdata, ¶ms);
18678a4d32f3SArik Nemtsov
18688a4d32f3SArik Nemtsov tdls_dbg(sdata,
18698a4d32f3SArik Nemtsov "TDLS channel switch response received from %pM status %d\n",
18708a4d32f3SArik Nemtsov tf->sa, params.status);
18718a4d32f3SArik Nemtsov
18728a4d32f3SArik Nemtsov out:
18738a4d32f3SArik Nemtsov mutex_unlock(&local->sta_mtx);
18748a4d32f3SArik Nemtsov dev_kfree_skb_any(params.tmpl_skb);
18755d24828dSJohannes Berg kfree(elems);
18768a4d32f3SArik Nemtsov return ret;
18778a4d32f3SArik Nemtsov }
18788a4d32f3SArik Nemtsov
18798a4d32f3SArik Nemtsov static int
ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data * sdata,struct sk_buff * skb)18808a4d32f3SArik Nemtsov ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
18818a4d32f3SArik Nemtsov struct sk_buff *skb)
18828a4d32f3SArik Nemtsov {
18838a4d32f3SArik Nemtsov struct ieee80211_local *local = sdata->local;
18845d24828dSJohannes Berg struct ieee802_11_elems *elems;
18858a4d32f3SArik Nemtsov struct cfg80211_chan_def chandef;
18868a4d32f3SArik Nemtsov struct ieee80211_channel *chan;
18878a4d32f3SArik Nemtsov enum nl80211_channel_type chan_type;
18888a4d32f3SArik Nemtsov int freq;
18898a4d32f3SArik Nemtsov u8 target_channel, oper_class;
18908a4d32f3SArik Nemtsov bool local_initiator;
18918a4d32f3SArik Nemtsov struct sta_info *sta;
189257fbcce3SJohannes Berg enum nl80211_band band;
18938a4d32f3SArik Nemtsov struct ieee80211_tdls_data *tf = (void *)skb->data;
18948a4d32f3SArik Nemtsov struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
18958a4d32f3SArik Nemtsov int baselen = offsetof(typeof(*tf), u.chan_switch_req.variable);
18968a4d32f3SArik Nemtsov struct ieee80211_tdls_ch_sw_params params = {};
18978a4d32f3SArik Nemtsov int ret = 0;
18988a4d32f3SArik Nemtsov
18998a4d32f3SArik Nemtsov params.action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST;
19008a4d32f3SArik Nemtsov params.timestamp = rx_status->device_timestamp;
19018a4d32f3SArik Nemtsov
19028a4d32f3SArik Nemtsov if (skb->len < baselen) {
19038a4d32f3SArik Nemtsov tdls_dbg(sdata, "TDLS channel switch req too short: %d\n",
19048a4d32f3SArik Nemtsov skb->len);
19058a4d32f3SArik Nemtsov return -EINVAL;
19068a4d32f3SArik Nemtsov }
19078a4d32f3SArik Nemtsov
19088a4d32f3SArik Nemtsov target_channel = tf->u.chan_switch_req.target_channel;
19098a4d32f3SArik Nemtsov oper_class = tf->u.chan_switch_req.oper_class;
19108a4d32f3SArik Nemtsov
19118a4d32f3SArik Nemtsov /*
19128a4d32f3SArik Nemtsov * We can't easily infer the channel band. The operating class is
19138a4d32f3SArik Nemtsov * ambiguous - there are multiple tables (US/Europe/JP/Global). The
19148a4d32f3SArik Nemtsov * solution here is to treat channels with number >14 as 5GHz ones,
19158a4d32f3SArik Nemtsov * and specifically check for the (oper_class, channel) combinations
19168a4d32f3SArik Nemtsov * where this doesn't hold. These are thankfully unique according to
19178a4d32f3SArik Nemtsov * IEEE802.11-2012.
19188a4d32f3SArik Nemtsov * We consider only the 2GHz and 5GHz bands and 20MHz+ channels as
19198a4d32f3SArik Nemtsov * valid here.
19208a4d32f3SArik Nemtsov */
19218a4d32f3SArik Nemtsov if ((oper_class == 112 || oper_class == 2 || oper_class == 3 ||
19228a4d32f3SArik Nemtsov oper_class == 4 || oper_class == 5 || oper_class == 6) &&
19238a4d32f3SArik Nemtsov target_channel < 14)
192457fbcce3SJohannes Berg band = NL80211_BAND_5GHZ;
19258a4d32f3SArik Nemtsov else
192657fbcce3SJohannes Berg band = target_channel < 14 ? NL80211_BAND_2GHZ :
192757fbcce3SJohannes Berg NL80211_BAND_5GHZ;
19288a4d32f3SArik Nemtsov
19298a4d32f3SArik Nemtsov freq = ieee80211_channel_to_frequency(target_channel, band);
19308a4d32f3SArik Nemtsov if (freq == 0) {
19318a4d32f3SArik Nemtsov tdls_dbg(sdata, "Invalid channel in TDLS chan switch: %d\n",
19328a4d32f3SArik Nemtsov target_channel);
19338a4d32f3SArik Nemtsov return -EINVAL;
19348a4d32f3SArik Nemtsov }
19358a4d32f3SArik Nemtsov
19368a4d32f3SArik Nemtsov chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq);
19378a4d32f3SArik Nemtsov if (!chan) {
19388a4d32f3SArik Nemtsov tdls_dbg(sdata,
19398a4d32f3SArik Nemtsov "Unsupported channel for TDLS chan switch: %d\n",
19408a4d32f3SArik Nemtsov target_channel);
19418a4d32f3SArik Nemtsov return -EINVAL;
19428a4d32f3SArik Nemtsov }
19438a4d32f3SArik Nemtsov
19445d24828dSJohannes Berg elems = ieee802_11_parse_elems(tf->u.chan_switch_req.variable,
194538c6aa29SJohannes Berg skb->len - baselen, false, NULL);
19465d24828dSJohannes Berg if (!elems)
19475d24828dSJohannes Berg return -ENOMEM;
19485d24828dSJohannes Berg
19495d24828dSJohannes Berg if (elems->parse_error) {
19508a4d32f3SArik Nemtsov tdls_dbg(sdata, "Invalid IEs in TDLS channel switch req\n");
19515d24828dSJohannes Berg ret = -EINVAL;
19525d24828dSJohannes Berg goto free;
19538a4d32f3SArik Nemtsov }
19548a4d32f3SArik Nemtsov
19555d24828dSJohannes Berg if (!elems->ch_sw_timing || !elems->lnk_id) {
19568a4d32f3SArik Nemtsov tdls_dbg(sdata, "TDLS channel switch req - missing IEs\n");
19575d24828dSJohannes Berg ret = -EINVAL;
19585d24828dSJohannes Berg goto free;
19598a4d32f3SArik Nemtsov }
19608a4d32f3SArik Nemtsov
19615d24828dSJohannes Berg if (!elems->sec_chan_offs) {
196242d8d789SArik Nemtsov chan_type = NL80211_CHAN_HT20;
196342d8d789SArik Nemtsov } else {
19645d24828dSJohannes Berg switch (elems->sec_chan_offs->sec_chan_offs) {
196542d8d789SArik Nemtsov case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
196642d8d789SArik Nemtsov chan_type = NL80211_CHAN_HT40PLUS;
196742d8d789SArik Nemtsov break;
196842d8d789SArik Nemtsov case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
196942d8d789SArik Nemtsov chan_type = NL80211_CHAN_HT40MINUS;
197042d8d789SArik Nemtsov break;
197142d8d789SArik Nemtsov default:
197242d8d789SArik Nemtsov chan_type = NL80211_CHAN_HT20;
197342d8d789SArik Nemtsov break;
197442d8d789SArik Nemtsov }
197542d8d789SArik Nemtsov }
197642d8d789SArik Nemtsov
197742d8d789SArik Nemtsov cfg80211_chandef_create(&chandef, chan, chan_type);
197842d8d789SArik Nemtsov
197942d8d789SArik Nemtsov /* we will be active on the TDLS link */
198042d8d789SArik Nemtsov if (!cfg80211_reg_can_beacon_relax(sdata->local->hw.wiphy, &chandef,
198142d8d789SArik Nemtsov sdata->wdev.iftype)) {
198242d8d789SArik Nemtsov tdls_dbg(sdata, "TDLS chan switch to forbidden channel\n");
19835d24828dSJohannes Berg ret = -EINVAL;
19845d24828dSJohannes Berg goto free;
198542d8d789SArik Nemtsov }
198642d8d789SArik Nemtsov
19878a4d32f3SArik Nemtsov mutex_lock(&local->sta_mtx);
19888a4d32f3SArik Nemtsov sta = sta_info_get(sdata, tf->sa);
19898a4d32f3SArik Nemtsov if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) {
19908a4d32f3SArik Nemtsov tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n",
19918a4d32f3SArik Nemtsov tf->sa);
19928a4d32f3SArik Nemtsov ret = -EINVAL;
19938a4d32f3SArik Nemtsov goto out;
19948a4d32f3SArik Nemtsov }
19958a4d32f3SArik Nemtsov
19968a4d32f3SArik Nemtsov params.sta = &sta->sta;
19978a4d32f3SArik Nemtsov
19988a4d32f3SArik Nemtsov /* validate the initiator is set correctly */
19998a4d32f3SArik Nemtsov local_initiator =
20005d24828dSJohannes Berg !memcmp(elems->lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
20018a4d32f3SArik Nemtsov if (local_initiator == sta->sta.tdls_initiator) {
20028a4d32f3SArik Nemtsov tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n");
20038a4d32f3SArik Nemtsov ret = -EINVAL;
20048a4d32f3SArik Nemtsov goto out;
20058a4d32f3SArik Nemtsov }
20068a4d32f3SArik Nemtsov
200742d8d789SArik Nemtsov /* peer should have known better */
2008046d2e7cSSriram R if (!sta->sta.deflink.ht_cap.ht_supported && elems->sec_chan_offs &&
20095d24828dSJohannes Berg elems->sec_chan_offs->sec_chan_offs) {
201042d8d789SArik Nemtsov tdls_dbg(sdata, "TDLS chan switch - wide chan unsupported\n");
201142d8d789SArik Nemtsov ret = -ENOTSUPP;
201242d8d789SArik Nemtsov goto out;
20138a4d32f3SArik Nemtsov }
20148a4d32f3SArik Nemtsov
20158a4d32f3SArik Nemtsov params.chandef = &chandef;
20165d24828dSJohannes Berg params.switch_time = le16_to_cpu(elems->ch_sw_timing->switch_time);
20175d24828dSJohannes Berg params.switch_timeout = le16_to_cpu(elems->ch_sw_timing->switch_timeout);
20188a4d32f3SArik Nemtsov
20198a4d32f3SArik Nemtsov params.tmpl_skb =
20208a4d32f3SArik Nemtsov ieee80211_tdls_ch_sw_resp_tmpl_get(sta,
20218a4d32f3SArik Nemtsov ¶ms.ch_sw_tm_ie);
20228a4d32f3SArik Nemtsov if (!params.tmpl_skb) {
20238a4d32f3SArik Nemtsov ret = -ENOENT;
20248a4d32f3SArik Nemtsov goto out;
20258a4d32f3SArik Nemtsov }
20268a4d32f3SArik Nemtsov
20278a4d32f3SArik Nemtsov drv_tdls_recv_channel_switch(sdata->local, sdata, ¶ms);
20288a4d32f3SArik Nemtsov
20298a4d32f3SArik Nemtsov tdls_dbg(sdata,
20308a4d32f3SArik Nemtsov "TDLS ch switch request received from %pM ch %d width %d\n",
20318a4d32f3SArik Nemtsov tf->sa, params.chandef->chan->center_freq,
20328a4d32f3SArik Nemtsov params.chandef->width);
20338a4d32f3SArik Nemtsov out:
20348a4d32f3SArik Nemtsov mutex_unlock(&local->sta_mtx);
20358a4d32f3SArik Nemtsov dev_kfree_skb_any(params.tmpl_skb);
20365d24828dSJohannes Berg free:
20375d24828dSJohannes Berg kfree(elems);
20388a4d32f3SArik Nemtsov return ret;
20398a4d32f3SArik Nemtsov }
20408a4d32f3SArik Nemtsov
2041f057d140SJohannes Berg void
ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data * sdata,struct sk_buff * skb)2042c8ff71e6SArik Nemtsov ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
20438a4d32f3SArik Nemtsov struct sk_buff *skb)
20448a4d32f3SArik Nemtsov {
20458a4d32f3SArik Nemtsov struct ieee80211_tdls_data *tf = (void *)skb->data;
20468a4d32f3SArik Nemtsov struct wiphy *wiphy = sdata->local->hw.wiphy;
20478a4d32f3SArik Nemtsov
2048a05829a7SJohannes Berg lockdep_assert_wiphy(wiphy);
2049c8ff71e6SArik Nemtsov
20508a4d32f3SArik Nemtsov /* make sure the driver supports it */
20518a4d32f3SArik Nemtsov if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
20528a4d32f3SArik Nemtsov return;
20538a4d32f3SArik Nemtsov
20548a4d32f3SArik Nemtsov /* we want to access the entire packet */
20558a4d32f3SArik Nemtsov if (skb_linearize(skb))
20568a4d32f3SArik Nemtsov return;
20578a4d32f3SArik Nemtsov /*
20588a4d32f3SArik Nemtsov * The packet/size was already validated by mac80211 Rx path, only look
20598a4d32f3SArik Nemtsov * at the action type.
20608a4d32f3SArik Nemtsov */
20618a4d32f3SArik Nemtsov switch (tf->action_code) {
20628a4d32f3SArik Nemtsov case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
20638a4d32f3SArik Nemtsov ieee80211_process_tdls_channel_switch_req(sdata, skb);
20648a4d32f3SArik Nemtsov break;
20658a4d32f3SArik Nemtsov case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
20668a4d32f3SArik Nemtsov ieee80211_process_tdls_channel_switch_resp(sdata, skb);
20678a4d32f3SArik Nemtsov break;
20688a4d32f3SArik Nemtsov default:
20698a4d32f3SArik Nemtsov WARN_ON_ONCE(1);
20708a4d32f3SArik Nemtsov return;
20718a4d32f3SArik Nemtsov }
20728a4d32f3SArik Nemtsov }
2073d51c2ea3SArik Nemtsov
ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data * sdata)2074d51c2ea3SArik Nemtsov void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata)
2075d51c2ea3SArik Nemtsov {
2076d51c2ea3SArik Nemtsov struct sta_info *sta;
2077d51c2ea3SArik Nemtsov u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
2078d51c2ea3SArik Nemtsov
2079d51c2ea3SArik Nemtsov rcu_read_lock();
2080d51c2ea3SArik Nemtsov list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
2081d51c2ea3SArik Nemtsov if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
2082d51c2ea3SArik Nemtsov !test_sta_flag(sta, WLAN_STA_AUTHORIZED))
2083d51c2ea3SArik Nemtsov continue;
2084d51c2ea3SArik Nemtsov
2085d51c2ea3SArik Nemtsov ieee80211_tdls_oper_request(&sdata->vif, sta->sta.addr,
2086d51c2ea3SArik Nemtsov NL80211_TDLS_TEARDOWN, reason,
2087d51c2ea3SArik Nemtsov GFP_ATOMIC);
2088d51c2ea3SArik Nemtsov }
2089d51c2ea3SArik Nemtsov rcu_read_unlock();
2090d51c2ea3SArik Nemtsov }
2091c8ff71e6SArik Nemtsov
ieee80211_tdls_handle_disconnect(struct ieee80211_sub_if_data * sdata,const u8 * peer,u16 reason)209279c92ca4SYu Wang void ieee80211_tdls_handle_disconnect(struct ieee80211_sub_if_data *sdata,
209379c92ca4SYu Wang const u8 *peer, u16 reason)
209479c92ca4SYu Wang {
209579c92ca4SYu Wang struct ieee80211_sta *sta;
209679c92ca4SYu Wang
209779c92ca4SYu Wang rcu_read_lock();
209879c92ca4SYu Wang sta = ieee80211_find_sta(&sdata->vif, peer);
209979c92ca4SYu Wang if (!sta || !sta->tdls) {
210079c92ca4SYu Wang rcu_read_unlock();
210179c92ca4SYu Wang return;
210279c92ca4SYu Wang }
210379c92ca4SYu Wang rcu_read_unlock();
210479c92ca4SYu Wang
210579c92ca4SYu Wang tdls_dbg(sdata, "disconnected from TDLS peer %pM (Reason: %u=%s)\n",
210679c92ca4SYu Wang peer, reason,
210779c92ca4SYu Wang ieee80211_get_reason_code_string(reason));
210879c92ca4SYu Wang
210979c92ca4SYu Wang ieee80211_tdls_oper_request(&sdata->vif, peer,
211079c92ca4SYu Wang NL80211_TDLS_TEARDOWN,
211179c92ca4SYu Wang WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE,
211279c92ca4SYu Wang GFP_ATOMIC);
211379c92ca4SYu Wang }
2114