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) || | 
