summaryrefslogtreecommitdiff
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.c91
1 files changed, 89 insertions, 2 deletions
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 83a7fb68fd24..53f1095de8ff 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -2151,6 +2151,10 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
struct ath10k_peer *peer;
struct htt_rx_indication_mpdu_range *mpdu_ranges;
struct fw_rx_desc_hl *fw_desc;
+ 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;
struct ieee80211_hdr *hdr;
struct ieee80211_rx_status *rx_status;
u16 peer_id;
@@ -2158,9 +2162,11 @@ 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;
+ bool pn_invalid, qos, first_msdu;
+ u32 tid, rx_desc_info;
peer_id = __le16_to_cpu(rx->hdr.peer_id);
+ tid = MS(rx->hdr.info0, HTT_RX_INDICATION_INFO0_EXT_TID);
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find_by_id(ar, peer_id);
@@ -2168,6 +2174,9 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
if (!peer && peer_id != HTT_INVALID_PEERID)
ath10k_warn(ar, "Got RX ind from invalid peer: %u\n", peer_id);
+ if (!peer)
+ return true;
+
num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1),
HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
mpdu_ranges = htt_rx_ind_get_mpdu_ranges_hl(rx);
@@ -2192,10 +2201,24 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
goto err;
}
- if (check_pn_type == HTT_RX_PN_CHECK) {
+ 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_MCAST_BCAST))
+ sec_index = HTT_TXRX_SEC_MCAST;
+ else
+ sec_index = HTT_TXRX_SEC_UCAST;
+
+ sec_type = peer->rx_pn[sec_index].sec_type;
+ first_msdu = rx->fw_desc.flags & FW_RX_DESC_FLAGS_FIRST_MSDU;
+
+ ath10k_htt_rx_mpdu_desc_pn_hl(rx_desc, &new_pn, peer->rx_pn[sec_index].pn_len);
+
+ if (check_pn_type == HTT_RX_PN_CHECK && tid >= IEEE80211_NUM_TIDS) {
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;
}
@@ -2211,6 +2234,7 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
skb_pull(skb, tot_hdr_len);
hdr = (struct ieee80211_hdr *)skb->data;
+ qos = ieee80211_is_data_qos(hdr->frame_control);
rx_status = IEEE80211_SKB_RXCB(skb);
rx_status->chains |= BIT(0);
if (rx->ppdu.combined_rssi == 0) {
@@ -2254,6 +2278,55 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
rx_status->flag |= RX_FLAG_DECRYPTED |
RX_FLAG_IV_STRIPPED |
RX_FLAG_MMIC_STRIPPED;
+
+ if (tid < IEEE80211_NUM_TIDS &&
+ first_msdu &&
+ check_pn_type == HTT_RX_PN_CHECK &&
+ (sec_type == HTT_SECURITY_AES_CCMP ||
+ sec_type == HTT_SECURITY_TKIP ||
+ sec_type == HTT_SECURITY_TKIP_NOMIC)) {
+ u8 offset, *ivp, i;
+ s8 keyidx = 0;
+ __le64 pn48 = cpu_to_le64(new_pn.pn48);
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ offset = ieee80211_hdrlen(hdr->frame_control);
+ hdr->frame_control |= __cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ rx_status->flag &= ~RX_FLAG_IV_STRIPPED;
+
+ memmove(skb->data - IEEE80211_CCMP_HDR_LEN,
+ skb->data, offset);
+ skb_push(skb, IEEE80211_CCMP_HDR_LEN);
+ ivp = skb->data + offset;
+ memset(skb->data + offset, 0, IEEE80211_CCMP_HDR_LEN);
+ /* Ext IV */
+ ivp[IEEE80211_WEP_IV_LEN - 1] |= ATH10K_IEEE80211_EXTIV;
+
+ for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
+ if (peer->keys[i] &&
+ peer->keys[i]->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+ keyidx = peer->keys[i]->keyidx;
+ }
+
+ /* Key ID */
+ ivp[IEEE80211_WEP_IV_LEN - 1] |= keyidx << 6;
+
+ if (sec_type == HTT_SECURITY_AES_CCMP) {
+ rx_status->flag |= RX_FLAG_MIC_STRIPPED;
+ /* pn 0, pn 1 */
+ memcpy(skb->data + offset, &pn48, 2);
+ /* pn 1, pn 3 , pn 34 , pn 5 */
+ memcpy(skb->data + offset + 4, ((u8 *)&pn48) + 2, 4);
+ } else {
+ rx_status->flag |= RX_FLAG_ICV_STRIPPED;
+ /* TSC 0 */
+ memcpy(skb->data + offset + 2, &pn48, 1);
+ /* TSC 1 */
+ memcpy(skb->data + offset, ((u8 *)&pn48) + 1, 1);
+ /* TSC 2 , TSC 3 , TSC 4 , TSC 5*/
+ memcpy(skb->data + offset + 4, ((u8 *)&pn48) + 2, 4);
+ }
+ }
}
if (tkip_mic_type == HTT_RX_TKIP_MIC)
@@ -2263,6 +2336,20 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
if (mpdu_ranges->mpdu_range_status == HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR)
rx_status->flag |= RX_FLAG_MMIC_ERROR;
+ if (!qos && tid < IEEE80211_NUM_TIDS) {
+ u8 offset;
+ __le16 qos_ctrl = 0;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ offset = ieee80211_hdrlen(hdr->frame_control);
+
+ hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
+ memmove(skb->data - IEEE80211_QOS_CTL_LEN, skb->data, offset);
+ skb_push(skb, IEEE80211_QOS_CTL_LEN);
+ qos_ctrl = cpu_to_le16(tid);
+ memcpy(skb->data + offset, &qos_ctrl, IEEE80211_QOS_CTL_LEN);
+ }
+
ieee80211_rx_ni(ar->hw, skb);
/* We have delivered the skb to the upper layers (mac80211) so we