| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Data receiving implementation. |
| * |
| * Copyright (c) 2017-2020, 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 void wfx_rx_handle_ba(struct wfx_vif *wvif, struct ieee80211_mgmt *mgmt) |
| { |
| struct ieee80211_vif *vif = wvif_to_vif(wvif); |
| int params, tid; |
| |
| if (wfx_api_older_than(wvif->wdev, 3, 6)) |
| return; |
| |
| switch (mgmt->u.action.u.addba_req.action_code) { |
| case WLAN_ACTION_ADDBA_REQ: |
| params = le16_to_cpu(mgmt->u.action.u.addba_req.capab); |
| tid = (params & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; |
| ieee80211_start_rx_ba_session_offl(vif, mgmt->sa, tid); |
| break; |
| case WLAN_ACTION_DELBA: |
| params = le16_to_cpu(mgmt->u.action.u.delba.params); |
| tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12; |
| ieee80211_stop_rx_ba_session_offl(vif, mgmt->sa, tid); |
| break; |
| } |
| } |
| |
| void wfx_rx_cb(struct wfx_vif *wvif, const struct wfx_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)); |
| |
| if (arg->status == HIF_STATUS_RX_FAIL_MIC) |
| hdr->flag |= RX_FLAG_MMIC_ERROR | RX_FLAG_IV_STRIPPED; |
| 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; |
| } |
| |
| if (!arg->rcpi_rssi) { |
| hdr->flag |= RX_FLAG_NO_SIGNAL_VAL; |
| dev_info(wvif->wdev->dev, "received frame without RSSI data\n"); |
| } |
| hdr->signal = arg->rcpi_rssi / 2 - 110; |
| hdr->antenna = 0; |
| |
| if (arg->encryp) |
| hdr->flag |= RX_FLAG_DECRYPTED; |
| |
| /* Block ack negotiation is offloaded by the firmware. However, re-ordering must be done by |
| * the mac80211. |
| */ |
| if (ieee80211_is_action(frame->frame_control) && |
| mgmt->u.action.category == WLAN_CATEGORY_BACK && |
| skb->len > IEEE80211_MIN_ACTION_SIZE) { |
| wfx_rx_handle_ba(wvif, mgmt); |
| goto drop; |
| } |
| |
| ieee80211_rx_irqsafe(wvif->wdev->hw, skb); |
| return; |
| |
| drop: |
| dev_kfree_skb(skb); |
| } |