18c2ecf20Sopenharmony_ci/****************************************************************************** 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2015, 2018 - 2020 Intel Corporation. All rights reserved. 98c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 108c2ecf20Sopenharmony_ci * Copyright(c) 2016 - 2017 Intel Deutschland GmbH 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 138c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 148c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 178c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 188c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 198c2ecf20Sopenharmony_ci * General Public License for more details. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution 228c2ecf20Sopenharmony_ci * in the file called COPYING. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Contact Information: 258c2ecf20Sopenharmony_ci * Intel Linux Wireless <linuxwifi@intel.com> 268c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * BSD LICENSE 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2015, 2018 - 2020 Intel Corporation. All rights reserved. 318c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 328c2ecf20Sopenharmony_ci * Copyright(c) 2016 - 2017 Intel Deutschland GmbH 338c2ecf20Sopenharmony_ci * All rights reserved. 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 368c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 378c2ecf20Sopenharmony_ci * are met: 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * * Redistributions of source code must retain the above copyright 408c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 418c2ecf20Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright 428c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 438c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 448c2ecf20Sopenharmony_ci * distribution. 458c2ecf20Sopenharmony_ci * * Neither the name Intel Corporation nor the names of its 468c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 478c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 508c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 518c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 528c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 538c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 548c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 558c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 568c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 578c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 588c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 598c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci *****************************************************************************/ 628c2ecf20Sopenharmony_ci#include <net/mac80211.h> 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#include "mvm.h" 658c2ecf20Sopenharmony_ci#include "sta.h" 668c2ecf20Sopenharmony_ci#include "rs.h" 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* 698c2ecf20Sopenharmony_ci * New version of ADD_STA_sta command added new fields at the end of the 708c2ecf20Sopenharmony_ci * structure, so sending the size of the relevant API's structure is enough to 718c2ecf20Sopenharmony_ci * support both API versions. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cistatic inline int iwl_mvm_add_sta_cmd_size(struct iwl_mvm *mvm) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_rx_api(mvm) || 768c2ecf20Sopenharmony_ci fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) 778c2ecf20Sopenharmony_ci return sizeof(struct iwl_mvm_add_sta_cmd); 788c2ecf20Sopenharmony_ci else 798c2ecf20Sopenharmony_ci return sizeof(struct iwl_mvm_add_sta_cmd_v7); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, 838c2ecf20Sopenharmony_ci enum nl80211_iftype iftype) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci int sta_id; 868c2ecf20Sopenharmony_ci u32 reserved_ids = 0; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci BUILD_BUG_ON(IWL_MVM_STATION_COUNT_MAX > 32); 898c2ecf20Sopenharmony_ci WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* d0i3/d3 assumes the AP's sta_id (of sta vif) is 0. reserve it. */ 948c2ecf20Sopenharmony_ci if (iftype != NL80211_IFTYPE_STATION) 958c2ecf20Sopenharmony_ci reserved_ids = BIT(0); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* Don't take rcu_read_lock() since we are protected by mvm->mutex */ 988c2ecf20Sopenharmony_ci for (sta_id = 0; sta_id < mvm->fw->ucode_capa.num_stations; sta_id++) { 998c2ecf20Sopenharmony_ci if (BIT(sta_id) & reserved_ids) 1008c2ecf20Sopenharmony_ci continue; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (!rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], 1038c2ecf20Sopenharmony_ci lockdep_is_held(&mvm->mutex))) 1048c2ecf20Sopenharmony_ci return sta_id; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci return IWL_MVM_INVALID_STA; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* send station add/update command to firmware */ 1108c2ecf20Sopenharmony_ciint iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 1118c2ecf20Sopenharmony_ci bool update, unsigned int flags) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); 1148c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_cmd add_sta_cmd = { 1158c2ecf20Sopenharmony_ci .sta_id = mvm_sta->sta_id, 1168c2ecf20Sopenharmony_ci .mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color), 1178c2ecf20Sopenharmony_ci .add_modify = update ? 1 : 0, 1188c2ecf20Sopenharmony_ci .station_flags_msk = cpu_to_le32(STA_FLG_FAT_EN_MSK | 1198c2ecf20Sopenharmony_ci STA_FLG_MIMO_EN_MSK | 1208c2ecf20Sopenharmony_ci STA_FLG_RTS_MIMO_PROT), 1218c2ecf20Sopenharmony_ci .tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg), 1228c2ecf20Sopenharmony_ci }; 1238c2ecf20Sopenharmony_ci int ret; 1248c2ecf20Sopenharmony_ci u32 status; 1258c2ecf20Sopenharmony_ci u32 agg_size = 0, mpdu_dens = 0; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) 1288c2ecf20Sopenharmony_ci add_sta_cmd.station_type = mvm_sta->sta_type; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (!update || (flags & STA_MODIFY_QUEUES)) { 1318c2ecf20Sopenharmony_ci memcpy(&add_sta_cmd.addr, sta->addr, ETH_ALEN); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_tx_api(mvm)) { 1348c2ecf20Sopenharmony_ci add_sta_cmd.tfd_queue_msk = 1358c2ecf20Sopenharmony_ci cpu_to_le32(mvm_sta->tfd_queue_msk); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (flags & STA_MODIFY_QUEUES) 1388c2ecf20Sopenharmony_ci add_sta_cmd.modify_mask |= STA_MODIFY_QUEUES; 1398c2ecf20Sopenharmony_ci } else { 1408c2ecf20Sopenharmony_ci WARN_ON(flags & STA_MODIFY_QUEUES); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci switch (sta->bandwidth) { 1458c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_160: 1468c2ecf20Sopenharmony_ci add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_160MHZ); 1478c2ecf20Sopenharmony_ci /* fall through */ 1488c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_80: 1498c2ecf20Sopenharmony_ci add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_80MHZ); 1508c2ecf20Sopenharmony_ci /* fall through */ 1518c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_40: 1528c2ecf20Sopenharmony_ci add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_40MHZ); 1538c2ecf20Sopenharmony_ci /* fall through */ 1548c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_20: 1558c2ecf20Sopenharmony_ci if (sta->ht_cap.ht_supported) 1568c2ecf20Sopenharmony_ci add_sta_cmd.station_flags |= 1578c2ecf20Sopenharmony_ci cpu_to_le32(STA_FLG_FAT_EN_20MHZ); 1588c2ecf20Sopenharmony_ci break; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci switch (sta->rx_nss) { 1628c2ecf20Sopenharmony_ci case 1: 1638c2ecf20Sopenharmony_ci add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO); 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci case 2: 1668c2ecf20Sopenharmony_ci add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO2); 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci case 3 ... 8: 1698c2ecf20Sopenharmony_ci add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO3); 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci switch (sta->smps_mode) { 1748c2ecf20Sopenharmony_ci case IEEE80211_SMPS_AUTOMATIC: 1758c2ecf20Sopenharmony_ci case IEEE80211_SMPS_NUM_MODES: 1768c2ecf20Sopenharmony_ci WARN_ON(1); 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci case IEEE80211_SMPS_STATIC: 1798c2ecf20Sopenharmony_ci /* override NSS */ 1808c2ecf20Sopenharmony_ci add_sta_cmd.station_flags &= ~cpu_to_le32(STA_FLG_MIMO_EN_MSK); 1818c2ecf20Sopenharmony_ci add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO); 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 1848c2ecf20Sopenharmony_ci add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_RTS_MIMO_PROT); 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci case IEEE80211_SMPS_OFF: 1878c2ecf20Sopenharmony_ci /* nothing */ 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (sta->ht_cap.ht_supported) { 1928c2ecf20Sopenharmony_ci add_sta_cmd.station_flags_msk |= 1938c2ecf20Sopenharmony_ci cpu_to_le32(STA_FLG_MAX_AGG_SIZE_MSK | 1948c2ecf20Sopenharmony_ci STA_FLG_AGG_MPDU_DENS_MSK); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci mpdu_dens = sta->ht_cap.ampdu_density; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (sta->vht_cap.vht_supported) { 2018c2ecf20Sopenharmony_ci agg_size = sta->vht_cap.cap & 2028c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; 2038c2ecf20Sopenharmony_ci agg_size >>= 2048c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; 2058c2ecf20Sopenharmony_ci } else if (sta->ht_cap.ht_supported) { 2068c2ecf20Sopenharmony_ci agg_size = sta->ht_cap.ampdu_factor; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* D6.0 10.12.2 A-MPDU length limit rules 2108c2ecf20Sopenharmony_ci * A STA indicates the maximum length of the A-MPDU preEOF padding 2118c2ecf20Sopenharmony_ci * that it can receive in an HE PPDU in the Maximum A-MPDU Length 2128c2ecf20Sopenharmony_ci * Exponent field in its HT Capabilities, VHT Capabilities, 2138c2ecf20Sopenharmony_ci * and HE 6 GHz Band Capabilities elements (if present) and the 2148c2ecf20Sopenharmony_ci * Maximum AMPDU Length Exponent Extension field in its HE 2158c2ecf20Sopenharmony_ci * Capabilities element 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_ci if (sta->he_cap.has_he) 2188c2ecf20Sopenharmony_ci agg_size += u8_get_bits(sta->he_cap.he_cap_elem.mac_cap_info[3], 2198c2ecf20Sopenharmony_ci IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Limit to max A-MPDU supported by FW */ 2228c2ecf20Sopenharmony_ci if (agg_size > (STA_FLG_MAX_AGG_SIZE_4M >> STA_FLG_MAX_AGG_SIZE_SHIFT)) 2238c2ecf20Sopenharmony_ci agg_size = (STA_FLG_MAX_AGG_SIZE_4M >> 2248c2ecf20Sopenharmony_ci STA_FLG_MAX_AGG_SIZE_SHIFT); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci add_sta_cmd.station_flags |= 2278c2ecf20Sopenharmony_ci cpu_to_le32(agg_size << STA_FLG_MAX_AGG_SIZE_SHIFT); 2288c2ecf20Sopenharmony_ci add_sta_cmd.station_flags |= 2298c2ecf20Sopenharmony_ci cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT); 2308c2ecf20Sopenharmony_ci if (mvm_sta->sta_state >= IEEE80211_STA_ASSOC) 2318c2ecf20Sopenharmony_ci add_sta_cmd.assoc_id = cpu_to_le16(sta->aid); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (sta->wme) { 2348c2ecf20Sopenharmony_ci add_sta_cmd.modify_mask |= STA_MODIFY_UAPSD_ACS; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) 2378c2ecf20Sopenharmony_ci add_sta_cmd.uapsd_acs |= BIT(AC_BK); 2388c2ecf20Sopenharmony_ci if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) 2398c2ecf20Sopenharmony_ci add_sta_cmd.uapsd_acs |= BIT(AC_BE); 2408c2ecf20Sopenharmony_ci if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) 2418c2ecf20Sopenharmony_ci add_sta_cmd.uapsd_acs |= BIT(AC_VI); 2428c2ecf20Sopenharmony_ci if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) 2438c2ecf20Sopenharmony_ci add_sta_cmd.uapsd_acs |= BIT(AC_VO); 2448c2ecf20Sopenharmony_ci add_sta_cmd.uapsd_acs |= add_sta_cmd.uapsd_acs << 4; 2458c2ecf20Sopenharmony_ci add_sta_cmd.sp_length = sta->max_sp ? sta->max_sp * 2 : 128; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci status = ADD_STA_SUCCESS; 2498c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, 2508c2ecf20Sopenharmony_ci iwl_mvm_add_sta_cmd_size(mvm), 2518c2ecf20Sopenharmony_ci &add_sta_cmd, &status); 2528c2ecf20Sopenharmony_ci if (ret) 2538c2ecf20Sopenharmony_ci return ret; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci switch (status & IWL_ADD_STA_STATUS_MASK) { 2568c2ecf20Sopenharmony_ci case ADD_STA_SUCCESS: 2578c2ecf20Sopenharmony_ci IWL_DEBUG_ASSOC(mvm, "ADD_STA PASSED\n"); 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci default: 2608c2ecf20Sopenharmony_ci ret = -EIO; 2618c2ecf20Sopenharmony_ci IWL_ERR(mvm, "ADD_STA failed\n"); 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return ret; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic void iwl_mvm_rx_agg_session_expired(struct timer_list *t) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct iwl_mvm_baid_data *data = 2718c2ecf20Sopenharmony_ci from_timer(data, t, session_timer); 2728c2ecf20Sopenharmony_ci struct iwl_mvm_baid_data __rcu **rcu_ptr = data->rcu_ptr; 2738c2ecf20Sopenharmony_ci struct iwl_mvm_baid_data *ba_data; 2748c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 2758c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta; 2768c2ecf20Sopenharmony_ci unsigned long timeout; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci rcu_read_lock(); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci ba_data = rcu_dereference(*rcu_ptr); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (WARN_ON(!ba_data)) 2838c2ecf20Sopenharmony_ci goto unlock; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (!ba_data->timeout) 2868c2ecf20Sopenharmony_ci goto unlock; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci timeout = ba_data->last_rx + TU_TO_JIFFIES(ba_data->timeout * 2); 2898c2ecf20Sopenharmony_ci if (time_is_after_jiffies(timeout)) { 2908c2ecf20Sopenharmony_ci mod_timer(&ba_data->session_timer, timeout); 2918c2ecf20Sopenharmony_ci goto unlock; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Timer expired */ 2958c2ecf20Sopenharmony_ci sta = rcu_dereference(ba_data->mvm->fw_id_to_mac_id[ba_data->sta_id]); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* 2988c2ecf20Sopenharmony_ci * sta should be valid unless the following happens: 2998c2ecf20Sopenharmony_ci * The firmware asserts which triggers a reconfig flow, but 3008c2ecf20Sopenharmony_ci * the reconfig fails before we set the pointer to sta into 3018c2ecf20Sopenharmony_ci * the fw_id_to_mac_id pointer table. Mac80211 can't stop 3028c2ecf20Sopenharmony_ci * A-MDPU and hence the timer continues to run. Then, the 3038c2ecf20Sopenharmony_ci * timer expires and sta is NULL. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci if (!sta) 3068c2ecf20Sopenharmony_ci goto unlock; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci mvm_sta = iwl_mvm_sta_from_mac80211(sta); 3098c2ecf20Sopenharmony_ci ieee80211_rx_ba_timer_expired(mvm_sta->vif, 3108c2ecf20Sopenharmony_ci sta->addr, ba_data->tid); 3118c2ecf20Sopenharmony_ciunlock: 3128c2ecf20Sopenharmony_ci rcu_read_unlock(); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/* Disable aggregations for a bitmap of TIDs for a given station */ 3168c2ecf20Sopenharmony_cistatic int iwl_mvm_invalidate_sta_queue(struct iwl_mvm *mvm, int queue, 3178c2ecf20Sopenharmony_ci unsigned long disable_agg_tids, 3188c2ecf20Sopenharmony_ci bool remove_queue) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_cmd cmd = {}; 3218c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 3228c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta; 3238c2ecf20Sopenharmony_ci u32 status; 3248c2ecf20Sopenharmony_ci u8 sta_id; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) 3278c2ecf20Sopenharmony_ci return -EINVAL; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci sta_id = mvm->queue_info[queue].ra_sta_id; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci rcu_read_lock(); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { 3368c2ecf20Sopenharmony_ci rcu_read_unlock(); 3378c2ecf20Sopenharmony_ci return -EINVAL; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_mac80211(sta); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci mvmsta->tid_disable_agg |= disable_agg_tids; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci cmd.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color); 3458c2ecf20Sopenharmony_ci cmd.sta_id = mvmsta->sta_id; 3468c2ecf20Sopenharmony_ci cmd.add_modify = STA_MODE_MODIFY; 3478c2ecf20Sopenharmony_ci cmd.modify_mask = STA_MODIFY_QUEUES; 3488c2ecf20Sopenharmony_ci if (disable_agg_tids) 3498c2ecf20Sopenharmony_ci cmd.modify_mask |= STA_MODIFY_TID_DISABLE_TX; 3508c2ecf20Sopenharmony_ci if (remove_queue) 3518c2ecf20Sopenharmony_ci cmd.modify_mask |= STA_MODIFY_QUEUE_REMOVAL; 3528c2ecf20Sopenharmony_ci cmd.tfd_queue_msk = cpu_to_le32(mvmsta->tfd_queue_msk); 3538c2ecf20Sopenharmony_ci cmd.tid_disable_tx = cpu_to_le16(mvmsta->tid_disable_agg); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci rcu_read_unlock(); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* Notify FW of queue removal from the STA queues */ 3588c2ecf20Sopenharmony_ci status = ADD_STA_SUCCESS; 3598c2ecf20Sopenharmony_ci return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, 3608c2ecf20Sopenharmony_ci iwl_mvm_add_sta_cmd_size(mvm), 3618c2ecf20Sopenharmony_ci &cmd, &status); 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic int iwl_mvm_disable_txq(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 3658c2ecf20Sopenharmony_ci u16 *queueptr, u8 tid, u8 flags) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci int queue = *queueptr; 3688c2ecf20Sopenharmony_ci struct iwl_scd_txq_cfg_cmd cmd = { 3698c2ecf20Sopenharmony_ci .scd_queue = queue, 3708c2ecf20Sopenharmony_ci .action = SCD_CFG_DISABLE_QUEUE, 3718c2ecf20Sopenharmony_ci }; 3728c2ecf20Sopenharmony_ci int ret; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) { 3758c2ecf20Sopenharmony_ci iwl_trans_txq_free(mvm->trans, queue); 3768c2ecf20Sopenharmony_ci *queueptr = IWL_MVM_INVALID_QUEUE; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return 0; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (WARN_ON(mvm->queue_info[queue].tid_bitmap == 0)) 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci mvm->queue_info[queue].tid_bitmap &= ~BIT(tid); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci cmd.action = mvm->queue_info[queue].tid_bitmap ? 3878c2ecf20Sopenharmony_ci SCD_CFG_ENABLE_QUEUE : SCD_CFG_DISABLE_QUEUE; 3888c2ecf20Sopenharmony_ci if (cmd.action == SCD_CFG_DISABLE_QUEUE) 3898c2ecf20Sopenharmony_ci mvm->queue_info[queue].status = IWL_MVM_QUEUE_FREE; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 3928c2ecf20Sopenharmony_ci "Disabling TXQ #%d tids=0x%x\n", 3938c2ecf20Sopenharmony_ci queue, 3948c2ecf20Sopenharmony_ci mvm->queue_info[queue].tid_bitmap); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* If the queue is still enabled - nothing left to do in this func */ 3978c2ecf20Sopenharmony_ci if (cmd.action == SCD_CFG_ENABLE_QUEUE) 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci cmd.sta_id = mvm->queue_info[queue].ra_sta_id; 4018c2ecf20Sopenharmony_ci cmd.tid = mvm->queue_info[queue].txq_tid; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* Make sure queue info is correct even though we overwrite it */ 4048c2ecf20Sopenharmony_ci WARN(mvm->queue_info[queue].tid_bitmap, 4058c2ecf20Sopenharmony_ci "TXQ #%d info out-of-sync - tids=0x%x\n", 4068c2ecf20Sopenharmony_ci queue, mvm->queue_info[queue].tid_bitmap); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* If we are here - the queue is freed and we can zero out these vals */ 4098c2ecf20Sopenharmony_ci mvm->queue_info[queue].tid_bitmap = 0; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (sta) { 4128c2ecf20Sopenharmony_ci struct iwl_mvm_txq *mvmtxq = 4138c2ecf20Sopenharmony_ci iwl_mvm_txq_from_tid(sta, tid); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* Regardless if this is a reserved TXQ for a STA - mark it as false */ 4198c2ecf20Sopenharmony_ci mvm->queue_info[queue].reserved = false; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci iwl_trans_txq_disable(mvm->trans, queue, false); 4228c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, 4238c2ecf20Sopenharmony_ci sizeof(struct iwl_scd_txq_cfg_cmd), &cmd); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (ret) 4268c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n", 4278c2ecf20Sopenharmony_ci queue, ret); 4288c2ecf20Sopenharmony_ci return ret; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int iwl_mvm_get_queue_agg_tids(struct iwl_mvm *mvm, int queue) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 4348c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta; 4358c2ecf20Sopenharmony_ci unsigned long tid_bitmap; 4368c2ecf20Sopenharmony_ci unsigned long agg_tids = 0; 4378c2ecf20Sopenharmony_ci u8 sta_id; 4388c2ecf20Sopenharmony_ci int tid; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) 4438c2ecf20Sopenharmony_ci return -EINVAL; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci sta_id = mvm->queue_info[queue].ra_sta_id; 4468c2ecf20Sopenharmony_ci tid_bitmap = mvm->queue_info[queue].tid_bitmap; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], 4498c2ecf20Sopenharmony_ci lockdep_is_held(&mvm->mutex)); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) 4528c2ecf20Sopenharmony_ci return -EINVAL; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_mac80211(sta); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci spin_lock_bh(&mvmsta->lock); 4578c2ecf20Sopenharmony_ci for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { 4588c2ecf20Sopenharmony_ci if (mvmsta->tid_data[tid].state == IWL_AGG_ON) 4598c2ecf20Sopenharmony_ci agg_tids |= BIT(tid); 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci spin_unlock_bh(&mvmsta->lock); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return agg_tids; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci/* 4678c2ecf20Sopenharmony_ci * Remove a queue from a station's resources. 4688c2ecf20Sopenharmony_ci * Note that this only marks as free. It DOESN'T delete a BA agreement, and 4698c2ecf20Sopenharmony_ci * doesn't disable the queue 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_cistatic int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 4748c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta; 4758c2ecf20Sopenharmony_ci unsigned long tid_bitmap; 4768c2ecf20Sopenharmony_ci unsigned long disable_agg_tids = 0; 4778c2ecf20Sopenharmony_ci u8 sta_id; 4788c2ecf20Sopenharmony_ci int tid; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) 4838c2ecf20Sopenharmony_ci return -EINVAL; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci sta_id = mvm->queue_info[queue].ra_sta_id; 4868c2ecf20Sopenharmony_ci tid_bitmap = mvm->queue_info[queue].tid_bitmap; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci rcu_read_lock(); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { 4938c2ecf20Sopenharmony_ci rcu_read_unlock(); 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_mac80211(sta); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci spin_lock_bh(&mvmsta->lock); 5008c2ecf20Sopenharmony_ci /* Unmap MAC queues and TIDs from this queue */ 5018c2ecf20Sopenharmony_ci for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { 5028c2ecf20Sopenharmony_ci struct iwl_mvm_txq *mvmtxq = 5038c2ecf20Sopenharmony_ci iwl_mvm_txq_from_tid(sta, tid); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (mvmsta->tid_data[tid].state == IWL_AGG_ON) 5068c2ecf20Sopenharmony_ci disable_agg_tids |= BIT(tid); 5078c2ecf20Sopenharmony_ci mvmsta->tid_data[tid].txq_id = IWL_MVM_INVALID_QUEUE; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci mvmsta->tfd_queue_msk &= ~BIT(queue); /* Don't use this queue anymore */ 5138c2ecf20Sopenharmony_ci spin_unlock_bh(&mvmsta->lock); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci rcu_read_unlock(); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* 5188c2ecf20Sopenharmony_ci * The TX path may have been using this TXQ_ID from the tid_data, 5198c2ecf20Sopenharmony_ci * so make sure it's no longer running so that we can safely reuse 5208c2ecf20Sopenharmony_ci * this TXQ later. We've set all the TIDs to IWL_MVM_INVALID_QUEUE 5218c2ecf20Sopenharmony_ci * above, but nothing guarantees we've stopped using them. Thus, 5228c2ecf20Sopenharmony_ci * without this, we could get to iwl_mvm_disable_txq() and remove 5238c2ecf20Sopenharmony_ci * the queue while still sending frames to it. 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_ci synchronize_net(); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci return disable_agg_tids; 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic int iwl_mvm_free_inactive_queue(struct iwl_mvm *mvm, int queue, 5318c2ecf20Sopenharmony_ci struct ieee80211_sta *old_sta, 5328c2ecf20Sopenharmony_ci u8 new_sta_id) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta; 5358c2ecf20Sopenharmony_ci u8 sta_id, tid; 5368c2ecf20Sopenharmony_ci unsigned long disable_agg_tids = 0; 5378c2ecf20Sopenharmony_ci bool same_sta; 5388c2ecf20Sopenharmony_ci u16 queue_tmp = queue; 5398c2ecf20Sopenharmony_ci int ret; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) 5448c2ecf20Sopenharmony_ci return -EINVAL; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci sta_id = mvm->queue_info[queue].ra_sta_id; 5478c2ecf20Sopenharmony_ci tid = mvm->queue_info[queue].txq_tid; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci same_sta = sta_id == new_sta_id; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); 5528c2ecf20Sopenharmony_ci if (WARN_ON(!mvmsta)) 5538c2ecf20Sopenharmony_ci return -EINVAL; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci disable_agg_tids = iwl_mvm_remove_sta_queue_marking(mvm, queue); 5568c2ecf20Sopenharmony_ci /* Disable the queue */ 5578c2ecf20Sopenharmony_ci if (disable_agg_tids) 5588c2ecf20Sopenharmony_ci iwl_mvm_invalidate_sta_queue(mvm, queue, 5598c2ecf20Sopenharmony_ci disable_agg_tids, false); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci ret = iwl_mvm_disable_txq(mvm, old_sta, &queue_tmp, tid, 0); 5628c2ecf20Sopenharmony_ci if (ret) { 5638c2ecf20Sopenharmony_ci IWL_ERR(mvm, 5648c2ecf20Sopenharmony_ci "Failed to free inactive queue %d (ret=%d)\n", 5658c2ecf20Sopenharmony_ci queue, ret); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci return ret; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* If TXQ is allocated to another STA, update removal in FW */ 5718c2ecf20Sopenharmony_ci if (!same_sta) 5728c2ecf20Sopenharmony_ci iwl_mvm_invalidate_sta_queue(mvm, queue, 0, true); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci return 0; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic int iwl_mvm_get_shared_queue(struct iwl_mvm *mvm, 5788c2ecf20Sopenharmony_ci unsigned long tfd_queue_mask, u8 ac) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci int queue = 0; 5818c2ecf20Sopenharmony_ci u8 ac_to_queue[IEEE80211_NUM_ACS]; 5828c2ecf20Sopenharmony_ci int i; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* 5858c2ecf20Sopenharmony_ci * This protects us against grabbing a queue that's being reconfigured 5868c2ecf20Sopenharmony_ci * by the inactivity checker. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) 5918c2ecf20Sopenharmony_ci return -EINVAL; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci memset(&ac_to_queue, IEEE80211_INVAL_HW_QUEUE, sizeof(ac_to_queue)); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* See what ACs the existing queues for this STA have */ 5968c2ecf20Sopenharmony_ci for_each_set_bit(i, &tfd_queue_mask, IWL_MVM_DQA_MAX_DATA_QUEUE) { 5978c2ecf20Sopenharmony_ci /* Only DATA queues can be shared */ 5988c2ecf20Sopenharmony_ci if (i < IWL_MVM_DQA_MIN_DATA_QUEUE && 5998c2ecf20Sopenharmony_ci i != IWL_MVM_DQA_BSS_CLIENT_QUEUE) 6008c2ecf20Sopenharmony_ci continue; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci ac_to_queue[mvm->queue_info[i].mac80211_ac] = i; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* 6068c2ecf20Sopenharmony_ci * The queue to share is chosen only from DATA queues as follows (in 6078c2ecf20Sopenharmony_ci * descending priority): 6088c2ecf20Sopenharmony_ci * 1. An AC_BE queue 6098c2ecf20Sopenharmony_ci * 2. Same AC queue 6108c2ecf20Sopenharmony_ci * 3. Highest AC queue that is lower than new AC 6118c2ecf20Sopenharmony_ci * 4. Any existing AC (there always is at least 1 DATA queue) 6128c2ecf20Sopenharmony_ci */ 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci /* Priority 1: An AC_BE queue */ 6158c2ecf20Sopenharmony_ci if (ac_to_queue[IEEE80211_AC_BE] != IEEE80211_INVAL_HW_QUEUE) 6168c2ecf20Sopenharmony_ci queue = ac_to_queue[IEEE80211_AC_BE]; 6178c2ecf20Sopenharmony_ci /* Priority 2: Same AC queue */ 6188c2ecf20Sopenharmony_ci else if (ac_to_queue[ac] != IEEE80211_INVAL_HW_QUEUE) 6198c2ecf20Sopenharmony_ci queue = ac_to_queue[ac]; 6208c2ecf20Sopenharmony_ci /* Priority 3a: If new AC is VO and VI exists - use VI */ 6218c2ecf20Sopenharmony_ci else if (ac == IEEE80211_AC_VO && 6228c2ecf20Sopenharmony_ci ac_to_queue[IEEE80211_AC_VI] != IEEE80211_INVAL_HW_QUEUE) 6238c2ecf20Sopenharmony_ci queue = ac_to_queue[IEEE80211_AC_VI]; 6248c2ecf20Sopenharmony_ci /* Priority 3b: No BE so only AC less than the new one is BK */ 6258c2ecf20Sopenharmony_ci else if (ac_to_queue[IEEE80211_AC_BK] != IEEE80211_INVAL_HW_QUEUE) 6268c2ecf20Sopenharmony_ci queue = ac_to_queue[IEEE80211_AC_BK]; 6278c2ecf20Sopenharmony_ci /* Priority 4a: No BE nor BK - use VI if exists */ 6288c2ecf20Sopenharmony_ci else if (ac_to_queue[IEEE80211_AC_VI] != IEEE80211_INVAL_HW_QUEUE) 6298c2ecf20Sopenharmony_ci queue = ac_to_queue[IEEE80211_AC_VI]; 6308c2ecf20Sopenharmony_ci /* Priority 4b: No BE, BK nor VI - use VO if exists */ 6318c2ecf20Sopenharmony_ci else if (ac_to_queue[IEEE80211_AC_VO] != IEEE80211_INVAL_HW_QUEUE) 6328c2ecf20Sopenharmony_ci queue = ac_to_queue[IEEE80211_AC_VO]; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* Make sure queue found (or not) is legal */ 6358c2ecf20Sopenharmony_ci if (!iwl_mvm_is_dqa_data_queue(mvm, queue) && 6368c2ecf20Sopenharmony_ci !iwl_mvm_is_dqa_mgmt_queue(mvm, queue) && 6378c2ecf20Sopenharmony_ci (queue != IWL_MVM_DQA_BSS_CLIENT_QUEUE)) { 6388c2ecf20Sopenharmony_ci IWL_ERR(mvm, "No DATA queues available to share\n"); 6398c2ecf20Sopenharmony_ci return -ENOSPC; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci return queue; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci/* 6468c2ecf20Sopenharmony_ci * If a given queue has a higher AC than the TID stream that is being compared 6478c2ecf20Sopenharmony_ci * to, the queue needs to be redirected to the lower AC. This function does that 6488c2ecf20Sopenharmony_ci * in such a case, otherwise - if no redirection required - it does nothing, 6498c2ecf20Sopenharmony_ci * unless the %force param is true. 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_cistatic int iwl_mvm_redirect_queue(struct iwl_mvm *mvm, int queue, int tid, 6528c2ecf20Sopenharmony_ci int ac, int ssn, unsigned int wdg_timeout, 6538c2ecf20Sopenharmony_ci bool force, struct iwl_mvm_txq *txq) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct iwl_scd_txq_cfg_cmd cmd = { 6568c2ecf20Sopenharmony_ci .scd_queue = queue, 6578c2ecf20Sopenharmony_ci .action = SCD_CFG_DISABLE_QUEUE, 6588c2ecf20Sopenharmony_ci }; 6598c2ecf20Sopenharmony_ci bool shared_queue; 6608c2ecf20Sopenharmony_ci int ret; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) 6638c2ecf20Sopenharmony_ci return -EINVAL; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* 6668c2ecf20Sopenharmony_ci * If the AC is lower than current one - FIFO needs to be redirected to 6678c2ecf20Sopenharmony_ci * the lowest one of the streams in the queue. Check if this is needed 6688c2ecf20Sopenharmony_ci * here. 6698c2ecf20Sopenharmony_ci * Notice that the enum ieee80211_ac_numbers is "flipped", so BK is with 6708c2ecf20Sopenharmony_ci * value 3 and VO with value 0, so to check if ac X is lower than ac Y 6718c2ecf20Sopenharmony_ci * we need to check if the numerical value of X is LARGER than of Y. 6728c2ecf20Sopenharmony_ci */ 6738c2ecf20Sopenharmony_ci if (ac <= mvm->queue_info[queue].mac80211_ac && !force) { 6748c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 6758c2ecf20Sopenharmony_ci "No redirection needed on TXQ #%d\n", 6768c2ecf20Sopenharmony_ci queue); 6778c2ecf20Sopenharmony_ci return 0; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci cmd.sta_id = mvm->queue_info[queue].ra_sta_id; 6818c2ecf20Sopenharmony_ci cmd.tx_fifo = iwl_mvm_ac_to_tx_fifo[mvm->queue_info[queue].mac80211_ac]; 6828c2ecf20Sopenharmony_ci cmd.tid = mvm->queue_info[queue].txq_tid; 6838c2ecf20Sopenharmony_ci shared_queue = hweight16(mvm->queue_info[queue].tid_bitmap) > 1; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, "Redirecting TXQ #%d to FIFO #%d\n", 6868c2ecf20Sopenharmony_ci queue, iwl_mvm_ac_to_tx_fifo[ac]); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* Stop the queue and wait for it to empty */ 6898c2ecf20Sopenharmony_ci txq->stopped = true; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci ret = iwl_trans_wait_tx_queues_empty(mvm->trans, BIT(queue)); 6928c2ecf20Sopenharmony_ci if (ret) { 6938c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Error draining queue %d before reconfig\n", 6948c2ecf20Sopenharmony_ci queue); 6958c2ecf20Sopenharmony_ci ret = -EIO; 6968c2ecf20Sopenharmony_ci goto out; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci /* Before redirecting the queue we need to de-activate it */ 7008c2ecf20Sopenharmony_ci iwl_trans_txq_disable(mvm->trans, queue, false); 7018c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd); 7028c2ecf20Sopenharmony_ci if (ret) 7038c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed SCD disable TXQ %d (ret=%d)\n", queue, 7048c2ecf20Sopenharmony_ci ret); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci /* Make sure the SCD wrptr is correctly set before reconfiguring */ 7078c2ecf20Sopenharmony_ci iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL, wdg_timeout); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* Update the TID "owner" of the queue */ 7108c2ecf20Sopenharmony_ci mvm->queue_info[queue].txq_tid = tid; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci /* TODO: Work-around SCD bug when moving back by multiples of 0x40 */ 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci /* Redirect to lower AC */ 7158c2ecf20Sopenharmony_ci iwl_mvm_reconfig_scd(mvm, queue, iwl_mvm_ac_to_tx_fifo[ac], 7168c2ecf20Sopenharmony_ci cmd.sta_id, tid, IWL_FRAME_LIMIT, ssn); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* Update AC marking of the queue */ 7198c2ecf20Sopenharmony_ci mvm->queue_info[queue].mac80211_ac = ac; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci /* 7228c2ecf20Sopenharmony_ci * Mark queue as shared in transport if shared 7238c2ecf20Sopenharmony_ci * Note this has to be done after queue enablement because enablement 7248c2ecf20Sopenharmony_ci * can also set this value, and there is no indication there to shared 7258c2ecf20Sopenharmony_ci * queues 7268c2ecf20Sopenharmony_ci */ 7278c2ecf20Sopenharmony_ci if (shared_queue) 7288c2ecf20Sopenharmony_ci iwl_trans_txq_set_shared_mode(mvm->trans, queue, true); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ciout: 7318c2ecf20Sopenharmony_ci /* Continue using the queue */ 7328c2ecf20Sopenharmony_ci txq->stopped = false; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci return ret; 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id, 7388c2ecf20Sopenharmony_ci u8 minq, u8 maxq) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci int i; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (WARN(maxq >= mvm->trans->trans_cfg->base_params->num_of_queues, 7458c2ecf20Sopenharmony_ci "max queue %d >= num_of_queues (%d)", maxq, 7468c2ecf20Sopenharmony_ci mvm->trans->trans_cfg->base_params->num_of_queues)) 7478c2ecf20Sopenharmony_ci maxq = mvm->trans->trans_cfg->base_params->num_of_queues - 1; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* This should not be hit with new TX path */ 7508c2ecf20Sopenharmony_ci if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) 7518c2ecf20Sopenharmony_ci return -ENOSPC; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* Start by looking for a free queue */ 7548c2ecf20Sopenharmony_ci for (i = minq; i <= maxq; i++) 7558c2ecf20Sopenharmony_ci if (mvm->queue_info[i].tid_bitmap == 0 && 7568c2ecf20Sopenharmony_ci mvm->queue_info[i].status == IWL_MVM_QUEUE_FREE) 7578c2ecf20Sopenharmony_ci return i; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci return -ENOSPC; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, 7638c2ecf20Sopenharmony_ci u8 sta_id, u8 tid, unsigned int timeout) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci int queue, size = max_t(u32, IWL_DEFAULT_QUEUE_SIZE, 7668c2ecf20Sopenharmony_ci mvm->trans->cfg->min_256_ba_txq_size); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (tid == IWL_MAX_TID_COUNT) { 7698c2ecf20Sopenharmony_ci tid = IWL_MGMT_TID; 7708c2ecf20Sopenharmony_ci size = max_t(u32, IWL_MGMT_QUEUE_SIZE, 7718c2ecf20Sopenharmony_ci mvm->trans->cfg->min_txq_size); 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci do { 7758c2ecf20Sopenharmony_ci __le16 enable = cpu_to_le16(TX_QUEUE_CFG_ENABLE_QUEUE); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci queue = iwl_trans_txq_alloc(mvm->trans, enable, 7788c2ecf20Sopenharmony_ci sta_id, tid, SCD_QUEUE_CFG, 7798c2ecf20Sopenharmony_ci size, timeout); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (queue < 0) 7828c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 7838c2ecf20Sopenharmony_ci "Failed allocating TXQ of size %d for sta %d tid %d, ret: %d\n", 7848c2ecf20Sopenharmony_ci size, sta_id, tid, queue); 7858c2ecf20Sopenharmony_ci size /= 2; 7868c2ecf20Sopenharmony_ci } while (queue < 0 && size >= 16); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (queue < 0) 7898c2ecf20Sopenharmony_ci return queue; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, "Enabling TXQ #%d for sta %d tid %d\n", 7928c2ecf20Sopenharmony_ci queue, sta_id, tid); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci return queue; 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic int iwl_mvm_sta_alloc_queue_tvqm(struct iwl_mvm *mvm, 7988c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, u8 ac, 7998c2ecf20Sopenharmony_ci int tid) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 8028c2ecf20Sopenharmony_ci struct iwl_mvm_txq *mvmtxq = 8038c2ecf20Sopenharmony_ci iwl_mvm_txq_from_tid(sta, tid); 8048c2ecf20Sopenharmony_ci unsigned int wdg_timeout = 8058c2ecf20Sopenharmony_ci iwl_mvm_get_wd_timeout(mvm, mvmsta->vif, false, false); 8068c2ecf20Sopenharmony_ci int queue = -1; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 8118c2ecf20Sopenharmony_ci "Allocating queue for sta %d on tid %d\n", 8128c2ecf20Sopenharmony_ci mvmsta->sta_id, tid); 8138c2ecf20Sopenharmony_ci queue = iwl_mvm_tvqm_enable_txq(mvm, mvmsta->sta_id, tid, wdg_timeout); 8148c2ecf20Sopenharmony_ci if (queue < 0) 8158c2ecf20Sopenharmony_ci return queue; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci mvmtxq->txq_id = queue; 8188c2ecf20Sopenharmony_ci mvm->tvqm_info[queue].txq_tid = tid; 8198c2ecf20Sopenharmony_ci mvm->tvqm_info[queue].sta_id = mvmsta->sta_id; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, "Allocated queue is %d\n", queue); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci spin_lock_bh(&mvmsta->lock); 8248c2ecf20Sopenharmony_ci mvmsta->tid_data[tid].txq_id = queue; 8258c2ecf20Sopenharmony_ci spin_unlock_bh(&mvmsta->lock); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci return 0; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic bool iwl_mvm_update_txq_mapping(struct iwl_mvm *mvm, 8318c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 8328c2ecf20Sopenharmony_ci int queue, u8 sta_id, u8 tid) 8338c2ecf20Sopenharmony_ci{ 8348c2ecf20Sopenharmony_ci bool enable_queue = true; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci /* Make sure this TID isn't already enabled */ 8378c2ecf20Sopenharmony_ci if (mvm->queue_info[queue].tid_bitmap & BIT(tid)) { 8388c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Trying to enable TXQ %d with existing TID %d\n", 8398c2ecf20Sopenharmony_ci queue, tid); 8408c2ecf20Sopenharmony_ci return false; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci /* Update mappings and refcounts */ 8448c2ecf20Sopenharmony_ci if (mvm->queue_info[queue].tid_bitmap) 8458c2ecf20Sopenharmony_ci enable_queue = false; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci mvm->queue_info[queue].tid_bitmap |= BIT(tid); 8488c2ecf20Sopenharmony_ci mvm->queue_info[queue].ra_sta_id = sta_id; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (enable_queue) { 8518c2ecf20Sopenharmony_ci if (tid != IWL_MAX_TID_COUNT) 8528c2ecf20Sopenharmony_ci mvm->queue_info[queue].mac80211_ac = 8538c2ecf20Sopenharmony_ci tid_to_mac80211_ac[tid]; 8548c2ecf20Sopenharmony_ci else 8558c2ecf20Sopenharmony_ci mvm->queue_info[queue].mac80211_ac = IEEE80211_AC_VO; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci mvm->queue_info[queue].txq_tid = tid; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci if (sta) { 8618c2ecf20Sopenharmony_ci struct iwl_mvm_txq *mvmtxq = 8628c2ecf20Sopenharmony_ci iwl_mvm_txq_from_tid(sta, tid); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci mvmtxq->txq_id = queue; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 8688c2ecf20Sopenharmony_ci "Enabling TXQ #%d tids=0x%x\n", 8698c2ecf20Sopenharmony_ci queue, mvm->queue_info[queue].tid_bitmap); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci return enable_queue; 8728c2ecf20Sopenharmony_ci} 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_cistatic bool iwl_mvm_enable_txq(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 8758c2ecf20Sopenharmony_ci int queue, u16 ssn, 8768c2ecf20Sopenharmony_ci const struct iwl_trans_txq_scd_cfg *cfg, 8778c2ecf20Sopenharmony_ci unsigned int wdg_timeout) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci struct iwl_scd_txq_cfg_cmd cmd = { 8808c2ecf20Sopenharmony_ci .scd_queue = queue, 8818c2ecf20Sopenharmony_ci .action = SCD_CFG_ENABLE_QUEUE, 8828c2ecf20Sopenharmony_ci .window = cfg->frame_limit, 8838c2ecf20Sopenharmony_ci .sta_id = cfg->sta_id, 8848c2ecf20Sopenharmony_ci .ssn = cpu_to_le16(ssn), 8858c2ecf20Sopenharmony_ci .tx_fifo = cfg->fifo, 8868c2ecf20Sopenharmony_ci .aggregate = cfg->aggregate, 8878c2ecf20Sopenharmony_ci .tid = cfg->tid, 8888c2ecf20Sopenharmony_ci }; 8898c2ecf20Sopenharmony_ci bool inc_ssn; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) 8928c2ecf20Sopenharmony_ci return false; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci /* Send the enabling command if we need to */ 8958c2ecf20Sopenharmony_ci if (!iwl_mvm_update_txq_mapping(mvm, sta, queue, cfg->sta_id, cfg->tid)) 8968c2ecf20Sopenharmony_ci return false; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci inc_ssn = iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, 8998c2ecf20Sopenharmony_ci NULL, wdg_timeout); 9008c2ecf20Sopenharmony_ci if (inc_ssn) 9018c2ecf20Sopenharmony_ci le16_add_cpu(&cmd.ssn, 1); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd), 9048c2ecf20Sopenharmony_ci "Failed to configure queue %d on FIFO %d\n", queue, cfg->fifo); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci return inc_ssn; 9078c2ecf20Sopenharmony_ci} 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic void iwl_mvm_change_queue_tid(struct iwl_mvm *mvm, int queue) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci struct iwl_scd_txq_cfg_cmd cmd = { 9128c2ecf20Sopenharmony_ci .scd_queue = queue, 9138c2ecf20Sopenharmony_ci .action = SCD_CFG_UPDATE_QUEUE_TID, 9148c2ecf20Sopenharmony_ci }; 9158c2ecf20Sopenharmony_ci int tid; 9168c2ecf20Sopenharmony_ci unsigned long tid_bitmap; 9178c2ecf20Sopenharmony_ci int ret; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) 9228c2ecf20Sopenharmony_ci return; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci tid_bitmap = mvm->queue_info[queue].tid_bitmap; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci if (WARN(!tid_bitmap, "TXQ %d has no tids assigned to it\n", queue)) 9278c2ecf20Sopenharmony_ci return; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci /* Find any TID for queue */ 9308c2ecf20Sopenharmony_ci tid = find_first_bit(&tid_bitmap, IWL_MAX_TID_COUNT + 1); 9318c2ecf20Sopenharmony_ci cmd.tid = tid; 9328c2ecf20Sopenharmony_ci cmd.tx_fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd); 9358c2ecf20Sopenharmony_ci if (ret) { 9368c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to update owner of TXQ %d (ret=%d)\n", 9378c2ecf20Sopenharmony_ci queue, ret); 9388c2ecf20Sopenharmony_ci return; 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci mvm->queue_info[queue].txq_tid = tid; 9428c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, "Changed TXQ %d ownership to tid %d\n", 9438c2ecf20Sopenharmony_ci queue, tid); 9448c2ecf20Sopenharmony_ci} 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cistatic void iwl_mvm_unshare_queue(struct iwl_mvm *mvm, int queue) 9478c2ecf20Sopenharmony_ci{ 9488c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 9498c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta; 9508c2ecf20Sopenharmony_ci u8 sta_id; 9518c2ecf20Sopenharmony_ci int tid = -1; 9528c2ecf20Sopenharmony_ci unsigned long tid_bitmap; 9538c2ecf20Sopenharmony_ci unsigned int wdg_timeout; 9548c2ecf20Sopenharmony_ci int ssn; 9558c2ecf20Sopenharmony_ci int ret = true; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci /* queue sharing is disabled on new TX path */ 9588c2ecf20Sopenharmony_ci if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) 9598c2ecf20Sopenharmony_ci return; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci sta_id = mvm->queue_info[queue].ra_sta_id; 9648c2ecf20Sopenharmony_ci tid_bitmap = mvm->queue_info[queue].tid_bitmap; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci /* Find TID for queue, and make sure it is the only one on the queue */ 9678c2ecf20Sopenharmony_ci tid = find_first_bit(&tid_bitmap, IWL_MAX_TID_COUNT + 1); 9688c2ecf20Sopenharmony_ci if (tid_bitmap != BIT(tid)) { 9698c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to unshare q %d, active tids=0x%lx\n", 9708c2ecf20Sopenharmony_ci queue, tid_bitmap); 9718c2ecf20Sopenharmony_ci return; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, "Unsharing TXQ %d, keeping tid %d\n", queue, 9758c2ecf20Sopenharmony_ci tid); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], 9788c2ecf20Sopenharmony_ci lockdep_is_held(&mvm->mutex)); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) 9818c2ecf20Sopenharmony_ci return; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_mac80211(sta); 9848c2ecf20Sopenharmony_ci wdg_timeout = iwl_mvm_get_wd_timeout(mvm, mvmsta->vif, false, false); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci ssn = IEEE80211_SEQ_TO_SN(mvmsta->tid_data[tid].seq_number); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci ret = iwl_mvm_redirect_queue(mvm, queue, tid, 9898c2ecf20Sopenharmony_ci tid_to_mac80211_ac[tid], ssn, 9908c2ecf20Sopenharmony_ci wdg_timeout, true, 9918c2ecf20Sopenharmony_ci iwl_mvm_txq_from_tid(sta, tid)); 9928c2ecf20Sopenharmony_ci if (ret) { 9938c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to redirect TXQ %d\n", queue); 9948c2ecf20Sopenharmony_ci return; 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci /* If aggs should be turned back on - do it */ 9988c2ecf20Sopenharmony_ci if (mvmsta->tid_data[tid].state == IWL_AGG_ON) { 9998c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_cmd cmd = {0}; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci mvmsta->tid_disable_agg &= ~BIT(tid); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci cmd.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color); 10048c2ecf20Sopenharmony_ci cmd.sta_id = mvmsta->sta_id; 10058c2ecf20Sopenharmony_ci cmd.add_modify = STA_MODE_MODIFY; 10068c2ecf20Sopenharmony_ci cmd.modify_mask = STA_MODIFY_TID_DISABLE_TX; 10078c2ecf20Sopenharmony_ci cmd.tfd_queue_msk = cpu_to_le32(mvmsta->tfd_queue_msk); 10088c2ecf20Sopenharmony_ci cmd.tid_disable_tx = cpu_to_le16(mvmsta->tid_disable_agg); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, 10118c2ecf20Sopenharmony_ci iwl_mvm_add_sta_cmd_size(mvm), &cmd); 10128c2ecf20Sopenharmony_ci if (!ret) { 10138c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 10148c2ecf20Sopenharmony_ci "TXQ #%d is now aggregated again\n", 10158c2ecf20Sopenharmony_ci queue); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* Mark queue intenally as aggregating again */ 10188c2ecf20Sopenharmony_ci iwl_trans_txq_set_shared_mode(mvm->trans, queue, false); 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci mvm->queue_info[queue].status = IWL_MVM_QUEUE_READY; 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci/* 10268c2ecf20Sopenharmony_ci * Remove inactive TIDs of a given queue. 10278c2ecf20Sopenharmony_ci * If all queue TIDs are inactive - mark the queue as inactive 10288c2ecf20Sopenharmony_ci * If only some the queue TIDs are inactive - unmap them from the queue 10298c2ecf20Sopenharmony_ci * 10308c2ecf20Sopenharmony_ci * Returns %true if all TIDs were removed and the queue could be reused. 10318c2ecf20Sopenharmony_ci */ 10328c2ecf20Sopenharmony_cistatic bool iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm, 10338c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta, int queue, 10348c2ecf20Sopenharmony_ci unsigned long tid_bitmap, 10358c2ecf20Sopenharmony_ci unsigned long *unshare_queues, 10368c2ecf20Sopenharmony_ci unsigned long *changetid_queues) 10378c2ecf20Sopenharmony_ci{ 10388c2ecf20Sopenharmony_ci int tid; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci lockdep_assert_held(&mvmsta->lock); 10418c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) 10448c2ecf20Sopenharmony_ci return false; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci /* Go over all non-active TIDs, incl. IWL_MAX_TID_COUNT (for mgmt) */ 10478c2ecf20Sopenharmony_ci for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { 10488c2ecf20Sopenharmony_ci /* If some TFDs are still queued - don't mark TID as inactive */ 10498c2ecf20Sopenharmony_ci if (iwl_mvm_tid_queued(mvm, &mvmsta->tid_data[tid])) 10508c2ecf20Sopenharmony_ci tid_bitmap &= ~BIT(tid); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci /* Don't mark as inactive any TID that has an active BA */ 10538c2ecf20Sopenharmony_ci if (mvmsta->tid_data[tid].state != IWL_AGG_OFF) 10548c2ecf20Sopenharmony_ci tid_bitmap &= ~BIT(tid); 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* If all TIDs in the queue are inactive - return it can be reused */ 10588c2ecf20Sopenharmony_ci if (tid_bitmap == mvm->queue_info[queue].tid_bitmap) { 10598c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, "Queue %d is inactive\n", queue); 10608c2ecf20Sopenharmony_ci return true; 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci /* 10648c2ecf20Sopenharmony_ci * If we are here, this is a shared queue and not all TIDs timed-out. 10658c2ecf20Sopenharmony_ci * Remove the ones that did. 10668c2ecf20Sopenharmony_ci */ 10678c2ecf20Sopenharmony_ci for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { 10688c2ecf20Sopenharmony_ci u16 tid_bitmap; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci mvmsta->tid_data[tid].txq_id = IWL_MVM_INVALID_QUEUE; 10718c2ecf20Sopenharmony_ci mvm->queue_info[queue].tid_bitmap &= ~BIT(tid); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci tid_bitmap = mvm->queue_info[queue].tid_bitmap; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci /* 10768c2ecf20Sopenharmony_ci * We need to take into account a situation in which a TXQ was 10778c2ecf20Sopenharmony_ci * allocated to TID x, and then turned shared by adding TIDs y 10788c2ecf20Sopenharmony_ci * and z. If TID x becomes inactive and is removed from the TXQ, 10798c2ecf20Sopenharmony_ci * ownership must be given to one of the remaining TIDs. 10808c2ecf20Sopenharmony_ci * This is mainly because if TID x continues - a new queue can't 10818c2ecf20Sopenharmony_ci * be allocated for it as long as it is an owner of another TXQ. 10828c2ecf20Sopenharmony_ci * 10838c2ecf20Sopenharmony_ci * Mark this queue in the right bitmap, we'll send the command 10848c2ecf20Sopenharmony_ci * to the firmware later. 10858c2ecf20Sopenharmony_ci */ 10868c2ecf20Sopenharmony_ci if (!(tid_bitmap & BIT(mvm->queue_info[queue].txq_tid))) 10878c2ecf20Sopenharmony_ci set_bit(queue, changetid_queues); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 10908c2ecf20Sopenharmony_ci "Removing inactive TID %d from shared Q:%d\n", 10918c2ecf20Sopenharmony_ci tid, queue); 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 10958c2ecf20Sopenharmony_ci "TXQ #%d left with tid bitmap 0x%x\n", queue, 10968c2ecf20Sopenharmony_ci mvm->queue_info[queue].tid_bitmap); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci /* 10998c2ecf20Sopenharmony_ci * There may be different TIDs with the same mac queues, so make 11008c2ecf20Sopenharmony_ci * sure all TIDs have existing corresponding mac queues enabled 11018c2ecf20Sopenharmony_ci */ 11028c2ecf20Sopenharmony_ci tid_bitmap = mvm->queue_info[queue].tid_bitmap; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci /* If the queue is marked as shared - "unshare" it */ 11058c2ecf20Sopenharmony_ci if (hweight16(mvm->queue_info[queue].tid_bitmap) == 1 && 11068c2ecf20Sopenharmony_ci mvm->queue_info[queue].status == IWL_MVM_QUEUE_SHARED) { 11078c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, "Marking Q:%d for reconfig\n", 11088c2ecf20Sopenharmony_ci queue); 11098c2ecf20Sopenharmony_ci set_bit(queue, unshare_queues); 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci return false; 11138c2ecf20Sopenharmony_ci} 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci/* 11168c2ecf20Sopenharmony_ci * Check for inactivity - this includes checking if any queue 11178c2ecf20Sopenharmony_ci * can be unshared and finding one (and only one) that can be 11188c2ecf20Sopenharmony_ci * reused. 11198c2ecf20Sopenharmony_ci * This function is also invoked as a sort of clean-up task, 11208c2ecf20Sopenharmony_ci * in which case @alloc_for_sta is IWL_MVM_INVALID_STA. 11218c2ecf20Sopenharmony_ci * 11228c2ecf20Sopenharmony_ci * Returns the queue number, or -ENOSPC. 11238c2ecf20Sopenharmony_ci */ 11248c2ecf20Sopenharmony_cistatic int iwl_mvm_inactivity_check(struct iwl_mvm *mvm, u8 alloc_for_sta) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci unsigned long now = jiffies; 11278c2ecf20Sopenharmony_ci unsigned long unshare_queues = 0; 11288c2ecf20Sopenharmony_ci unsigned long changetid_queues = 0; 11298c2ecf20Sopenharmony_ci int i, ret, free_queue = -ENOSPC; 11308c2ecf20Sopenharmony_ci struct ieee80211_sta *queue_owner = NULL; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) 11358c2ecf20Sopenharmony_ci return -ENOSPC; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci rcu_read_lock(); 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci /* we skip the CMD queue below by starting at 1 */ 11408c2ecf20Sopenharmony_ci BUILD_BUG_ON(IWL_MVM_DQA_CMD_QUEUE != 0); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci for (i = 1; i < IWL_MAX_HW_QUEUES; i++) { 11438c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 11448c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta; 11458c2ecf20Sopenharmony_ci u8 sta_id; 11468c2ecf20Sopenharmony_ci int tid; 11478c2ecf20Sopenharmony_ci unsigned long inactive_tid_bitmap = 0; 11488c2ecf20Sopenharmony_ci unsigned long queue_tid_bitmap; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci queue_tid_bitmap = mvm->queue_info[i].tid_bitmap; 11518c2ecf20Sopenharmony_ci if (!queue_tid_bitmap) 11528c2ecf20Sopenharmony_ci continue; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci /* If TXQ isn't in active use anyway - nothing to do here... */ 11558c2ecf20Sopenharmony_ci if (mvm->queue_info[i].status != IWL_MVM_QUEUE_READY && 11568c2ecf20Sopenharmony_ci mvm->queue_info[i].status != IWL_MVM_QUEUE_SHARED) 11578c2ecf20Sopenharmony_ci continue; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci /* Check to see if there are inactive TIDs on this queue */ 11608c2ecf20Sopenharmony_ci for_each_set_bit(tid, &queue_tid_bitmap, 11618c2ecf20Sopenharmony_ci IWL_MAX_TID_COUNT + 1) { 11628c2ecf20Sopenharmony_ci if (time_after(mvm->queue_info[i].last_frame_time[tid] + 11638c2ecf20Sopenharmony_ci IWL_MVM_DQA_QUEUE_TIMEOUT, now)) 11648c2ecf20Sopenharmony_ci continue; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci inactive_tid_bitmap |= BIT(tid); 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci /* If all TIDs are active - finish check on this queue */ 11708c2ecf20Sopenharmony_ci if (!inactive_tid_bitmap) 11718c2ecf20Sopenharmony_ci continue; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci /* 11748c2ecf20Sopenharmony_ci * If we are here - the queue hadn't been served recently and is 11758c2ecf20Sopenharmony_ci * in use 11768c2ecf20Sopenharmony_ci */ 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci sta_id = mvm->queue_info[i].ra_sta_id; 11798c2ecf20Sopenharmony_ci sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci /* 11828c2ecf20Sopenharmony_ci * If the STA doesn't exist anymore, it isn't an error. It could 11838c2ecf20Sopenharmony_ci * be that it was removed since getting the queues, and in this 11848c2ecf20Sopenharmony_ci * case it should've inactivated its queues anyway. 11858c2ecf20Sopenharmony_ci */ 11868c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(sta)) 11878c2ecf20Sopenharmony_ci continue; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_mac80211(sta); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci spin_lock_bh(&mvmsta->lock); 11928c2ecf20Sopenharmony_ci ret = iwl_mvm_remove_inactive_tids(mvm, mvmsta, i, 11938c2ecf20Sopenharmony_ci inactive_tid_bitmap, 11948c2ecf20Sopenharmony_ci &unshare_queues, 11958c2ecf20Sopenharmony_ci &changetid_queues); 11968c2ecf20Sopenharmony_ci if (ret && free_queue < 0) { 11978c2ecf20Sopenharmony_ci queue_owner = sta; 11988c2ecf20Sopenharmony_ci free_queue = i; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci /* only unlock sta lock - we still need the queue info lock */ 12018c2ecf20Sopenharmony_ci spin_unlock_bh(&mvmsta->lock); 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci /* Reconfigure queues requiring reconfiguation */ 12068c2ecf20Sopenharmony_ci for_each_set_bit(i, &unshare_queues, IWL_MAX_HW_QUEUES) 12078c2ecf20Sopenharmony_ci iwl_mvm_unshare_queue(mvm, i); 12088c2ecf20Sopenharmony_ci for_each_set_bit(i, &changetid_queues, IWL_MAX_HW_QUEUES) 12098c2ecf20Sopenharmony_ci iwl_mvm_change_queue_tid(mvm, i); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci rcu_read_unlock(); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci if (free_queue >= 0 && alloc_for_sta != IWL_MVM_INVALID_STA) { 12148c2ecf20Sopenharmony_ci ret = iwl_mvm_free_inactive_queue(mvm, free_queue, queue_owner, 12158c2ecf20Sopenharmony_ci alloc_for_sta); 12168c2ecf20Sopenharmony_ci if (ret) 12178c2ecf20Sopenharmony_ci return ret; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci return free_queue; 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_cistatic int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, 12248c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, u8 ac, int tid) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 12278c2ecf20Sopenharmony_ci struct iwl_trans_txq_scd_cfg cfg = { 12288c2ecf20Sopenharmony_ci .fifo = iwl_mvm_mac_ac_to_tx_fifo(mvm, ac), 12298c2ecf20Sopenharmony_ci .sta_id = mvmsta->sta_id, 12308c2ecf20Sopenharmony_ci .tid = tid, 12318c2ecf20Sopenharmony_ci .frame_limit = IWL_FRAME_LIMIT, 12328c2ecf20Sopenharmony_ci }; 12338c2ecf20Sopenharmony_ci unsigned int wdg_timeout = 12348c2ecf20Sopenharmony_ci iwl_mvm_get_wd_timeout(mvm, mvmsta->vif, false, false); 12358c2ecf20Sopenharmony_ci int queue = -1; 12368c2ecf20Sopenharmony_ci u16 queue_tmp; 12378c2ecf20Sopenharmony_ci unsigned long disable_agg_tids = 0; 12388c2ecf20Sopenharmony_ci enum iwl_mvm_agg_state queue_state; 12398c2ecf20Sopenharmony_ci bool shared_queue = false, inc_ssn; 12408c2ecf20Sopenharmony_ci int ssn; 12418c2ecf20Sopenharmony_ci unsigned long tfd_queue_mask; 12428c2ecf20Sopenharmony_ci int ret; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) 12478c2ecf20Sopenharmony_ci return iwl_mvm_sta_alloc_queue_tvqm(mvm, sta, ac, tid); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci spin_lock_bh(&mvmsta->lock); 12508c2ecf20Sopenharmony_ci tfd_queue_mask = mvmsta->tfd_queue_msk; 12518c2ecf20Sopenharmony_ci ssn = IEEE80211_SEQ_TO_SN(mvmsta->tid_data[tid].seq_number); 12528c2ecf20Sopenharmony_ci spin_unlock_bh(&mvmsta->lock); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci if (tid == IWL_MAX_TID_COUNT) { 12558c2ecf20Sopenharmony_ci queue = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, 12568c2ecf20Sopenharmony_ci IWL_MVM_DQA_MIN_MGMT_QUEUE, 12578c2ecf20Sopenharmony_ci IWL_MVM_DQA_MAX_MGMT_QUEUE); 12588c2ecf20Sopenharmony_ci if (queue >= IWL_MVM_DQA_MIN_MGMT_QUEUE) 12598c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, "Found free MGMT queue #%d\n", 12608c2ecf20Sopenharmony_ci queue); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci /* If no such queue is found, we'll use a DATA queue instead */ 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci if ((queue < 0 && mvmsta->reserved_queue != IEEE80211_INVAL_HW_QUEUE) && 12668c2ecf20Sopenharmony_ci (mvm->queue_info[mvmsta->reserved_queue].status == 12678c2ecf20Sopenharmony_ci IWL_MVM_QUEUE_RESERVED)) { 12688c2ecf20Sopenharmony_ci queue = mvmsta->reserved_queue; 12698c2ecf20Sopenharmony_ci mvm->queue_info[queue].reserved = true; 12708c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, "Using reserved queue #%d\n", queue); 12718c2ecf20Sopenharmony_ci } 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci if (queue < 0) 12748c2ecf20Sopenharmony_ci queue = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, 12758c2ecf20Sopenharmony_ci IWL_MVM_DQA_MIN_DATA_QUEUE, 12768c2ecf20Sopenharmony_ci IWL_MVM_DQA_MAX_DATA_QUEUE); 12778c2ecf20Sopenharmony_ci if (queue < 0) { 12788c2ecf20Sopenharmony_ci /* try harder - perhaps kill an inactive queue */ 12798c2ecf20Sopenharmony_ci queue = iwl_mvm_inactivity_check(mvm, mvmsta->sta_id); 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci /* No free queue - we'll have to share */ 12838c2ecf20Sopenharmony_ci if (queue <= 0) { 12848c2ecf20Sopenharmony_ci queue = iwl_mvm_get_shared_queue(mvm, tfd_queue_mask, ac); 12858c2ecf20Sopenharmony_ci if (queue > 0) { 12868c2ecf20Sopenharmony_ci shared_queue = true; 12878c2ecf20Sopenharmony_ci mvm->queue_info[queue].status = IWL_MVM_QUEUE_SHARED; 12888c2ecf20Sopenharmony_ci } 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci /* 12928c2ecf20Sopenharmony_ci * Mark TXQ as ready, even though it hasn't been fully configured yet, 12938c2ecf20Sopenharmony_ci * to make sure no one else takes it. 12948c2ecf20Sopenharmony_ci * This will allow avoiding re-acquiring the lock at the end of the 12958c2ecf20Sopenharmony_ci * configuration. On error we'll mark it back as free. 12968c2ecf20Sopenharmony_ci */ 12978c2ecf20Sopenharmony_ci if (queue > 0 && !shared_queue) 12988c2ecf20Sopenharmony_ci mvm->queue_info[queue].status = IWL_MVM_QUEUE_READY; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci /* This shouldn't happen - out of queues */ 13018c2ecf20Sopenharmony_ci if (WARN_ON(queue <= 0)) { 13028c2ecf20Sopenharmony_ci IWL_ERR(mvm, "No available queues for tid %d on sta_id %d\n", 13038c2ecf20Sopenharmony_ci tid, cfg.sta_id); 13048c2ecf20Sopenharmony_ci return queue; 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci /* 13088c2ecf20Sopenharmony_ci * Actual en/disablement of aggregations is through the ADD_STA HCMD, 13098c2ecf20Sopenharmony_ci * but for configuring the SCD to send A-MPDUs we need to mark the queue 13108c2ecf20Sopenharmony_ci * as aggregatable. 13118c2ecf20Sopenharmony_ci * Mark all DATA queues as allowing to be aggregated at some point 13128c2ecf20Sopenharmony_ci */ 13138c2ecf20Sopenharmony_ci cfg.aggregate = (queue >= IWL_MVM_DQA_MIN_DATA_QUEUE || 13148c2ecf20Sopenharmony_ci queue == IWL_MVM_DQA_BSS_CLIENT_QUEUE); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 13178c2ecf20Sopenharmony_ci "Allocating %squeue #%d to sta %d on tid %d\n", 13188c2ecf20Sopenharmony_ci shared_queue ? "shared " : "", queue, 13198c2ecf20Sopenharmony_ci mvmsta->sta_id, tid); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci if (shared_queue) { 13228c2ecf20Sopenharmony_ci /* Disable any open aggs on this queue */ 13238c2ecf20Sopenharmony_ci disable_agg_tids = iwl_mvm_get_queue_agg_tids(mvm, queue); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if (disable_agg_tids) { 13268c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, "Disabling aggs on queue %d\n", 13278c2ecf20Sopenharmony_ci queue); 13288c2ecf20Sopenharmony_ci iwl_mvm_invalidate_sta_queue(mvm, queue, 13298c2ecf20Sopenharmony_ci disable_agg_tids, false); 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci inc_ssn = iwl_mvm_enable_txq(mvm, sta, queue, ssn, &cfg, wdg_timeout); 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci /* 13368c2ecf20Sopenharmony_ci * Mark queue as shared in transport if shared 13378c2ecf20Sopenharmony_ci * Note this has to be done after queue enablement because enablement 13388c2ecf20Sopenharmony_ci * can also set this value, and there is no indication there to shared 13398c2ecf20Sopenharmony_ci * queues 13408c2ecf20Sopenharmony_ci */ 13418c2ecf20Sopenharmony_ci if (shared_queue) 13428c2ecf20Sopenharmony_ci iwl_trans_txq_set_shared_mode(mvm->trans, queue, true); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci spin_lock_bh(&mvmsta->lock); 13458c2ecf20Sopenharmony_ci /* 13468c2ecf20Sopenharmony_ci * This looks racy, but it is not. We have only one packet for 13478c2ecf20Sopenharmony_ci * this ra/tid in our Tx path since we stop the Qdisc when we 13488c2ecf20Sopenharmony_ci * need to allocate a new TFD queue. 13498c2ecf20Sopenharmony_ci */ 13508c2ecf20Sopenharmony_ci if (inc_ssn) { 13518c2ecf20Sopenharmony_ci mvmsta->tid_data[tid].seq_number += 0x10; 13528c2ecf20Sopenharmony_ci ssn = (ssn + 1) & IEEE80211_SCTL_SEQ; 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci mvmsta->tid_data[tid].txq_id = queue; 13558c2ecf20Sopenharmony_ci mvmsta->tfd_queue_msk |= BIT(queue); 13568c2ecf20Sopenharmony_ci queue_state = mvmsta->tid_data[tid].state; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci if (mvmsta->reserved_queue == queue) 13598c2ecf20Sopenharmony_ci mvmsta->reserved_queue = IEEE80211_INVAL_HW_QUEUE; 13608c2ecf20Sopenharmony_ci spin_unlock_bh(&mvmsta->lock); 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci if (!shared_queue) { 13638c2ecf20Sopenharmony_ci ret = iwl_mvm_sta_send_to_fw(mvm, sta, true, STA_MODIFY_QUEUES); 13648c2ecf20Sopenharmony_ci if (ret) 13658c2ecf20Sopenharmony_ci goto out_err; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci /* If we need to re-enable aggregations... */ 13688c2ecf20Sopenharmony_ci if (queue_state == IWL_AGG_ON) { 13698c2ecf20Sopenharmony_ci ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); 13708c2ecf20Sopenharmony_ci if (ret) 13718c2ecf20Sopenharmony_ci goto out_err; 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci } else { 13748c2ecf20Sopenharmony_ci /* Redirect queue, if needed */ 13758c2ecf20Sopenharmony_ci ret = iwl_mvm_redirect_queue(mvm, queue, tid, ac, ssn, 13768c2ecf20Sopenharmony_ci wdg_timeout, false, 13778c2ecf20Sopenharmony_ci iwl_mvm_txq_from_tid(sta, tid)); 13788c2ecf20Sopenharmony_ci if (ret) 13798c2ecf20Sopenharmony_ci goto out_err; 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci return 0; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ciout_err: 13858c2ecf20Sopenharmony_ci queue_tmp = queue; 13868c2ecf20Sopenharmony_ci iwl_mvm_disable_txq(mvm, sta, &queue_tmp, tid, 0); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci return ret; 13898c2ecf20Sopenharmony_ci} 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ciint iwl_mvm_sta_ensure_queue(struct iwl_mvm *mvm, 13928c2ecf20Sopenharmony_ci struct ieee80211_txq *txq) 13938c2ecf20Sopenharmony_ci{ 13948c2ecf20Sopenharmony_ci struct iwl_mvm_txq *mvmtxq = iwl_mvm_txq_from_mac80211(txq); 13958c2ecf20Sopenharmony_ci int ret = -EINVAL; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci if (likely(test_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state)) || 14008c2ecf20Sopenharmony_ci !txq->sta) { 14018c2ecf20Sopenharmony_ci return 0; 14028c2ecf20Sopenharmony_ci } 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci if (!iwl_mvm_sta_alloc_queue(mvm, txq->sta, txq->ac, txq->tid)) { 14058c2ecf20Sopenharmony_ci set_bit(IWL_MVM_TXQ_STATE_READY, &mvmtxq->state); 14068c2ecf20Sopenharmony_ci ret = 0; 14078c2ecf20Sopenharmony_ci } 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci local_bh_disable(); 14108c2ecf20Sopenharmony_ci spin_lock(&mvm->add_stream_lock); 14118c2ecf20Sopenharmony_ci if (!list_empty(&mvmtxq->list)) 14128c2ecf20Sopenharmony_ci list_del_init(&mvmtxq->list); 14138c2ecf20Sopenharmony_ci spin_unlock(&mvm->add_stream_lock); 14148c2ecf20Sopenharmony_ci local_bh_enable(); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci return ret; 14178c2ecf20Sopenharmony_ci} 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_civoid iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk) 14208c2ecf20Sopenharmony_ci{ 14218c2ecf20Sopenharmony_ci struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, 14228c2ecf20Sopenharmony_ci add_stream_wk); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci mutex_lock(&mvm->mutex); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci iwl_mvm_inactivity_check(mvm, IWL_MVM_INVALID_STA); 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci while (!list_empty(&mvm->add_stream_txqs)) { 14298c2ecf20Sopenharmony_ci struct iwl_mvm_txq *mvmtxq; 14308c2ecf20Sopenharmony_ci struct ieee80211_txq *txq; 14318c2ecf20Sopenharmony_ci u8 tid; 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci mvmtxq = list_first_entry(&mvm->add_stream_txqs, 14348c2ecf20Sopenharmony_ci struct iwl_mvm_txq, list); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci txq = container_of((void *)mvmtxq, struct ieee80211_txq, 14378c2ecf20Sopenharmony_ci drv_priv); 14388c2ecf20Sopenharmony_ci tid = txq->tid; 14398c2ecf20Sopenharmony_ci if (tid == IEEE80211_NUM_TIDS) 14408c2ecf20Sopenharmony_ci tid = IWL_MAX_TID_COUNT; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci /* 14438c2ecf20Sopenharmony_ci * We can't really do much here, but if this fails we can't 14448c2ecf20Sopenharmony_ci * transmit anyway - so just don't transmit the frame etc. 14458c2ecf20Sopenharmony_ci * and let them back up ... we've tried our best to allocate 14468c2ecf20Sopenharmony_ci * a queue in the function itself. 14478c2ecf20Sopenharmony_ci */ 14488c2ecf20Sopenharmony_ci if (iwl_mvm_sta_alloc_queue(mvm, txq->sta, txq->ac, tid)) { 14498c2ecf20Sopenharmony_ci list_del_init(&mvmtxq->list); 14508c2ecf20Sopenharmony_ci continue; 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci list_del_init(&mvmtxq->list); 14548c2ecf20Sopenharmony_ci local_bh_disable(); 14558c2ecf20Sopenharmony_ci iwl_mvm_mac_itxq_xmit(mvm->hw, txq); 14568c2ecf20Sopenharmony_ci local_bh_enable(); 14578c2ecf20Sopenharmony_ci } 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci mutex_unlock(&mvm->mutex); 14608c2ecf20Sopenharmony_ci} 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_cistatic int iwl_mvm_reserve_sta_stream(struct iwl_mvm *mvm, 14638c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 14648c2ecf20Sopenharmony_ci enum nl80211_iftype vif_type) 14658c2ecf20Sopenharmony_ci{ 14668c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 14678c2ecf20Sopenharmony_ci int queue; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci /* queue reserving is disabled on new TX path */ 14708c2ecf20Sopenharmony_ci if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) 14718c2ecf20Sopenharmony_ci return 0; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci /* run the general cleanup/unsharing of queues */ 14748c2ecf20Sopenharmony_ci iwl_mvm_inactivity_check(mvm, IWL_MVM_INVALID_STA); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci /* Make sure we have free resources for this STA */ 14778c2ecf20Sopenharmony_ci if (vif_type == NL80211_IFTYPE_STATION && !sta->tdls && 14788c2ecf20Sopenharmony_ci !mvm->queue_info[IWL_MVM_DQA_BSS_CLIENT_QUEUE].tid_bitmap && 14798c2ecf20Sopenharmony_ci (mvm->queue_info[IWL_MVM_DQA_BSS_CLIENT_QUEUE].status == 14808c2ecf20Sopenharmony_ci IWL_MVM_QUEUE_FREE)) 14818c2ecf20Sopenharmony_ci queue = IWL_MVM_DQA_BSS_CLIENT_QUEUE; 14828c2ecf20Sopenharmony_ci else 14838c2ecf20Sopenharmony_ci queue = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, 14848c2ecf20Sopenharmony_ci IWL_MVM_DQA_MIN_DATA_QUEUE, 14858c2ecf20Sopenharmony_ci IWL_MVM_DQA_MAX_DATA_QUEUE); 14868c2ecf20Sopenharmony_ci if (queue < 0) { 14878c2ecf20Sopenharmony_ci /* try again - this time kick out a queue if needed */ 14888c2ecf20Sopenharmony_ci queue = iwl_mvm_inactivity_check(mvm, mvmsta->sta_id); 14898c2ecf20Sopenharmony_ci if (queue < 0) { 14908c2ecf20Sopenharmony_ci IWL_ERR(mvm, "No available queues for new station\n"); 14918c2ecf20Sopenharmony_ci return -ENOSPC; 14928c2ecf20Sopenharmony_ci } 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci mvm->queue_info[queue].status = IWL_MVM_QUEUE_RESERVED; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci mvmsta->reserved_queue = queue; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, "Reserving data queue #%d for sta_id %d\n", 14998c2ecf20Sopenharmony_ci queue, mvmsta->sta_id); 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci return 0; 15028c2ecf20Sopenharmony_ci} 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci/* 15058c2ecf20Sopenharmony_ci * In DQA mode, after a HW restart the queues should be allocated as before, in 15068c2ecf20Sopenharmony_ci * order to avoid race conditions when there are shared queues. This function 15078c2ecf20Sopenharmony_ci * does the re-mapping and queue allocation. 15088c2ecf20Sopenharmony_ci * 15098c2ecf20Sopenharmony_ci * Note that re-enabling aggregations isn't done in this function. 15108c2ecf20Sopenharmony_ci */ 15118c2ecf20Sopenharmony_cistatic void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm, 15128c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 15138c2ecf20Sopenharmony_ci{ 15148c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); 15158c2ecf20Sopenharmony_ci unsigned int wdg = 15168c2ecf20Sopenharmony_ci iwl_mvm_get_wd_timeout(mvm, mvm_sta->vif, false, false); 15178c2ecf20Sopenharmony_ci int i; 15188c2ecf20Sopenharmony_ci struct iwl_trans_txq_scd_cfg cfg = { 15198c2ecf20Sopenharmony_ci .sta_id = mvm_sta->sta_id, 15208c2ecf20Sopenharmony_ci .frame_limit = IWL_FRAME_LIMIT, 15218c2ecf20Sopenharmony_ci }; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci /* Make sure reserved queue is still marked as such (if allocated) */ 15248c2ecf20Sopenharmony_ci if (mvm_sta->reserved_queue != IEEE80211_INVAL_HW_QUEUE) 15258c2ecf20Sopenharmony_ci mvm->queue_info[mvm_sta->reserved_queue].status = 15268c2ecf20Sopenharmony_ci IWL_MVM_QUEUE_RESERVED; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci for (i = 0; i <= IWL_MAX_TID_COUNT; i++) { 15298c2ecf20Sopenharmony_ci struct iwl_mvm_tid_data *tid_data = &mvm_sta->tid_data[i]; 15308c2ecf20Sopenharmony_ci int txq_id = tid_data->txq_id; 15318c2ecf20Sopenharmony_ci int ac; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci if (txq_id == IWL_MVM_INVALID_QUEUE) 15348c2ecf20Sopenharmony_ci continue; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci ac = tid_to_mac80211_ac[i]; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) { 15398c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 15408c2ecf20Sopenharmony_ci "Re-mapping sta %d tid %d\n", 15418c2ecf20Sopenharmony_ci mvm_sta->sta_id, i); 15428c2ecf20Sopenharmony_ci txq_id = iwl_mvm_tvqm_enable_txq(mvm, mvm_sta->sta_id, 15438c2ecf20Sopenharmony_ci i, wdg); 15448c2ecf20Sopenharmony_ci /* 15458c2ecf20Sopenharmony_ci * on failures, just set it to IWL_MVM_INVALID_QUEUE 15468c2ecf20Sopenharmony_ci * to try again later, we have no other good way of 15478c2ecf20Sopenharmony_ci * failing here 15488c2ecf20Sopenharmony_ci */ 15498c2ecf20Sopenharmony_ci if (txq_id < 0) 15508c2ecf20Sopenharmony_ci txq_id = IWL_MVM_INVALID_QUEUE; 15518c2ecf20Sopenharmony_ci tid_data->txq_id = txq_id; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci /* 15548c2ecf20Sopenharmony_ci * Since we don't set the seq number after reset, and HW 15558c2ecf20Sopenharmony_ci * sets it now, FW reset will cause the seq num to start 15568c2ecf20Sopenharmony_ci * at 0 again, so driver will need to update it 15578c2ecf20Sopenharmony_ci * internally as well, so it keeps in sync with real val 15588c2ecf20Sopenharmony_ci */ 15598c2ecf20Sopenharmony_ci tid_data->seq_number = 0; 15608c2ecf20Sopenharmony_ci } else { 15618c2ecf20Sopenharmony_ci u16 seq = IEEE80211_SEQ_TO_SN(tid_data->seq_number); 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci cfg.tid = i; 15648c2ecf20Sopenharmony_ci cfg.fifo = iwl_mvm_mac_ac_to_tx_fifo(mvm, ac); 15658c2ecf20Sopenharmony_ci cfg.aggregate = (txq_id >= IWL_MVM_DQA_MIN_DATA_QUEUE || 15668c2ecf20Sopenharmony_ci txq_id == 15678c2ecf20Sopenharmony_ci IWL_MVM_DQA_BSS_CLIENT_QUEUE); 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 15708c2ecf20Sopenharmony_ci "Re-mapping sta %d tid %d to queue %d\n", 15718c2ecf20Sopenharmony_ci mvm_sta->sta_id, i, txq_id); 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci iwl_mvm_enable_txq(mvm, sta, txq_id, seq, &cfg, wdg); 15748c2ecf20Sopenharmony_ci mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_READY; 15758c2ecf20Sopenharmony_ci } 15768c2ecf20Sopenharmony_ci } 15778c2ecf20Sopenharmony_ci} 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_cistatic int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, 15808c2ecf20Sopenharmony_ci struct iwl_mvm_int_sta *sta, 15818c2ecf20Sopenharmony_ci const u8 *addr, 15828c2ecf20Sopenharmony_ci u16 mac_id, u16 color) 15838c2ecf20Sopenharmony_ci{ 15848c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_cmd cmd; 15858c2ecf20Sopenharmony_ci int ret; 15868c2ecf20Sopenharmony_ci u32 status = ADD_STA_SUCCESS; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 15918c2ecf20Sopenharmony_ci cmd.sta_id = sta->sta_id; 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, ADD_STA, 15948c2ecf20Sopenharmony_ci 0) >= 12 && 15958c2ecf20Sopenharmony_ci sta->type == IWL_STA_AUX_ACTIVITY) 15968c2ecf20Sopenharmony_ci cmd.mac_id_n_color = cpu_to_le32(mac_id); 15978c2ecf20Sopenharmony_ci else 15988c2ecf20Sopenharmony_ci cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id, 15998c2ecf20Sopenharmony_ci color)); 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) 16028c2ecf20Sopenharmony_ci cmd.station_type = sta->type; 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_tx_api(mvm)) 16058c2ecf20Sopenharmony_ci cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk); 16068c2ecf20Sopenharmony_ci cmd.tid_disable_tx = cpu_to_le16(0xffff); 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci if (addr) 16098c2ecf20Sopenharmony_ci memcpy(cmd.addr, addr, ETH_ALEN); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, 16128c2ecf20Sopenharmony_ci iwl_mvm_add_sta_cmd_size(mvm), 16138c2ecf20Sopenharmony_ci &cmd, &status); 16148c2ecf20Sopenharmony_ci if (ret) 16158c2ecf20Sopenharmony_ci return ret; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci switch (status & IWL_ADD_STA_STATUS_MASK) { 16188c2ecf20Sopenharmony_ci case ADD_STA_SUCCESS: 16198c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(mvm, "Internal station added.\n"); 16208c2ecf20Sopenharmony_ci return 0; 16218c2ecf20Sopenharmony_ci default: 16228c2ecf20Sopenharmony_ci ret = -EIO; 16238c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Add internal station failed, status=0x%x\n", 16248c2ecf20Sopenharmony_ci status); 16258c2ecf20Sopenharmony_ci break; 16268c2ecf20Sopenharmony_ci } 16278c2ecf20Sopenharmony_ci return ret; 16288c2ecf20Sopenharmony_ci} 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ciint iwl_mvm_add_sta(struct iwl_mvm *mvm, 16318c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 16328c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 16338c2ecf20Sopenharmony_ci{ 16348c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 16358c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); 16368c2ecf20Sopenharmony_ci struct iwl_mvm_rxq_dup_data *dup_data; 16378c2ecf20Sopenharmony_ci int i, ret, sta_id; 16388c2ecf20Sopenharmony_ci bool sta_update = false; 16398c2ecf20Sopenharmony_ci unsigned int sta_flags = 0; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) 16448c2ecf20Sopenharmony_ci sta_id = iwl_mvm_find_free_sta_id(mvm, 16458c2ecf20Sopenharmony_ci ieee80211_vif_type_p2p(vif)); 16468c2ecf20Sopenharmony_ci else 16478c2ecf20Sopenharmony_ci sta_id = mvm_sta->sta_id; 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci if (sta_id == IWL_MVM_INVALID_STA) 16508c2ecf20Sopenharmony_ci return -ENOSPC; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci spin_lock_init(&mvm_sta->lock); 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci /* if this is a HW restart re-alloc existing queues */ 16558c2ecf20Sopenharmony_ci if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { 16568c2ecf20Sopenharmony_ci struct iwl_mvm_int_sta tmp_sta = { 16578c2ecf20Sopenharmony_ci .sta_id = sta_id, 16588c2ecf20Sopenharmony_ci .type = mvm_sta->sta_type, 16598c2ecf20Sopenharmony_ci }; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci /* 16628c2ecf20Sopenharmony_ci * First add an empty station since allocating 16638c2ecf20Sopenharmony_ci * a queue requires a valid station 16648c2ecf20Sopenharmony_ci */ 16658c2ecf20Sopenharmony_ci ret = iwl_mvm_add_int_sta_common(mvm, &tmp_sta, sta->addr, 16668c2ecf20Sopenharmony_ci mvmvif->id, mvmvif->color); 16678c2ecf20Sopenharmony_ci if (ret) 16688c2ecf20Sopenharmony_ci goto err; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci iwl_mvm_realloc_queues_after_restart(mvm, sta); 16718c2ecf20Sopenharmony_ci sta_update = true; 16728c2ecf20Sopenharmony_ci sta_flags = iwl_mvm_has_new_tx_api(mvm) ? 0 : STA_MODIFY_QUEUES; 16738c2ecf20Sopenharmony_ci goto update_fw; 16748c2ecf20Sopenharmony_ci } 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci mvm_sta->sta_id = sta_id; 16778c2ecf20Sopenharmony_ci mvm_sta->mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id, 16788c2ecf20Sopenharmony_ci mvmvif->color); 16798c2ecf20Sopenharmony_ci mvm_sta->vif = vif; 16808c2ecf20Sopenharmony_ci if (!mvm->trans->trans_cfg->gen2) 16818c2ecf20Sopenharmony_ci mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF; 16828c2ecf20Sopenharmony_ci else 16838c2ecf20Sopenharmony_ci mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_GEN2_DEF; 16848c2ecf20Sopenharmony_ci mvm_sta->tx_protection = 0; 16858c2ecf20Sopenharmony_ci mvm_sta->tt_tx_protection = false; 16868c2ecf20Sopenharmony_ci mvm_sta->sta_type = sta->tdls ? IWL_STA_TDLS_LINK : IWL_STA_LINK; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci /* HW restart, don't assume the memory has been zeroed */ 16898c2ecf20Sopenharmony_ci mvm_sta->tid_disable_agg = 0xffff; /* No aggs at first */ 16908c2ecf20Sopenharmony_ci mvm_sta->tfd_queue_msk = 0; 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci /* for HW restart - reset everything but the sequence number */ 16938c2ecf20Sopenharmony_ci for (i = 0; i <= IWL_MAX_TID_COUNT; i++) { 16948c2ecf20Sopenharmony_ci u16 seq = mvm_sta->tid_data[i].seq_number; 16958c2ecf20Sopenharmony_ci memset(&mvm_sta->tid_data[i], 0, sizeof(mvm_sta->tid_data[i])); 16968c2ecf20Sopenharmony_ci mvm_sta->tid_data[i].seq_number = seq; 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci /* 16998c2ecf20Sopenharmony_ci * Mark all queues for this STA as unallocated and defer TX 17008c2ecf20Sopenharmony_ci * frames until the queue is allocated 17018c2ecf20Sopenharmony_ci */ 17028c2ecf20Sopenharmony_ci mvm_sta->tid_data[i].txq_id = IWL_MVM_INVALID_QUEUE; 17038c2ecf20Sopenharmony_ci } 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sta->txq); i++) { 17068c2ecf20Sopenharmony_ci struct iwl_mvm_txq *mvmtxq = 17078c2ecf20Sopenharmony_ci iwl_mvm_txq_from_mac80211(sta->txq[i]); 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE; 17108c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mvmtxq->list); 17118c2ecf20Sopenharmony_ci atomic_set(&mvmtxq->tx_request, 0); 17128c2ecf20Sopenharmony_ci } 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci mvm_sta->agg_tids = 0; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_rx_api(mvm) && 17178c2ecf20Sopenharmony_ci !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { 17188c2ecf20Sopenharmony_ci int q; 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci dup_data = kcalloc(mvm->trans->num_rx_queues, 17218c2ecf20Sopenharmony_ci sizeof(*dup_data), GFP_KERNEL); 17228c2ecf20Sopenharmony_ci if (!dup_data) 17238c2ecf20Sopenharmony_ci return -ENOMEM; 17248c2ecf20Sopenharmony_ci /* 17258c2ecf20Sopenharmony_ci * Initialize all the last_seq values to 0xffff which can never 17268c2ecf20Sopenharmony_ci * compare equal to the frame's seq_ctrl in the check in 17278c2ecf20Sopenharmony_ci * iwl_mvm_is_dup() since the lower 4 bits are the fragment 17288c2ecf20Sopenharmony_ci * number and fragmented packets don't reach that function. 17298c2ecf20Sopenharmony_ci * 17308c2ecf20Sopenharmony_ci * This thus allows receiving a packet with seqno 0 and the 17318c2ecf20Sopenharmony_ci * retry bit set as the very first packet on a new TID. 17328c2ecf20Sopenharmony_ci */ 17338c2ecf20Sopenharmony_ci for (q = 0; q < mvm->trans->num_rx_queues; q++) 17348c2ecf20Sopenharmony_ci memset(dup_data[q].last_seq, 0xff, 17358c2ecf20Sopenharmony_ci sizeof(dup_data[q].last_seq)); 17368c2ecf20Sopenharmony_ci mvm_sta->dup_data = dup_data; 17378c2ecf20Sopenharmony_ci } 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_tx_api(mvm)) { 17408c2ecf20Sopenharmony_ci ret = iwl_mvm_reserve_sta_stream(mvm, sta, 17418c2ecf20Sopenharmony_ci ieee80211_vif_type_p2p(vif)); 17428c2ecf20Sopenharmony_ci if (ret) 17438c2ecf20Sopenharmony_ci goto err; 17448c2ecf20Sopenharmony_ci } 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci /* 17478c2ecf20Sopenharmony_ci * if rs is registered with mac80211, then "add station" will be handled 17488c2ecf20Sopenharmony_ci * via the corresponding ops, otherwise need to notify rate scaling here 17498c2ecf20Sopenharmony_ci */ 17508c2ecf20Sopenharmony_ci if (iwl_mvm_has_tlc_offload(mvm)) 17518c2ecf20Sopenharmony_ci iwl_mvm_rs_add_sta(mvm, mvm_sta); 17528c2ecf20Sopenharmony_ci else 17538c2ecf20Sopenharmony_ci spin_lock_init(&mvm_sta->lq_sta.rs_drv.pers.lock); 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci iwl_mvm_toggle_tx_ant(mvm, &mvm_sta->tx_ant); 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ciupdate_fw: 17588c2ecf20Sopenharmony_ci ret = iwl_mvm_sta_send_to_fw(mvm, sta, sta_update, sta_flags); 17598c2ecf20Sopenharmony_ci if (ret) 17608c2ecf20Sopenharmony_ci goto err; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_STATION) { 17638c2ecf20Sopenharmony_ci if (!sta->tdls) { 17648c2ecf20Sopenharmony_ci WARN_ON(mvmvif->ap_sta_id != IWL_MVM_INVALID_STA); 17658c2ecf20Sopenharmony_ci mvmvif->ap_sta_id = sta_id; 17668c2ecf20Sopenharmony_ci } else { 17678c2ecf20Sopenharmony_ci WARN_ON(mvmvif->ap_sta_id == IWL_MVM_INVALID_STA); 17688c2ecf20Sopenharmony_ci } 17698c2ecf20Sopenharmony_ci } 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta); 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci return 0; 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_cierr: 17768c2ecf20Sopenharmony_ci return ret; 17778c2ecf20Sopenharmony_ci} 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ciint iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, 17808c2ecf20Sopenharmony_ci bool drain) 17818c2ecf20Sopenharmony_ci{ 17828c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_cmd cmd = {}; 17838c2ecf20Sopenharmony_ci int ret; 17848c2ecf20Sopenharmony_ci u32 status; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci cmd.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color); 17898c2ecf20Sopenharmony_ci cmd.sta_id = mvmsta->sta_id; 17908c2ecf20Sopenharmony_ci cmd.add_modify = STA_MODE_MODIFY; 17918c2ecf20Sopenharmony_ci cmd.station_flags = drain ? cpu_to_le32(STA_FLG_DRAIN_FLOW) : 0; 17928c2ecf20Sopenharmony_ci cmd.station_flags_msk = cpu_to_le32(STA_FLG_DRAIN_FLOW); 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci status = ADD_STA_SUCCESS; 17958c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, 17968c2ecf20Sopenharmony_ci iwl_mvm_add_sta_cmd_size(mvm), 17978c2ecf20Sopenharmony_ci &cmd, &status); 17988c2ecf20Sopenharmony_ci if (ret) 17998c2ecf20Sopenharmony_ci return ret; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci switch (status & IWL_ADD_STA_STATUS_MASK) { 18028c2ecf20Sopenharmony_ci case ADD_STA_SUCCESS: 18038c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(mvm, "Frames for staid %d will drained in fw\n", 18048c2ecf20Sopenharmony_ci mvmsta->sta_id); 18058c2ecf20Sopenharmony_ci break; 18068c2ecf20Sopenharmony_ci default: 18078c2ecf20Sopenharmony_ci ret = -EIO; 18088c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Couldn't drain frames for staid %d\n", 18098c2ecf20Sopenharmony_ci mvmsta->sta_id); 18108c2ecf20Sopenharmony_ci break; 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci return ret; 18148c2ecf20Sopenharmony_ci} 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci/* 18178c2ecf20Sopenharmony_ci * Remove a station from the FW table. Before sending the command to remove 18188c2ecf20Sopenharmony_ci * the station validate that the station is indeed known to the driver (sanity 18198c2ecf20Sopenharmony_ci * only). 18208c2ecf20Sopenharmony_ci */ 18218c2ecf20Sopenharmony_cistatic int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id) 18228c2ecf20Sopenharmony_ci{ 18238c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 18248c2ecf20Sopenharmony_ci struct iwl_mvm_rm_sta_cmd rm_sta_cmd = { 18258c2ecf20Sopenharmony_ci .sta_id = sta_id, 18268c2ecf20Sopenharmony_ci }; 18278c2ecf20Sopenharmony_ci int ret; 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], 18308c2ecf20Sopenharmony_ci lockdep_is_held(&mvm->mutex)); 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci /* Note: internal stations are marked as error values */ 18338c2ecf20Sopenharmony_ci if (!sta) { 18348c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Invalid station id\n"); 18358c2ecf20Sopenharmony_ci return -EINVAL; 18368c2ecf20Sopenharmony_ci } 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, 0, 18398c2ecf20Sopenharmony_ci sizeof(rm_sta_cmd), &rm_sta_cmd); 18408c2ecf20Sopenharmony_ci if (ret) { 18418c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id); 18428c2ecf20Sopenharmony_ci return ret; 18438c2ecf20Sopenharmony_ci } 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci return 0; 18468c2ecf20Sopenharmony_ci} 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_cistatic void iwl_mvm_disable_sta_queues(struct iwl_mvm *mvm, 18498c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 18508c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 18518c2ecf20Sopenharmony_ci{ 18528c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); 18538c2ecf20Sopenharmony_ci int i; 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mvm_sta->tid_data); i++) { 18588c2ecf20Sopenharmony_ci if (mvm_sta->tid_data[i].txq_id == IWL_MVM_INVALID_QUEUE) 18598c2ecf20Sopenharmony_ci continue; 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci iwl_mvm_disable_txq(mvm, sta, &mvm_sta->tid_data[i].txq_id, i, 18628c2ecf20Sopenharmony_ci 0); 18638c2ecf20Sopenharmony_ci mvm_sta->tid_data[i].txq_id = IWL_MVM_INVALID_QUEUE; 18648c2ecf20Sopenharmony_ci } 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sta->txq); i++) { 18678c2ecf20Sopenharmony_ci struct iwl_mvm_txq *mvmtxq = 18688c2ecf20Sopenharmony_ci iwl_mvm_txq_from_mac80211(sta->txq[i]); 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci mvmtxq->txq_id = IWL_MVM_INVALID_QUEUE; 18718c2ecf20Sopenharmony_ci list_del_init(&mvmtxq->list); 18728c2ecf20Sopenharmony_ci } 18738c2ecf20Sopenharmony_ci} 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ciint iwl_mvm_wait_sta_queues_empty(struct iwl_mvm *mvm, 18768c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta) 18778c2ecf20Sopenharmony_ci{ 18788c2ecf20Sopenharmony_ci int i; 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mvm_sta->tid_data); i++) { 18818c2ecf20Sopenharmony_ci u16 txq_id; 18828c2ecf20Sopenharmony_ci int ret; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci spin_lock_bh(&mvm_sta->lock); 18858c2ecf20Sopenharmony_ci txq_id = mvm_sta->tid_data[i].txq_id; 18868c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm_sta->lock); 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci if (txq_id == IWL_MVM_INVALID_QUEUE) 18898c2ecf20Sopenharmony_ci continue; 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci ret = iwl_trans_wait_txq_empty(mvm->trans, txq_id); 18928c2ecf20Sopenharmony_ci if (ret) 18938c2ecf20Sopenharmony_ci return ret; 18948c2ecf20Sopenharmony_ci } 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci return 0; 18978c2ecf20Sopenharmony_ci} 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ciint iwl_mvm_rm_sta(struct iwl_mvm *mvm, 19008c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 19018c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 19028c2ecf20Sopenharmony_ci{ 19038c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 19048c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); 19058c2ecf20Sopenharmony_ci u8 sta_id = mvm_sta->sta_id; 19068c2ecf20Sopenharmony_ci int ret; 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_rx_api(mvm)) 19118c2ecf20Sopenharmony_ci kfree(mvm_sta->dup_data); 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); 19148c2ecf20Sopenharmony_ci if (ret) 19158c2ecf20Sopenharmony_ci return ret; 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci /* flush its queues here since we are freeing mvm_sta */ 19188c2ecf20Sopenharmony_ci ret = iwl_mvm_flush_sta(mvm, mvm_sta, false); 19198c2ecf20Sopenharmony_ci if (ret) 19208c2ecf20Sopenharmony_ci return ret; 19218c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) { 19228c2ecf20Sopenharmony_ci ret = iwl_mvm_wait_sta_queues_empty(mvm, mvm_sta); 19238c2ecf20Sopenharmony_ci } else { 19248c2ecf20Sopenharmony_ci u32 q_mask = mvm_sta->tfd_queue_msk; 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci ret = iwl_trans_wait_tx_queues_empty(mvm->trans, 19278c2ecf20Sopenharmony_ci q_mask); 19288c2ecf20Sopenharmony_ci } 19298c2ecf20Sopenharmony_ci if (ret) 19308c2ecf20Sopenharmony_ci return ret; 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci ret = iwl_mvm_drain_sta(mvm, mvm_sta, false); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci iwl_mvm_disable_sta_queues(mvm, vif, sta); 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci /* If there is a TXQ still marked as reserved - free it */ 19378c2ecf20Sopenharmony_ci if (mvm_sta->reserved_queue != IEEE80211_INVAL_HW_QUEUE) { 19388c2ecf20Sopenharmony_ci u8 reserved_txq = mvm_sta->reserved_queue; 19398c2ecf20Sopenharmony_ci enum iwl_mvm_queue_status *status; 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci /* 19428c2ecf20Sopenharmony_ci * If no traffic has gone through the reserved TXQ - it 19438c2ecf20Sopenharmony_ci * is still marked as IWL_MVM_QUEUE_RESERVED, and 19448c2ecf20Sopenharmony_ci * should be manually marked as free again 19458c2ecf20Sopenharmony_ci */ 19468c2ecf20Sopenharmony_ci status = &mvm->queue_info[reserved_txq].status; 19478c2ecf20Sopenharmony_ci if (WARN((*status != IWL_MVM_QUEUE_RESERVED) && 19488c2ecf20Sopenharmony_ci (*status != IWL_MVM_QUEUE_FREE), 19498c2ecf20Sopenharmony_ci "sta_id %d reserved txq %d status %d", 19508c2ecf20Sopenharmony_ci sta_id, reserved_txq, *status)) 19518c2ecf20Sopenharmony_ci return -EINVAL; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci *status = IWL_MVM_QUEUE_FREE; 19548c2ecf20Sopenharmony_ci } 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_STATION && 19578c2ecf20Sopenharmony_ci mvmvif->ap_sta_id == sta_id) { 19588c2ecf20Sopenharmony_ci /* if associated - we can't remove the AP STA now */ 19598c2ecf20Sopenharmony_ci if (vif->bss_conf.assoc) 19608c2ecf20Sopenharmony_ci return ret; 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci /* unassoc - go ahead - remove the AP STA now */ 19638c2ecf20Sopenharmony_ci mvmvif->ap_sta_id = IWL_MVM_INVALID_STA; 19648c2ecf20Sopenharmony_ci } 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci /* 19678c2ecf20Sopenharmony_ci * This shouldn't happen - the TDLS channel switch should be canceled 19688c2ecf20Sopenharmony_ci * before the STA is removed. 19698c2ecf20Sopenharmony_ci */ 19708c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(mvm->tdls_cs.peer.sta_id == sta_id)) { 19718c2ecf20Sopenharmony_ci mvm->tdls_cs.peer.sta_id = IWL_MVM_INVALID_STA; 19728c2ecf20Sopenharmony_ci cancel_delayed_work(&mvm->tdls_cs.dwork); 19738c2ecf20Sopenharmony_ci } 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci /* 19768c2ecf20Sopenharmony_ci * Make sure that the tx response code sees the station as -EBUSY and 19778c2ecf20Sopenharmony_ci * calls the drain worker. 19788c2ecf20Sopenharmony_ci */ 19798c2ecf20Sopenharmony_ci spin_lock_bh(&mvm_sta->lock); 19808c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm_sta->lock); 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id); 19838c2ecf20Sopenharmony_ci RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL); 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci return ret; 19868c2ecf20Sopenharmony_ci} 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ciint iwl_mvm_rm_sta_id(struct iwl_mvm *mvm, 19898c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 19908c2ecf20Sopenharmony_ci u8 sta_id) 19918c2ecf20Sopenharmony_ci{ 19928c2ecf20Sopenharmony_ci int ret = iwl_mvm_rm_sta_common(mvm, sta_id); 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL); 19978c2ecf20Sopenharmony_ci return ret; 19988c2ecf20Sopenharmony_ci} 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ciint iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, 20018c2ecf20Sopenharmony_ci struct iwl_mvm_int_sta *sta, 20028c2ecf20Sopenharmony_ci u32 qmask, enum nl80211_iftype iftype, 20038c2ecf20Sopenharmony_ci enum iwl_sta_type type) 20048c2ecf20Sopenharmony_ci{ 20058c2ecf20Sopenharmony_ci if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) || 20068c2ecf20Sopenharmony_ci sta->sta_id == IWL_MVM_INVALID_STA) { 20078c2ecf20Sopenharmony_ci sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype); 20088c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_INVALID_STA)) 20098c2ecf20Sopenharmony_ci return -ENOSPC; 20108c2ecf20Sopenharmony_ci } 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci sta->tfd_queue_msk = qmask; 20138c2ecf20Sopenharmony_ci sta->type = type; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci /* put a non-NULL value so iterating over the stations won't stop */ 20168c2ecf20Sopenharmony_ci rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], ERR_PTR(-EINVAL)); 20178c2ecf20Sopenharmony_ci return 0; 20188c2ecf20Sopenharmony_ci} 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_civoid iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta) 20218c2ecf20Sopenharmony_ci{ 20228c2ecf20Sopenharmony_ci RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], NULL); 20238c2ecf20Sopenharmony_ci memset(sta, 0, sizeof(struct iwl_mvm_int_sta)); 20248c2ecf20Sopenharmony_ci sta->sta_id = IWL_MVM_INVALID_STA; 20258c2ecf20Sopenharmony_ci} 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_cistatic void iwl_mvm_enable_aux_snif_queue(struct iwl_mvm *mvm, u16 queue, 20288c2ecf20Sopenharmony_ci u8 sta_id, u8 fifo) 20298c2ecf20Sopenharmony_ci{ 20308c2ecf20Sopenharmony_ci unsigned int wdg_timeout = 20318c2ecf20Sopenharmony_ci mvm->trans->trans_cfg->base_params->wd_timeout; 20328c2ecf20Sopenharmony_ci struct iwl_trans_txq_scd_cfg cfg = { 20338c2ecf20Sopenharmony_ci .fifo = fifo, 20348c2ecf20Sopenharmony_ci .sta_id = sta_id, 20358c2ecf20Sopenharmony_ci .tid = IWL_MAX_TID_COUNT, 20368c2ecf20Sopenharmony_ci .aggregate = false, 20378c2ecf20Sopenharmony_ci .frame_limit = IWL_FRAME_LIMIT, 20388c2ecf20Sopenharmony_ci }; 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci WARN_ON(iwl_mvm_has_new_tx_api(mvm)); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci iwl_mvm_enable_txq(mvm, NULL, queue, 0, &cfg, wdg_timeout); 20438c2ecf20Sopenharmony_ci} 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_cistatic int iwl_mvm_enable_aux_snif_queue_tvqm(struct iwl_mvm *mvm, u8 sta_id) 20468c2ecf20Sopenharmony_ci{ 20478c2ecf20Sopenharmony_ci unsigned int wdg_timeout = 20488c2ecf20Sopenharmony_ci mvm->trans->trans_cfg->base_params->wd_timeout; 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci WARN_ON(!iwl_mvm_has_new_tx_api(mvm)); 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci return iwl_mvm_tvqm_enable_txq(mvm, sta_id, IWL_MAX_TID_COUNT, 20538c2ecf20Sopenharmony_ci wdg_timeout); 20548c2ecf20Sopenharmony_ci} 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_cistatic int iwl_mvm_add_int_sta_with_queue(struct iwl_mvm *mvm, int macidx, 20578c2ecf20Sopenharmony_ci int maccolor, u8 *addr, 20588c2ecf20Sopenharmony_ci struct iwl_mvm_int_sta *sta, 20598c2ecf20Sopenharmony_ci u16 *queue, int fifo) 20608c2ecf20Sopenharmony_ci{ 20618c2ecf20Sopenharmony_ci int ret; 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci /* Map queue to fifo - needs to happen before adding station */ 20648c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_tx_api(mvm)) 20658c2ecf20Sopenharmony_ci iwl_mvm_enable_aux_snif_queue(mvm, *queue, sta->sta_id, fifo); 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci ret = iwl_mvm_add_int_sta_common(mvm, sta, addr, macidx, maccolor); 20688c2ecf20Sopenharmony_ci if (ret) { 20698c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_tx_api(mvm)) 20708c2ecf20Sopenharmony_ci iwl_mvm_disable_txq(mvm, NULL, queue, 20718c2ecf20Sopenharmony_ci IWL_MAX_TID_COUNT, 0); 20728c2ecf20Sopenharmony_ci return ret; 20738c2ecf20Sopenharmony_ci } 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci /* 20768c2ecf20Sopenharmony_ci * For 22000 firmware and on we cannot add queue to a station unknown 20778c2ecf20Sopenharmony_ci * to firmware so enable queue here - after the station was added 20788c2ecf20Sopenharmony_ci */ 20798c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) { 20808c2ecf20Sopenharmony_ci int txq; 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci txq = iwl_mvm_enable_aux_snif_queue_tvqm(mvm, sta->sta_id); 20838c2ecf20Sopenharmony_ci if (txq < 0) { 20848c2ecf20Sopenharmony_ci iwl_mvm_rm_sta_common(mvm, sta->sta_id); 20858c2ecf20Sopenharmony_ci return txq; 20868c2ecf20Sopenharmony_ci } 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci *queue = txq; 20898c2ecf20Sopenharmony_ci } 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_ci return 0; 20928c2ecf20Sopenharmony_ci} 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ciint iwl_mvm_add_aux_sta(struct iwl_mvm *mvm, u32 lmac_id) 20958c2ecf20Sopenharmony_ci{ 20968c2ecf20Sopenharmony_ci int ret; 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_ci /* Allocate aux station and assign to it the aux queue */ 21018c2ecf20Sopenharmony_ci ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue), 21028c2ecf20Sopenharmony_ci NL80211_IFTYPE_UNSPECIFIED, 21038c2ecf20Sopenharmony_ci IWL_STA_AUX_ACTIVITY); 21048c2ecf20Sopenharmony_ci if (ret) 21058c2ecf20Sopenharmony_ci return ret; 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci /* 21088c2ecf20Sopenharmony_ci * In CDB NICs we need to specify which lmac to use for aux activity 21098c2ecf20Sopenharmony_ci * using the mac_id argument place to send lmac_id to the function 21108c2ecf20Sopenharmony_ci */ 21118c2ecf20Sopenharmony_ci ret = iwl_mvm_add_int_sta_with_queue(mvm, lmac_id, 0, NULL, 21128c2ecf20Sopenharmony_ci &mvm->aux_sta, &mvm->aux_queue, 21138c2ecf20Sopenharmony_ci IWL_MVM_TX_FIFO_MCAST); 21148c2ecf20Sopenharmony_ci if (ret) { 21158c2ecf20Sopenharmony_ci iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta); 21168c2ecf20Sopenharmony_ci return ret; 21178c2ecf20Sopenharmony_ci } 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci return 0; 21208c2ecf20Sopenharmony_ci} 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ciint iwl_mvm_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 21238c2ecf20Sopenharmony_ci{ 21248c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci return iwl_mvm_add_int_sta_with_queue(mvm, mvmvif->id, mvmvif->color, 21298c2ecf20Sopenharmony_ci NULL, &mvm->snif_sta, 21308c2ecf20Sopenharmony_ci &mvm->snif_queue, 21318c2ecf20Sopenharmony_ci IWL_MVM_TX_FIFO_BE); 21328c2ecf20Sopenharmony_ci} 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ciint iwl_mvm_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 21358c2ecf20Sopenharmony_ci{ 21368c2ecf20Sopenharmony_ci int ret; 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(mvm->snif_sta.sta_id == IWL_MVM_INVALID_STA)) 21418c2ecf20Sopenharmony_ci return -EINVAL; 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci iwl_mvm_disable_txq(mvm, NULL, &mvm->snif_queue, IWL_MAX_TID_COUNT, 0); 21448c2ecf20Sopenharmony_ci ret = iwl_mvm_rm_sta_common(mvm, mvm->snif_sta.sta_id); 21458c2ecf20Sopenharmony_ci if (ret) 21468c2ecf20Sopenharmony_ci IWL_WARN(mvm, "Failed sending remove station\n"); 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci return ret; 21498c2ecf20Sopenharmony_ci} 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ciint iwl_mvm_rm_aux_sta(struct iwl_mvm *mvm) 21528c2ecf20Sopenharmony_ci{ 21538c2ecf20Sopenharmony_ci int ret; 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(mvm->aux_sta.sta_id == IWL_MVM_INVALID_STA)) 21588c2ecf20Sopenharmony_ci return -EINVAL; 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci iwl_mvm_disable_txq(mvm, NULL, &mvm->aux_queue, IWL_MAX_TID_COUNT, 0); 21618c2ecf20Sopenharmony_ci ret = iwl_mvm_rm_sta_common(mvm, mvm->aux_sta.sta_id); 21628c2ecf20Sopenharmony_ci if (ret) 21638c2ecf20Sopenharmony_ci IWL_WARN(mvm, "Failed sending remove station\n"); 21648c2ecf20Sopenharmony_ci iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta); 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci return ret; 21678c2ecf20Sopenharmony_ci} 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_civoid iwl_mvm_dealloc_snif_sta(struct iwl_mvm *mvm) 21708c2ecf20Sopenharmony_ci{ 21718c2ecf20Sopenharmony_ci iwl_mvm_dealloc_int_sta(mvm, &mvm->snif_sta); 21728c2ecf20Sopenharmony_ci} 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci/* 21758c2ecf20Sopenharmony_ci * Send the add station command for the vif's broadcast station. 21768c2ecf20Sopenharmony_ci * Assumes that the station was already allocated. 21778c2ecf20Sopenharmony_ci * 21788c2ecf20Sopenharmony_ci * @mvm: the mvm component 21798c2ecf20Sopenharmony_ci * @vif: the interface to which the broadcast station is added 21808c2ecf20Sopenharmony_ci * @bsta: the broadcast station to add. 21818c2ecf20Sopenharmony_ci */ 21828c2ecf20Sopenharmony_ciint iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 21838c2ecf20Sopenharmony_ci{ 21848c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 21858c2ecf20Sopenharmony_ci struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; 21868c2ecf20Sopenharmony_ci static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 21878c2ecf20Sopenharmony_ci const u8 *baddr = _baddr; 21888c2ecf20Sopenharmony_ci int queue; 21898c2ecf20Sopenharmony_ci int ret; 21908c2ecf20Sopenharmony_ci unsigned int wdg_timeout = 21918c2ecf20Sopenharmony_ci iwl_mvm_get_wd_timeout(mvm, vif, false, false); 21928c2ecf20Sopenharmony_ci struct iwl_trans_txq_scd_cfg cfg = { 21938c2ecf20Sopenharmony_ci .fifo = IWL_MVM_TX_FIFO_VO, 21948c2ecf20Sopenharmony_ci .sta_id = mvmvif->bcast_sta.sta_id, 21958c2ecf20Sopenharmony_ci .tid = IWL_MAX_TID_COUNT, 21968c2ecf20Sopenharmony_ci .aggregate = false, 21978c2ecf20Sopenharmony_ci .frame_limit = IWL_FRAME_LIMIT, 21988c2ecf20Sopenharmony_ci }; 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_tx_api(mvm)) { 22038c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_AP || 22048c2ecf20Sopenharmony_ci vif->type == NL80211_IFTYPE_ADHOC) { 22058c2ecf20Sopenharmony_ci queue = mvm->probe_queue; 22068c2ecf20Sopenharmony_ci } else if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { 22078c2ecf20Sopenharmony_ci queue = mvm->p2p_dev_queue; 22088c2ecf20Sopenharmony_ci } else { 22098c2ecf20Sopenharmony_ci WARN(1, "Missing required TXQ for adding bcast STA\n"); 22108c2ecf20Sopenharmony_ci return -EINVAL; 22118c2ecf20Sopenharmony_ci } 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci bsta->tfd_queue_msk |= BIT(queue); 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci iwl_mvm_enable_txq(mvm, NULL, queue, 0, &cfg, wdg_timeout); 22168c2ecf20Sopenharmony_ci } 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_ADHOC) 22198c2ecf20Sopenharmony_ci baddr = vif->bss_conf.bssid; 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_INVALID_STA)) 22228c2ecf20Sopenharmony_ci return -ENOSPC; 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci ret = iwl_mvm_add_int_sta_common(mvm, bsta, baddr, 22258c2ecf20Sopenharmony_ci mvmvif->id, mvmvif->color); 22268c2ecf20Sopenharmony_ci if (ret) 22278c2ecf20Sopenharmony_ci return ret; 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci /* 22308c2ecf20Sopenharmony_ci * For 22000 firmware and on we cannot add queue to a station unknown 22318c2ecf20Sopenharmony_ci * to firmware so enable queue here - after the station was added 22328c2ecf20Sopenharmony_ci */ 22338c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) { 22348c2ecf20Sopenharmony_ci queue = iwl_mvm_tvqm_enable_txq(mvm, bsta->sta_id, 22358c2ecf20Sopenharmony_ci IWL_MAX_TID_COUNT, 22368c2ecf20Sopenharmony_ci wdg_timeout); 22378c2ecf20Sopenharmony_ci if (queue < 0) { 22388c2ecf20Sopenharmony_ci iwl_mvm_rm_sta_common(mvm, bsta->sta_id); 22398c2ecf20Sopenharmony_ci return queue; 22408c2ecf20Sopenharmony_ci } 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_AP || 22438c2ecf20Sopenharmony_ci vif->type == NL80211_IFTYPE_ADHOC) 22448c2ecf20Sopenharmony_ci mvm->probe_queue = queue; 22458c2ecf20Sopenharmony_ci else if (vif->type == NL80211_IFTYPE_P2P_DEVICE) 22468c2ecf20Sopenharmony_ci mvm->p2p_dev_queue = queue; 22478c2ecf20Sopenharmony_ci } 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci return 0; 22508c2ecf20Sopenharmony_ci} 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_cistatic void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm, 22538c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 22548c2ecf20Sopenharmony_ci{ 22558c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 22568c2ecf20Sopenharmony_ci u16 *queueptr, queue; 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_ci iwl_mvm_flush_sta(mvm, &mvmvif->bcast_sta, true); 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci switch (vif->type) { 22638c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 22648c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 22658c2ecf20Sopenharmony_ci queueptr = &mvm->probe_queue; 22668c2ecf20Sopenharmony_ci break; 22678c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 22688c2ecf20Sopenharmony_ci queueptr = &mvm->p2p_dev_queue; 22698c2ecf20Sopenharmony_ci break; 22708c2ecf20Sopenharmony_ci default: 22718c2ecf20Sopenharmony_ci WARN(1, "Can't free bcast queue on vif type %d\n", 22728c2ecf20Sopenharmony_ci vif->type); 22738c2ecf20Sopenharmony_ci return; 22748c2ecf20Sopenharmony_ci } 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci queue = *queueptr; 22778c2ecf20Sopenharmony_ci iwl_mvm_disable_txq(mvm, NULL, queueptr, IWL_MAX_TID_COUNT, 0); 22788c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) 22798c2ecf20Sopenharmony_ci return; 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_ci WARN_ON(!(mvmvif->bcast_sta.tfd_queue_msk & BIT(queue))); 22828c2ecf20Sopenharmony_ci mvmvif->bcast_sta.tfd_queue_msk &= ~BIT(queue); 22838c2ecf20Sopenharmony_ci} 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci/* Send the FW a request to remove the station from it's internal data 22868c2ecf20Sopenharmony_ci * structures, but DO NOT remove the entry from the local data structures. */ 22878c2ecf20Sopenharmony_ciint iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 22888c2ecf20Sopenharmony_ci{ 22898c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 22908c2ecf20Sopenharmony_ci int ret; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci iwl_mvm_free_bcast_sta_queues(mvm, vif); 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci ret = iwl_mvm_rm_sta_common(mvm, mvmvif->bcast_sta.sta_id); 22978c2ecf20Sopenharmony_ci if (ret) 22988c2ecf20Sopenharmony_ci IWL_WARN(mvm, "Failed sending remove station\n"); 22998c2ecf20Sopenharmony_ci return ret; 23008c2ecf20Sopenharmony_ci} 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ciint iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 23038c2ecf20Sopenharmony_ci{ 23048c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci return iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, 0, 23098c2ecf20Sopenharmony_ci ieee80211_vif_type_p2p(vif), 23108c2ecf20Sopenharmony_ci IWL_STA_GENERAL_PURPOSE); 23118c2ecf20Sopenharmony_ci} 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci/* Allocate a new station entry for the broadcast station to the given vif, 23148c2ecf20Sopenharmony_ci * and send it to the FW. 23158c2ecf20Sopenharmony_ci * Note that each P2P mac should have its own broadcast station. 23168c2ecf20Sopenharmony_ci * 23178c2ecf20Sopenharmony_ci * @mvm: the mvm component 23188c2ecf20Sopenharmony_ci * @vif: the interface to which the broadcast station is added 23198c2ecf20Sopenharmony_ci * @bsta: the broadcast station to add. */ 23208c2ecf20Sopenharmony_ciint iwl_mvm_add_p2p_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 23218c2ecf20Sopenharmony_ci{ 23228c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 23238c2ecf20Sopenharmony_ci struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; 23248c2ecf20Sopenharmony_ci int ret; 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci ret = iwl_mvm_alloc_bcast_sta(mvm, vif); 23298c2ecf20Sopenharmony_ci if (ret) 23308c2ecf20Sopenharmony_ci return ret; 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_ci ret = iwl_mvm_send_add_bcast_sta(mvm, vif); 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci if (ret) 23358c2ecf20Sopenharmony_ci iwl_mvm_dealloc_int_sta(mvm, bsta); 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci return ret; 23388c2ecf20Sopenharmony_ci} 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_civoid iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 23418c2ecf20Sopenharmony_ci{ 23428c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta); 23458c2ecf20Sopenharmony_ci} 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_ci/* 23488c2ecf20Sopenharmony_ci * Send the FW a request to remove the station from it's internal data 23498c2ecf20Sopenharmony_ci * structures, and in addition remove it from the local data structure. 23508c2ecf20Sopenharmony_ci */ 23518c2ecf20Sopenharmony_ciint iwl_mvm_rm_p2p_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 23528c2ecf20Sopenharmony_ci{ 23538c2ecf20Sopenharmony_ci int ret; 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci ret = iwl_mvm_send_rm_bcast_sta(mvm, vif); 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci iwl_mvm_dealloc_bcast_sta(mvm, vif); 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci return ret; 23628c2ecf20Sopenharmony_ci} 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci/* 23658c2ecf20Sopenharmony_ci * Allocate a new station entry for the multicast station to the given vif, 23668c2ecf20Sopenharmony_ci * and send it to the FW. 23678c2ecf20Sopenharmony_ci * Note that each AP/GO mac should have its own multicast station. 23688c2ecf20Sopenharmony_ci * 23698c2ecf20Sopenharmony_ci * @mvm: the mvm component 23708c2ecf20Sopenharmony_ci * @vif: the interface to which the multicast station is added 23718c2ecf20Sopenharmony_ci */ 23728c2ecf20Sopenharmony_ciint iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 23738c2ecf20Sopenharmony_ci{ 23748c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 23758c2ecf20Sopenharmony_ci struct iwl_mvm_int_sta *msta = &mvmvif->mcast_sta; 23768c2ecf20Sopenharmony_ci static const u8 _maddr[] = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00}; 23778c2ecf20Sopenharmony_ci const u8 *maddr = _maddr; 23788c2ecf20Sopenharmony_ci struct iwl_trans_txq_scd_cfg cfg = { 23798c2ecf20Sopenharmony_ci .fifo = vif->type == NL80211_IFTYPE_AP ? 23808c2ecf20Sopenharmony_ci IWL_MVM_TX_FIFO_MCAST : IWL_MVM_TX_FIFO_BE, 23818c2ecf20Sopenharmony_ci .sta_id = msta->sta_id, 23828c2ecf20Sopenharmony_ci .tid = 0, 23838c2ecf20Sopenharmony_ci .aggregate = false, 23848c2ecf20Sopenharmony_ci .frame_limit = IWL_FRAME_LIMIT, 23858c2ecf20Sopenharmony_ci }; 23868c2ecf20Sopenharmony_ci unsigned int timeout = iwl_mvm_get_wd_timeout(mvm, vif, false, false); 23878c2ecf20Sopenharmony_ci int ret; 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_ci if (WARN_ON(vif->type != NL80211_IFTYPE_AP && 23928c2ecf20Sopenharmony_ci vif->type != NL80211_IFTYPE_ADHOC)) 23938c2ecf20Sopenharmony_ci return -ENOTSUPP; 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci /* 23968c2ecf20Sopenharmony_ci * In IBSS, ieee80211_check_queues() sets the cab_queue to be 23978c2ecf20Sopenharmony_ci * invalid, so make sure we use the queue we want. 23988c2ecf20Sopenharmony_ci * Note that this is done here as we want to avoid making DQA 23998c2ecf20Sopenharmony_ci * changes in mac80211 layer. 24008c2ecf20Sopenharmony_ci */ 24018c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_ADHOC) 24028c2ecf20Sopenharmony_ci mvmvif->cab_queue = IWL_MVM_DQA_GCAST_QUEUE; 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci /* 24058c2ecf20Sopenharmony_ci * While in previous FWs we had to exclude cab queue from TFD queue 24068c2ecf20Sopenharmony_ci * mask, now it is needed as any other queue. 24078c2ecf20Sopenharmony_ci */ 24088c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_tx_api(mvm) && 24098c2ecf20Sopenharmony_ci fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) { 24108c2ecf20Sopenharmony_ci iwl_mvm_enable_txq(mvm, NULL, mvmvif->cab_queue, 0, &cfg, 24118c2ecf20Sopenharmony_ci timeout); 24128c2ecf20Sopenharmony_ci msta->tfd_queue_msk |= BIT(mvmvif->cab_queue); 24138c2ecf20Sopenharmony_ci } 24148c2ecf20Sopenharmony_ci ret = iwl_mvm_add_int_sta_common(mvm, msta, maddr, 24158c2ecf20Sopenharmony_ci mvmvif->id, mvmvif->color); 24168c2ecf20Sopenharmony_ci if (ret) 24178c2ecf20Sopenharmony_ci goto err; 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci /* 24208c2ecf20Sopenharmony_ci * Enable cab queue after the ADD_STA command is sent. 24218c2ecf20Sopenharmony_ci * This is needed for 22000 firmware which won't accept SCD_QUEUE_CFG 24228c2ecf20Sopenharmony_ci * command with unknown station id, and for FW that doesn't support 24238c2ecf20Sopenharmony_ci * station API since the cab queue is not included in the 24248c2ecf20Sopenharmony_ci * tfd_queue_mask. 24258c2ecf20Sopenharmony_ci */ 24268c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) { 24278c2ecf20Sopenharmony_ci int queue = iwl_mvm_tvqm_enable_txq(mvm, msta->sta_id, 24288c2ecf20Sopenharmony_ci 0, 24298c2ecf20Sopenharmony_ci timeout); 24308c2ecf20Sopenharmony_ci if (queue < 0) { 24318c2ecf20Sopenharmony_ci ret = queue; 24328c2ecf20Sopenharmony_ci goto err; 24338c2ecf20Sopenharmony_ci } 24348c2ecf20Sopenharmony_ci mvmvif->cab_queue = queue; 24358c2ecf20Sopenharmony_ci } else if (!fw_has_api(&mvm->fw->ucode_capa, 24368c2ecf20Sopenharmony_ci IWL_UCODE_TLV_API_STA_TYPE)) 24378c2ecf20Sopenharmony_ci iwl_mvm_enable_txq(mvm, NULL, mvmvif->cab_queue, 0, &cfg, 24388c2ecf20Sopenharmony_ci timeout); 24398c2ecf20Sopenharmony_ci 24408c2ecf20Sopenharmony_ci return 0; 24418c2ecf20Sopenharmony_cierr: 24428c2ecf20Sopenharmony_ci iwl_mvm_dealloc_int_sta(mvm, msta); 24438c2ecf20Sopenharmony_ci return ret; 24448c2ecf20Sopenharmony_ci} 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_cistatic int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id, 24478c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf, 24488c2ecf20Sopenharmony_ci bool mcast) 24498c2ecf20Sopenharmony_ci{ 24508c2ecf20Sopenharmony_ci union { 24518c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_key_cmd_v1 cmd_v1; 24528c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_key_cmd cmd; 24538c2ecf20Sopenharmony_ci } u = {}; 24548c2ecf20Sopenharmony_ci bool new_api = fw_has_api(&mvm->fw->ucode_capa, 24558c2ecf20Sopenharmony_ci IWL_UCODE_TLV_API_TKIP_MIC_KEYS); 24568c2ecf20Sopenharmony_ci __le16 key_flags; 24578c2ecf20Sopenharmony_ci int ret, size; 24588c2ecf20Sopenharmony_ci u32 status; 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci /* This is a valid situation for GTK removal */ 24618c2ecf20Sopenharmony_ci if (sta_id == IWL_MVM_INVALID_STA) 24628c2ecf20Sopenharmony_ci return 0; 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_ci key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) & 24658c2ecf20Sopenharmony_ci STA_KEY_FLG_KEYID_MSK); 24668c2ecf20Sopenharmony_ci key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP); 24678c2ecf20Sopenharmony_ci key_flags |= cpu_to_le16(STA_KEY_NOT_VALID); 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci if (mcast) 24708c2ecf20Sopenharmony_ci key_flags |= cpu_to_le16(STA_KEY_MULTICAST); 24718c2ecf20Sopenharmony_ci 24728c2ecf20Sopenharmony_ci /* 24738c2ecf20Sopenharmony_ci * The fields assigned here are in the same location at the start 24748c2ecf20Sopenharmony_ci * of the command, so we can do this union trick. 24758c2ecf20Sopenharmony_ci */ 24768c2ecf20Sopenharmony_ci u.cmd.common.key_flags = key_flags; 24778c2ecf20Sopenharmony_ci u.cmd.common.key_offset = keyconf->hw_key_idx; 24788c2ecf20Sopenharmony_ci u.cmd.common.sta_id = sta_id; 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_ci size = new_api ? sizeof(u.cmd) : sizeof(u.cmd_v1); 24818c2ecf20Sopenharmony_ci 24828c2ecf20Sopenharmony_ci status = ADD_STA_SUCCESS; 24838c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, size, &u.cmd, 24848c2ecf20Sopenharmony_ci &status); 24858c2ecf20Sopenharmony_ci 24868c2ecf20Sopenharmony_ci switch (status) { 24878c2ecf20Sopenharmony_ci case ADD_STA_SUCCESS: 24888c2ecf20Sopenharmony_ci IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n"); 24898c2ecf20Sopenharmony_ci break; 24908c2ecf20Sopenharmony_ci default: 24918c2ecf20Sopenharmony_ci ret = -EIO; 24928c2ecf20Sopenharmony_ci IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n"); 24938c2ecf20Sopenharmony_ci break; 24948c2ecf20Sopenharmony_ci } 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_ci return ret; 24978c2ecf20Sopenharmony_ci} 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_ci/* 25008c2ecf20Sopenharmony_ci * Send the FW a request to remove the station from it's internal data 25018c2ecf20Sopenharmony_ci * structures, and in addition remove it from the local data structure. 25028c2ecf20Sopenharmony_ci */ 25038c2ecf20Sopenharmony_ciint iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 25048c2ecf20Sopenharmony_ci{ 25058c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 25068c2ecf20Sopenharmony_ci int ret; 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 25098c2ecf20Sopenharmony_ci 25108c2ecf20Sopenharmony_ci iwl_mvm_flush_sta(mvm, &mvmvif->mcast_sta, true); 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci iwl_mvm_disable_txq(mvm, NULL, &mvmvif->cab_queue, 0, 0); 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci ret = iwl_mvm_rm_sta_common(mvm, mvmvif->mcast_sta.sta_id); 25158c2ecf20Sopenharmony_ci if (ret) 25168c2ecf20Sopenharmony_ci IWL_WARN(mvm, "Failed sending remove station\n"); 25178c2ecf20Sopenharmony_ci 25188c2ecf20Sopenharmony_ci return ret; 25198c2ecf20Sopenharmony_ci} 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_ci#define IWL_MAX_RX_BA_SESSIONS 16 25228c2ecf20Sopenharmony_ci 25238c2ecf20Sopenharmony_cistatic void iwl_mvm_sync_rxq_del_ba(struct iwl_mvm *mvm, u8 baid) 25248c2ecf20Sopenharmony_ci{ 25258c2ecf20Sopenharmony_ci struct iwl_mvm_rss_sync_notif notif = { 25268c2ecf20Sopenharmony_ci .metadata.type = IWL_MVM_RXQ_NOTIF_DEL_BA, 25278c2ecf20Sopenharmony_ci .metadata.sync = 1, 25288c2ecf20Sopenharmony_ci .delba.baid = baid, 25298c2ecf20Sopenharmony_ci }; 25308c2ecf20Sopenharmony_ci iwl_mvm_sync_rx_queues_internal(mvm, (void *)¬if, sizeof(notif)); 25318c2ecf20Sopenharmony_ci}; 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_cistatic void iwl_mvm_free_reorder(struct iwl_mvm *mvm, 25348c2ecf20Sopenharmony_ci struct iwl_mvm_baid_data *data) 25358c2ecf20Sopenharmony_ci{ 25368c2ecf20Sopenharmony_ci int i; 25378c2ecf20Sopenharmony_ci 25388c2ecf20Sopenharmony_ci iwl_mvm_sync_rxq_del_ba(mvm, data->baid); 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci for (i = 0; i < mvm->trans->num_rx_queues; i++) { 25418c2ecf20Sopenharmony_ci int j; 25428c2ecf20Sopenharmony_ci struct iwl_mvm_reorder_buffer *reorder_buf = 25438c2ecf20Sopenharmony_ci &data->reorder_buf[i]; 25448c2ecf20Sopenharmony_ci struct iwl_mvm_reorder_buf_entry *entries = 25458c2ecf20Sopenharmony_ci &data->entries[i * data->entries_per_queue]; 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci spin_lock_bh(&reorder_buf->lock); 25488c2ecf20Sopenharmony_ci if (likely(!reorder_buf->num_stored)) { 25498c2ecf20Sopenharmony_ci spin_unlock_bh(&reorder_buf->lock); 25508c2ecf20Sopenharmony_ci continue; 25518c2ecf20Sopenharmony_ci } 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci /* 25548c2ecf20Sopenharmony_ci * This shouldn't happen in regular DELBA since the internal 25558c2ecf20Sopenharmony_ci * delBA notification should trigger a release of all frames in 25568c2ecf20Sopenharmony_ci * the reorder buffer. 25578c2ecf20Sopenharmony_ci */ 25588c2ecf20Sopenharmony_ci WARN_ON(1); 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci for (j = 0; j < reorder_buf->buf_size; j++) 25618c2ecf20Sopenharmony_ci __skb_queue_purge(&entries[j].e.frames); 25628c2ecf20Sopenharmony_ci /* 25638c2ecf20Sopenharmony_ci * Prevent timer re-arm. This prevents a very far fetched case 25648c2ecf20Sopenharmony_ci * where we timed out on the notification. There may be prior 25658c2ecf20Sopenharmony_ci * RX frames pending in the RX queue before the notification 25668c2ecf20Sopenharmony_ci * that might get processed between now and the actual deletion 25678c2ecf20Sopenharmony_ci * and we would re-arm the timer although we are deleting the 25688c2ecf20Sopenharmony_ci * reorder buffer. 25698c2ecf20Sopenharmony_ci */ 25708c2ecf20Sopenharmony_ci reorder_buf->removed = true; 25718c2ecf20Sopenharmony_ci spin_unlock_bh(&reorder_buf->lock); 25728c2ecf20Sopenharmony_ci del_timer_sync(&reorder_buf->reorder_timer); 25738c2ecf20Sopenharmony_ci } 25748c2ecf20Sopenharmony_ci} 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_cistatic void iwl_mvm_init_reorder_buffer(struct iwl_mvm *mvm, 25778c2ecf20Sopenharmony_ci struct iwl_mvm_baid_data *data, 25788c2ecf20Sopenharmony_ci u16 ssn, u16 buf_size) 25798c2ecf20Sopenharmony_ci{ 25808c2ecf20Sopenharmony_ci int i; 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci for (i = 0; i < mvm->trans->num_rx_queues; i++) { 25838c2ecf20Sopenharmony_ci struct iwl_mvm_reorder_buffer *reorder_buf = 25848c2ecf20Sopenharmony_ci &data->reorder_buf[i]; 25858c2ecf20Sopenharmony_ci struct iwl_mvm_reorder_buf_entry *entries = 25868c2ecf20Sopenharmony_ci &data->entries[i * data->entries_per_queue]; 25878c2ecf20Sopenharmony_ci int j; 25888c2ecf20Sopenharmony_ci 25898c2ecf20Sopenharmony_ci reorder_buf->num_stored = 0; 25908c2ecf20Sopenharmony_ci reorder_buf->head_sn = ssn; 25918c2ecf20Sopenharmony_ci reorder_buf->buf_size = buf_size; 25928c2ecf20Sopenharmony_ci /* rx reorder timer */ 25938c2ecf20Sopenharmony_ci timer_setup(&reorder_buf->reorder_timer, 25948c2ecf20Sopenharmony_ci iwl_mvm_reorder_timer_expired, 0); 25958c2ecf20Sopenharmony_ci spin_lock_init(&reorder_buf->lock); 25968c2ecf20Sopenharmony_ci reorder_buf->mvm = mvm; 25978c2ecf20Sopenharmony_ci reorder_buf->queue = i; 25988c2ecf20Sopenharmony_ci reorder_buf->valid = false; 25998c2ecf20Sopenharmony_ci for (j = 0; j < reorder_buf->buf_size; j++) 26008c2ecf20Sopenharmony_ci __skb_queue_head_init(&entries[j].e.frames); 26018c2ecf20Sopenharmony_ci } 26028c2ecf20Sopenharmony_ci} 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ciint iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 26058c2ecf20Sopenharmony_ci int tid, u16 ssn, bool start, u16 buf_size, u16 timeout) 26068c2ecf20Sopenharmony_ci{ 26078c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); 26088c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_cmd cmd = {}; 26098c2ecf20Sopenharmony_ci struct iwl_mvm_baid_data *baid_data = NULL; 26108c2ecf20Sopenharmony_ci int ret; 26118c2ecf20Sopenharmony_ci u32 status; 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci if (start && mvm->rx_ba_sessions >= IWL_MAX_RX_BA_SESSIONS) { 26168c2ecf20Sopenharmony_ci IWL_WARN(mvm, "Not enough RX BA SESSIONS\n"); 26178c2ecf20Sopenharmony_ci return -ENOSPC; 26188c2ecf20Sopenharmony_ci } 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_rx_api(mvm) && start) { 26218c2ecf20Sopenharmony_ci u32 reorder_buf_size = buf_size * sizeof(baid_data->entries[0]); 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_ci /* sparse doesn't like the __align() so don't check */ 26248c2ecf20Sopenharmony_ci#ifndef __CHECKER__ 26258c2ecf20Sopenharmony_ci /* 26268c2ecf20Sopenharmony_ci * The division below will be OK if either the cache line size 26278c2ecf20Sopenharmony_ci * can be divided by the entry size (ALIGN will round up) or if 26288c2ecf20Sopenharmony_ci * if the entry size can be divided by the cache line size, in 26298c2ecf20Sopenharmony_ci * which case the ALIGN() will do nothing. 26308c2ecf20Sopenharmony_ci */ 26318c2ecf20Sopenharmony_ci BUILD_BUG_ON(SMP_CACHE_BYTES % sizeof(baid_data->entries[0]) && 26328c2ecf20Sopenharmony_ci sizeof(baid_data->entries[0]) % SMP_CACHE_BYTES); 26338c2ecf20Sopenharmony_ci#endif 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci /* 26368c2ecf20Sopenharmony_ci * Upward align the reorder buffer size to fill an entire cache 26378c2ecf20Sopenharmony_ci * line for each queue, to avoid sharing cache lines between 26388c2ecf20Sopenharmony_ci * different queues. 26398c2ecf20Sopenharmony_ci */ 26408c2ecf20Sopenharmony_ci reorder_buf_size = ALIGN(reorder_buf_size, SMP_CACHE_BYTES); 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_ci /* 26438c2ecf20Sopenharmony_ci * Allocate here so if allocation fails we can bail out early 26448c2ecf20Sopenharmony_ci * before starting the BA session in the firmware 26458c2ecf20Sopenharmony_ci */ 26468c2ecf20Sopenharmony_ci baid_data = kzalloc(sizeof(*baid_data) + 26478c2ecf20Sopenharmony_ci mvm->trans->num_rx_queues * 26488c2ecf20Sopenharmony_ci reorder_buf_size, 26498c2ecf20Sopenharmony_ci GFP_KERNEL); 26508c2ecf20Sopenharmony_ci if (!baid_data) 26518c2ecf20Sopenharmony_ci return -ENOMEM; 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_ci /* 26548c2ecf20Sopenharmony_ci * This division is why we need the above BUILD_BUG_ON(), 26558c2ecf20Sopenharmony_ci * if that doesn't hold then this will not be right. 26568c2ecf20Sopenharmony_ci */ 26578c2ecf20Sopenharmony_ci baid_data->entries_per_queue = 26588c2ecf20Sopenharmony_ci reorder_buf_size / sizeof(baid_data->entries[0]); 26598c2ecf20Sopenharmony_ci } 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ci cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); 26628c2ecf20Sopenharmony_ci cmd.sta_id = mvm_sta->sta_id; 26638c2ecf20Sopenharmony_ci cmd.add_modify = STA_MODE_MODIFY; 26648c2ecf20Sopenharmony_ci if (start) { 26658c2ecf20Sopenharmony_ci cmd.add_immediate_ba_tid = (u8) tid; 26668c2ecf20Sopenharmony_ci cmd.add_immediate_ba_ssn = cpu_to_le16(ssn); 26678c2ecf20Sopenharmony_ci cmd.rx_ba_window = cpu_to_le16(buf_size); 26688c2ecf20Sopenharmony_ci } else { 26698c2ecf20Sopenharmony_ci cmd.remove_immediate_ba_tid = (u8) tid; 26708c2ecf20Sopenharmony_ci } 26718c2ecf20Sopenharmony_ci cmd.modify_mask = start ? STA_MODIFY_ADD_BA_TID : 26728c2ecf20Sopenharmony_ci STA_MODIFY_REMOVE_BA_TID; 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_ci status = ADD_STA_SUCCESS; 26758c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, 26768c2ecf20Sopenharmony_ci iwl_mvm_add_sta_cmd_size(mvm), 26778c2ecf20Sopenharmony_ci &cmd, &status); 26788c2ecf20Sopenharmony_ci if (ret) 26798c2ecf20Sopenharmony_ci goto out_free; 26808c2ecf20Sopenharmony_ci 26818c2ecf20Sopenharmony_ci switch (status & IWL_ADD_STA_STATUS_MASK) { 26828c2ecf20Sopenharmony_ci case ADD_STA_SUCCESS: 26838c2ecf20Sopenharmony_ci IWL_DEBUG_HT(mvm, "RX BA Session %sed in fw\n", 26848c2ecf20Sopenharmony_ci start ? "start" : "stopp"); 26858c2ecf20Sopenharmony_ci break; 26868c2ecf20Sopenharmony_ci case ADD_STA_IMMEDIATE_BA_FAILURE: 26878c2ecf20Sopenharmony_ci IWL_WARN(mvm, "RX BA Session refused by fw\n"); 26888c2ecf20Sopenharmony_ci ret = -ENOSPC; 26898c2ecf20Sopenharmony_ci break; 26908c2ecf20Sopenharmony_ci default: 26918c2ecf20Sopenharmony_ci ret = -EIO; 26928c2ecf20Sopenharmony_ci IWL_ERR(mvm, "RX BA Session failed %sing, status 0x%x\n", 26938c2ecf20Sopenharmony_ci start ? "start" : "stopp", status); 26948c2ecf20Sopenharmony_ci break; 26958c2ecf20Sopenharmony_ci } 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_ci if (ret) 26988c2ecf20Sopenharmony_ci goto out_free; 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci if (start) { 27018c2ecf20Sopenharmony_ci u8 baid; 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_ci mvm->rx_ba_sessions++; 27048c2ecf20Sopenharmony_ci 27058c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_rx_api(mvm)) 27068c2ecf20Sopenharmony_ci return 0; 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_ci if (WARN_ON(!(status & IWL_ADD_STA_BAID_VALID_MASK))) { 27098c2ecf20Sopenharmony_ci ret = -EINVAL; 27108c2ecf20Sopenharmony_ci goto out_free; 27118c2ecf20Sopenharmony_ci } 27128c2ecf20Sopenharmony_ci baid = (u8)((status & IWL_ADD_STA_BAID_MASK) >> 27138c2ecf20Sopenharmony_ci IWL_ADD_STA_BAID_SHIFT); 27148c2ecf20Sopenharmony_ci baid_data->baid = baid; 27158c2ecf20Sopenharmony_ci baid_data->timeout = timeout; 27168c2ecf20Sopenharmony_ci baid_data->last_rx = jiffies; 27178c2ecf20Sopenharmony_ci baid_data->rcu_ptr = &mvm->baid_map[baid]; 27188c2ecf20Sopenharmony_ci timer_setup(&baid_data->session_timer, 27198c2ecf20Sopenharmony_ci iwl_mvm_rx_agg_session_expired, 0); 27208c2ecf20Sopenharmony_ci baid_data->mvm = mvm; 27218c2ecf20Sopenharmony_ci baid_data->tid = tid; 27228c2ecf20Sopenharmony_ci baid_data->sta_id = mvm_sta->sta_id; 27238c2ecf20Sopenharmony_ci 27248c2ecf20Sopenharmony_ci mvm_sta->tid_to_baid[tid] = baid; 27258c2ecf20Sopenharmony_ci if (timeout) 27268c2ecf20Sopenharmony_ci mod_timer(&baid_data->session_timer, 27278c2ecf20Sopenharmony_ci TU_TO_EXP_TIME(timeout * 2)); 27288c2ecf20Sopenharmony_ci 27298c2ecf20Sopenharmony_ci iwl_mvm_init_reorder_buffer(mvm, baid_data, ssn, buf_size); 27308c2ecf20Sopenharmony_ci /* 27318c2ecf20Sopenharmony_ci * protect the BA data with RCU to cover a case where our 27328c2ecf20Sopenharmony_ci * internal RX sync mechanism will timeout (not that it's 27338c2ecf20Sopenharmony_ci * supposed to happen) and we will free the session data while 27348c2ecf20Sopenharmony_ci * RX is being processed in parallel 27358c2ecf20Sopenharmony_ci */ 27368c2ecf20Sopenharmony_ci IWL_DEBUG_HT(mvm, "Sta %d(%d) is assigned to BAID %d\n", 27378c2ecf20Sopenharmony_ci mvm_sta->sta_id, tid, baid); 27388c2ecf20Sopenharmony_ci WARN_ON(rcu_access_pointer(mvm->baid_map[baid])); 27398c2ecf20Sopenharmony_ci rcu_assign_pointer(mvm->baid_map[baid], baid_data); 27408c2ecf20Sopenharmony_ci } else { 27418c2ecf20Sopenharmony_ci u8 baid = mvm_sta->tid_to_baid[tid]; 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci if (mvm->rx_ba_sessions > 0) 27448c2ecf20Sopenharmony_ci /* check that restart flow didn't zero the counter */ 27458c2ecf20Sopenharmony_ci mvm->rx_ba_sessions--; 27468c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_rx_api(mvm)) 27478c2ecf20Sopenharmony_ci return 0; 27488c2ecf20Sopenharmony_ci 27498c2ecf20Sopenharmony_ci if (WARN_ON(baid == IWL_RX_REORDER_DATA_INVALID_BAID)) 27508c2ecf20Sopenharmony_ci return -EINVAL; 27518c2ecf20Sopenharmony_ci 27528c2ecf20Sopenharmony_ci baid_data = rcu_access_pointer(mvm->baid_map[baid]); 27538c2ecf20Sopenharmony_ci if (WARN_ON(!baid_data)) 27548c2ecf20Sopenharmony_ci return -EINVAL; 27558c2ecf20Sopenharmony_ci 27568c2ecf20Sopenharmony_ci /* synchronize all rx queues so we can safely delete */ 27578c2ecf20Sopenharmony_ci iwl_mvm_free_reorder(mvm, baid_data); 27588c2ecf20Sopenharmony_ci del_timer_sync(&baid_data->session_timer); 27598c2ecf20Sopenharmony_ci RCU_INIT_POINTER(mvm->baid_map[baid], NULL); 27608c2ecf20Sopenharmony_ci kfree_rcu(baid_data, rcu_head); 27618c2ecf20Sopenharmony_ci IWL_DEBUG_HT(mvm, "BAID %d is free\n", baid); 27628c2ecf20Sopenharmony_ci } 27638c2ecf20Sopenharmony_ci return 0; 27648c2ecf20Sopenharmony_ci 27658c2ecf20Sopenharmony_ciout_free: 27668c2ecf20Sopenharmony_ci kfree(baid_data); 27678c2ecf20Sopenharmony_ci return ret; 27688c2ecf20Sopenharmony_ci} 27698c2ecf20Sopenharmony_ci 27708c2ecf20Sopenharmony_ciint iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 27718c2ecf20Sopenharmony_ci int tid, u8 queue, bool start) 27728c2ecf20Sopenharmony_ci{ 27738c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); 27748c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_cmd cmd = {}; 27758c2ecf20Sopenharmony_ci int ret; 27768c2ecf20Sopenharmony_ci u32 status; 27778c2ecf20Sopenharmony_ci 27788c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 27798c2ecf20Sopenharmony_ci 27808c2ecf20Sopenharmony_ci if (start) { 27818c2ecf20Sopenharmony_ci mvm_sta->tfd_queue_msk |= BIT(queue); 27828c2ecf20Sopenharmony_ci mvm_sta->tid_disable_agg &= ~BIT(tid); 27838c2ecf20Sopenharmony_ci } else { 27848c2ecf20Sopenharmony_ci /* In DQA-mode the queue isn't removed on agg termination */ 27858c2ecf20Sopenharmony_ci mvm_sta->tid_disable_agg |= BIT(tid); 27868c2ecf20Sopenharmony_ci } 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); 27898c2ecf20Sopenharmony_ci cmd.sta_id = mvm_sta->sta_id; 27908c2ecf20Sopenharmony_ci cmd.add_modify = STA_MODE_MODIFY; 27918c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_tx_api(mvm)) 27928c2ecf20Sopenharmony_ci cmd.modify_mask = STA_MODIFY_QUEUES; 27938c2ecf20Sopenharmony_ci cmd.modify_mask |= STA_MODIFY_TID_DISABLE_TX; 27948c2ecf20Sopenharmony_ci cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk); 27958c2ecf20Sopenharmony_ci cmd.tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg); 27968c2ecf20Sopenharmony_ci 27978c2ecf20Sopenharmony_ci status = ADD_STA_SUCCESS; 27988c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, 27998c2ecf20Sopenharmony_ci iwl_mvm_add_sta_cmd_size(mvm), 28008c2ecf20Sopenharmony_ci &cmd, &status); 28018c2ecf20Sopenharmony_ci if (ret) 28028c2ecf20Sopenharmony_ci return ret; 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_ci switch (status & IWL_ADD_STA_STATUS_MASK) { 28058c2ecf20Sopenharmony_ci case ADD_STA_SUCCESS: 28068c2ecf20Sopenharmony_ci break; 28078c2ecf20Sopenharmony_ci default: 28088c2ecf20Sopenharmony_ci ret = -EIO; 28098c2ecf20Sopenharmony_ci IWL_ERR(mvm, "TX BA Session failed %sing, status 0x%x\n", 28108c2ecf20Sopenharmony_ci start ? "start" : "stopp", status); 28118c2ecf20Sopenharmony_ci break; 28128c2ecf20Sopenharmony_ci } 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_ci return ret; 28158c2ecf20Sopenharmony_ci} 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ciconst u8 tid_to_mac80211_ac[] = { 28188c2ecf20Sopenharmony_ci IEEE80211_AC_BE, 28198c2ecf20Sopenharmony_ci IEEE80211_AC_BK, 28208c2ecf20Sopenharmony_ci IEEE80211_AC_BK, 28218c2ecf20Sopenharmony_ci IEEE80211_AC_BE, 28228c2ecf20Sopenharmony_ci IEEE80211_AC_VI, 28238c2ecf20Sopenharmony_ci IEEE80211_AC_VI, 28248c2ecf20Sopenharmony_ci IEEE80211_AC_VO, 28258c2ecf20Sopenharmony_ci IEEE80211_AC_VO, 28268c2ecf20Sopenharmony_ci IEEE80211_AC_VO, /* We treat MGMT as TID 8, which is set as AC_VO */ 28278c2ecf20Sopenharmony_ci}; 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_cistatic const u8 tid_to_ucode_ac[] = { 28308c2ecf20Sopenharmony_ci AC_BE, 28318c2ecf20Sopenharmony_ci AC_BK, 28328c2ecf20Sopenharmony_ci AC_BK, 28338c2ecf20Sopenharmony_ci AC_BE, 28348c2ecf20Sopenharmony_ci AC_VI, 28358c2ecf20Sopenharmony_ci AC_VI, 28368c2ecf20Sopenharmony_ci AC_VO, 28378c2ecf20Sopenharmony_ci AC_VO, 28388c2ecf20Sopenharmony_ci}; 28398c2ecf20Sopenharmony_ci 28408c2ecf20Sopenharmony_ciint iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 28418c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, u16 tid, u16 *ssn) 28428c2ecf20Sopenharmony_ci{ 28438c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 28448c2ecf20Sopenharmony_ci struct iwl_mvm_tid_data *tid_data; 28458c2ecf20Sopenharmony_ci u16 normalized_ssn; 28468c2ecf20Sopenharmony_ci u16 txq_id; 28478c2ecf20Sopenharmony_ci int ret; 28488c2ecf20Sopenharmony_ci 28498c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) 28508c2ecf20Sopenharmony_ci return -EINVAL; 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci if (mvmsta->tid_data[tid].state != IWL_AGG_QUEUED && 28538c2ecf20Sopenharmony_ci mvmsta->tid_data[tid].state != IWL_AGG_OFF) { 28548c2ecf20Sopenharmony_ci IWL_ERR(mvm, 28558c2ecf20Sopenharmony_ci "Start AGG when state is not IWL_AGG_QUEUED or IWL_AGG_OFF %d!\n", 28568c2ecf20Sopenharmony_ci mvmsta->tid_data[tid].state); 28578c2ecf20Sopenharmony_ci return -ENXIO; 28588c2ecf20Sopenharmony_ci } 28598c2ecf20Sopenharmony_ci 28608c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci if (mvmsta->tid_data[tid].txq_id == IWL_MVM_INVALID_QUEUE && 28638c2ecf20Sopenharmony_ci iwl_mvm_has_new_tx_api(mvm)) { 28648c2ecf20Sopenharmony_ci u8 ac = tid_to_mac80211_ac[tid]; 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_ci ret = iwl_mvm_sta_alloc_queue_tvqm(mvm, sta, ac, tid); 28678c2ecf20Sopenharmony_ci if (ret) 28688c2ecf20Sopenharmony_ci return ret; 28698c2ecf20Sopenharmony_ci } 28708c2ecf20Sopenharmony_ci 28718c2ecf20Sopenharmony_ci spin_lock_bh(&mvmsta->lock); 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci /* 28748c2ecf20Sopenharmony_ci * Note the possible cases: 28758c2ecf20Sopenharmony_ci * 1. An enabled TXQ - TXQ needs to become agg'ed 28768c2ecf20Sopenharmony_ci * 2. The TXQ hasn't yet been enabled, so find a free one and mark 28778c2ecf20Sopenharmony_ci * it as reserved 28788c2ecf20Sopenharmony_ci */ 28798c2ecf20Sopenharmony_ci txq_id = mvmsta->tid_data[tid].txq_id; 28808c2ecf20Sopenharmony_ci if (txq_id == IWL_MVM_INVALID_QUEUE) { 28818c2ecf20Sopenharmony_ci ret = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, 28828c2ecf20Sopenharmony_ci IWL_MVM_DQA_MIN_DATA_QUEUE, 28838c2ecf20Sopenharmony_ci IWL_MVM_DQA_MAX_DATA_QUEUE); 28848c2ecf20Sopenharmony_ci if (ret < 0) { 28858c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to allocate agg queue\n"); 28868c2ecf20Sopenharmony_ci goto out; 28878c2ecf20Sopenharmony_ci } 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_ci txq_id = ret; 28908c2ecf20Sopenharmony_ci 28918c2ecf20Sopenharmony_ci /* TXQ hasn't yet been enabled, so mark it only as reserved */ 28928c2ecf20Sopenharmony_ci mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_RESERVED; 28938c2ecf20Sopenharmony_ci } else if (WARN_ON(txq_id >= IWL_MAX_HW_QUEUES)) { 28948c2ecf20Sopenharmony_ci ret = -ENXIO; 28958c2ecf20Sopenharmony_ci IWL_ERR(mvm, "tid_id %d out of range (0, %d)!\n", 28968c2ecf20Sopenharmony_ci tid, IWL_MAX_HW_QUEUES - 1); 28978c2ecf20Sopenharmony_ci goto out; 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_ci } else if (unlikely(mvm->queue_info[txq_id].status == 29008c2ecf20Sopenharmony_ci IWL_MVM_QUEUE_SHARED)) { 29018c2ecf20Sopenharmony_ci ret = -ENXIO; 29028c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 29038c2ecf20Sopenharmony_ci "Can't start tid %d agg on shared queue!\n", 29048c2ecf20Sopenharmony_ci tid); 29058c2ecf20Sopenharmony_ci goto out; 29068c2ecf20Sopenharmony_ci } 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 29098c2ecf20Sopenharmony_ci "AGG for tid %d will be on queue #%d\n", 29108c2ecf20Sopenharmony_ci tid, txq_id); 29118c2ecf20Sopenharmony_ci 29128c2ecf20Sopenharmony_ci tid_data = &mvmsta->tid_data[tid]; 29138c2ecf20Sopenharmony_ci tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); 29148c2ecf20Sopenharmony_ci tid_data->txq_id = txq_id; 29158c2ecf20Sopenharmony_ci *ssn = tid_data->ssn; 29168c2ecf20Sopenharmony_ci 29178c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 29188c2ecf20Sopenharmony_ci "Start AGG: sta %d tid %d queue %d - ssn = %d, next_recl = %d\n", 29198c2ecf20Sopenharmony_ci mvmsta->sta_id, tid, txq_id, tid_data->ssn, 29208c2ecf20Sopenharmony_ci tid_data->next_reclaimed); 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci /* 29238c2ecf20Sopenharmony_ci * In 22000 HW, the next_reclaimed index is only 8 bit, so we'll need 29248c2ecf20Sopenharmony_ci * to align the wrap around of ssn so we compare relevant values. 29258c2ecf20Sopenharmony_ci */ 29268c2ecf20Sopenharmony_ci normalized_ssn = tid_data->ssn; 29278c2ecf20Sopenharmony_ci if (mvm->trans->trans_cfg->gen2) 29288c2ecf20Sopenharmony_ci normalized_ssn &= 0xff; 29298c2ecf20Sopenharmony_ci 29308c2ecf20Sopenharmony_ci if (normalized_ssn == tid_data->next_reclaimed) { 29318c2ecf20Sopenharmony_ci tid_data->state = IWL_AGG_STARTING; 29328c2ecf20Sopenharmony_ci ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; 29338c2ecf20Sopenharmony_ci } else { 29348c2ecf20Sopenharmony_ci tid_data->state = IWL_EMPTYING_HW_QUEUE_ADDBA; 29358c2ecf20Sopenharmony_ci ret = IEEE80211_AMPDU_TX_START_DELAY_ADDBA; 29368c2ecf20Sopenharmony_ci } 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ciout: 29398c2ecf20Sopenharmony_ci spin_unlock_bh(&mvmsta->lock); 29408c2ecf20Sopenharmony_ci 29418c2ecf20Sopenharmony_ci return ret; 29428c2ecf20Sopenharmony_ci} 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ciint iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 29458c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, u16 tid, u16 buf_size, 29468c2ecf20Sopenharmony_ci bool amsdu) 29478c2ecf20Sopenharmony_ci{ 29488c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 29498c2ecf20Sopenharmony_ci struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; 29508c2ecf20Sopenharmony_ci unsigned int wdg_timeout = 29518c2ecf20Sopenharmony_ci iwl_mvm_get_wd_timeout(mvm, vif, sta->tdls, false); 29528c2ecf20Sopenharmony_ci int queue, ret; 29538c2ecf20Sopenharmony_ci bool alloc_queue = true; 29548c2ecf20Sopenharmony_ci enum iwl_mvm_queue_status queue_status; 29558c2ecf20Sopenharmony_ci u16 ssn; 29568c2ecf20Sopenharmony_ci 29578c2ecf20Sopenharmony_ci struct iwl_trans_txq_scd_cfg cfg = { 29588c2ecf20Sopenharmony_ci .sta_id = mvmsta->sta_id, 29598c2ecf20Sopenharmony_ci .tid = tid, 29608c2ecf20Sopenharmony_ci .frame_limit = buf_size, 29618c2ecf20Sopenharmony_ci .aggregate = true, 29628c2ecf20Sopenharmony_ci }; 29638c2ecf20Sopenharmony_ci 29648c2ecf20Sopenharmony_ci /* 29658c2ecf20Sopenharmony_ci * When FW supports TLC_OFFLOAD, it also implements Tx aggregation 29668c2ecf20Sopenharmony_ci * manager, so this function should never be called in this case. 29678c2ecf20Sopenharmony_ci */ 29688c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(iwl_mvm_has_tlc_offload(mvm))) 29698c2ecf20Sopenharmony_ci return -EINVAL; 29708c2ecf20Sopenharmony_ci 29718c2ecf20Sopenharmony_ci BUILD_BUG_ON((sizeof(mvmsta->agg_tids) * BITS_PER_BYTE) 29728c2ecf20Sopenharmony_ci != IWL_MAX_TID_COUNT); 29738c2ecf20Sopenharmony_ci 29748c2ecf20Sopenharmony_ci spin_lock_bh(&mvmsta->lock); 29758c2ecf20Sopenharmony_ci ssn = tid_data->ssn; 29768c2ecf20Sopenharmony_ci queue = tid_data->txq_id; 29778c2ecf20Sopenharmony_ci tid_data->state = IWL_AGG_ON; 29788c2ecf20Sopenharmony_ci mvmsta->agg_tids |= BIT(tid); 29798c2ecf20Sopenharmony_ci tid_data->ssn = 0xffff; 29808c2ecf20Sopenharmony_ci tid_data->amsdu_in_ampdu_allowed = amsdu; 29818c2ecf20Sopenharmony_ci spin_unlock_bh(&mvmsta->lock); 29828c2ecf20Sopenharmony_ci 29838c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) { 29848c2ecf20Sopenharmony_ci /* 29858c2ecf20Sopenharmony_ci * If there is no queue for this tid, iwl_mvm_sta_tx_agg_start() 29868c2ecf20Sopenharmony_ci * would have failed, so if we are here there is no need to 29878c2ecf20Sopenharmony_ci * allocate a queue. 29888c2ecf20Sopenharmony_ci * However, if aggregation size is different than the default 29898c2ecf20Sopenharmony_ci * size, the scheduler should be reconfigured. 29908c2ecf20Sopenharmony_ci * We cannot do this with the new TX API, so return unsupported 29918c2ecf20Sopenharmony_ci * for now, until it will be offloaded to firmware.. 29928c2ecf20Sopenharmony_ci * Note that if SCD default value changes - this condition 29938c2ecf20Sopenharmony_ci * should be updated as well. 29948c2ecf20Sopenharmony_ci */ 29958c2ecf20Sopenharmony_ci if (buf_size < IWL_FRAME_LIMIT) 29968c2ecf20Sopenharmony_ci return -ENOTSUPP; 29978c2ecf20Sopenharmony_ci 29988c2ecf20Sopenharmony_ci ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); 29998c2ecf20Sopenharmony_ci if (ret) 30008c2ecf20Sopenharmony_ci return -EIO; 30018c2ecf20Sopenharmony_ci goto out; 30028c2ecf20Sopenharmony_ci } 30038c2ecf20Sopenharmony_ci 30048c2ecf20Sopenharmony_ci cfg.fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; 30058c2ecf20Sopenharmony_ci 30068c2ecf20Sopenharmony_ci queue_status = mvm->queue_info[queue].status; 30078c2ecf20Sopenharmony_ci 30088c2ecf20Sopenharmony_ci /* Maybe there is no need to even alloc a queue... */ 30098c2ecf20Sopenharmony_ci if (mvm->queue_info[queue].status == IWL_MVM_QUEUE_READY) 30108c2ecf20Sopenharmony_ci alloc_queue = false; 30118c2ecf20Sopenharmony_ci 30128c2ecf20Sopenharmony_ci /* 30138c2ecf20Sopenharmony_ci * Only reconfig the SCD for the queue if the window size has 30148c2ecf20Sopenharmony_ci * changed from current (become smaller) 30158c2ecf20Sopenharmony_ci */ 30168c2ecf20Sopenharmony_ci if (!alloc_queue && buf_size < IWL_FRAME_LIMIT) { 30178c2ecf20Sopenharmony_ci /* 30188c2ecf20Sopenharmony_ci * If reconfiguring an existing queue, it first must be 30198c2ecf20Sopenharmony_ci * drained 30208c2ecf20Sopenharmony_ci */ 30218c2ecf20Sopenharmony_ci ret = iwl_trans_wait_tx_queues_empty(mvm->trans, 30228c2ecf20Sopenharmony_ci BIT(queue)); 30238c2ecf20Sopenharmony_ci if (ret) { 30248c2ecf20Sopenharmony_ci IWL_ERR(mvm, 30258c2ecf20Sopenharmony_ci "Error draining queue before reconfig\n"); 30268c2ecf20Sopenharmony_ci return ret; 30278c2ecf20Sopenharmony_ci } 30288c2ecf20Sopenharmony_ci 30298c2ecf20Sopenharmony_ci ret = iwl_mvm_reconfig_scd(mvm, queue, cfg.fifo, 30308c2ecf20Sopenharmony_ci mvmsta->sta_id, tid, 30318c2ecf20Sopenharmony_ci buf_size, ssn); 30328c2ecf20Sopenharmony_ci if (ret) { 30338c2ecf20Sopenharmony_ci IWL_ERR(mvm, 30348c2ecf20Sopenharmony_ci "Error reconfiguring TXQ #%d\n", queue); 30358c2ecf20Sopenharmony_ci return ret; 30368c2ecf20Sopenharmony_ci } 30378c2ecf20Sopenharmony_ci } 30388c2ecf20Sopenharmony_ci 30398c2ecf20Sopenharmony_ci if (alloc_queue) 30408c2ecf20Sopenharmony_ci iwl_mvm_enable_txq(mvm, sta, queue, ssn, 30418c2ecf20Sopenharmony_ci &cfg, wdg_timeout); 30428c2ecf20Sopenharmony_ci 30438c2ecf20Sopenharmony_ci /* Send ADD_STA command to enable aggs only if the queue isn't shared */ 30448c2ecf20Sopenharmony_ci if (queue_status != IWL_MVM_QUEUE_SHARED) { 30458c2ecf20Sopenharmony_ci ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); 30468c2ecf20Sopenharmony_ci if (ret) 30478c2ecf20Sopenharmony_ci return -EIO; 30488c2ecf20Sopenharmony_ci } 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci /* No need to mark as reserved */ 30518c2ecf20Sopenharmony_ci mvm->queue_info[queue].status = IWL_MVM_QUEUE_READY; 30528c2ecf20Sopenharmony_ci 30538c2ecf20Sopenharmony_ciout: 30548c2ecf20Sopenharmony_ci /* 30558c2ecf20Sopenharmony_ci * Even though in theory the peer could have different 30568c2ecf20Sopenharmony_ci * aggregation reorder buffer sizes for different sessions, 30578c2ecf20Sopenharmony_ci * our ucode doesn't allow for that and has a global limit 30588c2ecf20Sopenharmony_ci * for each station. Therefore, use the minimum of all the 30598c2ecf20Sopenharmony_ci * aggregation sessions and our default value. 30608c2ecf20Sopenharmony_ci */ 30618c2ecf20Sopenharmony_ci mvmsta->max_agg_bufsize = 30628c2ecf20Sopenharmony_ci min(mvmsta->max_agg_bufsize, buf_size); 30638c2ecf20Sopenharmony_ci mvmsta->lq_sta.rs_drv.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize; 30648c2ecf20Sopenharmony_ci 30658c2ecf20Sopenharmony_ci IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n", 30668c2ecf20Sopenharmony_ci sta->addr, tid); 30678c2ecf20Sopenharmony_ci 30688c2ecf20Sopenharmony_ci return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.rs_drv.lq); 30698c2ecf20Sopenharmony_ci} 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_cistatic void iwl_mvm_unreserve_agg_queue(struct iwl_mvm *mvm, 30728c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta, 30738c2ecf20Sopenharmony_ci struct iwl_mvm_tid_data *tid_data) 30748c2ecf20Sopenharmony_ci{ 30758c2ecf20Sopenharmony_ci u16 txq_id = tid_data->txq_id; 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 30788c2ecf20Sopenharmony_ci 30798c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) 30808c2ecf20Sopenharmony_ci return; 30818c2ecf20Sopenharmony_ci 30828c2ecf20Sopenharmony_ci /* 30838c2ecf20Sopenharmony_ci * The TXQ is marked as reserved only if no traffic came through yet 30848c2ecf20Sopenharmony_ci * This means no traffic has been sent on this TID (agg'd or not), so 30858c2ecf20Sopenharmony_ci * we no longer have use for the queue. Since it hasn't even been 30868c2ecf20Sopenharmony_ci * allocated through iwl_mvm_enable_txq, so we can just mark it back as 30878c2ecf20Sopenharmony_ci * free. 30888c2ecf20Sopenharmony_ci */ 30898c2ecf20Sopenharmony_ci if (mvm->queue_info[txq_id].status == IWL_MVM_QUEUE_RESERVED) { 30908c2ecf20Sopenharmony_ci mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_FREE; 30918c2ecf20Sopenharmony_ci tid_data->txq_id = IWL_MVM_INVALID_QUEUE; 30928c2ecf20Sopenharmony_ci } 30938c2ecf20Sopenharmony_ci} 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_ciint iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 30968c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, u16 tid) 30978c2ecf20Sopenharmony_ci{ 30988c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 30998c2ecf20Sopenharmony_ci struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; 31008c2ecf20Sopenharmony_ci u16 txq_id; 31018c2ecf20Sopenharmony_ci int err; 31028c2ecf20Sopenharmony_ci 31038c2ecf20Sopenharmony_ci /* 31048c2ecf20Sopenharmony_ci * If mac80211 is cleaning its state, then say that we finished since 31058c2ecf20Sopenharmony_ci * our state has been cleared anyway. 31068c2ecf20Sopenharmony_ci */ 31078c2ecf20Sopenharmony_ci if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { 31088c2ecf20Sopenharmony_ci ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); 31098c2ecf20Sopenharmony_ci return 0; 31108c2ecf20Sopenharmony_ci } 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_ci spin_lock_bh(&mvmsta->lock); 31138c2ecf20Sopenharmony_ci 31148c2ecf20Sopenharmony_ci txq_id = tid_data->txq_id; 31158c2ecf20Sopenharmony_ci 31168c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, "Stop AGG: sta %d tid %d q %d state %d\n", 31178c2ecf20Sopenharmony_ci mvmsta->sta_id, tid, txq_id, tid_data->state); 31188c2ecf20Sopenharmony_ci 31198c2ecf20Sopenharmony_ci mvmsta->agg_tids &= ~BIT(tid); 31208c2ecf20Sopenharmony_ci 31218c2ecf20Sopenharmony_ci iwl_mvm_unreserve_agg_queue(mvm, mvmsta, tid_data); 31228c2ecf20Sopenharmony_ci 31238c2ecf20Sopenharmony_ci switch (tid_data->state) { 31248c2ecf20Sopenharmony_ci case IWL_AGG_ON: 31258c2ecf20Sopenharmony_ci tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); 31268c2ecf20Sopenharmony_ci 31278c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 31288c2ecf20Sopenharmony_ci "ssn = %d, next_recl = %d\n", 31298c2ecf20Sopenharmony_ci tid_data->ssn, tid_data->next_reclaimed); 31308c2ecf20Sopenharmony_ci 31318c2ecf20Sopenharmony_ci tid_data->ssn = 0xffff; 31328c2ecf20Sopenharmony_ci tid_data->state = IWL_AGG_OFF; 31338c2ecf20Sopenharmony_ci spin_unlock_bh(&mvmsta->lock); 31348c2ecf20Sopenharmony_ci 31358c2ecf20Sopenharmony_ci ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); 31368c2ecf20Sopenharmony_ci 31378c2ecf20Sopenharmony_ci iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); 31388c2ecf20Sopenharmony_ci return 0; 31398c2ecf20Sopenharmony_ci case IWL_AGG_STARTING: 31408c2ecf20Sopenharmony_ci case IWL_EMPTYING_HW_QUEUE_ADDBA: 31418c2ecf20Sopenharmony_ci /* 31428c2ecf20Sopenharmony_ci * The agg session has been stopped before it was set up. This 31438c2ecf20Sopenharmony_ci * can happen when the AddBA timer times out for example. 31448c2ecf20Sopenharmony_ci */ 31458c2ecf20Sopenharmony_ci 31468c2ecf20Sopenharmony_ci /* No barriers since we are under mutex */ 31478c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 31488c2ecf20Sopenharmony_ci 31498c2ecf20Sopenharmony_ci ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); 31508c2ecf20Sopenharmony_ci tid_data->state = IWL_AGG_OFF; 31518c2ecf20Sopenharmony_ci err = 0; 31528c2ecf20Sopenharmony_ci break; 31538c2ecf20Sopenharmony_ci default: 31548c2ecf20Sopenharmony_ci IWL_ERR(mvm, 31558c2ecf20Sopenharmony_ci "Stopping AGG while state not ON or starting for %d on %d (%d)\n", 31568c2ecf20Sopenharmony_ci mvmsta->sta_id, tid, tid_data->state); 31578c2ecf20Sopenharmony_ci IWL_ERR(mvm, 31588c2ecf20Sopenharmony_ci "\ttid_data->txq_id = %d\n", tid_data->txq_id); 31598c2ecf20Sopenharmony_ci err = -EINVAL; 31608c2ecf20Sopenharmony_ci } 31618c2ecf20Sopenharmony_ci 31628c2ecf20Sopenharmony_ci spin_unlock_bh(&mvmsta->lock); 31638c2ecf20Sopenharmony_ci 31648c2ecf20Sopenharmony_ci return err; 31658c2ecf20Sopenharmony_ci} 31668c2ecf20Sopenharmony_ci 31678c2ecf20Sopenharmony_ciint iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 31688c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, u16 tid) 31698c2ecf20Sopenharmony_ci{ 31708c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 31718c2ecf20Sopenharmony_ci struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; 31728c2ecf20Sopenharmony_ci u16 txq_id; 31738c2ecf20Sopenharmony_ci enum iwl_mvm_agg_state old_state; 31748c2ecf20Sopenharmony_ci 31758c2ecf20Sopenharmony_ci /* 31768c2ecf20Sopenharmony_ci * First set the agg state to OFF to avoid calling 31778c2ecf20Sopenharmony_ci * ieee80211_stop_tx_ba_cb in iwl_mvm_check_ratid_empty. 31788c2ecf20Sopenharmony_ci */ 31798c2ecf20Sopenharmony_ci spin_lock_bh(&mvmsta->lock); 31808c2ecf20Sopenharmony_ci txq_id = tid_data->txq_id; 31818c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, "Flush AGG: sta %d tid %d q %d state %d\n", 31828c2ecf20Sopenharmony_ci mvmsta->sta_id, tid, txq_id, tid_data->state); 31838c2ecf20Sopenharmony_ci old_state = tid_data->state; 31848c2ecf20Sopenharmony_ci tid_data->state = IWL_AGG_OFF; 31858c2ecf20Sopenharmony_ci mvmsta->agg_tids &= ~BIT(tid); 31868c2ecf20Sopenharmony_ci spin_unlock_bh(&mvmsta->lock); 31878c2ecf20Sopenharmony_ci 31888c2ecf20Sopenharmony_ci iwl_mvm_unreserve_agg_queue(mvm, mvmsta, tid_data); 31898c2ecf20Sopenharmony_ci 31908c2ecf20Sopenharmony_ci if (old_state >= IWL_AGG_ON) { 31918c2ecf20Sopenharmony_ci iwl_mvm_drain_sta(mvm, mvmsta, true); 31928c2ecf20Sopenharmony_ci 31938c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) { 31948c2ecf20Sopenharmony_ci if (iwl_mvm_flush_sta_tids(mvm, mvmsta->sta_id, 31958c2ecf20Sopenharmony_ci BIT(tid), 0)) 31968c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Couldn't flush the AGG queue\n"); 31978c2ecf20Sopenharmony_ci iwl_trans_wait_txq_empty(mvm->trans, txq_id); 31988c2ecf20Sopenharmony_ci } else { 31998c2ecf20Sopenharmony_ci if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), 0)) 32008c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Couldn't flush the AGG queue\n"); 32018c2ecf20Sopenharmony_ci iwl_trans_wait_tx_queues_empty(mvm->trans, BIT(txq_id)); 32028c2ecf20Sopenharmony_ci } 32038c2ecf20Sopenharmony_ci 32048c2ecf20Sopenharmony_ci iwl_mvm_drain_sta(mvm, mvmsta, false); 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_ci iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); 32078c2ecf20Sopenharmony_ci } 32088c2ecf20Sopenharmony_ci 32098c2ecf20Sopenharmony_ci return 0; 32108c2ecf20Sopenharmony_ci} 32118c2ecf20Sopenharmony_ci 32128c2ecf20Sopenharmony_cistatic int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm) 32138c2ecf20Sopenharmony_ci{ 32148c2ecf20Sopenharmony_ci int i, max = -1, max_offs = -1; 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 32178c2ecf20Sopenharmony_ci 32188c2ecf20Sopenharmony_ci /* Pick the unused key offset with the highest 'deleted' 32198c2ecf20Sopenharmony_ci * counter. Every time a key is deleted, all the counters 32208c2ecf20Sopenharmony_ci * are incremented and the one that was just deleted is 32218c2ecf20Sopenharmony_ci * reset to zero. Thus, the highest counter is the one 32228c2ecf20Sopenharmony_ci * that was deleted longest ago. Pick that one. 32238c2ecf20Sopenharmony_ci */ 32248c2ecf20Sopenharmony_ci for (i = 0; i < STA_KEY_MAX_NUM; i++) { 32258c2ecf20Sopenharmony_ci if (test_bit(i, mvm->fw_key_table)) 32268c2ecf20Sopenharmony_ci continue; 32278c2ecf20Sopenharmony_ci if (mvm->fw_key_deleted[i] > max) { 32288c2ecf20Sopenharmony_ci max = mvm->fw_key_deleted[i]; 32298c2ecf20Sopenharmony_ci max_offs = i; 32308c2ecf20Sopenharmony_ci } 32318c2ecf20Sopenharmony_ci } 32328c2ecf20Sopenharmony_ci 32338c2ecf20Sopenharmony_ci if (max_offs < 0) 32348c2ecf20Sopenharmony_ci return STA_KEY_IDX_INVALID; 32358c2ecf20Sopenharmony_ci 32368c2ecf20Sopenharmony_ci return max_offs; 32378c2ecf20Sopenharmony_ci} 32388c2ecf20Sopenharmony_ci 32398c2ecf20Sopenharmony_cistatic struct iwl_mvm_sta *iwl_mvm_get_key_sta(struct iwl_mvm *mvm, 32408c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 32418c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 32428c2ecf20Sopenharmony_ci{ 32438c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 32448c2ecf20Sopenharmony_ci 32458c2ecf20Sopenharmony_ci if (sta) 32468c2ecf20Sopenharmony_ci return iwl_mvm_sta_from_mac80211(sta); 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_ci /* 32498c2ecf20Sopenharmony_ci * The device expects GTKs for station interfaces to be 32508c2ecf20Sopenharmony_ci * installed as GTKs for the AP station. If we have no 32518c2ecf20Sopenharmony_ci * station ID, then use AP's station ID. 32528c2ecf20Sopenharmony_ci */ 32538c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_STATION && 32548c2ecf20Sopenharmony_ci mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) { 32558c2ecf20Sopenharmony_ci u8 sta_id = mvmvif->ap_sta_id; 32568c2ecf20Sopenharmony_ci 32578c2ecf20Sopenharmony_ci sta = rcu_dereference_check(mvm->fw_id_to_mac_id[sta_id], 32588c2ecf20Sopenharmony_ci lockdep_is_held(&mvm->mutex)); 32598c2ecf20Sopenharmony_ci 32608c2ecf20Sopenharmony_ci /* 32618c2ecf20Sopenharmony_ci * It is possible that the 'sta' parameter is NULL, 32628c2ecf20Sopenharmony_ci * for example when a GTK is removed - the sta_id will then 32638c2ecf20Sopenharmony_ci * be the AP ID, and no station was passed by mac80211. 32648c2ecf20Sopenharmony_ci */ 32658c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(sta)) 32668c2ecf20Sopenharmony_ci return NULL; 32678c2ecf20Sopenharmony_ci 32688c2ecf20Sopenharmony_ci return iwl_mvm_sta_from_mac80211(sta); 32698c2ecf20Sopenharmony_ci } 32708c2ecf20Sopenharmony_ci 32718c2ecf20Sopenharmony_ci return NULL; 32728c2ecf20Sopenharmony_ci} 32738c2ecf20Sopenharmony_ci 32748c2ecf20Sopenharmony_cistatic int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, 32758c2ecf20Sopenharmony_ci u32 sta_id, 32768c2ecf20Sopenharmony_ci struct ieee80211_key_conf *key, bool mcast, 32778c2ecf20Sopenharmony_ci u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags, 32788c2ecf20Sopenharmony_ci u8 key_offset, bool mfp) 32798c2ecf20Sopenharmony_ci{ 32808c2ecf20Sopenharmony_ci union { 32818c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_key_cmd_v1 cmd_v1; 32828c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_key_cmd cmd; 32838c2ecf20Sopenharmony_ci } u = {}; 32848c2ecf20Sopenharmony_ci __le16 key_flags; 32858c2ecf20Sopenharmony_ci int ret; 32868c2ecf20Sopenharmony_ci u32 status; 32878c2ecf20Sopenharmony_ci u16 keyidx; 32888c2ecf20Sopenharmony_ci u64 pn = 0; 32898c2ecf20Sopenharmony_ci int i, size; 32908c2ecf20Sopenharmony_ci bool new_api = fw_has_api(&mvm->fw->ucode_capa, 32918c2ecf20Sopenharmony_ci IWL_UCODE_TLV_API_TKIP_MIC_KEYS); 32928c2ecf20Sopenharmony_ci 32938c2ecf20Sopenharmony_ci if (sta_id == IWL_MVM_INVALID_STA) 32948c2ecf20Sopenharmony_ci return -EINVAL; 32958c2ecf20Sopenharmony_ci 32968c2ecf20Sopenharmony_ci keyidx = (key->keyidx << STA_KEY_FLG_KEYID_POS) & 32978c2ecf20Sopenharmony_ci STA_KEY_FLG_KEYID_MSK; 32988c2ecf20Sopenharmony_ci key_flags = cpu_to_le16(keyidx); 32998c2ecf20Sopenharmony_ci key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_KEY_MAP); 33008c2ecf20Sopenharmony_ci 33018c2ecf20Sopenharmony_ci switch (key->cipher) { 33028c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 33038c2ecf20Sopenharmony_ci key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP); 33048c2ecf20Sopenharmony_ci if (new_api) { 33058c2ecf20Sopenharmony_ci memcpy((void *)&u.cmd.tx_mic_key, 33068c2ecf20Sopenharmony_ci &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], 33078c2ecf20Sopenharmony_ci IWL_MIC_KEY_SIZE); 33088c2ecf20Sopenharmony_ci 33098c2ecf20Sopenharmony_ci memcpy((void *)&u.cmd.rx_mic_key, 33108c2ecf20Sopenharmony_ci &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], 33118c2ecf20Sopenharmony_ci IWL_MIC_KEY_SIZE); 33128c2ecf20Sopenharmony_ci pn = atomic64_read(&key->tx_pn); 33138c2ecf20Sopenharmony_ci 33148c2ecf20Sopenharmony_ci } else { 33158c2ecf20Sopenharmony_ci u.cmd_v1.tkip_rx_tsc_byte2 = tkip_iv32; 33168c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) 33178c2ecf20Sopenharmony_ci u.cmd_v1.tkip_rx_ttak[i] = 33188c2ecf20Sopenharmony_ci cpu_to_le16(tkip_p1k[i]); 33198c2ecf20Sopenharmony_ci } 33208c2ecf20Sopenharmony_ci memcpy(u.cmd.common.key, key->key, key->keylen); 33218c2ecf20Sopenharmony_ci break; 33228c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 33238c2ecf20Sopenharmony_ci key_flags |= cpu_to_le16(STA_KEY_FLG_CCM); 33248c2ecf20Sopenharmony_ci memcpy(u.cmd.common.key, key->key, key->keylen); 33258c2ecf20Sopenharmony_ci if (new_api) 33268c2ecf20Sopenharmony_ci pn = atomic64_read(&key->tx_pn); 33278c2ecf20Sopenharmony_ci break; 33288c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 33298c2ecf20Sopenharmony_ci key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_13BYTES); 33308c2ecf20Sopenharmony_ci /* fall through */ 33318c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 33328c2ecf20Sopenharmony_ci key_flags |= cpu_to_le16(STA_KEY_FLG_WEP); 33338c2ecf20Sopenharmony_ci memcpy(u.cmd.common.key + 3, key->key, key->keylen); 33348c2ecf20Sopenharmony_ci break; 33358c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_GCMP_256: 33368c2ecf20Sopenharmony_ci key_flags |= cpu_to_le16(STA_KEY_FLG_KEY_32BYTES); 33378c2ecf20Sopenharmony_ci /* fall through */ 33388c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_GCMP: 33398c2ecf20Sopenharmony_ci key_flags |= cpu_to_le16(STA_KEY_FLG_GCMP); 33408c2ecf20Sopenharmony_ci memcpy(u.cmd.common.key, key->key, key->keylen); 33418c2ecf20Sopenharmony_ci if (new_api) 33428c2ecf20Sopenharmony_ci pn = atomic64_read(&key->tx_pn); 33438c2ecf20Sopenharmony_ci break; 33448c2ecf20Sopenharmony_ci default: 33458c2ecf20Sopenharmony_ci key_flags |= cpu_to_le16(STA_KEY_FLG_EXT); 33468c2ecf20Sopenharmony_ci memcpy(u.cmd.common.key, key->key, key->keylen); 33478c2ecf20Sopenharmony_ci } 33488c2ecf20Sopenharmony_ci 33498c2ecf20Sopenharmony_ci if (mcast) 33508c2ecf20Sopenharmony_ci key_flags |= cpu_to_le16(STA_KEY_MULTICAST); 33518c2ecf20Sopenharmony_ci if (mfp) 33528c2ecf20Sopenharmony_ci key_flags |= cpu_to_le16(STA_KEY_MFP); 33538c2ecf20Sopenharmony_ci 33548c2ecf20Sopenharmony_ci u.cmd.common.key_offset = key_offset; 33558c2ecf20Sopenharmony_ci u.cmd.common.key_flags = key_flags; 33568c2ecf20Sopenharmony_ci u.cmd.common.sta_id = sta_id; 33578c2ecf20Sopenharmony_ci 33588c2ecf20Sopenharmony_ci if (new_api) { 33598c2ecf20Sopenharmony_ci u.cmd.transmit_seq_cnt = cpu_to_le64(pn); 33608c2ecf20Sopenharmony_ci size = sizeof(u.cmd); 33618c2ecf20Sopenharmony_ci } else { 33628c2ecf20Sopenharmony_ci size = sizeof(u.cmd_v1); 33638c2ecf20Sopenharmony_ci } 33648c2ecf20Sopenharmony_ci 33658c2ecf20Sopenharmony_ci status = ADD_STA_SUCCESS; 33668c2ecf20Sopenharmony_ci if (cmd_flags & CMD_ASYNC) 33678c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, CMD_ASYNC, size, 33688c2ecf20Sopenharmony_ci &u.cmd); 33698c2ecf20Sopenharmony_ci else 33708c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, size, 33718c2ecf20Sopenharmony_ci &u.cmd, &status); 33728c2ecf20Sopenharmony_ci 33738c2ecf20Sopenharmony_ci switch (status) { 33748c2ecf20Sopenharmony_ci case ADD_STA_SUCCESS: 33758c2ecf20Sopenharmony_ci IWL_DEBUG_WEP(mvm, "MODIFY_STA: set dynamic key passed\n"); 33768c2ecf20Sopenharmony_ci break; 33778c2ecf20Sopenharmony_ci default: 33788c2ecf20Sopenharmony_ci ret = -EIO; 33798c2ecf20Sopenharmony_ci IWL_ERR(mvm, "MODIFY_STA: set dynamic key failed\n"); 33808c2ecf20Sopenharmony_ci break; 33818c2ecf20Sopenharmony_ci } 33828c2ecf20Sopenharmony_ci 33838c2ecf20Sopenharmony_ci return ret; 33848c2ecf20Sopenharmony_ci} 33858c2ecf20Sopenharmony_ci 33868c2ecf20Sopenharmony_cistatic int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm, 33878c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf, 33888c2ecf20Sopenharmony_ci u8 sta_id, bool remove_key) 33898c2ecf20Sopenharmony_ci{ 33908c2ecf20Sopenharmony_ci struct iwl_mvm_mgmt_mcast_key_cmd igtk_cmd = {}; 33918c2ecf20Sopenharmony_ci 33928c2ecf20Sopenharmony_ci /* verify the key details match the required command's expectations */ 33938c2ecf20Sopenharmony_ci if (WARN_ON((keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE) || 33948c2ecf20Sopenharmony_ci (keyconf->keyidx != 4 && keyconf->keyidx != 5) || 33958c2ecf20Sopenharmony_ci (keyconf->cipher != WLAN_CIPHER_SUITE_AES_CMAC && 33968c2ecf20Sopenharmony_ci keyconf->cipher != WLAN_CIPHER_SUITE_BIP_GMAC_128 && 33978c2ecf20Sopenharmony_ci keyconf->cipher != WLAN_CIPHER_SUITE_BIP_GMAC_256))) 33988c2ecf20Sopenharmony_ci return -EINVAL; 33998c2ecf20Sopenharmony_ci 34008c2ecf20Sopenharmony_ci if (WARN_ON(!iwl_mvm_has_new_rx_api(mvm) && 34018c2ecf20Sopenharmony_ci keyconf->cipher != WLAN_CIPHER_SUITE_AES_CMAC)) 34028c2ecf20Sopenharmony_ci return -EINVAL; 34038c2ecf20Sopenharmony_ci 34048c2ecf20Sopenharmony_ci igtk_cmd.key_id = cpu_to_le32(keyconf->keyidx); 34058c2ecf20Sopenharmony_ci igtk_cmd.sta_id = cpu_to_le32(sta_id); 34068c2ecf20Sopenharmony_ci 34078c2ecf20Sopenharmony_ci if (remove_key) { 34088c2ecf20Sopenharmony_ci /* This is a valid situation for IGTK */ 34098c2ecf20Sopenharmony_ci if (sta_id == IWL_MVM_INVALID_STA) 34108c2ecf20Sopenharmony_ci return 0; 34118c2ecf20Sopenharmony_ci 34128c2ecf20Sopenharmony_ci igtk_cmd.ctrl_flags |= cpu_to_le32(STA_KEY_NOT_VALID); 34138c2ecf20Sopenharmony_ci } else { 34148c2ecf20Sopenharmony_ci struct ieee80211_key_seq seq; 34158c2ecf20Sopenharmony_ci const u8 *pn; 34168c2ecf20Sopenharmony_ci 34178c2ecf20Sopenharmony_ci switch (keyconf->cipher) { 34188c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_AES_CMAC: 34198c2ecf20Sopenharmony_ci igtk_cmd.ctrl_flags |= cpu_to_le32(STA_KEY_FLG_CCM); 34208c2ecf20Sopenharmony_ci break; 34218c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_BIP_GMAC_128: 34228c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_BIP_GMAC_256: 34238c2ecf20Sopenharmony_ci igtk_cmd.ctrl_flags |= cpu_to_le32(STA_KEY_FLG_GCMP); 34248c2ecf20Sopenharmony_ci break; 34258c2ecf20Sopenharmony_ci default: 34268c2ecf20Sopenharmony_ci return -EINVAL; 34278c2ecf20Sopenharmony_ci } 34288c2ecf20Sopenharmony_ci 34298c2ecf20Sopenharmony_ci memcpy(igtk_cmd.igtk, keyconf->key, keyconf->keylen); 34308c2ecf20Sopenharmony_ci if (keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256) 34318c2ecf20Sopenharmony_ci igtk_cmd.ctrl_flags |= 34328c2ecf20Sopenharmony_ci cpu_to_le32(STA_KEY_FLG_KEY_32BYTES); 34338c2ecf20Sopenharmony_ci ieee80211_get_key_rx_seq(keyconf, 0, &seq); 34348c2ecf20Sopenharmony_ci pn = seq.aes_cmac.pn; 34358c2ecf20Sopenharmony_ci igtk_cmd.receive_seq_cnt = cpu_to_le64(((u64) pn[5] << 0) | 34368c2ecf20Sopenharmony_ci ((u64) pn[4] << 8) | 34378c2ecf20Sopenharmony_ci ((u64) pn[3] << 16) | 34388c2ecf20Sopenharmony_ci ((u64) pn[2] << 24) | 34398c2ecf20Sopenharmony_ci ((u64) pn[1] << 32) | 34408c2ecf20Sopenharmony_ci ((u64) pn[0] << 40)); 34418c2ecf20Sopenharmony_ci } 34428c2ecf20Sopenharmony_ci 34438c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(mvm, "%s igtk for sta %u\n", 34448c2ecf20Sopenharmony_ci remove_key ? "removing" : "installing", 34458c2ecf20Sopenharmony_ci igtk_cmd.sta_id); 34468c2ecf20Sopenharmony_ci 34478c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_rx_api(mvm)) { 34488c2ecf20Sopenharmony_ci struct iwl_mvm_mgmt_mcast_key_cmd_v1 igtk_cmd_v1 = { 34498c2ecf20Sopenharmony_ci .ctrl_flags = igtk_cmd.ctrl_flags, 34508c2ecf20Sopenharmony_ci .key_id = igtk_cmd.key_id, 34518c2ecf20Sopenharmony_ci .sta_id = igtk_cmd.sta_id, 34528c2ecf20Sopenharmony_ci .receive_seq_cnt = igtk_cmd.receive_seq_cnt 34538c2ecf20Sopenharmony_ci }; 34548c2ecf20Sopenharmony_ci 34558c2ecf20Sopenharmony_ci memcpy(igtk_cmd_v1.igtk, igtk_cmd.igtk, 34568c2ecf20Sopenharmony_ci ARRAY_SIZE(igtk_cmd_v1.igtk)); 34578c2ecf20Sopenharmony_ci return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, 0, 34588c2ecf20Sopenharmony_ci sizeof(igtk_cmd_v1), &igtk_cmd_v1); 34598c2ecf20Sopenharmony_ci } 34608c2ecf20Sopenharmony_ci return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, 0, 34618c2ecf20Sopenharmony_ci sizeof(igtk_cmd), &igtk_cmd); 34628c2ecf20Sopenharmony_ci} 34638c2ecf20Sopenharmony_ci 34648c2ecf20Sopenharmony_ci 34658c2ecf20Sopenharmony_cistatic inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm, 34668c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 34678c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 34688c2ecf20Sopenharmony_ci{ 34698c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 34708c2ecf20Sopenharmony_ci 34718c2ecf20Sopenharmony_ci if (sta) 34728c2ecf20Sopenharmony_ci return sta->addr; 34738c2ecf20Sopenharmony_ci 34748c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_STATION && 34758c2ecf20Sopenharmony_ci mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) { 34768c2ecf20Sopenharmony_ci u8 sta_id = mvmvif->ap_sta_id; 34778c2ecf20Sopenharmony_ci sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], 34788c2ecf20Sopenharmony_ci lockdep_is_held(&mvm->mutex)); 34798c2ecf20Sopenharmony_ci return sta->addr; 34808c2ecf20Sopenharmony_ci } 34818c2ecf20Sopenharmony_ci 34828c2ecf20Sopenharmony_ci 34838c2ecf20Sopenharmony_ci return NULL; 34848c2ecf20Sopenharmony_ci} 34858c2ecf20Sopenharmony_ci 34868c2ecf20Sopenharmony_cistatic int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, 34878c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 34888c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 34898c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf, 34908c2ecf20Sopenharmony_ci u8 key_offset, 34918c2ecf20Sopenharmony_ci bool mcast) 34928c2ecf20Sopenharmony_ci{ 34938c2ecf20Sopenharmony_ci int ret; 34948c2ecf20Sopenharmony_ci const u8 *addr; 34958c2ecf20Sopenharmony_ci struct ieee80211_key_seq seq; 34968c2ecf20Sopenharmony_ci u16 p1k[5]; 34978c2ecf20Sopenharmony_ci u32 sta_id; 34988c2ecf20Sopenharmony_ci bool mfp = false; 34998c2ecf20Sopenharmony_ci 35008c2ecf20Sopenharmony_ci if (sta) { 35018c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); 35028c2ecf20Sopenharmony_ci 35038c2ecf20Sopenharmony_ci sta_id = mvm_sta->sta_id; 35048c2ecf20Sopenharmony_ci mfp = sta->mfp; 35058c2ecf20Sopenharmony_ci } else if (vif->type == NL80211_IFTYPE_AP && 35068c2ecf20Sopenharmony_ci !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { 35078c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 35088c2ecf20Sopenharmony_ci 35098c2ecf20Sopenharmony_ci sta_id = mvmvif->mcast_sta.sta_id; 35108c2ecf20Sopenharmony_ci } else { 35118c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to find station id\n"); 35128c2ecf20Sopenharmony_ci return -EINVAL; 35138c2ecf20Sopenharmony_ci } 35148c2ecf20Sopenharmony_ci 35158c2ecf20Sopenharmony_ci switch (keyconf->cipher) { 35168c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 35178c2ecf20Sopenharmony_ci addr = iwl_mvm_get_mac_addr(mvm, vif, sta); 35188c2ecf20Sopenharmony_ci /* get phase 1 key from mac80211 */ 35198c2ecf20Sopenharmony_ci ieee80211_get_key_rx_seq(keyconf, 0, &seq); 35208c2ecf20Sopenharmony_ci ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); 35218c2ecf20Sopenharmony_ci ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast, 35228c2ecf20Sopenharmony_ci seq.tkip.iv32, p1k, 0, key_offset, 35238c2ecf20Sopenharmony_ci mfp); 35248c2ecf20Sopenharmony_ci break; 35258c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 35268c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 35278c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 35288c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_GCMP: 35298c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_GCMP_256: 35308c2ecf20Sopenharmony_ci ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast, 35318c2ecf20Sopenharmony_ci 0, NULL, 0, key_offset, mfp); 35328c2ecf20Sopenharmony_ci break; 35338c2ecf20Sopenharmony_ci default: 35348c2ecf20Sopenharmony_ci ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast, 35358c2ecf20Sopenharmony_ci 0, NULL, 0, key_offset, mfp); 35368c2ecf20Sopenharmony_ci } 35378c2ecf20Sopenharmony_ci 35388c2ecf20Sopenharmony_ci return ret; 35398c2ecf20Sopenharmony_ci} 35408c2ecf20Sopenharmony_ci 35418c2ecf20Sopenharmony_ciint iwl_mvm_set_sta_key(struct iwl_mvm *mvm, 35428c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 35438c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 35448c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf, 35458c2ecf20Sopenharmony_ci u8 key_offset) 35468c2ecf20Sopenharmony_ci{ 35478c2ecf20Sopenharmony_ci bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); 35488c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta; 35498c2ecf20Sopenharmony_ci u8 sta_id = IWL_MVM_INVALID_STA; 35508c2ecf20Sopenharmony_ci int ret; 35518c2ecf20Sopenharmony_ci static const u8 __maybe_unused zero_addr[ETH_ALEN] = {0}; 35528c2ecf20Sopenharmony_ci 35538c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 35548c2ecf20Sopenharmony_ci 35558c2ecf20Sopenharmony_ci if (vif->type != NL80211_IFTYPE_AP || 35568c2ecf20Sopenharmony_ci keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE) { 35578c2ecf20Sopenharmony_ci /* Get the station id from the mvm local station table */ 35588c2ecf20Sopenharmony_ci mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta); 35598c2ecf20Sopenharmony_ci if (!mvm_sta) { 35608c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to find station\n"); 35618c2ecf20Sopenharmony_ci return -EINVAL; 35628c2ecf20Sopenharmony_ci } 35638c2ecf20Sopenharmony_ci sta_id = mvm_sta->sta_id; 35648c2ecf20Sopenharmony_ci 35658c2ecf20Sopenharmony_ci /* 35668c2ecf20Sopenharmony_ci * It is possible that the 'sta' parameter is NULL, and thus 35678c2ecf20Sopenharmony_ci * there is a need to retrieve the sta from the local station 35688c2ecf20Sopenharmony_ci * table. 35698c2ecf20Sopenharmony_ci */ 35708c2ecf20Sopenharmony_ci if (!sta) { 35718c2ecf20Sopenharmony_ci sta = rcu_dereference_protected( 35728c2ecf20Sopenharmony_ci mvm->fw_id_to_mac_id[sta_id], 35738c2ecf20Sopenharmony_ci lockdep_is_held(&mvm->mutex)); 35748c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(sta)) { 35758c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Invalid station id\n"); 35768c2ecf20Sopenharmony_ci return -EINVAL; 35778c2ecf20Sopenharmony_ci } 35788c2ecf20Sopenharmony_ci } 35798c2ecf20Sopenharmony_ci 35808c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif)) 35818c2ecf20Sopenharmony_ci return -EINVAL; 35828c2ecf20Sopenharmony_ci } else { 35838c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 35848c2ecf20Sopenharmony_ci 35858c2ecf20Sopenharmony_ci sta_id = mvmvif->mcast_sta.sta_id; 35868c2ecf20Sopenharmony_ci } 35878c2ecf20Sopenharmony_ci 35888c2ecf20Sopenharmony_ci if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC || 35898c2ecf20Sopenharmony_ci keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 || 35908c2ecf20Sopenharmony_ci keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256) { 35918c2ecf20Sopenharmony_ci ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, false); 35928c2ecf20Sopenharmony_ci goto end; 35938c2ecf20Sopenharmony_ci } 35948c2ecf20Sopenharmony_ci 35958c2ecf20Sopenharmony_ci /* If the key_offset is not pre-assigned, we need to find a 35968c2ecf20Sopenharmony_ci * new offset to use. In normal cases, the offset is not 35978c2ecf20Sopenharmony_ci * pre-assigned, but during HW_RESTART we want to reuse the 35988c2ecf20Sopenharmony_ci * same indices, so we pass them when this function is called. 35998c2ecf20Sopenharmony_ci * 36008c2ecf20Sopenharmony_ci * In D3 entry, we need to hardcoded the indices (because the 36018c2ecf20Sopenharmony_ci * firmware hardcodes the PTK offset to 0). In this case, we 36028c2ecf20Sopenharmony_ci * need to make sure we don't overwrite the hw_key_idx in the 36038c2ecf20Sopenharmony_ci * keyconf structure, because otherwise we cannot configure 36048c2ecf20Sopenharmony_ci * the original ones back when resuming. 36058c2ecf20Sopenharmony_ci */ 36068c2ecf20Sopenharmony_ci if (key_offset == STA_KEY_IDX_INVALID) { 36078c2ecf20Sopenharmony_ci key_offset = iwl_mvm_set_fw_key_idx(mvm); 36088c2ecf20Sopenharmony_ci if (key_offset == STA_KEY_IDX_INVALID) 36098c2ecf20Sopenharmony_ci return -ENOSPC; 36108c2ecf20Sopenharmony_ci keyconf->hw_key_idx = key_offset; 36118c2ecf20Sopenharmony_ci } 36128c2ecf20Sopenharmony_ci 36138c2ecf20Sopenharmony_ci ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, key_offset, mcast); 36148c2ecf20Sopenharmony_ci if (ret) 36158c2ecf20Sopenharmony_ci goto end; 36168c2ecf20Sopenharmony_ci 36178c2ecf20Sopenharmony_ci /* 36188c2ecf20Sopenharmony_ci * For WEP, the same key is used for multicast and unicast. Upload it 36198c2ecf20Sopenharmony_ci * again, using the same key offset, and now pointing the other one 36208c2ecf20Sopenharmony_ci * to the same key slot (offset). 36218c2ecf20Sopenharmony_ci * If this fails, remove the original as well. 36228c2ecf20Sopenharmony_ci */ 36238c2ecf20Sopenharmony_ci if ((keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || 36248c2ecf20Sopenharmony_ci keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) && 36258c2ecf20Sopenharmony_ci sta) { 36268c2ecf20Sopenharmony_ci ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, 36278c2ecf20Sopenharmony_ci key_offset, !mcast); 36288c2ecf20Sopenharmony_ci if (ret) { 36298c2ecf20Sopenharmony_ci __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast); 36308c2ecf20Sopenharmony_ci goto end; 36318c2ecf20Sopenharmony_ci } 36328c2ecf20Sopenharmony_ci } 36338c2ecf20Sopenharmony_ci 36348c2ecf20Sopenharmony_ci __set_bit(key_offset, mvm->fw_key_table); 36358c2ecf20Sopenharmony_ci 36368c2ecf20Sopenharmony_ciend: 36378c2ecf20Sopenharmony_ci IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n", 36388c2ecf20Sopenharmony_ci keyconf->cipher, keyconf->keylen, keyconf->keyidx, 36398c2ecf20Sopenharmony_ci sta ? sta->addr : zero_addr, ret); 36408c2ecf20Sopenharmony_ci return ret; 36418c2ecf20Sopenharmony_ci} 36428c2ecf20Sopenharmony_ci 36438c2ecf20Sopenharmony_ciint iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, 36448c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 36458c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 36468c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf) 36478c2ecf20Sopenharmony_ci{ 36488c2ecf20Sopenharmony_ci bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); 36498c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta; 36508c2ecf20Sopenharmony_ci u8 sta_id = IWL_MVM_INVALID_STA; 36518c2ecf20Sopenharmony_ci int ret, i; 36528c2ecf20Sopenharmony_ci 36538c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 36548c2ecf20Sopenharmony_ci 36558c2ecf20Sopenharmony_ci /* Get the station from the mvm local station table */ 36568c2ecf20Sopenharmony_ci mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta); 36578c2ecf20Sopenharmony_ci if (mvm_sta) 36588c2ecf20Sopenharmony_ci sta_id = mvm_sta->sta_id; 36598c2ecf20Sopenharmony_ci else if (!sta && vif->type == NL80211_IFTYPE_AP && mcast) 36608c2ecf20Sopenharmony_ci sta_id = iwl_mvm_vif_from_mac80211(vif)->mcast_sta.sta_id; 36618c2ecf20Sopenharmony_ci 36628c2ecf20Sopenharmony_ci 36638c2ecf20Sopenharmony_ci IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n", 36648c2ecf20Sopenharmony_ci keyconf->keyidx, sta_id); 36658c2ecf20Sopenharmony_ci 36668c2ecf20Sopenharmony_ci if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC || 36678c2ecf20Sopenharmony_ci keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 || 36688c2ecf20Sopenharmony_ci keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256) 36698c2ecf20Sopenharmony_ci return iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, true); 36708c2ecf20Sopenharmony_ci 36718c2ecf20Sopenharmony_ci if (!__test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table)) { 36728c2ecf20Sopenharmony_ci IWL_ERR(mvm, "offset %d not used in fw key table.\n", 36738c2ecf20Sopenharmony_ci keyconf->hw_key_idx); 36748c2ecf20Sopenharmony_ci return -ENOENT; 36758c2ecf20Sopenharmony_ci } 36768c2ecf20Sopenharmony_ci 36778c2ecf20Sopenharmony_ci /* track which key was deleted last */ 36788c2ecf20Sopenharmony_ci for (i = 0; i < STA_KEY_MAX_NUM; i++) { 36798c2ecf20Sopenharmony_ci if (mvm->fw_key_deleted[i] < U8_MAX) 36808c2ecf20Sopenharmony_ci mvm->fw_key_deleted[i]++; 36818c2ecf20Sopenharmony_ci } 36828c2ecf20Sopenharmony_ci mvm->fw_key_deleted[keyconf->hw_key_idx] = 0; 36838c2ecf20Sopenharmony_ci 36848c2ecf20Sopenharmony_ci if (sta && !mvm_sta) { 36858c2ecf20Sopenharmony_ci IWL_DEBUG_WEP(mvm, "station non-existent, early return.\n"); 36868c2ecf20Sopenharmony_ci return 0; 36878c2ecf20Sopenharmony_ci } 36888c2ecf20Sopenharmony_ci 36898c2ecf20Sopenharmony_ci ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast); 36908c2ecf20Sopenharmony_ci if (ret) 36918c2ecf20Sopenharmony_ci return ret; 36928c2ecf20Sopenharmony_ci 36938c2ecf20Sopenharmony_ci /* delete WEP key twice to get rid of (now useless) offset */ 36948c2ecf20Sopenharmony_ci if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || 36958c2ecf20Sopenharmony_ci keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) 36968c2ecf20Sopenharmony_ci ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, !mcast); 36978c2ecf20Sopenharmony_ci 36988c2ecf20Sopenharmony_ci return ret; 36998c2ecf20Sopenharmony_ci} 37008c2ecf20Sopenharmony_ci 37018c2ecf20Sopenharmony_civoid iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, 37028c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 37038c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf, 37048c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, u32 iv32, 37058c2ecf20Sopenharmony_ci u16 *phase1key) 37068c2ecf20Sopenharmony_ci{ 37078c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta; 37088c2ecf20Sopenharmony_ci bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); 37098c2ecf20Sopenharmony_ci bool mfp = sta ? sta->mfp : false; 37108c2ecf20Sopenharmony_ci 37118c2ecf20Sopenharmony_ci rcu_read_lock(); 37128c2ecf20Sopenharmony_ci 37138c2ecf20Sopenharmony_ci mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta); 37148c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!mvm_sta)) 37158c2ecf20Sopenharmony_ci goto unlock; 37168c2ecf20Sopenharmony_ci iwl_mvm_send_sta_key(mvm, mvm_sta->sta_id, keyconf, mcast, 37178c2ecf20Sopenharmony_ci iv32, phase1key, CMD_ASYNC, keyconf->hw_key_idx, 37188c2ecf20Sopenharmony_ci mfp); 37198c2ecf20Sopenharmony_ci 37208c2ecf20Sopenharmony_ci unlock: 37218c2ecf20Sopenharmony_ci rcu_read_unlock(); 37228c2ecf20Sopenharmony_ci} 37238c2ecf20Sopenharmony_ci 37248c2ecf20Sopenharmony_civoid iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, 37258c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 37268c2ecf20Sopenharmony_ci{ 37278c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 37288c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_cmd cmd = { 37298c2ecf20Sopenharmony_ci .add_modify = STA_MODE_MODIFY, 37308c2ecf20Sopenharmony_ci .sta_id = mvmsta->sta_id, 37318c2ecf20Sopenharmony_ci .station_flags_msk = cpu_to_le32(STA_FLG_PS), 37328c2ecf20Sopenharmony_ci .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), 37338c2ecf20Sopenharmony_ci }; 37348c2ecf20Sopenharmony_ci int ret; 37358c2ecf20Sopenharmony_ci 37368c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, 37378c2ecf20Sopenharmony_ci iwl_mvm_add_sta_cmd_size(mvm), &cmd); 37388c2ecf20Sopenharmony_ci if (ret) 37398c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); 37408c2ecf20Sopenharmony_ci} 37418c2ecf20Sopenharmony_ci 37428c2ecf20Sopenharmony_civoid iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, 37438c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 37448c2ecf20Sopenharmony_ci enum ieee80211_frame_release_type reason, 37458c2ecf20Sopenharmony_ci u16 cnt, u16 tids, bool more_data, 37468c2ecf20Sopenharmony_ci bool single_sta_queue) 37478c2ecf20Sopenharmony_ci{ 37488c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 37498c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_cmd cmd = { 37508c2ecf20Sopenharmony_ci .add_modify = STA_MODE_MODIFY, 37518c2ecf20Sopenharmony_ci .sta_id = mvmsta->sta_id, 37528c2ecf20Sopenharmony_ci .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT, 37538c2ecf20Sopenharmony_ci .sleep_tx_count = cpu_to_le16(cnt), 37548c2ecf20Sopenharmony_ci .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), 37558c2ecf20Sopenharmony_ci }; 37568c2ecf20Sopenharmony_ci int tid, ret; 37578c2ecf20Sopenharmony_ci unsigned long _tids = tids; 37588c2ecf20Sopenharmony_ci 37598c2ecf20Sopenharmony_ci /* convert TIDs to ACs - we don't support TSPEC so that's OK 37608c2ecf20Sopenharmony_ci * Note that this field is reserved and unused by firmware not 37618c2ecf20Sopenharmony_ci * supporting GO uAPSD, so it's safe to always do this. 37628c2ecf20Sopenharmony_ci */ 37638c2ecf20Sopenharmony_ci for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) 37648c2ecf20Sopenharmony_ci cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]); 37658c2ecf20Sopenharmony_ci 37668c2ecf20Sopenharmony_ci /* If we're releasing frames from aggregation or dqa queues then check 37678c2ecf20Sopenharmony_ci * if all the queues that we're releasing frames from, combined, have: 37688c2ecf20Sopenharmony_ci * - more frames than the service period, in which case more_data 37698c2ecf20Sopenharmony_ci * needs to be set 37708c2ecf20Sopenharmony_ci * - fewer than 'cnt' frames, in which case we need to adjust the 37718c2ecf20Sopenharmony_ci * firmware command (but do that unconditionally) 37728c2ecf20Sopenharmony_ci */ 37738c2ecf20Sopenharmony_ci if (single_sta_queue) { 37748c2ecf20Sopenharmony_ci int remaining = cnt; 37758c2ecf20Sopenharmony_ci int sleep_tx_count; 37768c2ecf20Sopenharmony_ci 37778c2ecf20Sopenharmony_ci spin_lock_bh(&mvmsta->lock); 37788c2ecf20Sopenharmony_ci for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) { 37798c2ecf20Sopenharmony_ci struct iwl_mvm_tid_data *tid_data; 37808c2ecf20Sopenharmony_ci u16 n_queued; 37818c2ecf20Sopenharmony_ci 37828c2ecf20Sopenharmony_ci tid_data = &mvmsta->tid_data[tid]; 37838c2ecf20Sopenharmony_ci 37848c2ecf20Sopenharmony_ci n_queued = iwl_mvm_tid_queued(mvm, tid_data); 37858c2ecf20Sopenharmony_ci if (n_queued > remaining) { 37868c2ecf20Sopenharmony_ci more_data = true; 37878c2ecf20Sopenharmony_ci remaining = 0; 37888c2ecf20Sopenharmony_ci break; 37898c2ecf20Sopenharmony_ci } 37908c2ecf20Sopenharmony_ci remaining -= n_queued; 37918c2ecf20Sopenharmony_ci } 37928c2ecf20Sopenharmony_ci sleep_tx_count = cnt - remaining; 37938c2ecf20Sopenharmony_ci if (reason == IEEE80211_FRAME_RELEASE_UAPSD) 37948c2ecf20Sopenharmony_ci mvmsta->sleep_tx_count = sleep_tx_count; 37958c2ecf20Sopenharmony_ci spin_unlock_bh(&mvmsta->lock); 37968c2ecf20Sopenharmony_ci 37978c2ecf20Sopenharmony_ci cmd.sleep_tx_count = cpu_to_le16(sleep_tx_count); 37988c2ecf20Sopenharmony_ci if (WARN_ON(cnt - remaining == 0)) { 37998c2ecf20Sopenharmony_ci ieee80211_sta_eosp(sta); 38008c2ecf20Sopenharmony_ci return; 38018c2ecf20Sopenharmony_ci } 38028c2ecf20Sopenharmony_ci } 38038c2ecf20Sopenharmony_ci 38048c2ecf20Sopenharmony_ci /* Note: this is ignored by firmware not supporting GO uAPSD */ 38058c2ecf20Sopenharmony_ci if (more_data) 38068c2ecf20Sopenharmony_ci cmd.sleep_state_flags |= STA_SLEEP_STATE_MOREDATA; 38078c2ecf20Sopenharmony_ci 38088c2ecf20Sopenharmony_ci if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) { 38098c2ecf20Sopenharmony_ci mvmsta->next_status_eosp = true; 38108c2ecf20Sopenharmony_ci cmd.sleep_state_flags |= STA_SLEEP_STATE_PS_POLL; 38118c2ecf20Sopenharmony_ci } else { 38128c2ecf20Sopenharmony_ci cmd.sleep_state_flags |= STA_SLEEP_STATE_UAPSD; 38138c2ecf20Sopenharmony_ci } 38148c2ecf20Sopenharmony_ci 38158c2ecf20Sopenharmony_ci /* block the Tx queues until the FW updated the sleep Tx count */ 38168c2ecf20Sopenharmony_ci iwl_trans_block_txq_ptrs(mvm->trans, true); 38178c2ecf20Sopenharmony_ci 38188c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, 38198c2ecf20Sopenharmony_ci CMD_ASYNC | CMD_WANT_ASYNC_CALLBACK, 38208c2ecf20Sopenharmony_ci iwl_mvm_add_sta_cmd_size(mvm), &cmd); 38218c2ecf20Sopenharmony_ci if (ret) 38228c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); 38238c2ecf20Sopenharmony_ci} 38248c2ecf20Sopenharmony_ci 38258c2ecf20Sopenharmony_civoid iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, 38268c2ecf20Sopenharmony_ci struct iwl_rx_cmd_buffer *rxb) 38278c2ecf20Sopenharmony_ci{ 38288c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 38298c2ecf20Sopenharmony_ci struct iwl_mvm_eosp_notification *notif = (void *)pkt->data; 38308c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 38318c2ecf20Sopenharmony_ci u32 sta_id = le32_to_cpu(notif->sta_id); 38328c2ecf20Sopenharmony_ci 38338c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(sta_id >= mvm->fw->ucode_capa.num_stations)) 38348c2ecf20Sopenharmony_ci return; 38358c2ecf20Sopenharmony_ci 38368c2ecf20Sopenharmony_ci rcu_read_lock(); 38378c2ecf20Sopenharmony_ci sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); 38388c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(sta)) 38398c2ecf20Sopenharmony_ci ieee80211_sta_eosp(sta); 38408c2ecf20Sopenharmony_ci rcu_read_unlock(); 38418c2ecf20Sopenharmony_ci} 38428c2ecf20Sopenharmony_ci 38438c2ecf20Sopenharmony_civoid iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, 38448c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta, bool disable) 38458c2ecf20Sopenharmony_ci{ 38468c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_cmd cmd = { 38478c2ecf20Sopenharmony_ci .add_modify = STA_MODE_MODIFY, 38488c2ecf20Sopenharmony_ci .sta_id = mvmsta->sta_id, 38498c2ecf20Sopenharmony_ci .station_flags = disable ? cpu_to_le32(STA_FLG_DISABLE_TX) : 0, 38508c2ecf20Sopenharmony_ci .station_flags_msk = cpu_to_le32(STA_FLG_DISABLE_TX), 38518c2ecf20Sopenharmony_ci .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), 38528c2ecf20Sopenharmony_ci }; 38538c2ecf20Sopenharmony_ci int ret; 38548c2ecf20Sopenharmony_ci 38558c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, 38568c2ecf20Sopenharmony_ci iwl_mvm_add_sta_cmd_size(mvm), &cmd); 38578c2ecf20Sopenharmony_ci if (ret) 38588c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); 38598c2ecf20Sopenharmony_ci} 38608c2ecf20Sopenharmony_ci 38618c2ecf20Sopenharmony_civoid iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, 38628c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 38638c2ecf20Sopenharmony_ci bool disable) 38648c2ecf20Sopenharmony_ci{ 38658c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); 38668c2ecf20Sopenharmony_ci 38678c2ecf20Sopenharmony_ci spin_lock_bh(&mvm_sta->lock); 38688c2ecf20Sopenharmony_ci 38698c2ecf20Sopenharmony_ci if (mvm_sta->disable_tx == disable) { 38708c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm_sta->lock); 38718c2ecf20Sopenharmony_ci return; 38728c2ecf20Sopenharmony_ci } 38738c2ecf20Sopenharmony_ci 38748c2ecf20Sopenharmony_ci mvm_sta->disable_tx = disable; 38758c2ecf20Sopenharmony_ci 38768c2ecf20Sopenharmony_ci /* Tell mac80211 to start/stop queuing tx for this station */ 38778c2ecf20Sopenharmony_ci ieee80211_sta_block_awake(mvm->hw, sta, disable); 38788c2ecf20Sopenharmony_ci 38798c2ecf20Sopenharmony_ci iwl_mvm_sta_modify_disable_tx(mvm, mvm_sta, disable); 38808c2ecf20Sopenharmony_ci 38818c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm_sta->lock); 38828c2ecf20Sopenharmony_ci} 38838c2ecf20Sopenharmony_ci 38848c2ecf20Sopenharmony_cistatic void iwl_mvm_int_sta_modify_disable_tx(struct iwl_mvm *mvm, 38858c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif, 38868c2ecf20Sopenharmony_ci struct iwl_mvm_int_sta *sta, 38878c2ecf20Sopenharmony_ci bool disable) 38888c2ecf20Sopenharmony_ci{ 38898c2ecf20Sopenharmony_ci u32 id = FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color); 38908c2ecf20Sopenharmony_ci struct iwl_mvm_add_sta_cmd cmd = { 38918c2ecf20Sopenharmony_ci .add_modify = STA_MODE_MODIFY, 38928c2ecf20Sopenharmony_ci .sta_id = sta->sta_id, 38938c2ecf20Sopenharmony_ci .station_flags = disable ? cpu_to_le32(STA_FLG_DISABLE_TX) : 0, 38948c2ecf20Sopenharmony_ci .station_flags_msk = cpu_to_le32(STA_FLG_DISABLE_TX), 38958c2ecf20Sopenharmony_ci .mac_id_n_color = cpu_to_le32(id), 38968c2ecf20Sopenharmony_ci }; 38978c2ecf20Sopenharmony_ci int ret; 38988c2ecf20Sopenharmony_ci 38998c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, 0, 39008c2ecf20Sopenharmony_ci iwl_mvm_add_sta_cmd_size(mvm), &cmd); 39018c2ecf20Sopenharmony_ci if (ret) 39028c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); 39038c2ecf20Sopenharmony_ci} 39048c2ecf20Sopenharmony_ci 39058c2ecf20Sopenharmony_civoid iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, 39068c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif, 39078c2ecf20Sopenharmony_ci bool disable) 39088c2ecf20Sopenharmony_ci{ 39098c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 39108c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta; 39118c2ecf20Sopenharmony_ci int i; 39128c2ecf20Sopenharmony_ci 39138c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 39148c2ecf20Sopenharmony_ci 39158c2ecf20Sopenharmony_ci /* Block/unblock all the stations of the given mvmvif */ 39168c2ecf20Sopenharmony_ci for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) { 39178c2ecf20Sopenharmony_ci sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], 39188c2ecf20Sopenharmony_ci lockdep_is_held(&mvm->mutex)); 39198c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(sta)) 39208c2ecf20Sopenharmony_ci continue; 39218c2ecf20Sopenharmony_ci 39228c2ecf20Sopenharmony_ci mvm_sta = iwl_mvm_sta_from_mac80211(sta); 39238c2ecf20Sopenharmony_ci if (mvm_sta->mac_id_n_color != 39248c2ecf20Sopenharmony_ci FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)) 39258c2ecf20Sopenharmony_ci continue; 39268c2ecf20Sopenharmony_ci 39278c2ecf20Sopenharmony_ci iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable); 39288c2ecf20Sopenharmony_ci } 39298c2ecf20Sopenharmony_ci 39308c2ecf20Sopenharmony_ci if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) 39318c2ecf20Sopenharmony_ci return; 39328c2ecf20Sopenharmony_ci 39338c2ecf20Sopenharmony_ci /* Need to block/unblock also multicast station */ 39348c2ecf20Sopenharmony_ci if (mvmvif->mcast_sta.sta_id != IWL_MVM_INVALID_STA) 39358c2ecf20Sopenharmony_ci iwl_mvm_int_sta_modify_disable_tx(mvm, mvmvif, 39368c2ecf20Sopenharmony_ci &mvmvif->mcast_sta, disable); 39378c2ecf20Sopenharmony_ci 39388c2ecf20Sopenharmony_ci /* 39398c2ecf20Sopenharmony_ci * Only unblock the broadcast station (FW blocks it for immediate 39408c2ecf20Sopenharmony_ci * quiet, not the driver) 39418c2ecf20Sopenharmony_ci */ 39428c2ecf20Sopenharmony_ci if (!disable && mvmvif->bcast_sta.sta_id != IWL_MVM_INVALID_STA) 39438c2ecf20Sopenharmony_ci iwl_mvm_int_sta_modify_disable_tx(mvm, mvmvif, 39448c2ecf20Sopenharmony_ci &mvmvif->bcast_sta, disable); 39458c2ecf20Sopenharmony_ci} 39468c2ecf20Sopenharmony_ci 39478c2ecf20Sopenharmony_civoid iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 39488c2ecf20Sopenharmony_ci{ 39498c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 39508c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta; 39518c2ecf20Sopenharmony_ci 39528c2ecf20Sopenharmony_ci rcu_read_lock(); 39538c2ecf20Sopenharmony_ci 39548c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id); 39558c2ecf20Sopenharmony_ci 39568c2ecf20Sopenharmony_ci if (!WARN_ON(!mvmsta)) 39578c2ecf20Sopenharmony_ci iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true); 39588c2ecf20Sopenharmony_ci 39598c2ecf20Sopenharmony_ci rcu_read_unlock(); 39608c2ecf20Sopenharmony_ci} 39618c2ecf20Sopenharmony_ci 39628c2ecf20Sopenharmony_ciu16 iwl_mvm_tid_queued(struct iwl_mvm *mvm, struct iwl_mvm_tid_data *tid_data) 39638c2ecf20Sopenharmony_ci{ 39648c2ecf20Sopenharmony_ci u16 sn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); 39658c2ecf20Sopenharmony_ci 39668c2ecf20Sopenharmony_ci /* 39678c2ecf20Sopenharmony_ci * In 22000 HW, the next_reclaimed index is only 8 bit, so we'll need 39688c2ecf20Sopenharmony_ci * to align the wrap around of ssn so we compare relevant values. 39698c2ecf20Sopenharmony_ci */ 39708c2ecf20Sopenharmony_ci if (mvm->trans->trans_cfg->gen2) 39718c2ecf20Sopenharmony_ci sn &= 0xff; 39728c2ecf20Sopenharmony_ci 39738c2ecf20Sopenharmony_ci return ieee80211_sn_sub(sn, tid_data->next_reclaimed); 39748c2ecf20Sopenharmony_ci} 39758c2ecf20Sopenharmony_ci 39768c2ecf20Sopenharmony_ciint iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 39778c2ecf20Sopenharmony_ci struct iwl_mvm_int_sta *sta, u8 *addr, u32 cipher, 39788c2ecf20Sopenharmony_ci u8 *key, u32 key_len) 39798c2ecf20Sopenharmony_ci{ 39808c2ecf20Sopenharmony_ci int ret; 39818c2ecf20Sopenharmony_ci u16 queue; 39828c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 39838c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf; 39848c2ecf20Sopenharmony_ci 39858c2ecf20Sopenharmony_ci ret = iwl_mvm_allocate_int_sta(mvm, sta, 0, 39868c2ecf20Sopenharmony_ci NL80211_IFTYPE_UNSPECIFIED, 39878c2ecf20Sopenharmony_ci IWL_STA_LINK); 39888c2ecf20Sopenharmony_ci if (ret) 39898c2ecf20Sopenharmony_ci return ret; 39908c2ecf20Sopenharmony_ci 39918c2ecf20Sopenharmony_ci ret = iwl_mvm_add_int_sta_with_queue(mvm, mvmvif->id, mvmvif->color, 39928c2ecf20Sopenharmony_ci addr, sta, &queue, 39938c2ecf20Sopenharmony_ci IWL_MVM_TX_FIFO_BE); 39948c2ecf20Sopenharmony_ci if (ret) 39958c2ecf20Sopenharmony_ci goto out; 39968c2ecf20Sopenharmony_ci 39978c2ecf20Sopenharmony_ci keyconf = kzalloc(sizeof(*keyconf) + key_len, GFP_KERNEL); 39988c2ecf20Sopenharmony_ci if (!keyconf) { 39998c2ecf20Sopenharmony_ci ret = -ENOBUFS; 40008c2ecf20Sopenharmony_ci goto out; 40018c2ecf20Sopenharmony_ci } 40028c2ecf20Sopenharmony_ci 40038c2ecf20Sopenharmony_ci keyconf->cipher = cipher; 40048c2ecf20Sopenharmony_ci memcpy(keyconf->key, key, key_len); 40058c2ecf20Sopenharmony_ci keyconf->keylen = key_len; 40068c2ecf20Sopenharmony_ci 40078c2ecf20Sopenharmony_ci ret = iwl_mvm_send_sta_key(mvm, sta->sta_id, keyconf, false, 40088c2ecf20Sopenharmony_ci 0, NULL, 0, 0, true); 40098c2ecf20Sopenharmony_ci kfree(keyconf); 40108c2ecf20Sopenharmony_ci return 0; 40118c2ecf20Sopenharmony_ciout: 40128c2ecf20Sopenharmony_ci iwl_mvm_dealloc_int_sta(mvm, sta); 40138c2ecf20Sopenharmony_ci return ret; 40148c2ecf20Sopenharmony_ci} 4015