162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/****************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. 562306a36Sopenharmony_ci * Copyright (C) 2019 Intel Corporation 662306a36Sopenharmony_ci * Copyright (C) 2023 Intel Corporation 762306a36Sopenharmony_ci *****************************************************************************/ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/sched.h> 1262306a36Sopenharmony_ci#include <linux/ieee80211.h> 1362306a36Sopenharmony_ci#include "iwl-io.h" 1462306a36Sopenharmony_ci#include "iwl-trans.h" 1562306a36Sopenharmony_ci#include "iwl-agn-hw.h" 1662306a36Sopenharmony_ci#include "dev.h" 1762306a36Sopenharmony_ci#include "agn.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic const u8 tid_to_ac[] = { 2062306a36Sopenharmony_ci IEEE80211_AC_BE, 2162306a36Sopenharmony_ci IEEE80211_AC_BK, 2262306a36Sopenharmony_ci IEEE80211_AC_BK, 2362306a36Sopenharmony_ci IEEE80211_AC_BE, 2462306a36Sopenharmony_ci IEEE80211_AC_VI, 2562306a36Sopenharmony_ci IEEE80211_AC_VI, 2662306a36Sopenharmony_ci IEEE80211_AC_VO, 2762306a36Sopenharmony_ci IEEE80211_AC_VO, 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic void iwlagn_tx_cmd_protection(struct iwl_priv *priv, 3162306a36Sopenharmony_ci struct ieee80211_tx_info *info, 3262306a36Sopenharmony_ci __le16 fc, __le32 *tx_flags) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS || 3562306a36Sopenharmony_ci info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT || 3662306a36Sopenharmony_ci info->flags & IEEE80211_TX_CTL_AMPDU) 3762306a36Sopenharmony_ci *tx_flags |= TX_CMD_FLG_PROT_REQUIRE_MSK; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* 4162306a36Sopenharmony_ci * handle build REPLY_TX command notification. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_cistatic void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, 4462306a36Sopenharmony_ci struct sk_buff *skb, 4562306a36Sopenharmony_ci struct iwl_tx_cmd *tx_cmd, 4662306a36Sopenharmony_ci struct ieee80211_tx_info *info, 4762306a36Sopenharmony_ci struct ieee80211_hdr *hdr, u8 sta_id) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci __le16 fc = hdr->frame_control; 5062306a36Sopenharmony_ci __le32 tx_flags = tx_cmd->tx_flags; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) 5562306a36Sopenharmony_ci tx_flags |= TX_CMD_FLG_ACK_MSK; 5662306a36Sopenharmony_ci else 5762306a36Sopenharmony_ci tx_flags &= ~TX_CMD_FLG_ACK_MSK; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (ieee80211_is_probe_resp(fc)) 6062306a36Sopenharmony_ci tx_flags |= TX_CMD_FLG_TSF_MSK; 6162306a36Sopenharmony_ci else if (ieee80211_is_back_req(fc)) 6262306a36Sopenharmony_ci tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; 6362306a36Sopenharmony_ci else if (info->band == NL80211_BAND_2GHZ && 6462306a36Sopenharmony_ci priv->lib->bt_params && 6562306a36Sopenharmony_ci priv->lib->bt_params->advanced_bt_coexist && 6662306a36Sopenharmony_ci (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) || 6762306a36Sopenharmony_ci ieee80211_is_reassoc_req(fc) || 6862306a36Sopenharmony_ci info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) 6962306a36Sopenharmony_ci tx_flags |= TX_CMD_FLG_IGNORE_BT; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci tx_cmd->sta_id = sta_id; 7362306a36Sopenharmony_ci if (ieee80211_has_morefrags(fc)) 7462306a36Sopenharmony_ci tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (ieee80211_is_data_qos(fc)) { 7762306a36Sopenharmony_ci u8 *qc = ieee80211_get_qos_ctl(hdr); 7862306a36Sopenharmony_ci tx_cmd->tid_tspec = qc[0] & 0xf; 7962306a36Sopenharmony_ci tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; 8062306a36Sopenharmony_ci } else { 8162306a36Sopenharmony_ci tx_cmd->tid_tspec = IWL_TID_NON_QOS; 8262306a36Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) 8362306a36Sopenharmony_ci tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; 8462306a36Sopenharmony_ci else 8562306a36Sopenharmony_ci tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci iwlagn_tx_cmd_protection(priv, info, fc, &tx_flags); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); 9162306a36Sopenharmony_ci if (ieee80211_is_mgmt(fc)) { 9262306a36Sopenharmony_ci if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) 9362306a36Sopenharmony_ci tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); 9462306a36Sopenharmony_ci else 9562306a36Sopenharmony_ci tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); 9662306a36Sopenharmony_ci } else { 9762306a36Sopenharmony_ci tx_cmd->timeout.pm_frame_timeout = 0; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci tx_cmd->driver_txop = 0; 10162306a36Sopenharmony_ci tx_cmd->tx_flags = tx_flags; 10262306a36Sopenharmony_ci tx_cmd->next_frame_len = 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, 10662306a36Sopenharmony_ci struct iwl_tx_cmd *tx_cmd, 10762306a36Sopenharmony_ci struct ieee80211_tx_info *info, 10862306a36Sopenharmony_ci struct ieee80211_sta *sta, 10962306a36Sopenharmony_ci __le16 fc) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci u32 rate_flags; 11262306a36Sopenharmony_ci int rate_idx; 11362306a36Sopenharmony_ci u8 rts_retry_limit; 11462306a36Sopenharmony_ci u8 data_retry_limit; 11562306a36Sopenharmony_ci u8 rate_plcp; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (priv->wowlan) { 11862306a36Sopenharmony_ci rts_retry_limit = IWLAGN_LOW_RETRY_LIMIT; 11962306a36Sopenharmony_ci data_retry_limit = IWLAGN_LOW_RETRY_LIMIT; 12062306a36Sopenharmony_ci } else { 12162306a36Sopenharmony_ci /* Set retry limit on RTS packets */ 12262306a36Sopenharmony_ci rts_retry_limit = IWLAGN_RTS_DFAULT_RETRY_LIMIT; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Set retry limit on DATA packets and Probe Responses*/ 12562306a36Sopenharmony_ci if (ieee80211_is_probe_resp(fc)) { 12662306a36Sopenharmony_ci data_retry_limit = IWLAGN_MGMT_DFAULT_RETRY_LIMIT; 12762306a36Sopenharmony_ci rts_retry_limit = 12862306a36Sopenharmony_ci min(data_retry_limit, rts_retry_limit); 12962306a36Sopenharmony_ci } else if (ieee80211_is_back_req(fc)) 13062306a36Sopenharmony_ci data_retry_limit = IWLAGN_BAR_DFAULT_RETRY_LIMIT; 13162306a36Sopenharmony_ci else 13262306a36Sopenharmony_ci data_retry_limit = IWLAGN_DEFAULT_TX_RETRY; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci tx_cmd->data_retry_limit = data_retry_limit; 13662306a36Sopenharmony_ci tx_cmd->rts_retry_limit = rts_retry_limit; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* DATA packets will use the uCode station table for rate/antenna 13962306a36Sopenharmony_ci * selection */ 14062306a36Sopenharmony_ci if (ieee80211_is_data(fc)) { 14162306a36Sopenharmony_ci tx_cmd->initial_rate_index = 0; 14262306a36Sopenharmony_ci tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; 14362306a36Sopenharmony_ci return; 14462306a36Sopenharmony_ci } else if (ieee80211_is_back_req(fc)) 14562306a36Sopenharmony_ci tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /** 14862306a36Sopenharmony_ci * If the current TX rate stored in mac80211 has the MCS bit set, it's 14962306a36Sopenharmony_ci * not really a TX rate. Thus, we use the lowest supported rate for 15062306a36Sopenharmony_ci * this band. Also use the lowest supported rate if the stored rate 15162306a36Sopenharmony_ci * index is invalid. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci rate_idx = info->control.rates[0].idx; 15462306a36Sopenharmony_ci if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS || 15562306a36Sopenharmony_ci (rate_idx < 0) || (rate_idx > IWL_RATE_COUNT_LEGACY)) 15662306a36Sopenharmony_ci rate_idx = rate_lowest_index( 15762306a36Sopenharmony_ci &priv->nvm_data->bands[info->band], sta); 15862306a36Sopenharmony_ci /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ 15962306a36Sopenharmony_ci if (info->band == NL80211_BAND_5GHZ) 16062306a36Sopenharmony_ci rate_idx += IWL_FIRST_OFDM_RATE; 16162306a36Sopenharmony_ci /* Get PLCP rate for tx_cmd->rate_n_flags */ 16262306a36Sopenharmony_ci rate_plcp = iwl_rates[rate_idx].plcp; 16362306a36Sopenharmony_ci /* Zero out flags for this packet */ 16462306a36Sopenharmony_ci rate_flags = 0; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* Set CCK flag as needed */ 16762306a36Sopenharmony_ci if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE)) 16862306a36Sopenharmony_ci rate_flags |= RATE_MCS_CCK_MSK; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* Set up antennas */ 17162306a36Sopenharmony_ci if (priv->lib->bt_params && 17262306a36Sopenharmony_ci priv->lib->bt_params->advanced_bt_coexist && 17362306a36Sopenharmony_ci priv->bt_full_concurrent) { 17462306a36Sopenharmony_ci /* operated as 1x1 in full concurrency mode */ 17562306a36Sopenharmony_ci priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, 17662306a36Sopenharmony_ci first_antenna(priv->nvm_data->valid_tx_ant)); 17762306a36Sopenharmony_ci } else 17862306a36Sopenharmony_ci priv->mgmt_tx_ant = iwl_toggle_tx_ant( 17962306a36Sopenharmony_ci priv, priv->mgmt_tx_ant, 18062306a36Sopenharmony_ci priv->nvm_data->valid_tx_ant); 18162306a36Sopenharmony_ci rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Set the rate in the TX cmd */ 18462306a36Sopenharmony_ci tx_cmd->rate_n_flags = iwl_hw_set_rate_n_flags(rate_plcp, rate_flags); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv, 18862306a36Sopenharmony_ci struct ieee80211_tx_info *info, 18962306a36Sopenharmony_ci struct iwl_tx_cmd *tx_cmd, 19062306a36Sopenharmony_ci struct sk_buff *skb_frag) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct ieee80211_key_conf *keyconf = info->control.hw_key; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci switch (keyconf->cipher) { 19562306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 19662306a36Sopenharmony_ci tx_cmd->sec_ctl = TX_CMD_SEC_CCM; 19762306a36Sopenharmony_ci memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); 19862306a36Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_AMPDU) 19962306a36Sopenharmony_ci tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK; 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 20362306a36Sopenharmony_ci tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; 20462306a36Sopenharmony_ci ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); 20562306a36Sopenharmony_ci break; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 20862306a36Sopenharmony_ci tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; 20962306a36Sopenharmony_ci fallthrough; 21062306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 21162306a36Sopenharmony_ci tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP | 21262306a36Sopenharmony_ci (keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption " 21762306a36Sopenharmony_ci "with key %d\n", keyconf->keyidx); 21862306a36Sopenharmony_ci break; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci default: 22162306a36Sopenharmony_ci IWL_ERR(priv, "Unknown encode cipher %x\n", keyconf->cipher); 22262306a36Sopenharmony_ci break; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/** 22762306a36Sopenharmony_ci * iwl_sta_id_or_broadcast - return sta_id or broadcast sta 22862306a36Sopenharmony_ci * @context: the current context 22962306a36Sopenharmony_ci * @sta: mac80211 station 23062306a36Sopenharmony_ci * 23162306a36Sopenharmony_ci * In certain circumstances mac80211 passes a station pointer 23262306a36Sopenharmony_ci * that may be %NULL, for example during TX or key setup. In 23362306a36Sopenharmony_ci * that case, we need to use the broadcast station, so this 23462306a36Sopenharmony_ci * inline wraps that pattern. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_cistatic int iwl_sta_id_or_broadcast(struct iwl_rxon_context *context, 23762306a36Sopenharmony_ci struct ieee80211_sta *sta) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci int sta_id; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!sta) 24262306a36Sopenharmony_ci return context->bcast_sta_id; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci sta_id = iwl_sta_id(sta); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* 24762306a36Sopenharmony_ci * mac80211 should not be passing a partially 24862306a36Sopenharmony_ci * initialised station! 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ci WARN_ON(sta_id == IWL_INVALID_STATION); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return sta_id; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/* 25662306a36Sopenharmony_ci * start REPLY_TX command process 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ciint iwlagn_tx_skb(struct iwl_priv *priv, 25962306a36Sopenharmony_ci struct ieee80211_sta *sta, 26062306a36Sopenharmony_ci struct sk_buff *skb) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 26362306a36Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 26462306a36Sopenharmony_ci struct iwl_station_priv *sta_priv = NULL; 26562306a36Sopenharmony_ci struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; 26662306a36Sopenharmony_ci struct iwl_device_tx_cmd *dev_cmd; 26762306a36Sopenharmony_ci struct iwl_tx_cmd *tx_cmd; 26862306a36Sopenharmony_ci __le16 fc; 26962306a36Sopenharmony_ci u8 hdr_len; 27062306a36Sopenharmony_ci u16 len, seq_number = 0; 27162306a36Sopenharmony_ci u8 sta_id, tid = IWL_MAX_TID_COUNT; 27262306a36Sopenharmony_ci bool is_agg = false, is_data_qos = false; 27362306a36Sopenharmony_ci int txq_id; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (info->control.vif) 27662306a36Sopenharmony_ci ctx = iwl_rxon_ctx_from_vif(info->control.vif); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (iwl_is_rfkill(priv)) { 27962306a36Sopenharmony_ci IWL_DEBUG_DROP(priv, "Dropping - RF KILL\n"); 28062306a36Sopenharmony_ci goto drop_unlock_priv; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci fc = hdr->frame_control; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 28662306a36Sopenharmony_ci if (ieee80211_is_auth(fc)) 28762306a36Sopenharmony_ci IWL_DEBUG_TX(priv, "Sending AUTH frame\n"); 28862306a36Sopenharmony_ci else if (ieee80211_is_assoc_req(fc)) 28962306a36Sopenharmony_ci IWL_DEBUG_TX(priv, "Sending ASSOC frame\n"); 29062306a36Sopenharmony_ci else if (ieee80211_is_reassoc_req(fc)) 29162306a36Sopenharmony_ci IWL_DEBUG_TX(priv, "Sending REASSOC frame\n"); 29262306a36Sopenharmony_ci#endif 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (unlikely(ieee80211_is_probe_resp(fc))) { 29562306a36Sopenharmony_ci struct iwl_wipan_noa_data *noa_data = 29662306a36Sopenharmony_ci rcu_dereference(priv->noa_data); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (noa_data && 29962306a36Sopenharmony_ci pskb_expand_head(skb, 0, noa_data->length, 30062306a36Sopenharmony_ci GFP_ATOMIC) == 0) { 30162306a36Sopenharmony_ci skb_put_data(skb, noa_data->data, noa_data->length); 30262306a36Sopenharmony_ci hdr = (struct ieee80211_hdr *)skb->data; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci hdr_len = ieee80211_hdrlen(fc); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* For management frames use broadcast id to do not break aggregation */ 30962306a36Sopenharmony_ci if (!ieee80211_is_data(fc)) 31062306a36Sopenharmony_ci sta_id = ctx->bcast_sta_id; 31162306a36Sopenharmony_ci else { 31262306a36Sopenharmony_ci /* Find index into station table for destination station */ 31362306a36Sopenharmony_ci sta_id = iwl_sta_id_or_broadcast(ctx, sta); 31462306a36Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) { 31562306a36Sopenharmony_ci IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", 31662306a36Sopenharmony_ci hdr->addr1); 31762306a36Sopenharmony_ci goto drop_unlock_priv; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (sta) 32262306a36Sopenharmony_ci sta_priv = (void *)sta->drv_priv; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (sta_priv && sta_priv->asleep && 32562306a36Sopenharmony_ci (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { 32662306a36Sopenharmony_ci /* 32762306a36Sopenharmony_ci * This sends an asynchronous command to the device, 32862306a36Sopenharmony_ci * but we can rely on it being processed before the 32962306a36Sopenharmony_ci * next frame is processed -- and the next frame to 33062306a36Sopenharmony_ci * this station is the one that will consume this 33162306a36Sopenharmony_ci * counter. 33262306a36Sopenharmony_ci * For now set the counter to just 1 since we do not 33362306a36Sopenharmony_ci * support uAPSD yet. 33462306a36Sopenharmony_ci * 33562306a36Sopenharmony_ci * FIXME: If we get two non-bufferable frames one 33662306a36Sopenharmony_ci * after the other, we might only send out one of 33762306a36Sopenharmony_ci * them because this is racy. 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ci iwl_sta_modify_sleep_tx_count(priv, sta_id, 1); 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci dev_cmd = iwl_trans_alloc_tx_cmd(priv->trans); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (unlikely(!dev_cmd)) 34562306a36Sopenharmony_ci goto drop_unlock_priv; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci dev_cmd->hdr.cmd = REPLY_TX; 34862306a36Sopenharmony_ci tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Total # bytes to be transmitted */ 35162306a36Sopenharmony_ci len = (u16)skb->len; 35262306a36Sopenharmony_ci tx_cmd->len = cpu_to_le16(len); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (info->control.hw_key) 35562306a36Sopenharmony_ci iwlagn_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* TODO need this for burst mode later on */ 35862306a36Sopenharmony_ci iwlagn_tx_cmd_build_basic(priv, skb, tx_cmd, info, hdr, sta_id); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, sta, fc); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci memset(&info->status, 0, sizeof(info->status)); 36362306a36Sopenharmony_ci memset(info->driver_data, 0, sizeof(info->driver_data)); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci info->driver_data[0] = ctx; 36662306a36Sopenharmony_ci info->driver_data[1] = dev_cmd; 36762306a36Sopenharmony_ci /* From now on, we cannot access info->control */ 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci spin_lock(&priv->sta_lock); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { 37262306a36Sopenharmony_ci u8 *qc = NULL; 37362306a36Sopenharmony_ci struct iwl_tid_data *tid_data; 37462306a36Sopenharmony_ci qc = ieee80211_get_qos_ctl(hdr); 37562306a36Sopenharmony_ci tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; 37662306a36Sopenharmony_ci if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) 37762306a36Sopenharmony_ci goto drop_unlock_sta; 37862306a36Sopenharmony_ci tid_data = &priv->tid_data[sta_id][tid]; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* aggregation is on for this <sta,tid> */ 38162306a36Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_AMPDU && 38262306a36Sopenharmony_ci tid_data->agg.state != IWL_AGG_ON) { 38362306a36Sopenharmony_ci IWL_ERR(priv, 38462306a36Sopenharmony_ci "TX_CTL_AMPDU while not in AGG: Tx flags = 0x%08x, agg.state = %d\n", 38562306a36Sopenharmony_ci info->flags, tid_data->agg.state); 38662306a36Sopenharmony_ci IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d\n", 38762306a36Sopenharmony_ci sta_id, tid, 38862306a36Sopenharmony_ci IEEE80211_SEQ_TO_SN(tid_data->seq_number)); 38962306a36Sopenharmony_ci goto drop_unlock_sta; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* We can receive packets from the stack in IWL_AGG_{ON,OFF} 39362306a36Sopenharmony_ci * only. Check this here. 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_ci if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON && 39662306a36Sopenharmony_ci tid_data->agg.state != IWL_AGG_OFF, 39762306a36Sopenharmony_ci "Tx while agg.state = %d\n", tid_data->agg.state)) 39862306a36Sopenharmony_ci goto drop_unlock_sta; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci seq_number = tid_data->seq_number; 40162306a36Sopenharmony_ci seq_number &= IEEE80211_SCTL_SEQ; 40262306a36Sopenharmony_ci hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); 40362306a36Sopenharmony_ci hdr->seq_ctrl |= cpu_to_le16(seq_number); 40462306a36Sopenharmony_ci seq_number += 0x10; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_AMPDU) 40762306a36Sopenharmony_ci is_agg = true; 40862306a36Sopenharmony_ci is_data_qos = true; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* Copy MAC header from skb into command buffer */ 41262306a36Sopenharmony_ci memcpy(tx_cmd->hdr, hdr, hdr_len); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci txq_id = info->hw_queue; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (is_agg) 41762306a36Sopenharmony_ci txq_id = priv->tid_data[sta_id][tid].agg.txq_id; 41862306a36Sopenharmony_ci else if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { 41962306a36Sopenharmony_ci /* 42062306a36Sopenharmony_ci * The microcode will clear the more data 42162306a36Sopenharmony_ci * bit in the last frame it transmits. 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_ci hdr->frame_control |= 42462306a36Sopenharmony_ci cpu_to_le16(IEEE80211_FCTL_MOREDATA); 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci WARN_ON_ONCE(is_agg && 42862306a36Sopenharmony_ci priv->queue_to_mac80211[txq_id] != info->hw_queue); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci IWL_DEBUG_TX(priv, "TX to [%d|%d] Q:%d - seq: 0x%x\n", sta_id, tid, 43162306a36Sopenharmony_ci txq_id, seq_number); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (iwl_trans_tx(priv->trans, skb, dev_cmd, txq_id)) 43462306a36Sopenharmony_ci goto drop_unlock_sta; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (is_data_qos && !ieee80211_has_morefrags(fc)) 43762306a36Sopenharmony_ci priv->tid_data[sta_id][tid].seq_number = seq_number; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci spin_unlock(&priv->sta_lock); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* 44262306a36Sopenharmony_ci * Avoid atomic ops if it isn't an associated client. 44362306a36Sopenharmony_ci * Also, if this is a packet for aggregation, don't 44462306a36Sopenharmony_ci * increase the counter because the ucode will stop 44562306a36Sopenharmony_ci * aggregation queues when their respective station 44662306a36Sopenharmony_ci * goes to sleep. 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_ci if (sta_priv && sta_priv->client && !is_agg) 44962306a36Sopenharmony_ci atomic_inc(&sta_priv->pending_frames); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cidrop_unlock_sta: 45462306a36Sopenharmony_ci if (dev_cmd) 45562306a36Sopenharmony_ci iwl_trans_free_tx_cmd(priv->trans, dev_cmd); 45662306a36Sopenharmony_ci spin_unlock(&priv->sta_lock); 45762306a36Sopenharmony_cidrop_unlock_priv: 45862306a36Sopenharmony_ci return -1; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int iwlagn_alloc_agg_txq(struct iwl_priv *priv, int mq) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci int q; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci for (q = IWLAGN_FIRST_AMPDU_QUEUE; 46662306a36Sopenharmony_ci q < priv->trans->trans_cfg->base_params->num_of_queues; q++) { 46762306a36Sopenharmony_ci if (!test_and_set_bit(q, priv->agg_q_alloc)) { 46862306a36Sopenharmony_ci priv->queue_to_mac80211[q] = mq; 46962306a36Sopenharmony_ci return q; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return -ENOSPC; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic void iwlagn_dealloc_agg_txq(struct iwl_priv *priv, int q) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci clear_bit(q, priv->agg_q_alloc); 47962306a36Sopenharmony_ci priv->queue_to_mac80211[q] = IWL_INVALID_MAC80211_QUEUE; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ciint iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, 48362306a36Sopenharmony_ci struct ieee80211_sta *sta, u16 tid) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct iwl_tid_data *tid_data; 48662306a36Sopenharmony_ci int sta_id, txq_id; 48762306a36Sopenharmony_ci enum iwl_agg_state agg_state; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci sta_id = iwl_sta_id(sta); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) { 49262306a36Sopenharmony_ci IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); 49362306a36Sopenharmony_ci return -ENXIO; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci tid_data = &priv->tid_data[sta_id][tid]; 49962306a36Sopenharmony_ci txq_id = tid_data->agg.txq_id; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci switch (tid_data->agg.state) { 50262306a36Sopenharmony_ci case IWL_EMPTYING_HW_QUEUE_ADDBA: 50362306a36Sopenharmony_ci /* 50462306a36Sopenharmony_ci * This can happen if the peer stops aggregation 50562306a36Sopenharmony_ci * again before we've had a chance to drain the 50662306a36Sopenharmony_ci * queue we selected previously, i.e. before the 50762306a36Sopenharmony_ci * session was really started completely. 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_ci IWL_DEBUG_HT(priv, "AGG stop before setup done\n"); 51062306a36Sopenharmony_ci goto turn_off; 51162306a36Sopenharmony_ci case IWL_AGG_STARTING: 51262306a36Sopenharmony_ci /* 51362306a36Sopenharmony_ci * This can happen when the session is stopped before 51462306a36Sopenharmony_ci * we receive ADDBA response 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_ci IWL_DEBUG_HT(priv, "AGG stop before AGG became operational\n"); 51762306a36Sopenharmony_ci goto turn_off; 51862306a36Sopenharmony_ci case IWL_AGG_ON: 51962306a36Sopenharmony_ci break; 52062306a36Sopenharmony_ci default: 52162306a36Sopenharmony_ci IWL_WARN(priv, 52262306a36Sopenharmony_ci "Stopping AGG while state not ON or starting for %d on %d (%d)\n", 52362306a36Sopenharmony_ci sta_id, tid, tid_data->agg.state); 52462306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* There are still packets for this RA / TID in the HW */ 53162306a36Sopenharmony_ci if (!test_bit(txq_id, priv->agg_q_alloc)) { 53262306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, 53362306a36Sopenharmony_ci "stopping AGG on STA/TID %d/%d but hwq %d not used\n", 53462306a36Sopenharmony_ci sta_id, tid, txq_id); 53562306a36Sopenharmony_ci } else if (tid_data->agg.ssn != tid_data->next_reclaimed) { 53662306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, 53762306a36Sopenharmony_ci "Can't proceed: ssn %d, next_recl = %d\n", 53862306a36Sopenharmony_ci tid_data->agg.ssn, 53962306a36Sopenharmony_ci tid_data->next_reclaimed); 54062306a36Sopenharmony_ci tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_DELBA; 54162306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n", 54662306a36Sopenharmony_ci tid_data->agg.ssn); 54762306a36Sopenharmony_citurn_off: 54862306a36Sopenharmony_ci agg_state = tid_data->agg.state; 54962306a36Sopenharmony_ci tid_data->agg.state = IWL_AGG_OFF; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (test_bit(txq_id, priv->agg_q_alloc)) { 55462306a36Sopenharmony_ci /* 55562306a36Sopenharmony_ci * If the transport didn't know that we wanted to start 55662306a36Sopenharmony_ci * agreggation, don't tell it that we want to stop them. 55762306a36Sopenharmony_ci * This can happen when we don't get the addBA response on 55862306a36Sopenharmony_ci * time, or we hadn't time to drain the AC queues. 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_ci if (agg_state == IWL_AGG_ON) 56162306a36Sopenharmony_ci iwl_trans_txq_disable(priv->trans, txq_id, true); 56262306a36Sopenharmony_ci else 56362306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n", 56462306a36Sopenharmony_ci agg_state); 56562306a36Sopenharmony_ci iwlagn_dealloc_agg_txq(priv, txq_id); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ciint iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, 57462306a36Sopenharmony_ci struct ieee80211_sta *sta, u16 tid, u16 *ssn) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); 57762306a36Sopenharmony_ci struct iwl_tid_data *tid_data; 57862306a36Sopenharmony_ci int sta_id, txq_id, ret; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci IWL_DEBUG_HT(priv, "TX AGG request on ra = %pM tid = %d\n", 58162306a36Sopenharmony_ci sta->addr, tid); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci sta_id = iwl_sta_id(sta); 58462306a36Sopenharmony_ci if (sta_id == IWL_INVALID_STATION) { 58562306a36Sopenharmony_ci IWL_ERR(priv, "Start AGG on invalid station\n"); 58662306a36Sopenharmony_ci return -ENXIO; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci if (unlikely(tid >= IWL_MAX_TID_COUNT)) 58962306a36Sopenharmony_ci return -EINVAL; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (priv->tid_data[sta_id][tid].agg.state != IWL_AGG_OFF) { 59262306a36Sopenharmony_ci IWL_ERR(priv, "Start AGG when state is not IWL_AGG_OFF !\n"); 59362306a36Sopenharmony_ci return -ENXIO; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci txq_id = iwlagn_alloc_agg_txq(priv, ctx->ac_to_queue[tid_to_ac[tid]]); 59762306a36Sopenharmony_ci if (txq_id < 0) { 59862306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, 59962306a36Sopenharmony_ci "No free aggregation queue for %pM/%d\n", 60062306a36Sopenharmony_ci sta->addr, tid); 60162306a36Sopenharmony_ci return txq_id; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); 60562306a36Sopenharmony_ci if (ret) 60662306a36Sopenharmony_ci return ret; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 60962306a36Sopenharmony_ci tid_data = &priv->tid_data[sta_id][tid]; 61062306a36Sopenharmony_ci tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); 61162306a36Sopenharmony_ci tid_data->agg.txq_id = txq_id; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci *ssn = tid_data->agg.ssn; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (*ssn == tid_data->next_reclaimed) { 61662306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n", 61762306a36Sopenharmony_ci tid_data->agg.ssn); 61862306a36Sopenharmony_ci tid_data->agg.state = IWL_AGG_STARTING; 61962306a36Sopenharmony_ci ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; 62062306a36Sopenharmony_ci } else { 62162306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, " 62262306a36Sopenharmony_ci "next_reclaimed = %d\n", 62362306a36Sopenharmony_ci tid_data->agg.ssn, 62462306a36Sopenharmony_ci tid_data->next_reclaimed); 62562306a36Sopenharmony_ci tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return ret; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ciint iwlagn_tx_agg_flush(struct iwl_priv *priv, struct ieee80211_vif *vif, 63362306a36Sopenharmony_ci struct ieee80211_sta *sta, u16 tid) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct iwl_tid_data *tid_data; 63662306a36Sopenharmony_ci enum iwl_agg_state agg_state; 63762306a36Sopenharmony_ci int sta_id, txq_id; 63862306a36Sopenharmony_ci sta_id = iwl_sta_id(sta); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* 64162306a36Sopenharmony_ci * First set the agg state to OFF to avoid calling 64262306a36Sopenharmony_ci * ieee80211_stop_tx_ba_cb in iwlagn_check_ratid_empty. 64362306a36Sopenharmony_ci */ 64462306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci tid_data = &priv->tid_data[sta_id][tid]; 64762306a36Sopenharmony_ci txq_id = tid_data->agg.txq_id; 64862306a36Sopenharmony_ci agg_state = tid_data->agg.state; 64962306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, "Flush AGG: sta %d tid %d q %d state %d\n", 65062306a36Sopenharmony_ci sta_id, tid, txq_id, tid_data->agg.state); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci tid_data->agg.state = IWL_AGG_OFF; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (iwlagn_txfifo_flush(priv, BIT(txq_id))) 65762306a36Sopenharmony_ci IWL_ERR(priv, "Couldn't flush the AGG queue\n"); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (test_bit(txq_id, priv->agg_q_alloc)) { 66062306a36Sopenharmony_ci /* 66162306a36Sopenharmony_ci * If the transport didn't know that we wanted to start 66262306a36Sopenharmony_ci * agreggation, don't tell it that we want to stop them. 66362306a36Sopenharmony_ci * This can happen when we don't get the addBA response on 66462306a36Sopenharmony_ci * time, or we hadn't time to drain the AC queues. 66562306a36Sopenharmony_ci */ 66662306a36Sopenharmony_ci if (agg_state == IWL_AGG_ON) 66762306a36Sopenharmony_ci iwl_trans_txq_disable(priv->trans, txq_id, true); 66862306a36Sopenharmony_ci else 66962306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n", 67062306a36Sopenharmony_ci agg_state); 67162306a36Sopenharmony_ci iwlagn_dealloc_agg_txq(priv, txq_id); 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return 0; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ciint iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, 67862306a36Sopenharmony_ci struct ieee80211_sta *sta, u16 tid, u8 buf_size) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; 68162306a36Sopenharmony_ci struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); 68262306a36Sopenharmony_ci int q, fifo; 68362306a36Sopenharmony_ci u16 ssn; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 68862306a36Sopenharmony_ci ssn = priv->tid_data[sta_priv->sta_id][tid].agg.ssn; 68962306a36Sopenharmony_ci q = priv->tid_data[sta_priv->sta_id][tid].agg.txq_id; 69062306a36Sopenharmony_ci priv->tid_data[sta_priv->sta_id][tid].agg.state = IWL_AGG_ON; 69162306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci fifo = ctx->ac_to_fifo[tid_to_ac[tid]]; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci iwl_trans_txq_enable(priv->trans, q, fifo, sta_priv->sta_id, tid, 69662306a36Sopenharmony_ci buf_size, ssn, 0); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* 69962306a36Sopenharmony_ci * If the limit is 0, then it wasn't initialised yet, 70062306a36Sopenharmony_ci * use the default. We can do that since we take the 70162306a36Sopenharmony_ci * minimum below, and we don't want to go above our 70262306a36Sopenharmony_ci * default due to hardware restrictions. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_ci if (sta_priv->max_agg_bufsize == 0) 70562306a36Sopenharmony_ci sta_priv->max_agg_bufsize = 70662306a36Sopenharmony_ci LINK_QUAL_AGG_FRAME_LIMIT_DEF; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* 70962306a36Sopenharmony_ci * Even though in theory the peer could have different 71062306a36Sopenharmony_ci * aggregation reorder buffer sizes for different sessions, 71162306a36Sopenharmony_ci * our ucode doesn't allow for that and has a global limit 71262306a36Sopenharmony_ci * for each station. Therefore, use the minimum of all the 71362306a36Sopenharmony_ci * aggregation sessions and our default value. 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_ci sta_priv->max_agg_bufsize = 71662306a36Sopenharmony_ci min(sta_priv->max_agg_bufsize, buf_size); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (priv->hw_params.use_rts_for_aggregation) { 71962306a36Sopenharmony_ci /* 72062306a36Sopenharmony_ci * switch to RTS/CTS if it is the prefer protection 72162306a36Sopenharmony_ci * method for HT traffic 72262306a36Sopenharmony_ci */ 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci sta_priv->lq_sta.lq.general_params.flags |= 72562306a36Sopenharmony_ci LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci priv->agg_tids_count++; 72862306a36Sopenharmony_ci IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", 72962306a36Sopenharmony_ci priv->agg_tids_count); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci sta_priv->lq_sta.lq.agg_params.agg_frame_cnt_limit = 73262306a36Sopenharmony_ci sta_priv->max_agg_bufsize; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci IWL_DEBUG_HT(priv, "Tx aggregation enabled on ra = %pM tid = %d\n", 73562306a36Sopenharmony_ci sta->addr, tid); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return iwl_send_lq_cmd(priv, ctx, 73862306a36Sopenharmony_ci &sta_priv->lq_sta.lq, CMD_ASYNC, false); 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci struct iwl_tid_data *tid_data = &priv->tid_data[sta_id][tid]; 74462306a36Sopenharmony_ci enum iwl_rxon_context_id ctx; 74562306a36Sopenharmony_ci struct ieee80211_vif *vif; 74662306a36Sopenharmony_ci u8 *addr; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci lockdep_assert_held(&priv->sta_lock); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci addr = priv->stations[sta_id].sta.sta.addr; 75162306a36Sopenharmony_ci ctx = priv->stations[sta_id].ctxid; 75262306a36Sopenharmony_ci vif = priv->contexts[ctx].vif; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci switch (priv->tid_data[sta_id][tid].agg.state) { 75562306a36Sopenharmony_ci case IWL_EMPTYING_HW_QUEUE_DELBA: 75662306a36Sopenharmony_ci /* There are no packets for this RA / TID in the HW any more */ 75762306a36Sopenharmony_ci if (tid_data->agg.ssn == tid_data->next_reclaimed) { 75862306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, 75962306a36Sopenharmony_ci "Can continue DELBA flow ssn = next_recl = %d\n", 76062306a36Sopenharmony_ci tid_data->next_reclaimed); 76162306a36Sopenharmony_ci iwl_trans_txq_disable(priv->trans, 76262306a36Sopenharmony_ci tid_data->agg.txq_id, true); 76362306a36Sopenharmony_ci iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id); 76462306a36Sopenharmony_ci tid_data->agg.state = IWL_AGG_OFF; 76562306a36Sopenharmony_ci ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci break; 76862306a36Sopenharmony_ci case IWL_EMPTYING_HW_QUEUE_ADDBA: 76962306a36Sopenharmony_ci /* There are no packets for this RA / TID in the HW any more */ 77062306a36Sopenharmony_ci if (tid_data->agg.ssn == tid_data->next_reclaimed) { 77162306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, 77262306a36Sopenharmony_ci "Can continue ADDBA flow ssn = next_recl = %d\n", 77362306a36Sopenharmony_ci tid_data->next_reclaimed); 77462306a36Sopenharmony_ci tid_data->agg.state = IWL_AGG_STARTING; 77562306a36Sopenharmony_ci ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci break; 77862306a36Sopenharmony_ci default: 77962306a36Sopenharmony_ci break; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic void iwlagn_non_agg_tx_status(struct iwl_priv *priv, 78462306a36Sopenharmony_ci struct iwl_rxon_context *ctx, 78562306a36Sopenharmony_ci const u8 *addr1) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct ieee80211_sta *sta; 78862306a36Sopenharmony_ci struct iwl_station_priv *sta_priv; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci rcu_read_lock(); 79162306a36Sopenharmony_ci sta = ieee80211_find_sta(ctx->vif, addr1); 79262306a36Sopenharmony_ci if (sta) { 79362306a36Sopenharmony_ci sta_priv = (void *)sta->drv_priv; 79462306a36Sopenharmony_ci /* avoid atomic ops if this isn't a client */ 79562306a36Sopenharmony_ci if (sta_priv->client && 79662306a36Sopenharmony_ci atomic_dec_return(&sta_priv->pending_frames) == 0) 79762306a36Sopenharmony_ci ieee80211_sta_block_awake(priv->hw, sta, false); 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci rcu_read_unlock(); 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci/* 80362306a36Sopenharmony_ci * translate ucode response to mac80211 tx status control values 80462306a36Sopenharmony_ci */ 80562306a36Sopenharmony_cistatic void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags, 80662306a36Sopenharmony_ci struct ieee80211_tx_info *info) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct ieee80211_tx_rate *r = &info->status.rates[0]; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci info->status.antenna = 81162306a36Sopenharmony_ci ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); 81262306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_HT_MSK) 81362306a36Sopenharmony_ci r->flags |= IEEE80211_TX_RC_MCS; 81462306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_GF_MSK) 81562306a36Sopenharmony_ci r->flags |= IEEE80211_TX_RC_GREEN_FIELD; 81662306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_HT40_MSK) 81762306a36Sopenharmony_ci r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; 81862306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_DUP_MSK) 81962306a36Sopenharmony_ci r->flags |= IEEE80211_TX_RC_DUP_DATA; 82062306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_SGI_MSK) 82162306a36Sopenharmony_ci r->flags |= IEEE80211_TX_RC_SHORT_GI; 82262306a36Sopenharmony_ci r->idx = iwlagn_hwrate_to_mac80211_idx(rate_n_flags, info->band); 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 82662306a36Sopenharmony_ciconst char *iwl_get_tx_fail_reason(u32 status) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x 82962306a36Sopenharmony_ci#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci switch (status & TX_STATUS_MSK) { 83262306a36Sopenharmony_ci case TX_STATUS_SUCCESS: 83362306a36Sopenharmony_ci return "SUCCESS"; 83462306a36Sopenharmony_ci TX_STATUS_POSTPONE(DELAY); 83562306a36Sopenharmony_ci TX_STATUS_POSTPONE(FEW_BYTES); 83662306a36Sopenharmony_ci TX_STATUS_POSTPONE(BT_PRIO); 83762306a36Sopenharmony_ci TX_STATUS_POSTPONE(QUIET_PERIOD); 83862306a36Sopenharmony_ci TX_STATUS_POSTPONE(CALC_TTAK); 83962306a36Sopenharmony_ci TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); 84062306a36Sopenharmony_ci TX_STATUS_FAIL(SHORT_LIMIT); 84162306a36Sopenharmony_ci TX_STATUS_FAIL(LONG_LIMIT); 84262306a36Sopenharmony_ci TX_STATUS_FAIL(FIFO_UNDERRUN); 84362306a36Sopenharmony_ci TX_STATUS_FAIL(DRAIN_FLOW); 84462306a36Sopenharmony_ci TX_STATUS_FAIL(RFKILL_FLUSH); 84562306a36Sopenharmony_ci TX_STATUS_FAIL(LIFE_EXPIRE); 84662306a36Sopenharmony_ci TX_STATUS_FAIL(DEST_PS); 84762306a36Sopenharmony_ci TX_STATUS_FAIL(HOST_ABORTED); 84862306a36Sopenharmony_ci TX_STATUS_FAIL(BT_RETRY); 84962306a36Sopenharmony_ci TX_STATUS_FAIL(STA_INVALID); 85062306a36Sopenharmony_ci TX_STATUS_FAIL(FRAG_DROPPED); 85162306a36Sopenharmony_ci TX_STATUS_FAIL(TID_DISABLE); 85262306a36Sopenharmony_ci TX_STATUS_FAIL(FIFO_FLUSHED); 85362306a36Sopenharmony_ci TX_STATUS_FAIL(INSUFFICIENT_CF_POLL); 85462306a36Sopenharmony_ci TX_STATUS_FAIL(PASSIVE_NO_RX); 85562306a36Sopenharmony_ci TX_STATUS_FAIL(NO_BEACON_ON_RADAR); 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci return "UNKNOWN"; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci#undef TX_STATUS_FAIL 86162306a36Sopenharmony_ci#undef TX_STATUS_POSTPONE 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci#endif /* CONFIG_IWLWIFI_DEBUG */ 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci status &= AGG_TX_STATUS_MSK; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci switch (status) { 87062306a36Sopenharmony_ci case AGG_TX_STATE_UNDERRUN_MSK: 87162306a36Sopenharmony_ci priv->reply_agg_tx_stats.underrun++; 87262306a36Sopenharmony_ci break; 87362306a36Sopenharmony_ci case AGG_TX_STATE_BT_PRIO_MSK: 87462306a36Sopenharmony_ci priv->reply_agg_tx_stats.bt_prio++; 87562306a36Sopenharmony_ci break; 87662306a36Sopenharmony_ci case AGG_TX_STATE_FEW_BYTES_MSK: 87762306a36Sopenharmony_ci priv->reply_agg_tx_stats.few_bytes++; 87862306a36Sopenharmony_ci break; 87962306a36Sopenharmony_ci case AGG_TX_STATE_ABORT_MSK: 88062306a36Sopenharmony_ci priv->reply_agg_tx_stats.abort++; 88162306a36Sopenharmony_ci break; 88262306a36Sopenharmony_ci case AGG_TX_STATE_LAST_SENT_TTL_MSK: 88362306a36Sopenharmony_ci priv->reply_agg_tx_stats.last_sent_ttl++; 88462306a36Sopenharmony_ci break; 88562306a36Sopenharmony_ci case AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK: 88662306a36Sopenharmony_ci priv->reply_agg_tx_stats.last_sent_try++; 88762306a36Sopenharmony_ci break; 88862306a36Sopenharmony_ci case AGG_TX_STATE_LAST_SENT_BT_KILL_MSK: 88962306a36Sopenharmony_ci priv->reply_agg_tx_stats.last_sent_bt_kill++; 89062306a36Sopenharmony_ci break; 89162306a36Sopenharmony_ci case AGG_TX_STATE_SCD_QUERY_MSK: 89262306a36Sopenharmony_ci priv->reply_agg_tx_stats.scd_query++; 89362306a36Sopenharmony_ci break; 89462306a36Sopenharmony_ci case AGG_TX_STATE_TEST_BAD_CRC32_MSK: 89562306a36Sopenharmony_ci priv->reply_agg_tx_stats.bad_crc32++; 89662306a36Sopenharmony_ci break; 89762306a36Sopenharmony_ci case AGG_TX_STATE_RESPONSE_MSK: 89862306a36Sopenharmony_ci priv->reply_agg_tx_stats.response++; 89962306a36Sopenharmony_ci break; 90062306a36Sopenharmony_ci case AGG_TX_STATE_DUMP_TX_MSK: 90162306a36Sopenharmony_ci priv->reply_agg_tx_stats.dump_tx++; 90262306a36Sopenharmony_ci break; 90362306a36Sopenharmony_ci case AGG_TX_STATE_DELAY_TX_MSK: 90462306a36Sopenharmony_ci priv->reply_agg_tx_stats.delay_tx++; 90562306a36Sopenharmony_ci break; 90662306a36Sopenharmony_ci default: 90762306a36Sopenharmony_ci priv->reply_agg_tx_stats.unknown++; 90862306a36Sopenharmony_ci break; 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci return le32_to_cpup((__le32 *)&tx_resp->status + 91562306a36Sopenharmony_ci tx_resp->frame_count) & IEEE80211_MAX_SN; 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic void iwl_rx_reply_tx_agg(struct iwl_priv *priv, 91962306a36Sopenharmony_ci struct iwlagn_tx_resp *tx_resp) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci struct agg_tx_status *frame_status = &tx_resp->status; 92262306a36Sopenharmony_ci int tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> 92362306a36Sopenharmony_ci IWLAGN_TX_RES_TID_POS; 92462306a36Sopenharmony_ci int sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> 92562306a36Sopenharmony_ci IWLAGN_TX_RES_RA_POS; 92662306a36Sopenharmony_ci struct iwl_ht_agg *agg = &priv->tid_data[sta_id][tid].agg; 92762306a36Sopenharmony_ci u32 status = le16_to_cpu(tx_resp->status.status); 92862306a36Sopenharmony_ci int i; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci WARN_ON(tid == IWL_TID_NON_QOS); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (agg->wait_for_ba) 93362306a36Sopenharmony_ci IWL_DEBUG_TX_REPLY(priv, 93462306a36Sopenharmony_ci "got tx response w/o block-ack\n"); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); 93762306a36Sopenharmony_ci agg->wait_for_ba = (tx_resp->frame_count > 1); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* 94062306a36Sopenharmony_ci * If the BT kill count is non-zero, we'll get this 94162306a36Sopenharmony_ci * notification again. 94262306a36Sopenharmony_ci */ 94362306a36Sopenharmony_ci if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 && 94462306a36Sopenharmony_ci priv->lib->bt_params && 94562306a36Sopenharmony_ci priv->lib->bt_params->advanced_bt_coexist) { 94662306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n"); 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci if (tx_resp->frame_count == 1) 95062306a36Sopenharmony_ci return; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci IWL_DEBUG_TX_REPLY(priv, "TXQ %d initial_rate 0x%x ssn %d frm_cnt %d\n", 95362306a36Sopenharmony_ci agg->txq_id, 95462306a36Sopenharmony_ci le32_to_cpu(tx_resp->rate_n_flags), 95562306a36Sopenharmony_ci iwlagn_get_scd_ssn(tx_resp), tx_resp->frame_count); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* Construct bit-map of pending frames within Tx window */ 95862306a36Sopenharmony_ci for (i = 0; i < tx_resp->frame_count; i++) { 95962306a36Sopenharmony_ci u16 fstatus = le16_to_cpu(frame_status[i].status); 96062306a36Sopenharmony_ci u8 retry_cnt = (fstatus & AGG_TX_TRY_MSK) >> AGG_TX_TRY_POS; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (status & AGG_TX_STATUS_MSK) 96362306a36Sopenharmony_ci iwlagn_count_agg_tx_err_status(priv, fstatus); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (status & (AGG_TX_STATE_FEW_BYTES_MSK | 96662306a36Sopenharmony_ci AGG_TX_STATE_ABORT_MSK)) 96762306a36Sopenharmony_ci continue; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (status & AGG_TX_STATUS_MSK || retry_cnt > 1) 97062306a36Sopenharmony_ci IWL_DEBUG_TX_REPLY(priv, 97162306a36Sopenharmony_ci "%d: status %s (0x%04x), try-count (0x%01x)\n", 97262306a36Sopenharmony_ci i, 97362306a36Sopenharmony_ci iwl_get_agg_tx_fail_reason(fstatus), 97462306a36Sopenharmony_ci fstatus & AGG_TX_STATUS_MSK, 97562306a36Sopenharmony_ci retry_cnt); 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci} 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 98062306a36Sopenharmony_ci#define AGG_TX_STATE_FAIL(x) case AGG_TX_STATE_ ## x: return #x 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ciconst char *iwl_get_agg_tx_fail_reason(u16 status) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci status &= AGG_TX_STATUS_MSK; 98562306a36Sopenharmony_ci switch (status) { 98662306a36Sopenharmony_ci case AGG_TX_STATE_TRANSMITTED: 98762306a36Sopenharmony_ci return "SUCCESS"; 98862306a36Sopenharmony_ci AGG_TX_STATE_FAIL(UNDERRUN_MSK); 98962306a36Sopenharmony_ci AGG_TX_STATE_FAIL(BT_PRIO_MSK); 99062306a36Sopenharmony_ci AGG_TX_STATE_FAIL(FEW_BYTES_MSK); 99162306a36Sopenharmony_ci AGG_TX_STATE_FAIL(ABORT_MSK); 99262306a36Sopenharmony_ci AGG_TX_STATE_FAIL(LAST_SENT_TTL_MSK); 99362306a36Sopenharmony_ci AGG_TX_STATE_FAIL(LAST_SENT_TRY_CNT_MSK); 99462306a36Sopenharmony_ci AGG_TX_STATE_FAIL(LAST_SENT_BT_KILL_MSK); 99562306a36Sopenharmony_ci AGG_TX_STATE_FAIL(SCD_QUERY_MSK); 99662306a36Sopenharmony_ci AGG_TX_STATE_FAIL(TEST_BAD_CRC32_MSK); 99762306a36Sopenharmony_ci AGG_TX_STATE_FAIL(RESPONSE_MSK); 99862306a36Sopenharmony_ci AGG_TX_STATE_FAIL(DUMP_TX_MSK); 99962306a36Sopenharmony_ci AGG_TX_STATE_FAIL(DELAY_TX_MSK); 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci return "UNKNOWN"; 100362306a36Sopenharmony_ci} 100462306a36Sopenharmony_ci#endif /* CONFIG_IWLWIFI_DEBUG */ 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci status &= TX_STATUS_MSK; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci switch (status) { 101162306a36Sopenharmony_ci case TX_STATUS_POSTPONE_DELAY: 101262306a36Sopenharmony_ci priv->reply_tx_stats.pp_delay++; 101362306a36Sopenharmony_ci break; 101462306a36Sopenharmony_ci case TX_STATUS_POSTPONE_FEW_BYTES: 101562306a36Sopenharmony_ci priv->reply_tx_stats.pp_few_bytes++; 101662306a36Sopenharmony_ci break; 101762306a36Sopenharmony_ci case TX_STATUS_POSTPONE_BT_PRIO: 101862306a36Sopenharmony_ci priv->reply_tx_stats.pp_bt_prio++; 101962306a36Sopenharmony_ci break; 102062306a36Sopenharmony_ci case TX_STATUS_POSTPONE_QUIET_PERIOD: 102162306a36Sopenharmony_ci priv->reply_tx_stats.pp_quiet_period++; 102262306a36Sopenharmony_ci break; 102362306a36Sopenharmony_ci case TX_STATUS_POSTPONE_CALC_TTAK: 102462306a36Sopenharmony_ci priv->reply_tx_stats.pp_calc_ttak++; 102562306a36Sopenharmony_ci break; 102662306a36Sopenharmony_ci case TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY: 102762306a36Sopenharmony_ci priv->reply_tx_stats.int_crossed_retry++; 102862306a36Sopenharmony_ci break; 102962306a36Sopenharmony_ci case TX_STATUS_FAIL_SHORT_LIMIT: 103062306a36Sopenharmony_ci priv->reply_tx_stats.short_limit++; 103162306a36Sopenharmony_ci break; 103262306a36Sopenharmony_ci case TX_STATUS_FAIL_LONG_LIMIT: 103362306a36Sopenharmony_ci priv->reply_tx_stats.long_limit++; 103462306a36Sopenharmony_ci break; 103562306a36Sopenharmony_ci case TX_STATUS_FAIL_FIFO_UNDERRUN: 103662306a36Sopenharmony_ci priv->reply_tx_stats.fifo_underrun++; 103762306a36Sopenharmony_ci break; 103862306a36Sopenharmony_ci case TX_STATUS_FAIL_DRAIN_FLOW: 103962306a36Sopenharmony_ci priv->reply_tx_stats.drain_flow++; 104062306a36Sopenharmony_ci break; 104162306a36Sopenharmony_ci case TX_STATUS_FAIL_RFKILL_FLUSH: 104262306a36Sopenharmony_ci priv->reply_tx_stats.rfkill_flush++; 104362306a36Sopenharmony_ci break; 104462306a36Sopenharmony_ci case TX_STATUS_FAIL_LIFE_EXPIRE: 104562306a36Sopenharmony_ci priv->reply_tx_stats.life_expire++; 104662306a36Sopenharmony_ci break; 104762306a36Sopenharmony_ci case TX_STATUS_FAIL_DEST_PS: 104862306a36Sopenharmony_ci priv->reply_tx_stats.dest_ps++; 104962306a36Sopenharmony_ci break; 105062306a36Sopenharmony_ci case TX_STATUS_FAIL_HOST_ABORTED: 105162306a36Sopenharmony_ci priv->reply_tx_stats.host_abort++; 105262306a36Sopenharmony_ci break; 105362306a36Sopenharmony_ci case TX_STATUS_FAIL_BT_RETRY: 105462306a36Sopenharmony_ci priv->reply_tx_stats.bt_retry++; 105562306a36Sopenharmony_ci break; 105662306a36Sopenharmony_ci case TX_STATUS_FAIL_STA_INVALID: 105762306a36Sopenharmony_ci priv->reply_tx_stats.sta_invalid++; 105862306a36Sopenharmony_ci break; 105962306a36Sopenharmony_ci case TX_STATUS_FAIL_FRAG_DROPPED: 106062306a36Sopenharmony_ci priv->reply_tx_stats.frag_drop++; 106162306a36Sopenharmony_ci break; 106262306a36Sopenharmony_ci case TX_STATUS_FAIL_TID_DISABLE: 106362306a36Sopenharmony_ci priv->reply_tx_stats.tid_disable++; 106462306a36Sopenharmony_ci break; 106562306a36Sopenharmony_ci case TX_STATUS_FAIL_FIFO_FLUSHED: 106662306a36Sopenharmony_ci priv->reply_tx_stats.fifo_flush++; 106762306a36Sopenharmony_ci break; 106862306a36Sopenharmony_ci case TX_STATUS_FAIL_INSUFFICIENT_CF_POLL: 106962306a36Sopenharmony_ci priv->reply_tx_stats.insuff_cf_poll++; 107062306a36Sopenharmony_ci break; 107162306a36Sopenharmony_ci case TX_STATUS_FAIL_PASSIVE_NO_RX: 107262306a36Sopenharmony_ci priv->reply_tx_stats.fail_hw_drop++; 107362306a36Sopenharmony_ci break; 107462306a36Sopenharmony_ci case TX_STATUS_FAIL_NO_BEACON_ON_RADAR: 107562306a36Sopenharmony_ci priv->reply_tx_stats.sta_color_mismatch++; 107662306a36Sopenharmony_ci break; 107762306a36Sopenharmony_ci default: 107862306a36Sopenharmony_ci priv->reply_tx_stats.unknown++; 107962306a36Sopenharmony_ci break; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic void iwlagn_set_tx_status(struct iwl_priv *priv, 108462306a36Sopenharmony_ci struct ieee80211_tx_info *info, 108562306a36Sopenharmony_ci struct iwlagn_tx_resp *tx_resp) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci u16 status = le16_to_cpu(tx_resp->status.status); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci info->flags &= ~IEEE80211_TX_CTL_AMPDU; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci info->status.rates[0].count = tx_resp->failure_frame + 1; 109262306a36Sopenharmony_ci info->flags |= iwl_tx_status_to_mac80211(status); 109362306a36Sopenharmony_ci iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags), 109462306a36Sopenharmony_ci info); 109562306a36Sopenharmony_ci if (!iwl_is_tx_success(status)) 109662306a36Sopenharmony_ci iwlagn_count_tx_err_status(priv, status); 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_cistatic void iwl_check_abort_status(struct iwl_priv *priv, 110062306a36Sopenharmony_ci u8 frame_count, u32 status) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) { 110362306a36Sopenharmony_ci IWL_ERR(priv, "Tx flush command to flush out all frames\n"); 110462306a36Sopenharmony_ci if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) 110562306a36Sopenharmony_ci queue_work(priv->workqueue, &priv->tx_flush); 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_civoid iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb) 111062306a36Sopenharmony_ci{ 111162306a36Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 111262306a36Sopenharmony_ci u16 sequence = le16_to_cpu(pkt->hdr.sequence); 111362306a36Sopenharmony_ci int txq_id = SEQ_TO_QUEUE(sequence); 111462306a36Sopenharmony_ci int cmd_index __maybe_unused = SEQ_TO_INDEX(sequence); 111562306a36Sopenharmony_ci struct iwlagn_tx_resp *tx_resp = (void *)pkt->data; 111662306a36Sopenharmony_ci struct ieee80211_hdr *hdr; 111762306a36Sopenharmony_ci u32 status = le16_to_cpu(tx_resp->status.status); 111862306a36Sopenharmony_ci u16 ssn = iwlagn_get_scd_ssn(tx_resp); 111962306a36Sopenharmony_ci int tid; 112062306a36Sopenharmony_ci int sta_id; 112162306a36Sopenharmony_ci int freed; 112262306a36Sopenharmony_ci struct ieee80211_tx_info *info; 112362306a36Sopenharmony_ci struct sk_buff_head skbs; 112462306a36Sopenharmony_ci struct sk_buff *skb; 112562306a36Sopenharmony_ci struct iwl_rxon_context *ctx; 112662306a36Sopenharmony_ci bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> 112962306a36Sopenharmony_ci IWLAGN_TX_RES_TID_POS; 113062306a36Sopenharmony_ci sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> 113162306a36Sopenharmony_ci IWLAGN_TX_RES_RA_POS; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci if (is_agg) { 113662306a36Sopenharmony_ci WARN_ON_ONCE(sta_id >= IWLAGN_STATION_COUNT || 113762306a36Sopenharmony_ci tid >= IWL_MAX_TID_COUNT); 113862306a36Sopenharmony_ci if (txq_id != priv->tid_data[sta_id][tid].agg.txq_id) 113962306a36Sopenharmony_ci IWL_ERR(priv, "txq_id mismatch: %d %d\n", txq_id, 114062306a36Sopenharmony_ci priv->tid_data[sta_id][tid].agg.txq_id); 114162306a36Sopenharmony_ci iwl_rx_reply_tx_agg(priv, tx_resp); 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci __skb_queue_head_init(&skbs); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (tx_resp->frame_count == 1) { 114762306a36Sopenharmony_ci u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl); 114862306a36Sopenharmony_ci next_reclaimed = IEEE80211_SEQ_TO_SN(next_reclaimed + 0x10); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (is_agg) { 115162306a36Sopenharmony_ci /* If this is an aggregation queue, we can rely on the 115262306a36Sopenharmony_ci * ssn since the wifi sequence number corresponds to 115362306a36Sopenharmony_ci * the index in the TFD ring (%256). 115462306a36Sopenharmony_ci * The seq_ctl is the sequence control of the packet 115562306a36Sopenharmony_ci * to which this Tx response relates. But if there is a 115662306a36Sopenharmony_ci * hole in the bitmap of the BA we received, this Tx 115762306a36Sopenharmony_ci * response may allow to reclaim the hole and all the 115862306a36Sopenharmony_ci * subsequent packets that were already acked. 115962306a36Sopenharmony_ci * In that case, seq_ctl != ssn, and the next packet 116062306a36Sopenharmony_ci * to be reclaimed will be ssn and not seq_ctl. 116162306a36Sopenharmony_ci */ 116262306a36Sopenharmony_ci next_reclaimed = ssn; 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci if (tid != IWL_TID_NON_QOS) { 116662306a36Sopenharmony_ci priv->tid_data[sta_id][tid].next_reclaimed = 116762306a36Sopenharmony_ci next_reclaimed; 116862306a36Sopenharmony_ci IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", 116962306a36Sopenharmony_ci next_reclaimed); 117062306a36Sopenharmony_ci iwlagn_check_ratid_empty(priv, sta_id, tid); 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs, false); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci freed = 0; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci /* process frames */ 117862306a36Sopenharmony_ci skb_queue_walk(&skbs, skb) { 117962306a36Sopenharmony_ci hdr = (struct ieee80211_hdr *)skb->data; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci if (!ieee80211_is_data_qos(hdr->frame_control)) 118262306a36Sopenharmony_ci priv->last_seq_ctl = tx_resp->seq_ctl; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 118562306a36Sopenharmony_ci ctx = info->driver_data[0]; 118662306a36Sopenharmony_ci iwl_trans_free_tx_cmd(priv->trans, 118762306a36Sopenharmony_ci info->driver_data[1]); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci memset(&info->status, 0, sizeof(info->status)); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci if (status == TX_STATUS_FAIL_PASSIVE_NO_RX && 119262306a36Sopenharmony_ci ctx->vif && 119362306a36Sopenharmony_ci ctx->vif->type == NL80211_IFTYPE_STATION) { 119462306a36Sopenharmony_ci /* block and stop all queues */ 119562306a36Sopenharmony_ci priv->passive_no_rx = true; 119662306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, 119762306a36Sopenharmony_ci "stop all queues: passive channel\n"); 119862306a36Sopenharmony_ci ieee80211_stop_queues(priv->hw); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci IWL_DEBUG_TX_REPLY(priv, 120162306a36Sopenharmony_ci "TXQ %d status %s (0x%08x) " 120262306a36Sopenharmony_ci "rate_n_flags 0x%x retries %d\n", 120362306a36Sopenharmony_ci txq_id, 120462306a36Sopenharmony_ci iwl_get_tx_fail_reason(status), 120562306a36Sopenharmony_ci status, 120662306a36Sopenharmony_ci le32_to_cpu(tx_resp->rate_n_flags), 120762306a36Sopenharmony_ci tx_resp->failure_frame); 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci IWL_DEBUG_TX_REPLY(priv, 121062306a36Sopenharmony_ci "FrameCnt = %d, idx=%d\n", 121162306a36Sopenharmony_ci tx_resp->frame_count, cmd_index); 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci /* check if BAR is needed */ 121562306a36Sopenharmony_ci if (is_agg && !iwl_is_tx_success(status)) 121662306a36Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; 121762306a36Sopenharmony_ci iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb), 121862306a36Sopenharmony_ci tx_resp); 121962306a36Sopenharmony_ci if (!is_agg) 122062306a36Sopenharmony_ci iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci freed++; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (tid != IWL_TID_NON_QOS) { 122662306a36Sopenharmony_ci priv->tid_data[sta_id][tid].next_reclaimed = 122762306a36Sopenharmony_ci next_reclaimed; 122862306a36Sopenharmony_ci IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", 122962306a36Sopenharmony_ci next_reclaimed); 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci if (!is_agg && freed != 1) 123362306a36Sopenharmony_ci IWL_ERR(priv, "Q: %d, freed %d\n", txq_id, freed); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x)\n", txq_id, 123662306a36Sopenharmony_ci iwl_get_tx_fail_reason(status), status); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci IWL_DEBUG_TX_REPLY(priv, 123962306a36Sopenharmony_ci "\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d seq_ctl=0x%x\n", 124062306a36Sopenharmony_ci le32_to_cpu(tx_resp->rate_n_flags), 124162306a36Sopenharmony_ci tx_resp->failure_frame, 124262306a36Sopenharmony_ci SEQ_TO_INDEX(sequence), ssn, 124362306a36Sopenharmony_ci le16_to_cpu(tx_resp->seq_ctl)); 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci iwl_check_abort_status(priv, tx_resp->frame_count, status); 124762306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci while (!skb_queue_empty(&skbs)) { 125062306a36Sopenharmony_ci skb = __skb_dequeue(&skbs); 125162306a36Sopenharmony_ci ieee80211_tx_status(priv->hw, skb); 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci} 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci/* 125662306a36Sopenharmony_ci * iwlagn_rx_reply_compressed_ba - Handler for REPLY_COMPRESSED_BA 125762306a36Sopenharmony_ci * 125862306a36Sopenharmony_ci * Handles block-acknowledge notification from device, which reports success 125962306a36Sopenharmony_ci * of frames sent via aggregation. 126062306a36Sopenharmony_ci */ 126162306a36Sopenharmony_civoid iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, 126262306a36Sopenharmony_ci struct iwl_rx_cmd_buffer *rxb) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 126562306a36Sopenharmony_ci struct iwl_compressed_ba_resp *ba_resp = (void *)pkt->data; 126662306a36Sopenharmony_ci struct iwl_ht_agg *agg; 126762306a36Sopenharmony_ci struct sk_buff_head reclaimed_skbs; 126862306a36Sopenharmony_ci struct sk_buff *skb; 126962306a36Sopenharmony_ci int sta_id; 127062306a36Sopenharmony_ci int tid; 127162306a36Sopenharmony_ci int freed; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci /* "flow" corresponds to Tx queue */ 127462306a36Sopenharmony_ci u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci /* "ssn" is start of block-ack Tx window, corresponds to index 127762306a36Sopenharmony_ci * (in Tx queue's circular buffer) of first TFD/frame in window */ 127862306a36Sopenharmony_ci u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci if (scd_flow >= priv->trans->trans_cfg->base_params->num_of_queues) { 128162306a36Sopenharmony_ci IWL_ERR(priv, 128262306a36Sopenharmony_ci "BUG_ON scd_flow is bigger than number of queues\n"); 128362306a36Sopenharmony_ci return; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci sta_id = ba_resp->sta_id; 128762306a36Sopenharmony_ci tid = ba_resp->tid; 128862306a36Sopenharmony_ci agg = &priv->tid_data[sta_id][tid].agg; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci spin_lock_bh(&priv->sta_lock); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci if (unlikely(!agg->wait_for_ba)) { 129362306a36Sopenharmony_ci if (unlikely(ba_resp->bitmap)) 129462306a36Sopenharmony_ci IWL_ERR(priv, "Received BA when not expected\n"); 129562306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 129662306a36Sopenharmony_ci return; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci if (unlikely(scd_flow != agg->txq_id)) { 130062306a36Sopenharmony_ci /* 130162306a36Sopenharmony_ci * FIXME: this is a uCode bug which need to be addressed, 130262306a36Sopenharmony_ci * log the information and return for now. 130362306a36Sopenharmony_ci * Since it is can possibly happen very often and in order 130462306a36Sopenharmony_ci * not to fill the syslog, don't use IWL_ERR or IWL_WARN 130562306a36Sopenharmony_ci */ 130662306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(priv, 130762306a36Sopenharmony_ci "Bad queue mapping txq_id=%d, agg_txq[sta:%d,tid:%d]=%d\n", 130862306a36Sopenharmony_ci scd_flow, sta_id, tid, agg->txq_id); 130962306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 131062306a36Sopenharmony_ci return; 131162306a36Sopenharmony_ci } 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci __skb_queue_head_init(&reclaimed_skbs); 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci /* Release all TFDs before the SSN, i.e. all TFDs in front of 131662306a36Sopenharmony_ci * block-ack window (we assume that they've been successfully 131762306a36Sopenharmony_ci * transmitted ... if not, it's too late anyway). */ 131862306a36Sopenharmony_ci iwl_trans_reclaim(priv->trans, scd_flow, ba_resp_scd_ssn, 131962306a36Sopenharmony_ci &reclaimed_skbs, false); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, " 132262306a36Sopenharmony_ci "sta_id = %d\n", 132362306a36Sopenharmony_ci agg->wait_for_ba, 132462306a36Sopenharmony_ci (u8 *) &ba_resp->sta_addr_lo32, 132562306a36Sopenharmony_ci ba_resp->sta_id); 132662306a36Sopenharmony_ci IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx, " 132762306a36Sopenharmony_ci "scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n", 132862306a36Sopenharmony_ci ba_resp->tid, le16_to_cpu(ba_resp->seq_ctl), 132962306a36Sopenharmony_ci (unsigned long long)le64_to_cpu(ba_resp->bitmap), 133062306a36Sopenharmony_ci scd_flow, ba_resp_scd_ssn, ba_resp->txed, 133162306a36Sopenharmony_ci ba_resp->txed_2_done); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci /* Mark that the expected block-ack response arrived */ 133462306a36Sopenharmony_ci agg->wait_for_ba = false; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci /* Sanity check values reported by uCode */ 133762306a36Sopenharmony_ci if (ba_resp->txed_2_done > ba_resp->txed) { 133862306a36Sopenharmony_ci IWL_DEBUG_TX_REPLY(priv, 133962306a36Sopenharmony_ci "bogus sent(%d) and ack(%d) count\n", 134062306a36Sopenharmony_ci ba_resp->txed, ba_resp->txed_2_done); 134162306a36Sopenharmony_ci /* 134262306a36Sopenharmony_ci * set txed_2_done = txed, 134362306a36Sopenharmony_ci * so it won't impact rate scale 134462306a36Sopenharmony_ci */ 134562306a36Sopenharmony_ci ba_resp->txed = ba_resp->txed_2_done; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci priv->tid_data[sta_id][tid].next_reclaimed = ba_resp_scd_ssn; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci iwlagn_check_ratid_empty(priv, sta_id, tid); 135162306a36Sopenharmony_ci freed = 0; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci skb_queue_walk(&reclaimed_skbs, skb) { 135462306a36Sopenharmony_ci struct ieee80211_hdr *hdr = (void *)skb->data; 135562306a36Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci if (ieee80211_is_data_qos(hdr->frame_control)) 135862306a36Sopenharmony_ci freed++; 135962306a36Sopenharmony_ci else 136062306a36Sopenharmony_ci WARN_ON_ONCE(1); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci memset(&info->status, 0, sizeof(info->status)); 136562306a36Sopenharmony_ci /* Packet was transmitted successfully, failures come as single 136662306a36Sopenharmony_ci * frames because before failing a frame the firmware transmits 136762306a36Sopenharmony_ci * it without aggregation at least once. 136862306a36Sopenharmony_ci */ 136962306a36Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci if (freed == 1) { 137262306a36Sopenharmony_ci /* this is the first skb we deliver in this batch */ 137362306a36Sopenharmony_ci /* put the rate scaling data there */ 137462306a36Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 137562306a36Sopenharmony_ci memset(&info->status, 0, sizeof(info->status)); 137662306a36Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_AMPDU; 137762306a36Sopenharmony_ci info->status.ampdu_ack_len = ba_resp->txed_2_done; 137862306a36Sopenharmony_ci info->status.ampdu_len = ba_resp->txed; 137962306a36Sopenharmony_ci iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, 138062306a36Sopenharmony_ci info); 138162306a36Sopenharmony_ci } 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci spin_unlock_bh(&priv->sta_lock); 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci while (!skb_queue_empty(&reclaimed_skbs)) { 138762306a36Sopenharmony_ci skb = __skb_dequeue(&reclaimed_skbs); 138862306a36Sopenharmony_ci ieee80211_tx_status(priv->hw, skb); 138962306a36Sopenharmony_ci } 139062306a36Sopenharmony_ci} 1391