summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath10k/htt_rx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ath10k/htt_rx.c')
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_rx.c554
1 files changed, 496 insertions, 58 deletions
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index f42bac204ef8..83a7fb68fd24 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -1,19 +1,8 @@
+// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "core.h"
@@ -265,7 +254,7 @@ int ath10k_htt_rx_ring_refill(struct ath10k *ar)
struct ath10k_htt *htt = &ar->htt;
int ret;
- if (ar->dev_type == ATH10K_DEV_TYPE_HL)
+ if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL)
return 0;
spin_lock_bh(&htt->rx_ring.lock);
@@ -282,7 +271,7 @@ int ath10k_htt_rx_ring_refill(struct ath10k *ar)
void ath10k_htt_rx_free(struct ath10k_htt *htt)
{
- if (htt->ar->dev_type == ATH10K_DEV_TYPE_HL)
+ if (htt->ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL)
return;
del_timer_sync(&htt->rx_ring.refill_retry_timer);
@@ -760,7 +749,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
size_t size;
struct timer_list *timer = &htt->rx_ring.refill_retry_timer;
- if (ar->dev_type == ATH10K_DEV_TYPE_HL)
+ if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL)
return 0;
htt->rx_confused = false;
@@ -1905,7 +1894,7 @@ static void ath10k_htt_rx_h_enqueue(struct ath10k *ar,
}
static int ath10k_unchain_msdu(struct sk_buff_head *amsdu,
- unsigned long int *unchain_cnt)
+ unsigned long *unchain_cnt)
{
struct sk_buff *skb, *first;
int space;
@@ -1954,8 +1943,8 @@ static int ath10k_unchain_msdu(struct sk_buff_head *amsdu,
static void ath10k_htt_rx_h_unchain(struct ath10k *ar,
struct sk_buff_head *amsdu,
- unsigned long int *drop_cnt,
- unsigned long int *unchain_cnt)
+ unsigned long *drop_cnt,
+ unsigned long *unchain_cnt)
{
struct sk_buff *first;
struct htt_rx_desc *rxd;
@@ -2005,7 +1994,7 @@ static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
static void ath10k_htt_rx_h_filter(struct ath10k *ar,
struct sk_buff_head *amsdu,
struct ieee80211_rx_status *rx_status,
- unsigned long int *drop_cnt)
+ unsigned long *drop_cnt)
{
if (skb_queue_empty(amsdu))
return;
@@ -2025,10 +2014,10 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
struct ieee80211_rx_status *rx_status = &htt->rx_status;
struct sk_buff_head amsdu;
int ret;
- unsigned long int drop_cnt = 0;
- unsigned long int unchain_cnt = 0;
- unsigned long int drop_cnt_filter = 0;
- unsigned long int msdus_to_queue, num_msdus;
+ unsigned long drop_cnt = 0;
+ unsigned long unchain_cnt = 0;
+ unsigned long drop_cnt_filter = 0;
+ unsigned long msdus_to_queue, num_msdus;
enum ath10k_pkt_rx_err err = ATH10K_PKT_RX_ERR_MAX;
u8 first_hdr[RX_HTT_HDR_STATUS_LEN];
@@ -2072,9 +2061,91 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
return 0;
}
+static void ath10k_htt_rx_mpdu_desc_pn_hl(struct htt_hl_rx_desc *rx_desc,
+ union htt_rx_pn_t *pn,
+ int pn_len_bits)
+{
+ switch (pn_len_bits) {
+ case 48:
+ pn->pn48 = __le32_to_cpu(rx_desc->pn_31_0) +
+ ((u64)(__le32_to_cpu(rx_desc->u0.pn_63_32) & 0xFFFF) << 32);
+ break;
+ case 24:
+ pn->pn24 = __le32_to_cpu(rx_desc->pn_31_0);
+ break;
+ };
+}
+
+static bool ath10k_htt_rx_pn_cmp48(union htt_rx_pn_t *new_pn,
+ union htt_rx_pn_t *old_pn)
+{
+ return ((new_pn->pn48 & 0xffffffffffffULL) <=
+ (old_pn->pn48 & 0xffffffffffffULL));
+}
+
+static bool ath10k_htt_rx_pn_check_replay_hl(struct ath10k *ar,
+ struct ath10k_peer *peer,
+ struct htt_rx_indication_hl *rx)
+{
+ bool last_pn_valid, pn_invalid = false;
+ enum htt_txrx_sec_cast_type sec_index;
+ enum htt_security_types sec_type;
+ union htt_rx_pn_t new_pn = {0};
+ struct htt_hl_rx_desc *rx_desc;
+ union htt_rx_pn_t *last_pn;
+ u32 rx_desc_info, tid;
+ int num_mpdu_ranges;
+
+ lockdep_assert_held(&ar->data_lock);
+
+ if (!peer)
+ return false;
+
+ if (!(rx->fw_desc.flags & FW_RX_DESC_FLAGS_FIRST_MSDU))
+ return false;
+
+ num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1),
+ HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
+
+ rx_desc = (struct htt_hl_rx_desc *)&rx->mpdu_ranges[num_mpdu_ranges];
+ rx_desc_info = __le32_to_cpu(rx_desc->info);
+
+ if (!MS(rx_desc_info, HTT_RX_DESC_HL_INFO_ENCRYPTED))
+ return false;
+
+ tid = MS(rx->hdr.info0, HTT_RX_INDICATION_INFO0_EXT_TID);
+ last_pn_valid = peer->tids_last_pn_valid[tid];
+ last_pn = &peer->tids_last_pn[tid];
+
+ if (MS(rx_desc_info, HTT_RX_DESC_HL_INFO_MCAST_BCAST))
+ sec_index = HTT_TXRX_SEC_MCAST;
+ else
+ sec_index = HTT_TXRX_SEC_UCAST;
+
+ sec_type = peer->rx_pn[sec_index].sec_type;
+ ath10k_htt_rx_mpdu_desc_pn_hl(rx_desc, &new_pn, peer->rx_pn[sec_index].pn_len);
+
+ if (sec_type != HTT_SECURITY_AES_CCMP &&
+ sec_type != HTT_SECURITY_TKIP &&
+ sec_type != HTT_SECURITY_TKIP_NOMIC)
+ return false;
+
+ if (last_pn_valid)
+ pn_invalid = ath10k_htt_rx_pn_cmp48(&new_pn, last_pn);
+ else
+ peer->tids_last_pn_valid[tid] = 1;
+
+ if (!pn_invalid)
+ last_pn->pn48 = new_pn.pn48;
+
+ return pn_invalid;
+}
+
static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
struct htt_rx_indication_hl *rx,
- struct sk_buff *skb)
+ struct sk_buff *skb,
+ enum htt_rx_pn_check_type check_pn_type,
+ enum htt_rx_tkip_demic_type tkip_mic_type)
{
struct ath10k *ar = htt->ar;
struct ath10k_peer *peer;
@@ -2087,13 +2158,14 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
int num_mpdu_ranges;
size_t tot_hdr_len;
struct ieee80211_channel *ch;
+ bool pn_invalid;
peer_id = __le16_to_cpu(rx->hdr.peer_id);
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find_by_id(ar, peer_id);
spin_unlock_bh(&ar->data_lock);
- if (!peer)
+ if (!peer && peer_id != HTT_INVALID_PEERID)
ath10k_warn(ar, "Got RX ind from invalid peer: %u\n", peer_id);
num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1),
@@ -2112,12 +2184,22 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
num_mpdu_ranges);
if (mpdu_ranges->mpdu_range_status !=
- HTT_RX_IND_MPDU_STATUS_OK) {
+ HTT_RX_IND_MPDU_STATUS_OK &&
+ mpdu_ranges->mpdu_range_status !=
+ HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR) {
ath10k_warn(ar, "MPDU range status: %d\n",
mpdu_ranges->mpdu_range_status);
goto err;
}
+ if (check_pn_type == HTT_RX_PN_CHECK) {
+ spin_lock_bh(&ar->data_lock);
+ pn_invalid = ath10k_htt_rx_pn_check_replay_hl(ar, peer, rx);
+ spin_unlock_bh(&ar->data_lock);
+ if (pn_invalid)
+ goto err;
+ }
+
/* Strip off all headers before the MAC header before delivery to
* mac80211
*/
@@ -2125,14 +2207,21 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
sizeof(rx->ppdu) + sizeof(rx->prefix) +
sizeof(rx->fw_desc) +
sizeof(*mpdu_ranges) * num_mpdu_ranges + rx_desc_len;
+
skb_pull(skb, tot_hdr_len);
hdr = (struct ieee80211_hdr *)skb->data;
rx_status = IEEE80211_SKB_RXCB(skb);
rx_status->chains |= BIT(0);
- rx_status->signal = ATH10K_DEFAULT_NOISE_FLOOR +
- rx->ppdu.combined_rssi;
- rx_status->flag &= ~RX_FLAG_NO_SIGNAL_VAL;
+ if (rx->ppdu.combined_rssi == 0) {
+ /* SDIO firmware does not provide signal */
+ rx_status->signal = 0;
+ rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
+ } else {
+ rx_status->signal = ATH10K_DEFAULT_NOISE_FLOOR +
+ rx->ppdu.combined_rssi;
+ rx_status->flag &= ~RX_FLAG_NO_SIGNAL_VAL;
+ }
spin_lock_bh(&ar->data_lock);
ch = ar->scan_channel;
@@ -2167,6 +2256,13 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
RX_FLAG_MMIC_STRIPPED;
}
+ if (tkip_mic_type == HTT_RX_TKIP_MIC)
+ rx_status->flag &= ~RX_FLAG_IV_STRIPPED &
+ ~RX_FLAG_MMIC_STRIPPED;
+
+ if (mpdu_ranges->mpdu_range_status == HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR)
+ rx_status->flag |= RX_FLAG_MMIC_ERROR;
+
ieee80211_rx_ni(ar->hw, skb);
/* We have delivered the skb to the upper layers (mac80211) so we
@@ -2180,6 +2276,231 @@ err:
return true;
}
+static int ath10k_htt_rx_frag_tkip_decap_nomic(struct sk_buff *skb,
+ u16 head_len,
+ u16 hdr_len)
+{
+ u8 *ivp, *orig_hdr;
+
+ orig_hdr = skb->data;
+ ivp = orig_hdr + hdr_len + head_len;
+
+ /* the ExtIV bit is always set to 1 for TKIP */
+ if (!(ivp[IEEE80211_WEP_IV_LEN - 1] & ATH10K_IEEE80211_EXTIV))
+ return -EINVAL;
+
+ memmove(orig_hdr + IEEE80211_TKIP_IV_LEN, orig_hdr, head_len + hdr_len);
+ skb_pull(skb, IEEE80211_TKIP_IV_LEN);
+ skb_trim(skb, skb->len - ATH10K_IEEE80211_TKIP_MICLEN);
+ return 0;
+}
+
+static int ath10k_htt_rx_frag_tkip_decap_withmic(struct sk_buff *skb,
+ u16 head_len,
+ u16 hdr_len)
+{
+ u8 *ivp, *orig_hdr;
+
+ orig_hdr = skb->data;
+ ivp = orig_hdr + hdr_len + head_len;
+
+ /* the ExtIV bit is always set to 1 for TKIP */
+ if (!(ivp[IEEE80211_WEP_IV_LEN - 1] & ATH10K_IEEE80211_EXTIV))
+ return -EINVAL;
+
+ memmove(orig_hdr + IEEE80211_TKIP_IV_LEN, orig_hdr, head_len + hdr_len);
+ skb_pull(skb, IEEE80211_TKIP_IV_LEN);
+ skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);
+ return 0;
+}
+
+static int ath10k_htt_rx_frag_ccmp_decap(struct sk_buff *skb,
+ u16 head_len,
+ u16 hdr_len)
+{
+ u8 *ivp, *orig_hdr;
+
+ orig_hdr = skb->data;
+ ivp = orig_hdr + hdr_len + head_len;
+
+ /* the ExtIV bit is always set to 1 for CCMP */
+ if (!(ivp[IEEE80211_WEP_IV_LEN - 1] & ATH10K_IEEE80211_EXTIV))
+ return -EINVAL;
+
+ skb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN);
+ memmove(orig_hdr + IEEE80211_CCMP_HDR_LEN, orig_hdr, head_len + hdr_len);
+ skb_pull(skb, IEEE80211_CCMP_HDR_LEN);
+ return 0;
+}
+
+static int ath10k_htt_rx_frag_wep_decap(struct sk_buff *skb,
+ u16 head_len,
+ u16 hdr_len)
+{
+ u8 *orig_hdr;
+
+ orig_hdr = skb->data;
+
+ memmove(orig_hdr + IEEE80211_WEP_IV_LEN,
+ orig_hdr, head_len + hdr_len);
+ skb_pull(skb, IEEE80211_WEP_IV_LEN);
+ skb_trim(skb, skb->len - IEEE80211_WEP_ICV_LEN);
+ return 0;
+}
+
+static bool ath10k_htt_rx_proc_rx_frag_ind_hl(struct ath10k_htt *htt,
+ struct htt_rx_fragment_indication *rx,
+ struct sk_buff *skb)
+{
+ struct ath10k *ar = htt->ar;
+ enum htt_rx_tkip_demic_type tkip_mic = HTT_RX_NON_TKIP_MIC;
+ enum htt_txrx_sec_cast_type sec_index;
+ struct htt_rx_indication_hl *rx_hl;
+ enum htt_security_types sec_type;
+ u32 tid, frag, seq, rx_desc_info;
+ union htt_rx_pn_t new_pn = {0};
+ struct htt_hl_rx_desc *rx_desc;
+ u16 peer_id, sc, hdr_space;
+ union htt_rx_pn_t *last_pn;
+ struct ieee80211_hdr *hdr;
+ int ret, num_mpdu_ranges;
+ struct ath10k_peer *peer;
+ struct htt_resp *resp;
+ size_t tot_hdr_len;
+
+ resp = (struct htt_resp *)(skb->data + HTT_RX_FRAG_IND_INFO0_HEADER_LEN);
+ skb_pull(skb, HTT_RX_FRAG_IND_INFO0_HEADER_LEN);
+ skb_trim(skb, skb->len - FCS_LEN);
+
+ peer_id = __le16_to_cpu(rx->peer_id);
+ rx_hl = (struct htt_rx_indication_hl *)(&resp->rx_ind_hl);
+
+ spin_lock_bh(&ar->data_lock);
+ peer = ath10k_peer_find_by_id(ar, peer_id);
+ if (!peer) {
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "invalid peer: %u\n", peer_id);
+ goto err;
+ }
+
+ num_mpdu_ranges = MS(__le32_to_cpu(rx_hl->hdr.info1),
+ HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
+
+ tot_hdr_len = sizeof(struct htt_resp_hdr) +
+ sizeof(rx_hl->hdr) +
+ sizeof(rx_hl->ppdu) +
+ sizeof(rx_hl->prefix) +
+ sizeof(rx_hl->fw_desc) +
+ sizeof(struct htt_rx_indication_mpdu_range) * num_mpdu_ranges;
+
+ tid = MS(rx_hl->hdr.info0, HTT_RX_INDICATION_INFO0_EXT_TID);
+ rx_desc = (struct htt_hl_rx_desc *)(skb->data + tot_hdr_len);
+ rx_desc_info = __le32_to_cpu(rx_desc->info);
+
+ if (!MS(rx_desc_info, HTT_RX_DESC_HL_INFO_ENCRYPTED)) {
+ spin_unlock_bh(&ar->data_lock);
+ return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb,
+ HTT_RX_NON_PN_CHECK,
+ HTT_RX_NON_TKIP_MIC);
+ }
+
+ hdr = (struct ieee80211_hdr *)((u8 *)rx_desc + rx_hl->fw_desc.len);
+
+ if (ieee80211_has_retry(hdr->frame_control))
+ goto err;
+
+ hdr_space = ieee80211_hdrlen(hdr->frame_control);
+ sc = __le16_to_cpu(hdr->seq_ctrl);
+ seq = (sc & IEEE80211_SCTL_SEQ) >> 4;
+ frag = sc & IEEE80211_SCTL_FRAG;
+
+ sec_index = MS(rx_desc_info, HTT_RX_DESC_HL_INFO_MCAST_BCAST) ?
+ HTT_TXRX_SEC_MCAST : HTT_TXRX_SEC_UCAST;
+ sec_type = peer->rx_pn[sec_index].sec_type;
+ ath10k_htt_rx_mpdu_desc_pn_hl(rx_desc, &new_pn, peer->rx_pn[sec_index].pn_len);
+
+ switch (sec_type) {
+ case HTT_SECURITY_TKIP:
+ tkip_mic = HTT_RX_TKIP_MIC;
+ ret = ath10k_htt_rx_frag_tkip_decap_withmic(skb,
+ tot_hdr_len +
+ rx_hl->fw_desc.len,
+ hdr_space);
+ if (ret)
+ goto err;
+ break;
+ case HTT_SECURITY_TKIP_NOMIC:
+ ret = ath10k_htt_rx_frag_tkip_decap_nomic(skb,
+ tot_hdr_len +
+ rx_hl->fw_desc.len,
+ hdr_space);
+ if (ret)
+ goto err;
+ break;
+ case HTT_SECURITY_AES_CCMP:
+ ret = ath10k_htt_rx_frag_ccmp_decap(skb,
+ tot_hdr_len + rx_hl->fw_desc.len,
+ hdr_space);
+ if (ret)
+ goto err;
+ break;
+ case HTT_SECURITY_WEP128:
+ case HTT_SECURITY_WEP104:
+ case HTT_SECURITY_WEP40:
+ ret = ath10k_htt_rx_frag_wep_decap(skb,
+ tot_hdr_len + rx_hl->fw_desc.len,
+ hdr_space);
+ if (ret)
+ goto err;
+ break;
+ default:
+ break;
+ }
+
+ resp = (struct htt_resp *)(skb->data);
+
+ if (sec_type != HTT_SECURITY_AES_CCMP &&
+ sec_type != HTT_SECURITY_TKIP &&
+ sec_type != HTT_SECURITY_TKIP_NOMIC) {
+ spin_unlock_bh(&ar->data_lock);
+ return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb,
+ HTT_RX_NON_PN_CHECK,
+ HTT_RX_NON_TKIP_MIC);
+ }
+
+ last_pn = &peer->frag_tids_last_pn[tid];
+
+ if (frag == 0) {
+ if (ath10k_htt_rx_pn_check_replay_hl(ar, peer, &resp->rx_ind_hl))
+ goto err;
+
+ last_pn->pn48 = new_pn.pn48;
+ peer->frag_tids_seq[tid] = seq;
+ } else if (sec_type == HTT_SECURITY_AES_CCMP) {
+ if (seq != peer->frag_tids_seq[tid])
+ goto err;
+
+ if (new_pn.pn48 != last_pn->pn48 + 1)
+ goto err;
+
+ last_pn->pn48 = new_pn.pn48;
+ last_pn = &peer->tids_last_pn[tid];
+ last_pn->pn48 = new_pn.pn48;
+ }
+
+ spin_unlock_bh(&ar->data_lock);
+
+ return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb,
+ HTT_RX_NON_PN_CHECK, tkip_mic);
+
+err:
+ spin_unlock_bh(&ar->data_lock);
+
+ /* Tell the caller that it must free the skb since we have not
+ * consumed it
+ */
+ return true;
+}
+
static void ath10k_htt_rx_proc_rx_ind_ll(struct ath10k_htt *htt,
struct htt_rx_indication *rx)
{
@@ -2198,9 +2519,7 @@ static void ath10k_htt_rx_proc_rx_ind_ll(struct ath10k_htt *htt,
mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx);
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ",
- rx, sizeof(*rx) +
- (sizeof(struct htt_rx_indication_mpdu_range) *
- num_mpdu_ranges));
+ rx, struct_size(rx, mpdu_ranges, num_mpdu_ranges));
for (i = 0; i < num_mpdu_ranges; i++)
mpdu_count += mpdu_ranges[i].mpdu_count;
@@ -2220,8 +2539,12 @@ static void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar,
int status = MS(resp->data_tx_completion.flags, HTT_DATA_TX_STATUS);
__le16 msdu_id, *msdus;
bool rssi_enabled = false;
- u8 msdu_count = 0;
- int i;
+ u8 msdu_count = 0, num_airtime_records, tid;
+ int i, htt_pad = 0;
+ struct htt_data_tx_compl_ppdu_dur *ppdu_info;
+ struct ath10k_peer *peer;
+ u16 ppdu_info_offset = 0, peer_id;
+ u32 tx_duration;
switch (status) {
case HTT_DATA_TX_STATUS_NO_ACK:
@@ -2245,12 +2568,14 @@ static void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar,
resp->data_tx_completion.num_msdus);
msdu_count = resp->data_tx_completion.num_msdus;
+ msdus = resp->data_tx_completion.msdus;
+ rssi_enabled = ath10k_is_rssi_enable(&ar->hw_params, resp);
- if (resp->data_tx_completion.flags2 & HTT_TX_CMPL_FLAG_DATA_RSSI)
- rssi_enabled = true;
+ if (rssi_enabled)
+ htt_pad = ath10k_tx_data_rssi_get_pad_bytes(&ar->hw_params,
+ resp);
for (i = 0; i < msdu_count; i++) {
- msdus = resp->data_tx_completion.msdus;
msdu_id = msdus[i];
tx_done.msdu_id = __le16_to_cpu(msdu_id);
@@ -2260,10 +2585,10 @@ static void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar,
* last msdu id with 0xffff
*/
if (msdu_count & 0x01) {
- msdu_id = msdus[msdu_count + i + 1];
+ msdu_id = msdus[msdu_count + i + 1 + htt_pad];
tx_done.ack_rssi = __le16_to_cpu(msdu_id);
} else {
- msdu_id = msdus[msdu_count + i];
+ msdu_id = msdus[msdu_count + i + htt_pad];
tx_done.ack_rssi = __le16_to_cpu(msdu_id);
}
}
@@ -2276,12 +2601,58 @@ static void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar,
* Note that with only one concurrent reader and one concurrent
* writer, you don't need extra locking to use these macro.
*/
- if (!kfifo_put(&htt->txdone_fifo, tx_done)) {
+ if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL) {
+ ath10k_txrx_tx_unref(htt, &tx_done);
+ } else if (!kfifo_put(&htt->txdone_fifo, tx_done)) {
ath10k_warn(ar, "txdone fifo overrun, msdu_id %d status %d\n",
tx_done.msdu_id, tx_done.status);
ath10k_txrx_tx_unref(htt, &tx_done);
}
}
+
+ if (!(resp->data_tx_completion.flags2 & HTT_TX_CMPL_FLAG_PPDU_DURATION_PRESENT))
+ return;
+
+ ppdu_info_offset = (msdu_count & 0x01) ? msdu_count + 1 : msdu_count;
+
+ if (rssi_enabled)
+ ppdu_info_offset += ppdu_info_offset;
+
+ if (resp->data_tx_completion.flags2 &
+ (HTT_TX_CMPL_FLAG_PPID_PRESENT | HTT_TX_CMPL_FLAG_PA_PRESENT))
+ ppdu_info_offset += 2;
+
+ ppdu_info = (struct htt_data_tx_compl_ppdu_dur *)&msdus[ppdu_info_offset];
+ num_airtime_records = FIELD_GET(HTT_TX_COMPL_PPDU_DUR_INFO0_NUM_ENTRIES_MASK,
+ __le32_to_cpu(ppdu_info->info0));
+
+ for (i = 0; i < num_airtime_records; i++) {
+ struct htt_data_tx_ppdu_dur *ppdu_dur;
+ u32 info0;
+
+ ppdu_dur = &ppdu_info->ppdu_dur[i];
+ info0 = __le32_to_cpu(ppdu_dur->info0);
+
+ peer_id = FIELD_GET(HTT_TX_PPDU_DUR_INFO0_PEER_ID_MASK,
+ info0);
+ rcu_read_lock();
+ spin_lock_bh(&ar->data_lock);
+
+ peer = ath10k_peer_find_by_id(ar, peer_id);
+ if (!peer) {
+ spin_unlock_bh(&ar->data_lock);
+ rcu_read_unlock();
+ continue;
+ }
+
+ tid = FIELD_GET(HTT_TX_PPDU_DUR_INFO0_TID_MASK, info0);
+ tx_duration = __le32_to_cpu(ppdu_dur->tx_duration);
+
+ ieee80211_sta_register_airtime(peer->sta, tid, tx_duration, 0);
+
+ spin_unlock_bh(&ar->data_lock);
+ rcu_read_unlock();
+ }
}
static void ath10k_htt_rx_addba(struct ath10k *ar, struct htt_resp *resp)
@@ -2596,6 +2967,7 @@ static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff *skb)
u8 tid;
int ret;
int i;
+ bool may_tx;
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind\n");
@@ -2668,8 +3040,13 @@ static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff *skb)
num_msdus = 0;
num_bytes = 0;
+ ieee80211_txq_schedule_start(hw, txq->ac);
+ may_tx = ieee80211_txq_may_transmit(hw, txq);
while (num_msdus < max_num_msdus &&
num_bytes < max_num_bytes) {
+ if (!may_tx)
+ break;
+
ret = ath10k_mac_tx_push_txq(hw, txq);
if (ret < 0)
break;
@@ -2677,6 +3054,8 @@ static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff *skb)
num_msdus++;
num_bytes += ret;
}
+ ieee80211_return_txq(hw, txq, false);
+ ieee80211_txq_schedule_end(hw, txq->ac);
record->num_msdus = cpu_to_le16(num_msdus);
record->num_bytes = cpu_to_le32(num_bytes);
@@ -2868,29 +3247,31 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
struct rate_info *txrate = &arsta->txrate;
struct ath10k_htt_tx_stats *tx_stats;
int idx, ht_idx, gi, mcs, bw, nss;
+ unsigned long flags;
if (!arsta->tx_stats)
return;
tx_stats = arsta->tx_stats;
- gi = (arsta->txrate.flags & RATE_INFO_FLAGS_SHORT_GI);
- ht_idx = txrate->mcs + txrate->nss * 8;
- mcs = txrate->mcs;
+ flags = txrate->flags;
+ gi = test_bit(ATH10K_RATE_INFO_FLAGS_SGI_BIT, &flags);
+ mcs = ATH10K_HW_MCS_RATE(pstats->ratecode);
bw = txrate->bw;
nss = txrate->nss;
- idx = mcs * 8 + 8 * 10 * nss;
+ ht_idx = mcs + (nss - 1) * 8;
+ idx = mcs * 8 + 8 * 10 * (nss - 1);
idx += bw * 2 + gi;
#define STATS_OP_FMT(name) tx_stats->stats[ATH10K_STATS_TYPE_##name]
- if (txrate->flags == RATE_INFO_FLAGS_VHT_MCS) {
+ if (txrate->flags & RATE_INFO_FLAGS_VHT_MCS) {
STATS_OP_FMT(SUCC).vht[0][mcs] += pstats->succ_bytes;
STATS_OP_FMT(SUCC).vht[1][mcs] += pstats->succ_pkts;
STATS_OP_FMT(FAIL).vht[0][mcs] += pstats->failed_bytes;
STATS_OP_FMT(FAIL).vht[1][mcs] += pstats->failed_pkts;
STATS_OP_FMT(RETRY).vht[0][mcs] += pstats->retry_bytes;
STATS_OP_FMT(RETRY).vht[1][mcs] += pstats->retry_pkts;
- } else if (txrate->flags == RATE_INFO_FLAGS_MCS) {
+ } else if (txrate->flags & RATE_INFO_FLAGS_MCS) {
STATS_OP_FMT(SUCC).ht[0][ht_idx] += pstats->succ_bytes;
STATS_OP_FMT(SUCC).ht[1][ht_idx] += pstats->succ_pkts;
STATS_OP_FMT(FAIL).ht[0][ht_idx] += pstats->failed_bytes;
@@ -2911,7 +3292,7 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
if (ATH10K_HW_AMPDU(pstats->flags)) {
tx_stats->ba_fails += ATH10K_HW_BA_FAIL(pstats->flags);
- if (txrate->flags == RATE_INFO_FLAGS_MCS) {
+ if (txrate->flags & RATE_INFO_FLAGS_MCS) {
STATS_OP_FMT(AMPDU).ht[0][ht_idx] +=
pstats->succ_bytes + pstats->retry_bytes;
STATS_OP_FMT(AMPDU).ht[1][ht_idx] +=
@@ -2924,7 +3305,7 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
}
STATS_OP_FMT(AMPDU).bw[0][bw] +=
pstats->succ_bytes + pstats->retry_bytes;
- STATS_OP_FMT(AMPDU).nss[0][nss] +=
+ STATS_OP_FMT(AMPDU).nss[0][nss - 1] +=
pstats->succ_bytes + pstats->retry_bytes;
STATS_OP_FMT(AMPDU).gi[0][gi] +=
pstats->succ_bytes + pstats->retry_bytes;
@@ -2932,7 +3313,7 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
pstats->succ_bytes + pstats->retry_bytes;
STATS_OP_FMT(AMPDU).bw[1][bw] +=
pstats->succ_pkts + pstats->retry_pkts;
- STATS_OP_FMT(AMPDU).nss[1][nss] +=
+ STATS_OP_FMT(AMPDU).nss[1][nss - 1] +=
pstats->succ_pkts + pstats->retry_pkts;
STATS_OP_FMT(AMPDU).gi[1][gi] +=
pstats->succ_pkts + pstats->retry_pkts;
@@ -2944,27 +3325,27 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
}
STATS_OP_FMT(SUCC).bw[0][bw] += pstats->succ_bytes;
- STATS_OP_FMT(SUCC).nss[0][nss] += pstats->succ_bytes;
+ STATS_OP_FMT(SUCC).nss[0][nss - 1] += pstats->succ_bytes;
STATS_OP_FMT(SUCC).gi[0][gi] += pstats->succ_bytes;
STATS_OP_FMT(SUCC).bw[1][bw] += pstats->succ_pkts;
- STATS_OP_FMT(SUCC).nss[1][nss] += pstats->succ_pkts;
+ STATS_OP_FMT(SUCC).nss[1][nss - 1] += pstats->succ_pkts;
STATS_OP_FMT(SUCC).gi[1][gi] += pstats->succ_pkts;
STATS_OP_FMT(FAIL).bw[0][bw] += pstats->failed_bytes;
- STATS_OP_FMT(FAIL).nss[0][nss] += pstats->failed_bytes;
+ STATS_OP_FMT(FAIL).nss[0][nss - 1] += pstats->failed_bytes;
STATS_OP_FMT(FAIL).gi[0][gi] += pstats->failed_bytes;
STATS_OP_FMT(FAIL).bw[1][bw] += pstats->failed_pkts;
- STATS_OP_FMT(FAIL).nss[1][nss] += pstats->failed_pkts;
+ STATS_OP_FMT(FAIL).nss[1][nss - 1] += pstats->failed_pkts;
STATS_OP_FMT(FAIL).gi[1][gi] += pstats->failed_pkts;
STATS_OP_FMT(RETRY).bw[0][bw] += pstats->retry_bytes;
- STATS_OP_FMT(RETRY).nss[0][nss] += pstats->retry_bytes;
+ STATS_OP_FMT(RETRY).nss[0][nss - 1] += pstats->retry_bytes;
STATS_OP_FMT(RETRY).gi[0][gi] += pstats->retry_bytes;
STATS_OP_FMT(RETRY).bw[1][bw] += pstats->retry_pkts;
- STATS_OP_FMT(RETRY).nss[1][nss] += pstats->retry_pkts;
+ STATS_OP_FMT(RETRY).nss[1][nss - 1] += pstats->retry_pkts;
STATS_OP_FMT(RETRY).gi[1][gi] += pstats->retry_pkts;
if (txrate->flags >= RATE_INFO_FLAGS_MCS) {
@@ -2975,6 +3356,8 @@ ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
STATS_OP_FMT(RETRY).rate_table[0][idx] += pstats->retry_bytes;
STATS_OP_FMT(RETRY).rate_table[1][idx] += pstats->retry_pkts;
}
+
+ tx_stats->tx_duration += pstats->duration;
}
static void
@@ -3070,6 +3453,7 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar,
arsta->txrate.nss = txrate.nss;
arsta->txrate.bw = ath10k_bw_to_mac80211_bw(txrate.bw);
+ arsta->last_tx_bitrate = cfg80211_calculate_bitrate(&arsta->txrate);
if (sgi)
arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
@@ -3141,6 +3525,7 @@ static void ath10k_htt_fetch_peer_stats(struct ath10k *ar,
p_tx_stats->succ_pkts = __le16_to_cpu(tx_stats->succ_pkts);
p_tx_stats->retry_pkts = __le16_to_cpu(tx_stats->retry_pkts);
p_tx_stats->failed_pkts = __le16_to_cpu(tx_stats->failed_pkts);
+ p_tx_stats->duration = __le16_to_cpu(tx_stats->tx_duration);
ath10k_update_per_peer_tx_stats(ar, sta, p_tx_stats);
}
@@ -3206,6 +3591,51 @@ out:
rcu_read_unlock();
}
+static int ath10k_htt_rx_pn_len(enum htt_security_types sec_type)
+{
+ switch (sec_type) {
+ case HTT_SECURITY_TKIP:
+ case HTT_SECURITY_TKIP_NOMIC:
+ case HTT_SECURITY_AES_CCMP:
+ return 48;
+ default:
+ return 0;
+ }
+}
+
+static void ath10k_htt_rx_sec_ind_handler(struct ath10k *ar,
+ struct htt_security_indication *ev)
+{
+ enum htt_txrx_sec_cast_type sec_index;
+ enum htt_security_types sec_type;
+ struct ath10k_peer *peer;
+
+ spin_lock_bh(&ar->data_lock);
+
+ peer = ath10k_peer_find_by_id(ar, __le16_to_cpu(ev->peer_id));
+ if (!peer) {
+ ath10k_warn(ar, "failed to find peer id %d for security indication",
+ __le16_to_cpu(ev->peer_id));
+ goto out;
+ }
+
+ sec_type = MS(ev->flags, HTT_SECURITY_TYPE);
+
+ if (ev->flags & HTT_SECURITY_IS_UNICAST)
+ sec_index = HTT_TXRX_SEC_UCAST;
+ else
+ sec_index = HTT_TXRX_SEC_MCAST;
+
+ peer->rx_pn[sec_index].sec_type = sec_type;
+ peer->rx_pn[sec_index].pn_len = ath10k_htt_rx_pn_len(sec_type);
+
+ memset(peer->tids_last_pn_valid, 0, sizeof(peer->tids_last_pn_valid));
+ memset(peer->tids_last_pn, 0, sizeof(peer->tids_last_pn));
+
+out:
+ spin_unlock_bh(&ar->data_lock);
+}
+
bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
@@ -3234,10 +3664,12 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
break;
}
case HTT_T2H_MSG_TYPE_RX_IND:
- if (ar->dev_type == ATH10K_DEV_TYPE_HL)
+ if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL)
return ath10k_htt_rx_proc_rx_ind_hl(htt,
&resp->rx_ind_hl,
- skb);
+ skb,
+ HTT_RX_PN_CHECK,
+ HTT_RX_NON_TKIP_MIC);
else
ath10k_htt_rx_proc_rx_ind_ll(htt, &resp->rx_ind);
break;
@@ -3299,6 +3731,7 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
struct ath10k *ar = htt->ar;
struct htt_security_indication *ev = &resp->security_indication;
+ ath10k_htt_rx_sec_ind_handler(ar, ev);
ath10k_dbg(ar, ATH10K_DBG_HTT,
"sec ind peer_id %d unicast %d type %d\n",
__le16_to_cpu(ev->peer_id),
@@ -3311,6 +3744,10 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
skb->data, skb->len);
atomic_inc(&htt->num_mpdus_ready);
+
+ return ath10k_htt_rx_proc_rx_frag_ind(htt,
+ &resp->rx_frag_ind,
+ skb);
break;
}
case HTT_T2H_MSG_TYPE_TEST:
@@ -3524,13 +3961,14 @@ static const struct ath10k_htt_rx_ops htt_rx_ops_64 = {
};
static const struct ath10k_htt_rx_ops htt_rx_ops_hl = {
+ .htt_rx_proc_rx_frag_ind = ath10k_htt_rx_proc_rx_frag_ind_hl,
};
void ath10k_htt_set_rx_ops(struct ath10k_htt *htt)
{
struct ath10k *ar = htt->ar;
- if (ar->dev_type == ATH10K_DEV_TYPE_HL)
+ if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL)
htt->rx_ops = &htt_rx_ops_hl;
else if (ar->hw_params.target_64bit)
htt->rx_ops = &htt_rx_ops_64;