diff options
Diffstat (limited to 'drivers/net/wireless/mediatek/mt76/mt7915/mac.c')
-rw-r--r-- | drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 205 |
1 files changed, 167 insertions, 38 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 809dc18e5083..48f115502282 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -268,10 +268,9 @@ mt7915_mac_decode_he_radiotap_ru(struct mt76_rx_status *status, } static void -mt7915_mac_decode_he_mu_radiotap(struct sk_buff *skb, - struct mt76_rx_status *status, - __le32 *rxv) +mt7915_mac_decode_he_mu_radiotap(struct sk_buff *skb, __le32 *rxv) { + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; static const struct ieee80211_radiotap_he_mu mu_known = { .flags1 = HE_BITS(MU_FLAGS1_SIG_B_MCS_KNOWN) | HE_BITS(MU_FLAGS1_SIG_B_DCM_KNOWN) | @@ -281,6 +280,8 @@ mt7915_mac_decode_he_mu_radiotap(struct sk_buff *skb, }; struct ieee80211_radiotap_he_mu *he_mu = NULL; + status->flag |= RX_FLAG_RADIOTAP_HE_MU; + he_mu = skb_push(skb, sizeof(mu_known)); memcpy(he_mu, &mu_known, sizeof(mu_known)); @@ -308,10 +309,9 @@ mt7915_mac_decode_he_mu_radiotap(struct sk_buff *skb, } static void -mt7915_mac_decode_he_radiotap(struct sk_buff *skb, - struct mt76_rx_status *status, - __le32 *rxv, u32 phy) +mt7915_mac_decode_he_radiotap(struct sk_buff *skb, __le32 *rxv, u32 mode) { + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; static const struct ieee80211_radiotap_he known = { .data1 = HE_BITS(DATA1_DATA_MCS_KNOWN) | HE_BITS(DATA1_DATA_DCM_KNOWN) | @@ -329,6 +329,8 @@ mt7915_mac_decode_he_radiotap(struct sk_buff *skb, struct ieee80211_radiotap_he *he = NULL; u32 ltf_size = le32_get_bits(rxv[2], MT_CRXV_HE_LTF_SIZE) + 1; + status->flag |= RX_FLAG_RADIOTAP_HE; + he = skb_push(skb, sizeof(known)); memcpy(he, &known, sizeof(known)); @@ -343,7 +345,7 @@ mt7915_mac_decode_he_radiotap(struct sk_buff *skb, he->data6 = HE_PREP(DATA6_TXOP, TXOP_DUR, rxv[14]) | HE_PREP(DATA6_DOPPLER, DOPPLER, rxv[14]); - switch (phy) { + switch (mode) { case MT_PHY_TYPE_HE_SU: he->data1 |= HE_BITS(DATA1_FORMAT_SU) | HE_BITS(DATA1_UL_DL_KNOWN) | @@ -366,6 +368,7 @@ mt7915_mac_decode_he_radiotap(struct sk_buff *skb, he->data4 |= HE_PREP(DATA4_MU_STA_ID, MU_AID, rxv[7]); mt7915_mac_decode_he_radiotap_ru(status, he, rxv); + mt7915_mac_decode_he_mu_radiotap(skb, rxv); break; case MT_PHY_TYPE_HE_TB: he->data1 |= HE_BITS(DATA1_FORMAT_TRIG) | @@ -384,6 +387,81 @@ mt7915_mac_decode_he_radiotap(struct sk_buff *skb, } } +/* The HW does not translate the mac header to 802.3 for mesh point */ +static int mt7915_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct mt7915_sta *msta = (struct mt7915_sta *)status->wcid; + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; + struct ieee80211_hdr hdr; + struct ethhdr eth_hdr; + __le32 *rxd = (__le32 *)skb->data; + __le32 qos_ctrl, ht_ctrl; + + if (FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, le32_to_cpu(rxd[3])) != + MT_RXD3_NORMAL_U2M) + return -EINVAL; + + if (!(le32_to_cpu(rxd[1]) & MT_RXD1_NORMAL_GROUP_4)) + return -EINVAL; + + if (!msta || !msta->vif) + return -EINVAL; + + sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + + /* store the info from RXD and ethhdr to avoid being overridden */ + memcpy(ð_hdr, skb->data + hdr_gap, sizeof(eth_hdr)); + hdr.frame_control = FIELD_GET(MT_RXD6_FRAME_CONTROL, rxd[6]); + hdr.seq_ctrl = FIELD_GET(MT_RXD8_SEQ_CTRL, rxd[8]); + qos_ctrl = FIELD_GET(MT_RXD8_QOS_CTL, rxd[8]); + ht_ctrl = FIELD_GET(MT_RXD9_HT_CONTROL, rxd[9]); + + hdr.duration_id = 0; + ether_addr_copy(hdr.addr1, vif->addr); + ether_addr_copy(hdr.addr2, sta->addr); + switch (le16_to_cpu(hdr.frame_control) & + (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { + case 0: + ether_addr_copy(hdr.addr3, vif->bss_conf.bssid); + break; + case IEEE80211_FCTL_FROMDS: + ether_addr_copy(hdr.addr3, eth_hdr.h_source); + break; + case IEEE80211_FCTL_TODS: + ether_addr_copy(hdr.addr3, eth_hdr.h_dest); + break; + case IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS: + ether_addr_copy(hdr.addr3, eth_hdr.h_dest); + ether_addr_copy(hdr.addr4, eth_hdr.h_source); + break; + default: + break; + } + + skb_pull(skb, hdr_gap + sizeof(struct ethhdr) - 2); + if (eth_hdr.h_proto == htons(ETH_P_AARP) || + eth_hdr.h_proto == htons(ETH_P_IPX)) + ether_addr_copy(skb_push(skb, ETH_ALEN), bridge_tunnel_header); + else if (eth_hdr.h_proto >= htons(ETH_P_802_3_MIN)) + ether_addr_copy(skb_push(skb, ETH_ALEN), rfc1042_header); + else + skb_pull(skb, 2); + + if (ieee80211_has_order(hdr.frame_control)) + memcpy(skb_push(skb, 2), &ht_ctrl, 2); + if (ieee80211_is_data_qos(hdr.frame_control)) + memcpy(skb_push(skb, 2), &qos_ctrl, 2); + if (ieee80211_has_a4(hdr.frame_control)) + memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr)); + else + memcpy(skb_push(skb, sizeof(hdr) - 6), &hdr, sizeof(hdr) - 6); + + return 0; +} + static int mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) { @@ -391,7 +469,6 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) struct mt76_phy *mphy = &dev->mt76.phy; struct mt7915_phy *phy = &dev->phy; struct ieee80211_supported_band *sband; - struct ieee80211_hdr *hdr; __le32 *rxd = (__le32 *)skb->data; __le32 *rxv = NULL; u32 mode = 0; @@ -404,6 +481,7 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) bool unicast, insert_ccmp_hdr = false; u8 remove_pad, amsdu_info; bool hdr_trans; + u16 hdr_gap; u16 seq_ctrl = 0; u8 qos_ctl = 0; __le16 fc = 0; @@ -426,9 +504,16 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) if (rxd2 & MT_RXD2_NORMAL_AMSDU_ERR) return -EINVAL; + hdr_trans = rxd2 & MT_RXD2_NORMAL_HDR_TRANS; + if (hdr_trans && (rxd1 & MT_RXD1_NORMAL_CM)) + return -EINVAL; + + /* ICV error or CCMP/BIP/WPI MIC error */ + if (rxd1 & MT_RXD1_NORMAL_ICV_ERR) + status->flag |= RX_FLAG_ONLY_MONITOR; + unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M; idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1); - hdr_trans = rxd2 & MT_RXD2_NORMAL_HDR_TRANS; status->wcid = mt7915_rx_get_wcid(dev, idx, unicast); if (status->wcid) { @@ -604,15 +689,12 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) return -EINVAL; break; case MT_PHY_TYPE_HE_MU: - status->flag |= RX_FLAG_RADIOTAP_HE_MU; - fallthrough; case MT_PHY_TYPE_HE_SU: case MT_PHY_TYPE_HE_EXT_SU: case MT_PHY_TYPE_HE_TB: status->nss = FIELD_GET(MT_PRXV_NSTS, v0) + 1; status->encoding = RX_ENC_HE; - status->flag |= RX_FLAG_RADIOTAP_HE; i &= GENMASK(3, 0); if (gi <= NL80211_RATE_INFO_HE_GI_3_2) @@ -654,27 +736,55 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) } } - skb_pull(skb, (u8 *)rxd - skb->data + 2 * remove_pad); - amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4); status->amsdu = !!amsdu_info; if (status->amsdu) { status->first_amsdu = amsdu_info == MT_RXD4_FIRST_AMSDU_FRAME; status->last_amsdu = amsdu_info == MT_RXD4_LAST_AMSDU_FRAME; - if (!hdr_trans) { - memmove(skb->data + 2, skb->data, - ieee80211_get_hdrlen_from_skb(skb)); - skb_pull(skb, 2); - } } - if (insert_ccmp_hdr && !hdr_trans) { - u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); + hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad; + if (hdr_trans && ieee80211_has_morefrags(fc)) { + if (mt7915_reverse_frag0_hdr_trans(skb, hdr_gap)) + return -EINVAL; + hdr_trans = false; + } else { + int pad_start = 0; + + skb_pull(skb, hdr_gap); + if (!hdr_trans && status->amsdu) { + pad_start = ieee80211_get_hdrlen_from_skb(skb); + } else if (hdr_trans && (rxd2 & MT_RXD2_NORMAL_HDR_TRANS_ERROR)) { + /* + * When header translation failure is indicated, + * the hardware will insert an extra 2-byte field + * containing the data length after the protocol + * type field. + */ + pad_start = 12; + if (get_unaligned_be16(skb->data + pad_start) == ETH_P_8021Q) + pad_start += 4; + + if (get_unaligned_be16(skb->data + pad_start) != + skb->len - pad_start - 2) + pad_start = 0; + } - mt76_insert_ccmp_hdr(skb, key_id); + if (pad_start) { + memmove(skb->data + 2, skb->data, pad_start); + skb_pull(skb, 2); + } } if (!hdr_trans) { + struct ieee80211_hdr *hdr; + + if (insert_ccmp_hdr) { + u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); + + mt76_insert_ccmp_hdr(skb, key_id); + } + hdr = mt76_skb_get_hdr(skb); fc = hdr->frame_control; if (ieee80211_is_data_qos(fc)) { @@ -682,16 +792,11 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) qos_ctl = *ieee80211_get_qos_ctl(hdr); } } else { - status->flag &= ~(RX_FLAG_RADIOTAP_HE | - RX_FLAG_RADIOTAP_HE_MU); status->flag |= RX_FLAG_8023; } - if (rxv && status->flag & RX_FLAG_RADIOTAP_HE) { - mt7915_mac_decode_he_radiotap(skb, status, rxv, mode); - if (status->flag & RX_FLAG_RADIOTAP_HE_MU) - mt7915_mac_decode_he_mu_radiotap(skb, status, rxv); - } + if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023)) + mt7915_mac_decode_he_radiotap(skb, rxv, mode); if (!status->wcid || !ieee80211_is_data_qos(fc)) return 0; @@ -1044,8 +1149,8 @@ void mt7915_mac_write_txwi(struct mt7915_dev *dev, __le32 *txwi, if (vif) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; - omac_idx = mvif->omac_idx; - wmm_idx = mvif->wmm_idx; + omac_idx = mvif->mt76.omac_idx; + wmm_idx = mvif->mt76.wmm_idx; } if (ext_phy && dev->mt76.phy2) @@ -1181,7 +1286,7 @@ int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, if (vif) { struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; - txp->bss_idx = mvif->idx; + txp->bss_idx = mvif->mt76.idx; } txp->token = cpu_to_le16(id); @@ -1268,15 +1373,16 @@ out: } static void -mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb) +mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len) { - struct mt7915_tx_free *free = (struct mt7915_tx_free *)skb->data; + struct mt7915_tx_free *free = (struct mt7915_tx_free *)data; struct mt76_dev *mdev = &dev->mt76; struct mt76_phy *mphy_ext = mdev->phy2; struct mt76_txwi_cache *txwi; struct ieee80211_sta *sta = NULL; LIST_HEAD(free_list); - struct sk_buff *tmp; + struct sk_buff *skb, *tmp; + void *end = data + len; u8 i, count; bool wake = false; @@ -1294,6 +1400,9 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb) * Should avoid accessing WTBL to get Tx airtime, and use it instead. */ count = FIELD_GET(MT_TX_FREE_MSDU_CNT, le16_to_cpu(free->ctrl)); + if (WARN_ON_ONCE((void *)&free->info[count] > end)) + return; + for (i = 0; i < count; i++) { u32 msdu, info = le32_to_cpu(free->info[i]); @@ -1336,8 +1445,6 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb) mt76_worker_schedule(&dev->mt76.tx_worker); - napi_consume_skb(skb, 1); - list_for_each_entry_safe(skb, tmp, &free_list, list) { skb_list_del_init(skb); napi_consume_skb(skb, 1); @@ -1512,6 +1619,27 @@ out: rcu_read_unlock(); } +bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len) +{ + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + __le32 *rxd = (__le32 *)data; + __le32 *end = (__le32 *)&rxd[len / 4]; + enum rx_pkt_type type; + + type = FIELD_GET(MT_RXD0_PKT_TYPE, le32_to_cpu(rxd[0])); + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + mt7915_mac_tx_free(dev, data, len); + return false; + case PKT_TYPE_TXS: + for (rxd += 2; rxd + 8 <= end; rxd += 8) + mt7915_mac_add_txs(dev, rxd); + return false; + default: + return true; + } +} + void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, struct sk_buff *skb) { @@ -1524,7 +1652,8 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, switch (type) { case PKT_TYPE_TXRX_NOTIFY: - mt7915_mac_tx_free(dev, skb); + mt7915_mac_tx_free(dev, skb->data, skb->len); + napi_consume_skb(skb, 1); break; case PKT_TYPE_RX_EVENT: mt7915_mcu_rx_event(dev, skb); |