diff options
Diffstat (limited to 'drivers/net/wireless/mediatek/mt76/testmode.c')
-rw-r--r-- | drivers/net/wireless/mediatek/mt76/testmode.c | 159 |
1 files changed, 127 insertions, 32 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/testmode.c b/drivers/net/wireless/mediatek/mt76/testmode.c index cc769645afa5..001d0ba5f73e 100644 --- a/drivers/net/wireless/mediatek/mt76/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/testmode.c @@ -62,36 +62,83 @@ void mt76_testmode_tx_pending(struct mt76_phy *phy) spin_unlock_bh(&q->lock); } +static u32 +mt76_testmode_max_mpdu_len(struct mt76_phy *phy, u8 tx_rate_mode) +{ + switch (tx_rate_mode) { + case MT76_TM_TX_MODE_HT: + return IEEE80211_MAX_MPDU_LEN_HT_7935; + case MT76_TM_TX_MODE_VHT: + case MT76_TM_TX_MODE_HE_SU: + case MT76_TM_TX_MODE_HE_EXT_SU: + case MT76_TM_TX_MODE_HE_TB: + case MT76_TM_TX_MODE_HE_MU: + if (phy->sband_5g.sband.vht_cap.cap & + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991) + return IEEE80211_MAX_MPDU_LEN_VHT_7991; + return IEEE80211_MAX_MPDU_LEN_VHT_11454; + case MT76_TM_TX_MODE_CCK: + case MT76_TM_TX_MODE_OFDM: + default: + return IEEE80211_MAX_FRAME_LEN; + } +} -static int -mt76_testmode_tx_init(struct mt76_phy *phy) +static void +mt76_testmode_free_skb(struct mt76_phy *phy) { struct mt76_testmode_data *td = &phy->test; - struct ieee80211_tx_info *info; - struct ieee80211_hdr *hdr; - struct sk_buff *skb; + struct sk_buff *skb = td->tx_skb; + + if (!skb) + return; + + if (skb_has_frag_list(skb)) { + kfree_skb_list(skb_shinfo(skb)->frag_list); + skb_shinfo(skb)->frag_list = NULL; + } + + dev_kfree_skb(skb); + td->tx_skb = NULL; +} + +int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len) +{ +#define MT_TXP_MAX_LEN 4095 u16 fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA | IEEE80211_FCTL_FROMDS; - struct ieee80211_tx_rate *rate; - u8 max_nss = hweight8(phy->antenna_mask); + struct mt76_testmode_data *td = &phy->test; bool ext_phy = phy != &phy->dev->phy; + struct sk_buff **frag_tail, *head; + struct ieee80211_tx_info *info; + struct ieee80211_hdr *hdr; + u32 max_len, head_len; + int nfrags, i; - if (td->tx_antenna_mask) - max_nss = min_t(u8, max_nss, hweight8(td->tx_antenna_mask)); + max_len = mt76_testmode_max_mpdu_len(phy, td->tx_rate_mode); + if (len > max_len) + len = max_len; + else if (len < sizeof(struct ieee80211_hdr)) + len = sizeof(struct ieee80211_hdr); - skb = alloc_skb(td->tx_msdu_len, GFP_KERNEL); - if (!skb) + nfrags = len / MT_TXP_MAX_LEN; + head_len = nfrags ? MT_TXP_MAX_LEN : len; + + if (len > IEEE80211_MAX_FRAME_LEN) + fc |= IEEE80211_STYPE_QOS_DATA; + + head = alloc_skb(head_len, GFP_KERNEL); + if (!head) return -ENOMEM; - dev_kfree_skb(td->tx_skb); - td->tx_skb = skb; - hdr = __skb_put_zero(skb, td->tx_msdu_len); + hdr = __skb_put_zero(head, head_len); hdr->frame_control = cpu_to_le16(fc); memcpy(hdr->addr1, phy->macaddr, sizeof(phy->macaddr)); memcpy(hdr->addr2, phy->macaddr, sizeof(phy->macaddr)); memcpy(hdr->addr3, phy->macaddr, sizeof(phy->macaddr)); + skb_set_queue_mapping(head, IEEE80211_AC_BE); - info = IEEE80211_SKB_CB(skb); + info = IEEE80211_SKB_CB(head); info->flags = IEEE80211_TX_CTL_INJECTED | IEEE80211_TX_CTL_NO_ACK | IEEE80211_TX_CTL_NO_PS_BUFFER; @@ -99,9 +146,60 @@ mt76_testmode_tx_init(struct mt76_phy *phy) if (ext_phy) info->hw_queue |= MT_TX_HW_QUEUE_EXT_PHY; + frag_tail = &skb_shinfo(head)->frag_list; + + for (i = 0; i < nfrags; i++) { + struct sk_buff *frag; + u16 frag_len; + + if (i == nfrags - 1) + frag_len = len % MT_TXP_MAX_LEN; + else + frag_len = MT_TXP_MAX_LEN; + + frag = alloc_skb(frag_len, GFP_KERNEL); + if (!frag) + return -ENOMEM; + + __skb_put_zero(frag, frag_len); + head->len += frag->len; + head->data_len += frag->len; + + if (*frag_tail) { + (*frag_tail)->next = frag; + frag_tail = &frag; + } else { + *frag_tail = frag; + } + } + + mt76_testmode_free_skb(phy); + td->tx_skb = head; + + return 0; +} +EXPORT_SYMBOL(mt76_testmode_alloc_skb); + +static int +mt76_testmode_tx_init(struct mt76_phy *phy) +{ + struct mt76_testmode_data *td = &phy->test; + struct ieee80211_tx_info *info; + struct ieee80211_tx_rate *rate; + u8 max_nss = hweight8(phy->antenna_mask); + int ret; + + ret = mt76_testmode_alloc_skb(phy, td->tx_mpdu_len); + if (ret) + return ret; + if (td->tx_rate_mode > MT76_TM_TX_MODE_VHT) goto out; + if (td->tx_antenna_mask) + max_nss = min_t(u8, max_nss, hweight8(td->tx_antenna_mask)); + + info = IEEE80211_SKB_CB(td->tx_skb); rate = &info->control.rates[0]; rate->count = 1; rate->idx = td->tx_rate_idx; @@ -171,8 +269,6 @@ mt76_testmode_tx_init(struct mt76_phy *phy) } } out: - skb_set_queue_mapping(skb, IEEE80211_AC_BE); - return 0; } @@ -203,8 +299,7 @@ mt76_testmode_tx_stop(struct mt76_phy *phy) wait_event_timeout(dev->tx_wait, td->tx_done == td->tx_queued, MT76_TM_TIMEOUT * HZ); - dev_kfree_skb(td->tx_skb); - td->tx_skb = NULL; + mt76_testmode_free_skb(phy); } static inline void @@ -224,10 +319,10 @@ mt76_testmode_init_defaults(struct mt76_phy *phy) { struct mt76_testmode_data *td = &phy->test; - if (td->tx_msdu_len > 0) + if (td->tx_mpdu_len > 0) return; - td->tx_msdu_len = 1024; + td->tx_mpdu_len = 1024; td->tx_count = 1; td->tx_rate_mode = MT76_TM_TX_MODE_OFDM; td->tx_rate_nss = 1; @@ -345,16 +440,6 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (tb[MT76_TM_ATTR_TX_COUNT]) td->tx_count = nla_get_u32(tb[MT76_TM_ATTR_TX_COUNT]); - if (tb[MT76_TM_ATTR_TX_LENGTH]) { - u32 val = nla_get_u32(tb[MT76_TM_ATTR_TX_LENGTH]); - - if (val > IEEE80211_MAX_FRAME_LEN || - val < sizeof(struct ieee80211_hdr)) - goto out; - - td->tx_msdu_len = val; - } - if (tb[MT76_TM_ATTR_TX_RATE_IDX]) td->tx_rate_idx = nla_get_u8(tb[MT76_TM_ATTR_TX_RATE_IDX]); @@ -375,6 +460,16 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, &td->tx_power_control, 0, 1)) goto out; + if (tb[MT76_TM_ATTR_TX_LENGTH]) { + u32 val = nla_get_u32(tb[MT76_TM_ATTR_TX_LENGTH]); + + if (val > mt76_testmode_max_mpdu_len(phy, td->tx_rate_mode) || + val < sizeof(struct ieee80211_hdr)) + goto out; + + td->tx_mpdu_len = val; + } + if (tb[MT76_TM_ATTR_TX_IPG]) td->tx_ipg = nla_get_u32(tb[MT76_TM_ATTR_TX_IPG]); @@ -506,7 +601,7 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg, goto out; if (nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, td->tx_count) || - nla_put_u32(msg, MT76_TM_ATTR_TX_LENGTH, td->tx_msdu_len) || + nla_put_u32(msg, MT76_TM_ATTR_TX_LENGTH, td->tx_mpdu_len) || nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_MODE, td->tx_rate_mode) || nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_NSS, td->tx_rate_nss) || nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, td->tx_rate_idx) || |