| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Datapath implementation. |
| * |
| * Copyright (c) 2017-2019, Silicon Laboratories, Inc. |
| * Copyright (c) 2010, ST-Ericsson |
| */ |
| #include <linux/etherdevice.h> |
| #include <net/mac80211.h> |
| |
| #include "data_rx.h" |
| #include "wfx.h" |
| #include "bh.h" |
| #include "sta.h" |
| |
| static int wfx_drop_encrypt_data(struct wfx_dev *wdev, |
| const struct hif_ind_rx *arg, |
| struct sk_buff *skb) |
| { |
| struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; |
| size_t hdrlen = ieee80211_hdrlen(frame->frame_control); |
| size_t iv_len, icv_len; |
| |
| /* Oops... There is no fast way to ask mac80211 about |
| * IV/ICV lengths. Even defineas are not exposed. |
| */ |
| switch (arg->rx_flags.encryp) { |
| case HIF_RI_FLAGS_WEP_ENCRYPTED: |
| iv_len = 4 /* WEP_IV_LEN */; |
| icv_len = 4 /* WEP_ICV_LEN */; |
| break; |
| case HIF_RI_FLAGS_TKIP_ENCRYPTED: |
| iv_len = 8 /* TKIP_IV_LEN */; |
| icv_len = 4 /* TKIP_ICV_LEN */ |
| + 8 /*MICHAEL_MIC_LEN*/; |
| break; |
| case HIF_RI_FLAGS_AES_ENCRYPTED: |
| iv_len = 8 /* CCMP_HDR_LEN */; |
| icv_len = 8 /* CCMP_MIC_LEN */; |
| break; |
| case HIF_RI_FLAGS_WAPI_ENCRYPTED: |
| iv_len = 18 /* WAPI_HDR_LEN */; |
| icv_len = 16 /* WAPI_MIC_LEN */; |
| break; |
| default: |
| dev_err(wdev->dev, "unknown encryption type %d\n", |
| arg->rx_flags.encryp); |
| return -EIO; |
| } |
| |
| /* Firmware strips ICV in case of MIC failure. */ |
| if (arg->status == HIF_STATUS_RX_FAIL_MIC) |
| icv_len = 0; |
| |
| if (skb->len < hdrlen + iv_len + icv_len) { |
| dev_warn(wdev->dev, "malformed SDU received\n"); |
| return -EIO; |
| } |
| |
| /* Remove IV, ICV and MIC */ |
| skb_trim(skb, skb->len - icv_len); |
| memmove(skb->data + iv_len, skb->data, hdrlen); |
| skb_pull(skb, iv_len); |
| return 0; |
| } |
| |
| void wfx_rx_cb(struct wfx_vif *wvif, |
| const struct hif_ind_rx *arg, struct sk_buff *skb) |
| { |
| struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); |
| struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; |
| struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; |
| |
| memset(hdr, 0, sizeof(*hdr)); |
| |
| // FIXME: Why do we drop these frames? |
| if (!arg->rcpi_rssi && |
| (ieee80211_is_probe_resp(frame->frame_control) || |
| ieee80211_is_beacon(frame->frame_control))) |
| goto drop; |
| |
| if (arg->status == HIF_STATUS_RX_FAIL_MIC) |
| hdr->flag |= RX_FLAG_MMIC_ERROR; |
| else if (arg->status) |
| goto drop; |
| |
| if (skb->len < sizeof(struct ieee80211_pspoll)) { |
| dev_warn(wvif->wdev->dev, "malformed SDU received\n"); |
| goto drop; |
| } |
| |
| hdr->band = NL80211_BAND_2GHZ; |
| hdr->freq = ieee80211_channel_to_frequency(arg->channel_number, |
| hdr->band); |
| |
| if (arg->rxed_rate >= 14) { |
| hdr->encoding = RX_ENC_HT; |
| hdr->rate_idx = arg->rxed_rate - 14; |
| } else if (arg->rxed_rate >= 4) { |
| hdr->rate_idx = arg->rxed_rate - 2; |
| } else { |
| hdr->rate_idx = arg->rxed_rate; |
| } |
| |
| hdr->signal = arg->rcpi_rssi / 2 - 110; |
| hdr->antenna = 0; |
| |
| if (arg->rx_flags.encryp) { |
| if (wfx_drop_encrypt_data(wvif->wdev, arg, skb)) |
| goto drop; |
| hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED; |
| if (arg->rx_flags.encryp == HIF_RI_FLAGS_TKIP_ENCRYPTED) |
| hdr->flag |= RX_FLAG_MMIC_STRIPPED; |
| } |
| |
| /* Filter block ACK negotiation: fully controlled by firmware */ |
| if (ieee80211_is_action(frame->frame_control) && |
| arg->rx_flags.match_uc_addr && |
| mgmt->u.action.category == WLAN_CATEGORY_BACK) |
| goto drop; |
| ieee80211_rx_irqsafe(wvif->wdev->hw, skb); |
| |
| return; |
| |
| drop: |
| dev_kfree_skb(skb); |
| } |