diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath10k/htt_rx.c')
-rw-r--r-- | drivers/net/wireless/ath/ath10k/htt_rx.c | 554 |
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; |