xref: /openbmc/linux/net/mac80211/tdls.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
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, &params.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, &params);
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 						   &params.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, &params);
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