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