xref: /freebsd/sys/contrib/dev/iwlwifi/mvm/tdls.c (revision a4128aad8503277614f2d214011ef60a19447b83)
1bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2bfcc09ddSBjoern A. Zeeb /*
3bfcc09ddSBjoern A. Zeeb  * Copyright (C) 2014 Intel Mobile Communications GmbH
4bfcc09ddSBjoern A. Zeeb  * Copyright (C) 2017 Intel Deutschland GmbH
5*a4128aadSBjoern A. Zeeb  * Copyright (C) 2018-2020, 2022-2024 Intel Corporation
6bfcc09ddSBjoern A. Zeeb  */
7bfcc09ddSBjoern A. Zeeb #if defined(__FreeBSD__)
8bfcc09ddSBjoern A. Zeeb #include <linux/delay.h>
9bfcc09ddSBjoern A. Zeeb #endif
10bfcc09ddSBjoern A. Zeeb #include <linux/etherdevice.h>
11bfcc09ddSBjoern A. Zeeb #include "mvm.h"
12bfcc09ddSBjoern A. Zeeb #include "time-event.h"
13bfcc09ddSBjoern A. Zeeb #include "iwl-io.h"
14bfcc09ddSBjoern A. Zeeb #include "iwl-prph.h"
15bfcc09ddSBjoern A. Zeeb 
16bfcc09ddSBjoern A. Zeeb #define TU_TO_US(x) (x * 1024)
17bfcc09ddSBjoern A. Zeeb #define TU_TO_MS(x) (TU_TO_US(x) / 1000)
18bfcc09ddSBjoern A. Zeeb 
19bfcc09ddSBjoern A. Zeeb void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm)
20bfcc09ddSBjoern A. Zeeb {
21bfcc09ddSBjoern A. Zeeb 	struct ieee80211_sta *sta;
22bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_sta *mvmsta;
23bfcc09ddSBjoern A. Zeeb 	int i;
24bfcc09ddSBjoern A. Zeeb 
25bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
26bfcc09ddSBjoern A. Zeeb 
27bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
28bfcc09ddSBjoern A. Zeeb 		sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
29bfcc09ddSBjoern A. Zeeb 						lockdep_is_held(&mvm->mutex));
30bfcc09ddSBjoern A. Zeeb 		if (!sta || IS_ERR(sta) || !sta->tdls)
31bfcc09ddSBjoern A. Zeeb 			continue;
32bfcc09ddSBjoern A. Zeeb 
33bfcc09ddSBjoern A. Zeeb 		mvmsta = iwl_mvm_sta_from_mac80211(sta);
34bfcc09ddSBjoern A. Zeeb 		ieee80211_tdls_oper_request(mvmsta->vif, sta->addr,
35bfcc09ddSBjoern A. Zeeb 				NL80211_TDLS_TEARDOWN,
36bfcc09ddSBjoern A. Zeeb 				WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED,
37bfcc09ddSBjoern A. Zeeb 				GFP_KERNEL);
38bfcc09ddSBjoern A. Zeeb 	}
39bfcc09ddSBjoern A. Zeeb }
40bfcc09ddSBjoern A. Zeeb 
41bfcc09ddSBjoern A. Zeeb int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
42bfcc09ddSBjoern A. Zeeb {
43bfcc09ddSBjoern A. Zeeb 	struct ieee80211_sta *sta;
44bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_sta *mvmsta;
45bfcc09ddSBjoern A. Zeeb 	int count = 0;
46bfcc09ddSBjoern A. Zeeb 	int i;
47bfcc09ddSBjoern A. Zeeb 
48bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
49bfcc09ddSBjoern A. Zeeb 
50bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
51bfcc09ddSBjoern A. Zeeb 		sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
52bfcc09ddSBjoern A. Zeeb 						lockdep_is_held(&mvm->mutex));
53bfcc09ddSBjoern A. Zeeb 		if (!sta || IS_ERR(sta) || !sta->tdls)
54bfcc09ddSBjoern A. Zeeb 			continue;
55bfcc09ddSBjoern A. Zeeb 
56bfcc09ddSBjoern A. Zeeb 		if (vif) {
57bfcc09ddSBjoern A. Zeeb 			mvmsta = iwl_mvm_sta_from_mac80211(sta);
58bfcc09ddSBjoern A. Zeeb 			if (mvmsta->vif != vif)
59bfcc09ddSBjoern A. Zeeb 				continue;
60bfcc09ddSBjoern A. Zeeb 		}
61bfcc09ddSBjoern A. Zeeb 
62bfcc09ddSBjoern A. Zeeb 		count++;
63bfcc09ddSBjoern A. Zeeb 	}
64bfcc09ddSBjoern A. Zeeb 
65bfcc09ddSBjoern A. Zeeb 	return count;
66bfcc09ddSBjoern A. Zeeb }
67bfcc09ddSBjoern A. Zeeb 
68bfcc09ddSBjoern A. Zeeb static void iwl_mvm_tdls_config(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
69bfcc09ddSBjoern A. Zeeb {
70bfcc09ddSBjoern A. Zeeb 	struct iwl_rx_packet *pkt;
71bfcc09ddSBjoern A. Zeeb 	struct iwl_tdls_config_res *resp;
72bfcc09ddSBjoern A. Zeeb 	struct iwl_tdls_config_cmd tdls_cfg_cmd = {};
73bfcc09ddSBjoern A. Zeeb 	struct iwl_host_cmd cmd = {
74bfcc09ddSBjoern A. Zeeb 		.id = TDLS_CONFIG_CMD,
75bfcc09ddSBjoern A. Zeeb 		.flags = CMD_WANT_SKB,
76bfcc09ddSBjoern A. Zeeb 		.data = { &tdls_cfg_cmd, },
77bfcc09ddSBjoern A. Zeeb 		.len = { sizeof(struct iwl_tdls_config_cmd), },
78bfcc09ddSBjoern A. Zeeb 	};
79bfcc09ddSBjoern A. Zeeb 	struct ieee80211_sta *sta;
80bfcc09ddSBjoern A. Zeeb 	int ret, i, cnt;
81bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
82bfcc09ddSBjoern A. Zeeb 
83bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
84bfcc09ddSBjoern A. Zeeb 
85bfcc09ddSBjoern A. Zeeb 	tdls_cfg_cmd.id_and_color =
86bfcc09ddSBjoern A. Zeeb 		cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
87bfcc09ddSBjoern A. Zeeb 	tdls_cfg_cmd.tx_to_ap_tid = IWL_MVM_TDLS_FW_TID;
88bfcc09ddSBjoern A. Zeeb 	tdls_cfg_cmd.tx_to_ap_ssn = cpu_to_le16(0); /* not used for now */
89bfcc09ddSBjoern A. Zeeb 
90bfcc09ddSBjoern A. Zeeb 	/* for now the Tx cmd is empty and unused */
91bfcc09ddSBjoern A. Zeeb 
92bfcc09ddSBjoern A. Zeeb 	/* populate TDLS peer data */
93bfcc09ddSBjoern A. Zeeb 	cnt = 0;
94bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
95bfcc09ddSBjoern A. Zeeb 		sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
96bfcc09ddSBjoern A. Zeeb 						lockdep_is_held(&mvm->mutex));
97bfcc09ddSBjoern A. Zeeb 		if (IS_ERR_OR_NULL(sta) || !sta->tdls)
98bfcc09ddSBjoern A. Zeeb 			continue;
99bfcc09ddSBjoern A. Zeeb 
100bfcc09ddSBjoern A. Zeeb 		tdls_cfg_cmd.sta_info[cnt].sta_id = i;
101bfcc09ddSBjoern A. Zeeb 		tdls_cfg_cmd.sta_info[cnt].tx_to_peer_tid =
102bfcc09ddSBjoern A. Zeeb 							IWL_MVM_TDLS_FW_TID;
103bfcc09ddSBjoern A. Zeeb 		tdls_cfg_cmd.sta_info[cnt].tx_to_peer_ssn = cpu_to_le16(0);
104bfcc09ddSBjoern A. Zeeb 		tdls_cfg_cmd.sta_info[cnt].is_initiator =
105bfcc09ddSBjoern A. Zeeb 				cpu_to_le32(sta->tdls_initiator ? 1 : 0);
106bfcc09ddSBjoern A. Zeeb 
107bfcc09ddSBjoern A. Zeeb 		cnt++;
108bfcc09ddSBjoern A. Zeeb 	}
109bfcc09ddSBjoern A. Zeeb 
110bfcc09ddSBjoern A. Zeeb 	tdls_cfg_cmd.tdls_peer_count = cnt;
111bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_TDLS(mvm, "send TDLS config to FW for %d peers\n", cnt);
112bfcc09ddSBjoern A. Zeeb 
113bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_send_cmd(mvm, &cmd);
114bfcc09ddSBjoern A. Zeeb 	if (WARN_ON_ONCE(ret))
115bfcc09ddSBjoern A. Zeeb 		return;
116bfcc09ddSBjoern A. Zeeb 
117bfcc09ddSBjoern A. Zeeb 	pkt = cmd.resp_pkt;
118bfcc09ddSBjoern A. Zeeb 
119bfcc09ddSBjoern A. Zeeb 	WARN_ON_ONCE(iwl_rx_packet_payload_len(pkt) != sizeof(*resp));
120bfcc09ddSBjoern A. Zeeb 
121bfcc09ddSBjoern A. Zeeb 	/* we don't really care about the response at this point */
122bfcc09ddSBjoern A. Zeeb 
123bfcc09ddSBjoern A. Zeeb 	iwl_free_resp(&cmd);
124bfcc09ddSBjoern A. Zeeb }
125bfcc09ddSBjoern A. Zeeb 
126bfcc09ddSBjoern A. Zeeb void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
127bfcc09ddSBjoern A. Zeeb 			       bool sta_added)
128bfcc09ddSBjoern A. Zeeb {
129bfcc09ddSBjoern A. Zeeb 	int tdls_sta_cnt = iwl_mvm_tdls_sta_count(mvm, vif);
130bfcc09ddSBjoern A. Zeeb 
131bfcc09ddSBjoern A. Zeeb 	/* when the first peer joins, send a power update first */
132bfcc09ddSBjoern A. Zeeb 	if (tdls_sta_cnt == 1 && sta_added)
133bfcc09ddSBjoern A. Zeeb 		iwl_mvm_power_update_mac(mvm);
134bfcc09ddSBjoern A. Zeeb 
135bfcc09ddSBjoern A. Zeeb 	/* Configure the FW with TDLS peer info only if TDLS channel switch
136bfcc09ddSBjoern A. Zeeb 	 * capability is set.
137bfcc09ddSBjoern A. Zeeb 	 * TDLS config data is used currently only in TDLS channel switch code.
138bfcc09ddSBjoern A. Zeeb 	 * Supposed to serve also TDLS buffer station which is not implemneted
139bfcc09ddSBjoern A. Zeeb 	 * yet in FW*/
140bfcc09ddSBjoern A. Zeeb 	if (fw_has_capa(&mvm->fw->ucode_capa,
141bfcc09ddSBjoern A. Zeeb 			IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH))
142bfcc09ddSBjoern A. Zeeb 		iwl_mvm_tdls_config(mvm, vif);
143bfcc09ddSBjoern A. Zeeb 
144bfcc09ddSBjoern A. Zeeb 	/* when the last peer leaves, send a power update last */
145bfcc09ddSBjoern A. Zeeb 	if (tdls_sta_cnt == 0 && !sta_added)
146bfcc09ddSBjoern A. Zeeb 		iwl_mvm_power_update_mac(mvm);
147bfcc09ddSBjoern A. Zeeb }
148bfcc09ddSBjoern A. Zeeb 
149bfcc09ddSBjoern A. Zeeb void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
150*a4128aadSBjoern A. Zeeb 					   struct ieee80211_vif *vif,
151*a4128aadSBjoern A. Zeeb 					   unsigned int link_id)
152bfcc09ddSBjoern A. Zeeb {
153bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
154bfcc09ddSBjoern A. Zeeb 	u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int;
155bfcc09ddSBjoern A. Zeeb 
156bfcc09ddSBjoern A. Zeeb 	/* Protect the session to hear the TDLS setup response on the channel */
157*a4128aadSBjoern A. Zeeb 	guard(mvm)(mvm);
158bfcc09ddSBjoern A. Zeeb 	if (fw_has_capa(&mvm->fw->ucode_capa,
159bfcc09ddSBjoern A. Zeeb 			IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD))
160bfcc09ddSBjoern A. Zeeb 		iwl_mvm_schedule_session_protection(mvm, vif, duration,
161*a4128aadSBjoern A. Zeeb 						    duration, true, link_id);
162bfcc09ddSBjoern A. Zeeb 	else
163bfcc09ddSBjoern A. Zeeb 		iwl_mvm_protect_session(mvm, vif, duration,
164bfcc09ddSBjoern A. Zeeb 					duration, 100, true);
165bfcc09ddSBjoern A. Zeeb }
166bfcc09ddSBjoern A. Zeeb 
167bfcc09ddSBjoern A. Zeeb static const char *
168bfcc09ddSBjoern A. Zeeb iwl_mvm_tdls_cs_state_str(enum iwl_mvm_tdls_cs_state state)
169bfcc09ddSBjoern A. Zeeb {
170bfcc09ddSBjoern A. Zeeb 	switch (state) {
171bfcc09ddSBjoern A. Zeeb 	case IWL_MVM_TDLS_SW_IDLE:
172bfcc09ddSBjoern A. Zeeb 		return "IDLE";
173bfcc09ddSBjoern A. Zeeb 	case IWL_MVM_TDLS_SW_REQ_SENT:
174bfcc09ddSBjoern A. Zeeb 		return "REQ SENT";
175bfcc09ddSBjoern A. Zeeb 	case IWL_MVM_TDLS_SW_RESP_RCVD:
176bfcc09ddSBjoern A. Zeeb 		return "RESP RECEIVED";
177bfcc09ddSBjoern A. Zeeb 	case IWL_MVM_TDLS_SW_REQ_RCVD:
178bfcc09ddSBjoern A. Zeeb 		return "REQ RECEIVED";
179bfcc09ddSBjoern A. Zeeb 	case IWL_MVM_TDLS_SW_ACTIVE:
180bfcc09ddSBjoern A. Zeeb 		return "ACTIVE";
181bfcc09ddSBjoern A. Zeeb 	}
182bfcc09ddSBjoern A. Zeeb 
183bfcc09ddSBjoern A. Zeeb 	return NULL;
184bfcc09ddSBjoern A. Zeeb }
185bfcc09ddSBjoern A. Zeeb 
186bfcc09ddSBjoern A. Zeeb static void iwl_mvm_tdls_update_cs_state(struct iwl_mvm *mvm,
187bfcc09ddSBjoern A. Zeeb 					 enum iwl_mvm_tdls_cs_state state)
188bfcc09ddSBjoern A. Zeeb {
189bfcc09ddSBjoern A. Zeeb 	if (mvm->tdls_cs.state == state)
190bfcc09ddSBjoern A. Zeeb 		return;
191bfcc09ddSBjoern A. Zeeb 
192bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_TDLS(mvm, "TDLS channel switch state: %s -> %s\n",
193bfcc09ddSBjoern A. Zeeb 		       iwl_mvm_tdls_cs_state_str(mvm->tdls_cs.state),
194bfcc09ddSBjoern A. Zeeb 		       iwl_mvm_tdls_cs_state_str(state));
195bfcc09ddSBjoern A. Zeeb 	mvm->tdls_cs.state = state;
196bfcc09ddSBjoern A. Zeeb 
197bfcc09ddSBjoern A. Zeeb 	/* we only send requests to our switching peer - update sent time */
198bfcc09ddSBjoern A. Zeeb 	if (state == IWL_MVM_TDLS_SW_REQ_SENT)
199bfcc09ddSBjoern A. Zeeb 		mvm->tdls_cs.peer.sent_timestamp = iwl_mvm_get_systime(mvm);
200bfcc09ddSBjoern A. Zeeb 
201bfcc09ddSBjoern A. Zeeb 	if (state == IWL_MVM_TDLS_SW_IDLE)
202bfcc09ddSBjoern A. Zeeb 		mvm->tdls_cs.cur_sta_id = IWL_MVM_INVALID_STA;
203bfcc09ddSBjoern A. Zeeb }
204bfcc09ddSBjoern A. Zeeb 
205bfcc09ddSBjoern A. Zeeb void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
206bfcc09ddSBjoern A. Zeeb {
207bfcc09ddSBjoern A. Zeeb 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
208bfcc09ddSBjoern A. Zeeb 	struct iwl_tdls_channel_switch_notif *notif = (void *)pkt->data;
209bfcc09ddSBjoern A. Zeeb 	struct ieee80211_sta *sta;
210bfcc09ddSBjoern A. Zeeb 	unsigned int delay;
211bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_sta *mvmsta;
212bfcc09ddSBjoern A. Zeeb 	struct ieee80211_vif *vif;
213bfcc09ddSBjoern A. Zeeb 	u32 sta_id = le32_to_cpu(notif->sta_id);
214bfcc09ddSBjoern A. Zeeb 
215bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
216bfcc09ddSBjoern A. Zeeb 
217bfcc09ddSBjoern A. Zeeb 	/* can fail sometimes */
218bfcc09ddSBjoern A. Zeeb 	if (!le32_to_cpu(notif->status)) {
219bfcc09ddSBjoern A. Zeeb 		iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
220bfcc09ddSBjoern A. Zeeb 		return;
221bfcc09ddSBjoern A. Zeeb 	}
222bfcc09ddSBjoern A. Zeeb 
223bfcc09ddSBjoern A. Zeeb 	if (WARN_ON(sta_id >= mvm->fw->ucode_capa.num_stations))
224bfcc09ddSBjoern A. Zeeb 		return;
225bfcc09ddSBjoern A. Zeeb 
226bfcc09ddSBjoern A. Zeeb 	sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
227bfcc09ddSBjoern A. Zeeb 					lockdep_is_held(&mvm->mutex));
228bfcc09ddSBjoern A. Zeeb 	/* the station may not be here, but if it is, it must be a TDLS peer */
229bfcc09ddSBjoern A. Zeeb 	if (IS_ERR_OR_NULL(sta) || WARN_ON(!sta->tdls))
230bfcc09ddSBjoern A. Zeeb 		return;
231bfcc09ddSBjoern A. Zeeb 
232bfcc09ddSBjoern A. Zeeb 	mvmsta = iwl_mvm_sta_from_mac80211(sta);
233bfcc09ddSBjoern A. Zeeb 	vif = mvmsta->vif;
234bfcc09ddSBjoern A. Zeeb 
235bfcc09ddSBjoern A. Zeeb 	/*
236bfcc09ddSBjoern A. Zeeb 	 * Update state and possibly switch again after this is over (DTIM).
237bfcc09ddSBjoern A. Zeeb 	 * Also convert TU to msec.
238bfcc09ddSBjoern A. Zeeb 	 */
239bfcc09ddSBjoern A. Zeeb 	delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
240bfcc09ddSBjoern A. Zeeb 	mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
241bfcc09ddSBjoern A. Zeeb 			 msecs_to_jiffies(delay));
242bfcc09ddSBjoern A. Zeeb 
243bfcc09ddSBjoern A. Zeeb 	iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_ACTIVE);
244bfcc09ddSBjoern A. Zeeb }
245bfcc09ddSBjoern A. Zeeb 
246bfcc09ddSBjoern A. Zeeb static int
247bfcc09ddSBjoern A. Zeeb iwl_mvm_tdls_check_action(struct iwl_mvm *mvm,
248bfcc09ddSBjoern A. Zeeb 			  enum iwl_tdls_channel_switch_type type,
249bfcc09ddSBjoern A. Zeeb 			  const u8 *peer, bool peer_initiator, u32 timestamp)
250bfcc09ddSBjoern A. Zeeb {
251bfcc09ddSBjoern A. Zeeb 	bool same_peer = false;
252bfcc09ddSBjoern A. Zeeb 	int ret = 0;
253bfcc09ddSBjoern A. Zeeb 
254bfcc09ddSBjoern A. Zeeb 	/* get the existing peer if it's there */
255bfcc09ddSBjoern A. Zeeb 	if (mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE &&
256bfcc09ddSBjoern A. Zeeb 	    mvm->tdls_cs.cur_sta_id != IWL_MVM_INVALID_STA) {
257bfcc09ddSBjoern A. Zeeb 		struct ieee80211_sta *sta = rcu_dereference_protected(
258bfcc09ddSBjoern A. Zeeb 				mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id],
259bfcc09ddSBjoern A. Zeeb 				lockdep_is_held(&mvm->mutex));
260bfcc09ddSBjoern A. Zeeb 		if (!IS_ERR_OR_NULL(sta))
261bfcc09ddSBjoern A. Zeeb 			same_peer = ether_addr_equal(peer, sta->addr);
262bfcc09ddSBjoern A. Zeeb 	}
263bfcc09ddSBjoern A. Zeeb 
264bfcc09ddSBjoern A. Zeeb 	switch (mvm->tdls_cs.state) {
265bfcc09ddSBjoern A. Zeeb 	case IWL_MVM_TDLS_SW_IDLE:
266bfcc09ddSBjoern A. Zeeb 		/*
267bfcc09ddSBjoern A. Zeeb 		 * might be spurious packet from the peer after the switch is
268bfcc09ddSBjoern A. Zeeb 		 * already done
269bfcc09ddSBjoern A. Zeeb 		 */
270bfcc09ddSBjoern A. Zeeb 		if (type == TDLS_MOVE_CH)
271bfcc09ddSBjoern A. Zeeb 			ret = -EINVAL;
272bfcc09ddSBjoern A. Zeeb 		break;
273bfcc09ddSBjoern A. Zeeb 	case IWL_MVM_TDLS_SW_REQ_SENT:
274bfcc09ddSBjoern A. Zeeb 		/* only allow requests from the same peer */
275bfcc09ddSBjoern A. Zeeb 		if (!same_peer)
276bfcc09ddSBjoern A. Zeeb 			ret = -EBUSY;
277bfcc09ddSBjoern A. Zeeb 		else if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH &&
278bfcc09ddSBjoern A. Zeeb 			 !peer_initiator)
279bfcc09ddSBjoern A. Zeeb 			/*
280bfcc09ddSBjoern A. Zeeb 			 * We received a ch-switch request while an outgoing
281bfcc09ddSBjoern A. Zeeb 			 * one is pending. Allow it if the peer is the link
282bfcc09ddSBjoern A. Zeeb 			 * initiator.
283bfcc09ddSBjoern A. Zeeb 			 */
284bfcc09ddSBjoern A. Zeeb 			ret = -EBUSY;
285bfcc09ddSBjoern A. Zeeb 		else if (type == TDLS_SEND_CHAN_SW_REQ)
286bfcc09ddSBjoern A. Zeeb 			/* wait for idle before sending another request */
287bfcc09ddSBjoern A. Zeeb 			ret = -EBUSY;
288bfcc09ddSBjoern A. Zeeb 		else if (timestamp <= mvm->tdls_cs.peer.sent_timestamp)
289bfcc09ddSBjoern A. Zeeb 			/* we got a stale response - ignore it */
290bfcc09ddSBjoern A. Zeeb 			ret = -EINVAL;
291bfcc09ddSBjoern A. Zeeb 		break;
292bfcc09ddSBjoern A. Zeeb 	case IWL_MVM_TDLS_SW_RESP_RCVD:
293bfcc09ddSBjoern A. Zeeb 		/*
294bfcc09ddSBjoern A. Zeeb 		 * we are waiting for the FW to give an "active" notification,
295bfcc09ddSBjoern A. Zeeb 		 * so ignore requests in the meantime
296bfcc09ddSBjoern A. Zeeb 		 */
297bfcc09ddSBjoern A. Zeeb 		ret = -EBUSY;
298bfcc09ddSBjoern A. Zeeb 		break;
299bfcc09ddSBjoern A. Zeeb 	case IWL_MVM_TDLS_SW_REQ_RCVD:
300bfcc09ddSBjoern A. Zeeb 		/* as above, allow the link initiator to proceed */
301bfcc09ddSBjoern A. Zeeb 		if (type == TDLS_SEND_CHAN_SW_REQ) {
302bfcc09ddSBjoern A. Zeeb 			if (!same_peer)
303bfcc09ddSBjoern A. Zeeb 				ret = -EBUSY;
304bfcc09ddSBjoern A. Zeeb 			else if (peer_initiator) /* they are the initiator */
305bfcc09ddSBjoern A. Zeeb 				ret = -EBUSY;
306bfcc09ddSBjoern A. Zeeb 		} else if (type == TDLS_MOVE_CH) {
307bfcc09ddSBjoern A. Zeeb 			ret = -EINVAL;
308bfcc09ddSBjoern A. Zeeb 		}
309bfcc09ddSBjoern A. Zeeb 		break;
310bfcc09ddSBjoern A. Zeeb 	case IWL_MVM_TDLS_SW_ACTIVE:
311bfcc09ddSBjoern A. Zeeb 		/*
312bfcc09ddSBjoern A. Zeeb 		 * the only valid request when active is a request to return
313bfcc09ddSBjoern A. Zeeb 		 * to the base channel by the current off-channel peer
314bfcc09ddSBjoern A. Zeeb 		 */
315bfcc09ddSBjoern A. Zeeb 		if (type != TDLS_MOVE_CH || !same_peer)
316bfcc09ddSBjoern A. Zeeb 			ret = -EBUSY;
317bfcc09ddSBjoern A. Zeeb 		break;
318bfcc09ddSBjoern A. Zeeb 	}
319bfcc09ddSBjoern A. Zeeb 
320bfcc09ddSBjoern A. Zeeb 	if (ret)
321bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_TDLS(mvm,
322bfcc09ddSBjoern A. Zeeb 			       "Invalid TDLS action %d state %d peer %pM same_peer %d initiator %d\n",
323bfcc09ddSBjoern A. Zeeb 			       type, mvm->tdls_cs.state, peer, same_peer,
324bfcc09ddSBjoern A. Zeeb 			       peer_initiator);
325bfcc09ddSBjoern A. Zeeb 
326bfcc09ddSBjoern A. Zeeb 	return ret;
327bfcc09ddSBjoern A. Zeeb }
328bfcc09ddSBjoern A. Zeeb 
329bfcc09ddSBjoern A. Zeeb static int
330bfcc09ddSBjoern A. Zeeb iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm,
331bfcc09ddSBjoern A. Zeeb 				   struct ieee80211_vif *vif,
332bfcc09ddSBjoern A. Zeeb 				   enum iwl_tdls_channel_switch_type type,
333bfcc09ddSBjoern A. Zeeb 				   const u8 *peer, bool peer_initiator,
334bfcc09ddSBjoern A. Zeeb 				   u8 oper_class,
335bfcc09ddSBjoern A. Zeeb 				   struct cfg80211_chan_def *chandef,
336bfcc09ddSBjoern A. Zeeb 				   u32 timestamp, u16 switch_time,
337bfcc09ddSBjoern A. Zeeb 				   u16 switch_timeout, struct sk_buff *skb,
338bfcc09ddSBjoern A. Zeeb 				   u32 ch_sw_tm_ie)
339bfcc09ddSBjoern A. Zeeb {
340bfcc09ddSBjoern A. Zeeb 	struct ieee80211_sta *sta;
341bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_sta *mvmsta;
342bfcc09ddSBjoern A. Zeeb 	struct ieee80211_tx_info *info;
343bfcc09ddSBjoern A. Zeeb 	struct ieee80211_hdr *hdr;
344bfcc09ddSBjoern A. Zeeb 	struct iwl_tdls_channel_switch_cmd cmd = {0};
345bfcc09ddSBjoern A. Zeeb 	struct iwl_tdls_channel_switch_cmd_tail *tail =
346bfcc09ddSBjoern A. Zeeb 		iwl_mvm_chan_info_cmd_tail(mvm, &cmd.ci);
347bfcc09ddSBjoern A. Zeeb 	u16 len = sizeof(cmd) - iwl_mvm_chan_info_padding(mvm);
348bfcc09ddSBjoern A. Zeeb 	int ret;
349bfcc09ddSBjoern A. Zeeb 
350bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
351bfcc09ddSBjoern A. Zeeb 
352bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator,
353bfcc09ddSBjoern A. Zeeb 					timestamp);
354bfcc09ddSBjoern A. Zeeb 	if (ret)
355bfcc09ddSBjoern A. Zeeb 		return ret;
356bfcc09ddSBjoern A. Zeeb 
357bfcc09ddSBjoern A. Zeeb 	if (!skb || WARN_ON(skb->len > IWL_TDLS_CH_SW_FRAME_MAX_SIZE)) {
358bfcc09ddSBjoern A. Zeeb 		ret = -EINVAL;
359bfcc09ddSBjoern A. Zeeb 		goto out;
360bfcc09ddSBjoern A. Zeeb 	}
361bfcc09ddSBjoern A. Zeeb 
362bfcc09ddSBjoern A. Zeeb 	cmd.switch_type = type;
363bfcc09ddSBjoern A. Zeeb 	tail->timing.frame_timestamp = cpu_to_le32(timestamp);
364bfcc09ddSBjoern A. Zeeb 	tail->timing.switch_time = cpu_to_le32(switch_time);
365bfcc09ddSBjoern A. Zeeb 	tail->timing.switch_timeout = cpu_to_le32(switch_timeout);
366bfcc09ddSBjoern A. Zeeb 
367bfcc09ddSBjoern A. Zeeb 	rcu_read_lock();
368bfcc09ddSBjoern A. Zeeb 	sta = ieee80211_find_sta(vif, peer);
369bfcc09ddSBjoern A. Zeeb 	if (!sta) {
370bfcc09ddSBjoern A. Zeeb 		rcu_read_unlock();
371bfcc09ddSBjoern A. Zeeb 		ret = -ENOENT;
372bfcc09ddSBjoern A. Zeeb 		goto out;
373bfcc09ddSBjoern A. Zeeb 	}
374bfcc09ddSBjoern A. Zeeb 	mvmsta = iwl_mvm_sta_from_mac80211(sta);
3759af1bba4SBjoern A. Zeeb 	cmd.peer_sta_id = cpu_to_le32(mvmsta->deflink.sta_id);
376bfcc09ddSBjoern A. Zeeb 
377bfcc09ddSBjoern A. Zeeb 	if (!chandef) {
378bfcc09ddSBjoern A. Zeeb 		if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT &&
379bfcc09ddSBjoern A. Zeeb 		    mvm->tdls_cs.peer.chandef.chan) {
380bfcc09ddSBjoern A. Zeeb 			/* actually moving to the channel */
381bfcc09ddSBjoern A. Zeeb 			chandef = &mvm->tdls_cs.peer.chandef;
382bfcc09ddSBjoern A. Zeeb 		} else if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_ACTIVE &&
383bfcc09ddSBjoern A. Zeeb 			   type == TDLS_MOVE_CH) {
384bfcc09ddSBjoern A. Zeeb 			/* we need to return to base channel */
385bfcc09ddSBjoern A. Zeeb 			struct ieee80211_chanctx_conf *chanctx =
3869af1bba4SBjoern A. Zeeb 					rcu_dereference(vif->bss_conf.chanctx_conf);
387bfcc09ddSBjoern A. Zeeb 
388bfcc09ddSBjoern A. Zeeb 			if (WARN_ON_ONCE(!chanctx)) {
389bfcc09ddSBjoern A. Zeeb 				rcu_read_unlock();
390bfcc09ddSBjoern A. Zeeb 				goto out;
391bfcc09ddSBjoern A. Zeeb 			}
392bfcc09ddSBjoern A. Zeeb 
393bfcc09ddSBjoern A. Zeeb 			chandef = &chanctx->def;
394bfcc09ddSBjoern A. Zeeb 		}
395bfcc09ddSBjoern A. Zeeb 	}
396bfcc09ddSBjoern A. Zeeb 
397bfcc09ddSBjoern A. Zeeb 	if (chandef)
398bfcc09ddSBjoern A. Zeeb 		iwl_mvm_set_chan_info_chandef(mvm, &cmd.ci, chandef);
399bfcc09ddSBjoern A. Zeeb 
400bfcc09ddSBjoern A. Zeeb 	/* keep quota calculation simple for now - 50% of DTIM for TDLS */
401bfcc09ddSBjoern A. Zeeb 	tail->timing.max_offchan_duration =
402bfcc09ddSBjoern A. Zeeb 			cpu_to_le32(TU_TO_US(vif->bss_conf.dtim_period *
403bfcc09ddSBjoern A. Zeeb 					     vif->bss_conf.beacon_int) / 2);
404bfcc09ddSBjoern A. Zeeb 
405bfcc09ddSBjoern A. Zeeb 	/* Switch time is the first element in the switch-timing IE. */
406bfcc09ddSBjoern A. Zeeb 	tail->frame.switch_time_offset = cpu_to_le32(ch_sw_tm_ie + 2);
407bfcc09ddSBjoern A. Zeeb 
408bfcc09ddSBjoern A. Zeeb 	info = IEEE80211_SKB_CB(skb);
409bfcc09ddSBjoern A. Zeeb 	hdr = (void *)skb->data;
410bfcc09ddSBjoern A. Zeeb 	if (info->control.hw_key) {
411bfcc09ddSBjoern A. Zeeb 		if (info->control.hw_key->cipher != WLAN_CIPHER_SUITE_CCMP) {
412bfcc09ddSBjoern A. Zeeb 			rcu_read_unlock();
413bfcc09ddSBjoern A. Zeeb 			ret = -EINVAL;
414bfcc09ddSBjoern A. Zeeb 			goto out;
415bfcc09ddSBjoern A. Zeeb 		}
416bfcc09ddSBjoern A. Zeeb 		iwl_mvm_set_tx_cmd_ccmp(info, &tail->frame.tx_cmd);
417bfcc09ddSBjoern A. Zeeb 	}
418bfcc09ddSBjoern A. Zeeb 
419bfcc09ddSBjoern A. Zeeb 	iwl_mvm_set_tx_cmd(mvm, skb, &tail->frame.tx_cmd, info,
4209af1bba4SBjoern A. Zeeb 			   mvmsta->deflink.sta_id);
421bfcc09ddSBjoern A. Zeeb 
422bfcc09ddSBjoern A. Zeeb 	iwl_mvm_set_tx_cmd_rate(mvm, &tail->frame.tx_cmd, info, sta,
423bfcc09ddSBjoern A. Zeeb 				hdr->frame_control);
424bfcc09ddSBjoern A. Zeeb 	rcu_read_unlock();
425bfcc09ddSBjoern A. Zeeb 
426bfcc09ddSBjoern A. Zeeb 	memcpy(tail->frame.data, skb->data, skb->len);
427bfcc09ddSBjoern A. Zeeb 
428bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_send_cmd_pdu(mvm, TDLS_CHANNEL_SWITCH_CMD, 0, len, &cmd);
429bfcc09ddSBjoern A. Zeeb 	if (ret) {
430bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to send TDLS_CHANNEL_SWITCH cmd: %d\n",
431bfcc09ddSBjoern A. Zeeb 			ret);
432bfcc09ddSBjoern A. Zeeb 		goto out;
433bfcc09ddSBjoern A. Zeeb 	}
434bfcc09ddSBjoern A. Zeeb 
435bfcc09ddSBjoern A. Zeeb 	/* channel switch has started, update state */
436bfcc09ddSBjoern A. Zeeb 	if (type != TDLS_MOVE_CH) {
4379af1bba4SBjoern A. Zeeb 		mvm->tdls_cs.cur_sta_id = mvmsta->deflink.sta_id;
438bfcc09ddSBjoern A. Zeeb 		iwl_mvm_tdls_update_cs_state(mvm,
439bfcc09ddSBjoern A. Zeeb 					     type == TDLS_SEND_CHAN_SW_REQ ?
440bfcc09ddSBjoern A. Zeeb 					     IWL_MVM_TDLS_SW_REQ_SENT :
441bfcc09ddSBjoern A. Zeeb 					     IWL_MVM_TDLS_SW_REQ_RCVD);
442bfcc09ddSBjoern A. Zeeb 	} else {
443bfcc09ddSBjoern A. Zeeb 		iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_RESP_RCVD);
444bfcc09ddSBjoern A. Zeeb 	}
445bfcc09ddSBjoern A. Zeeb 
446bfcc09ddSBjoern A. Zeeb out:
447bfcc09ddSBjoern A. Zeeb 
448bfcc09ddSBjoern A. Zeeb 	/* channel switch failed - we are idle */
449bfcc09ddSBjoern A. Zeeb 	if (ret)
450bfcc09ddSBjoern A. Zeeb 		iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
451bfcc09ddSBjoern A. Zeeb 
452bfcc09ddSBjoern A. Zeeb 	return ret;
453bfcc09ddSBjoern A. Zeeb }
454bfcc09ddSBjoern A. Zeeb 
455bfcc09ddSBjoern A. Zeeb void iwl_mvm_tdls_ch_switch_work(struct work_struct *work)
456bfcc09ddSBjoern A. Zeeb {
457bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm *mvm;
458bfcc09ddSBjoern A. Zeeb 	struct ieee80211_sta *sta;
459bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_sta *mvmsta;
460bfcc09ddSBjoern A. Zeeb 	struct ieee80211_vif *vif;
461bfcc09ddSBjoern A. Zeeb 	unsigned int delay;
462bfcc09ddSBjoern A. Zeeb 	int ret;
463bfcc09ddSBjoern A. Zeeb 
464bfcc09ddSBjoern A. Zeeb 	mvm = container_of(work, struct iwl_mvm, tdls_cs.dwork.work);
465*a4128aadSBjoern A. Zeeb 	guard(mvm)(mvm);
466bfcc09ddSBjoern A. Zeeb 
467bfcc09ddSBjoern A. Zeeb 	/* called after an active channel switch has finished or timed-out */
468bfcc09ddSBjoern A. Zeeb 	iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
469bfcc09ddSBjoern A. Zeeb 
470bfcc09ddSBjoern A. Zeeb 	/* station might be gone, in that case do nothing */
471bfcc09ddSBjoern A. Zeeb 	if (mvm->tdls_cs.peer.sta_id == IWL_MVM_INVALID_STA)
472*a4128aadSBjoern A. Zeeb 		return;
473bfcc09ddSBjoern A. Zeeb 
474bfcc09ddSBjoern A. Zeeb 	sta = rcu_dereference_protected(
475bfcc09ddSBjoern A. Zeeb 				mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id],
476bfcc09ddSBjoern A. Zeeb 				lockdep_is_held(&mvm->mutex));
477bfcc09ddSBjoern A. Zeeb 	/* the station may not be here, but if it is, it must be a TDLS peer */
478bfcc09ddSBjoern A. Zeeb 	if (!sta || IS_ERR(sta) || WARN_ON(!sta->tdls))
479*a4128aadSBjoern A. Zeeb 		return;
480bfcc09ddSBjoern A. Zeeb 
481bfcc09ddSBjoern A. Zeeb 	mvmsta = iwl_mvm_sta_from_mac80211(sta);
482bfcc09ddSBjoern A. Zeeb 	vif = mvmsta->vif;
483bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_tdls_config_channel_switch(mvm, vif,
484bfcc09ddSBjoern A. Zeeb 						 TDLS_SEND_CHAN_SW_REQ,
485bfcc09ddSBjoern A. Zeeb 						 sta->addr,
486bfcc09ddSBjoern A. Zeeb 						 mvm->tdls_cs.peer.initiator,
487bfcc09ddSBjoern A. Zeeb 						 mvm->tdls_cs.peer.op_class,
488bfcc09ddSBjoern A. Zeeb 						 &mvm->tdls_cs.peer.chandef,
489bfcc09ddSBjoern A. Zeeb 						 0, 0, 0,
490bfcc09ddSBjoern A. Zeeb 						 mvm->tdls_cs.peer.skb,
491bfcc09ddSBjoern A. Zeeb 						 mvm->tdls_cs.peer.ch_sw_tm_ie);
492bfcc09ddSBjoern A. Zeeb 	if (ret)
493bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Not sending TDLS channel switch: %d\n", ret);
494bfcc09ddSBjoern A. Zeeb 
495bfcc09ddSBjoern A. Zeeb 	/* retry after a DTIM if we failed sending now */
496bfcc09ddSBjoern A. Zeeb 	delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
497bfcc09ddSBjoern A. Zeeb 	schedule_delayed_work(&mvm->tdls_cs.dwork, msecs_to_jiffies(delay));
498bfcc09ddSBjoern A. Zeeb }
499bfcc09ddSBjoern A. Zeeb 
500bfcc09ddSBjoern A. Zeeb int
501bfcc09ddSBjoern A. Zeeb iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,
502bfcc09ddSBjoern A. Zeeb 			    struct ieee80211_vif *vif,
503bfcc09ddSBjoern A. Zeeb 			    struct ieee80211_sta *sta, u8 oper_class,
504bfcc09ddSBjoern A. Zeeb 			    struct cfg80211_chan_def *chandef,
505bfcc09ddSBjoern A. Zeeb 			    struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie)
506bfcc09ddSBjoern A. Zeeb {
507bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
508bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_sta *mvmsta;
509bfcc09ddSBjoern A. Zeeb 	unsigned int delay;
510bfcc09ddSBjoern A. Zeeb 	int ret;
511bfcc09ddSBjoern A. Zeeb 
512*a4128aadSBjoern A. Zeeb 	guard(mvm)(mvm);
513bfcc09ddSBjoern A. Zeeb 
514bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_TDLS(mvm, "TDLS channel switch with %pM ch %d width %d\n",
515bfcc09ddSBjoern A. Zeeb 		       sta->addr, chandef->chan->center_freq, chandef->width);
516bfcc09ddSBjoern A. Zeeb 
517bfcc09ddSBjoern A. Zeeb 	/* we only support a single peer for channel switching */
518bfcc09ddSBjoern A. Zeeb 	if (mvm->tdls_cs.peer.sta_id != IWL_MVM_INVALID_STA) {
519bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_TDLS(mvm,
520bfcc09ddSBjoern A. Zeeb 			       "Existing peer. Can't start switch with %pM\n",
521bfcc09ddSBjoern A. Zeeb 			       sta->addr);
522*a4128aadSBjoern A. Zeeb 		return -EBUSY;
523bfcc09ddSBjoern A. Zeeb 	}
524bfcc09ddSBjoern A. Zeeb 
525bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_tdls_config_channel_switch(mvm, vif,
526bfcc09ddSBjoern A. Zeeb 						 TDLS_SEND_CHAN_SW_REQ,
527bfcc09ddSBjoern A. Zeeb 						 sta->addr, sta->tdls_initiator,
528bfcc09ddSBjoern A. Zeeb 						 oper_class, chandef, 0, 0, 0,
529bfcc09ddSBjoern A. Zeeb 						 tmpl_skb, ch_sw_tm_ie);
530bfcc09ddSBjoern A. Zeeb 	if (ret)
531*a4128aadSBjoern A. Zeeb 		return ret;
532bfcc09ddSBjoern A. Zeeb 
533bfcc09ddSBjoern A. Zeeb 	/*
534bfcc09ddSBjoern A. Zeeb 	 * Mark the peer as "in tdls switch" for this vif. We only allow a
535bfcc09ddSBjoern A. Zeeb 	 * single such peer per vif.
536bfcc09ddSBjoern A. Zeeb 	 */
537bfcc09ddSBjoern A. Zeeb 	mvm->tdls_cs.peer.skb = skb_copy(tmpl_skb, GFP_KERNEL);
538*a4128aadSBjoern A. Zeeb 	if (!mvm->tdls_cs.peer.skb)
539*a4128aadSBjoern A. Zeeb 		return -ENOMEM;
540bfcc09ddSBjoern A. Zeeb 
541bfcc09ddSBjoern A. Zeeb 	mvmsta = iwl_mvm_sta_from_mac80211(sta);
5429af1bba4SBjoern A. Zeeb 	mvm->tdls_cs.peer.sta_id = mvmsta->deflink.sta_id;
543bfcc09ddSBjoern A. Zeeb 	mvm->tdls_cs.peer.chandef = *chandef;
544bfcc09ddSBjoern A. Zeeb 	mvm->tdls_cs.peer.initiator = sta->tdls_initiator;
545bfcc09ddSBjoern A. Zeeb 	mvm->tdls_cs.peer.op_class = oper_class;
546bfcc09ddSBjoern A. Zeeb 	mvm->tdls_cs.peer.ch_sw_tm_ie = ch_sw_tm_ie;
547bfcc09ddSBjoern A. Zeeb 
548bfcc09ddSBjoern A. Zeeb 	/*
549bfcc09ddSBjoern A. Zeeb 	 * Wait for 2 DTIM periods before attempting the next switch. The next
550bfcc09ddSBjoern A. Zeeb 	 * switch will be made sooner if the current one completes before that.
551bfcc09ddSBjoern A. Zeeb 	 */
552bfcc09ddSBjoern A. Zeeb 	delay = 2 * TU_TO_MS(vif->bss_conf.dtim_period *
553bfcc09ddSBjoern A. Zeeb 			     vif->bss_conf.beacon_int);
554bfcc09ddSBjoern A. Zeeb 	mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
555bfcc09ddSBjoern A. Zeeb 			 msecs_to_jiffies(delay));
556*a4128aadSBjoern A. Zeeb 	return 0;
557bfcc09ddSBjoern A. Zeeb }
558bfcc09ddSBjoern A. Zeeb 
559bfcc09ddSBjoern A. Zeeb void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw,
560bfcc09ddSBjoern A. Zeeb 					struct ieee80211_vif *vif,
561bfcc09ddSBjoern A. Zeeb 					struct ieee80211_sta *sta)
562bfcc09ddSBjoern A. Zeeb {
563bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
564bfcc09ddSBjoern A. Zeeb 	struct ieee80211_sta *cur_sta;
565bfcc09ddSBjoern A. Zeeb 	bool wait_for_phy = false;
566bfcc09ddSBjoern A. Zeeb 
567bfcc09ddSBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
568bfcc09ddSBjoern A. Zeeb 
569bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_TDLS(mvm, "TDLS cancel channel switch with %pM\n", sta->addr);
570bfcc09ddSBjoern A. Zeeb 
571bfcc09ddSBjoern A. Zeeb 	/* we only support a single peer for channel switching */
572bfcc09ddSBjoern A. Zeeb 	if (mvm->tdls_cs.peer.sta_id == IWL_MVM_INVALID_STA) {
573bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_TDLS(mvm, "No ch switch peer - %pM\n", sta->addr);
574bfcc09ddSBjoern A. Zeeb 		goto out;
575bfcc09ddSBjoern A. Zeeb 	}
576bfcc09ddSBjoern A. Zeeb 
577bfcc09ddSBjoern A. Zeeb 	cur_sta = rcu_dereference_protected(
578bfcc09ddSBjoern A. Zeeb 				mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id],
579bfcc09ddSBjoern A. Zeeb 				lockdep_is_held(&mvm->mutex));
580bfcc09ddSBjoern A. Zeeb 	/* make sure it's the same peer */
581bfcc09ddSBjoern A. Zeeb 	if (cur_sta != sta)
582bfcc09ddSBjoern A. Zeeb 		goto out;
583bfcc09ddSBjoern A. Zeeb 
584bfcc09ddSBjoern A. Zeeb 	/*
585bfcc09ddSBjoern A. Zeeb 	 * If we're currently in a switch because of the now canceled peer,
586bfcc09ddSBjoern A. Zeeb 	 * wait a DTIM here to make sure the phy is back on the base channel.
587bfcc09ddSBjoern A. Zeeb 	 * We can't otherwise force it.
588bfcc09ddSBjoern A. Zeeb 	 */
589bfcc09ddSBjoern A. Zeeb 	if (mvm->tdls_cs.cur_sta_id == mvm->tdls_cs.peer.sta_id &&
590bfcc09ddSBjoern A. Zeeb 	    mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE)
591bfcc09ddSBjoern A. Zeeb 		wait_for_phy = true;
592bfcc09ddSBjoern A. Zeeb 
593bfcc09ddSBjoern A. Zeeb 	mvm->tdls_cs.peer.sta_id = IWL_MVM_INVALID_STA;
594bfcc09ddSBjoern A. Zeeb 	dev_kfree_skb(mvm->tdls_cs.peer.skb);
595bfcc09ddSBjoern A. Zeeb 	mvm->tdls_cs.peer.skb = NULL;
596bfcc09ddSBjoern A. Zeeb 
597bfcc09ddSBjoern A. Zeeb out:
598bfcc09ddSBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
599bfcc09ddSBjoern A. Zeeb 
600bfcc09ddSBjoern A. Zeeb 	/* make sure the phy is on the base channel */
601bfcc09ddSBjoern A. Zeeb 	if (wait_for_phy)
602bfcc09ddSBjoern A. Zeeb #if defined(__linux__)
603d9836fb4SBjoern A. Zeeb 		msleep(TU_TO_MS(vif->bss_conf.dtim_period *
604bfcc09ddSBjoern A. Zeeb #elif defined(__FreeBSD__)
605d9836fb4SBjoern A. Zeeb 		linux_msleep(TU_TO_MS(vif->bss_conf.dtim_period *
606bfcc09ddSBjoern A. Zeeb #endif
607bfcc09ddSBjoern A. Zeeb 				vif->bss_conf.beacon_int));
608bfcc09ddSBjoern A. Zeeb 
609bfcc09ddSBjoern A. Zeeb 	/* flush the channel switch state */
610bfcc09ddSBjoern A. Zeeb 	flush_delayed_work(&mvm->tdls_cs.dwork);
611bfcc09ddSBjoern A. Zeeb 
612bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_TDLS(mvm, "TDLS ending channel switch with %pM\n", sta->addr);
613bfcc09ddSBjoern A. Zeeb }
614bfcc09ddSBjoern A. Zeeb 
615bfcc09ddSBjoern A. Zeeb void
616bfcc09ddSBjoern A. Zeeb iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw,
617bfcc09ddSBjoern A. Zeeb 				 struct ieee80211_vif *vif,
618bfcc09ddSBjoern A. Zeeb 				 struct ieee80211_tdls_ch_sw_params *params)
619bfcc09ddSBjoern A. Zeeb {
620bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
621bfcc09ddSBjoern A. Zeeb 	enum iwl_tdls_channel_switch_type type;
622bfcc09ddSBjoern A. Zeeb 	unsigned int delay;
623bfcc09ddSBjoern A. Zeeb 	const char *action_str =
624bfcc09ddSBjoern A. Zeeb 		params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ?
625bfcc09ddSBjoern A. Zeeb 		"REQ" : "RESP";
626bfcc09ddSBjoern A. Zeeb 
627*a4128aadSBjoern A. Zeeb 	guard(mvm)(mvm);
628bfcc09ddSBjoern A. Zeeb 
629bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_TDLS(mvm,
630bfcc09ddSBjoern A. Zeeb 		       "Received TDLS ch switch action %s from %pM status %d\n",
631bfcc09ddSBjoern A. Zeeb 		       action_str, params->sta->addr, params->status);
632bfcc09ddSBjoern A. Zeeb 
633bfcc09ddSBjoern A. Zeeb 	/*
634bfcc09ddSBjoern A. Zeeb 	 * we got a non-zero status from a peer we were switching to - move to
635bfcc09ddSBjoern A. Zeeb 	 * the idle state and retry again later
636bfcc09ddSBjoern A. Zeeb 	 */
637bfcc09ddSBjoern A. Zeeb 	if (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE &&
638bfcc09ddSBjoern A. Zeeb 	    params->status != 0 &&
639bfcc09ddSBjoern A. Zeeb 	    mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT &&
640bfcc09ddSBjoern A. Zeeb 	    mvm->tdls_cs.cur_sta_id != IWL_MVM_INVALID_STA) {
641bfcc09ddSBjoern A. Zeeb 		struct ieee80211_sta *cur_sta;
642bfcc09ddSBjoern A. Zeeb 
643bfcc09ddSBjoern A. Zeeb 		/* make sure it's the same peer */
644bfcc09ddSBjoern A. Zeeb 		cur_sta = rcu_dereference_protected(
645bfcc09ddSBjoern A. Zeeb 				mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id],
646bfcc09ddSBjoern A. Zeeb 				lockdep_is_held(&mvm->mutex));
647bfcc09ddSBjoern A. Zeeb 		if (cur_sta == params->sta) {
648bfcc09ddSBjoern A. Zeeb 			iwl_mvm_tdls_update_cs_state(mvm,
649bfcc09ddSBjoern A. Zeeb 						     IWL_MVM_TDLS_SW_IDLE);
650bfcc09ddSBjoern A. Zeeb 			goto retry;
651bfcc09ddSBjoern A. Zeeb 		}
652bfcc09ddSBjoern A. Zeeb 	}
653bfcc09ddSBjoern A. Zeeb 
654bfcc09ddSBjoern A. Zeeb 	type = (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST) ?
655bfcc09ddSBjoern A. Zeeb 	       TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH : TDLS_MOVE_CH;
656bfcc09ddSBjoern A. Zeeb 
657bfcc09ddSBjoern A. Zeeb 	iwl_mvm_tdls_config_channel_switch(mvm, vif, type, params->sta->addr,
658bfcc09ddSBjoern A. Zeeb 					   params->sta->tdls_initiator, 0,
659bfcc09ddSBjoern A. Zeeb 					   params->chandef, params->timestamp,
660bfcc09ddSBjoern A. Zeeb 					   params->switch_time,
661bfcc09ddSBjoern A. Zeeb 					   params->switch_timeout,
662bfcc09ddSBjoern A. Zeeb 					   params->tmpl_skb,
663bfcc09ddSBjoern A. Zeeb 					   params->ch_sw_tm_ie);
664bfcc09ddSBjoern A. Zeeb 
665bfcc09ddSBjoern A. Zeeb retry:
666bfcc09ddSBjoern A. Zeeb 	/* register a timeout in case we don't succeed in switching */
667bfcc09ddSBjoern A. Zeeb 	delay = vif->bss_conf.dtim_period * vif->bss_conf.beacon_int *
668bfcc09ddSBjoern A. Zeeb 		1024 / 1000;
669bfcc09ddSBjoern A. Zeeb 	mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
670bfcc09ddSBjoern A. Zeeb 			 msecs_to_jiffies(delay));
671bfcc09ddSBjoern A. Zeeb }
672