Commit aadf0953 authored by Shayne Chen's avatar Shayne Chen Committed by Felix Fietkau
Browse files

mt76: mt7915: implement testmode tx support



Support testmode tx for MT7915A, including tx streams and HE rate
settings.

Reviewed-by: default avatarRyder Lee <ryder.lee@mediatek.com>
Signed-off-by: default avatarShayne Chen <shayne.chen@mediatek.com>
Signed-off-by: default avatarFelix Fietkau <nbd@nbd.name>
parent 61fe7357
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -4,3 +4,5 @@ obj-$(CONFIG_MT7915E) += mt7915e.o


mt7915e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
mt7915e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
	     debugfs.o
	     debugfs.o

mt7915e-$(CONFIG_NL80211_TESTMODE) += testmode.o
+5 −3
Original line number Original line Diff line number Diff line
@@ -264,6 +264,7 @@ mt7915_init_wiphy(struct ieee80211_hw *hw)


	ieee80211_hw_set(hw, HAS_RATE_CONTROL);
	ieee80211_hw_set(hw, HAS_RATE_CONTROL);
	ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD);
	ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD);
	ieee80211_hw_set(hw, WANT_MONITOR_VIF);


	hw->max_tx_fragments = 4;
	hw->max_tx_fragments = 4;
}
}
@@ -620,9 +621,6 @@ int mt7915_register_ext_phy(struct mt7915_dev *dev)
	mphy->hw->wiphy->perm_addr[0] |= 2;
	mphy->hw->wiphy->perm_addr[0] |= 2;
	mphy->hw->wiphy->perm_addr[0] ^= BIT(7);
	mphy->hw->wiphy->perm_addr[0] ^= BIT(7);


	/* The second interface does not get any packets unless it has a vif */
	ieee80211_hw_set(mphy->hw, WANT_MONITOR_VIF);

	ret = mt76_register_phy(mphy);
	ret = mt76_register_phy(mphy);
	if (ret)
	if (ret)
		ieee80211_free_hw(mphy->hw);
		ieee80211_free_hw(mphy->hw);
@@ -678,6 +676,10 @@ int mt7915_register_device(struct mt7915_dev *dev)
	mt7915_cap_dbdc_disable(dev);
	mt7915_cap_dbdc_disable(dev);
	dev->phy.dfs_state = -1;
	dev->phy.dfs_state = -1;


#ifdef CONFIG_NL80211_TESTMODE
	dev->mt76.test_ops = &mt7915_testmode_ops;
#endif

	ret = mt76_register_device(&dev->mt76, true, mt7915_rates,
	ret = mt76_register_device(&dev->mt76, true, mt7915_rates,
				   ARRAY_SIZE(mt7915_rates));
				   ARRAY_SIZE(mt7915_rates));
	if (ret)
	if (ret)
+117 −0
Original line number Original line Diff line number Diff line
@@ -562,6 +562,108 @@ int mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb)
	return 0;
	return 0;
}
}


static void
mt7915_mac_write_txwi_tm(struct mt7915_dev *dev, struct mt76_phy *mphy,
			 __le32 *txwi, struct sk_buff *skb)
{
#ifdef CONFIG_NL80211_TESTMODE
	struct mt76_testmode_data *td = &dev->mt76.test;
	u8 rate_idx = td->tx_rate_idx;
	u8 nss = td->tx_rate_nss;
	u8 bw, mode;
	u16 rateval = 0;
	u32 val;

	if (skb != dev->mt76.test.tx_skb)
		return;

	switch (td->tx_rate_mode) {
	case MT76_TM_TX_MODE_CCK:
		mode = MT_PHY_TYPE_CCK;
		break;
	case MT76_TM_TX_MODE_HT:
		nss = 1 + (rate_idx >> 3);
		mode = MT_PHY_TYPE_HT;
		break;
	case MT76_TM_TX_MODE_VHT:
		mode = MT_PHY_TYPE_VHT;
		break;
	case MT76_TM_TX_MODE_HE_SU:
		mode = MT_PHY_TYPE_HE_SU;
		break;
	case MT76_TM_TX_MODE_HE_EXT_SU:
		mode = MT_PHY_TYPE_HE_EXT_SU;
		break;
	case MT76_TM_TX_MODE_HE_TB:
		mode = MT_PHY_TYPE_HE_TB;
		break;
	case MT76_TM_TX_MODE_HE_MU:
		mode = MT_PHY_TYPE_HE_MU;
		break;
	case MT76_TM_TX_MODE_OFDM:
	default:
		mode = MT_PHY_TYPE_OFDM;
		break;
	}

	switch (mphy->chandef.width) {
	case NL80211_CHAN_WIDTH_40:
		bw = 1;
		break;
	case NL80211_CHAN_WIDTH_80:
		bw = 2;
		break;
	case NL80211_CHAN_WIDTH_80P80:
	case NL80211_CHAN_WIDTH_160:
		bw = 3;
		break;
	default:
		bw = 0;
		break;
	}

	if (td->tx_rate_stbc && nss == 1) {
		nss++;
		rateval |= MT_TX_RATE_STBC;
	}

	rateval |= FIELD_PREP(MT_TX_RATE_IDX, rate_idx) |
		   FIELD_PREP(MT_TX_RATE_MODE, mode) |
		   FIELD_PREP(MT_TX_RATE_NSS, nss - 1);

	txwi[2] |= cpu_to_le32(MT_TXD2_FIX_RATE);

	le32p_replace_bits(&txwi[3], 1, MT_TXD3_REM_TX_COUNT);
	if (td->tx_rate_mode < MT76_TM_TX_MODE_HT)
		txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);

	val = MT_TXD6_FIXED_BW |
	      FIELD_PREP(MT_TXD6_BW, bw) |
	      FIELD_PREP(MT_TXD6_TX_RATE, rateval) |
	      FIELD_PREP(MT_TXD6_SGI, td->tx_rate_sgi);

	/* for HE_SU/HE_EXT_SU PPDU
	 * - 1x, 2x, 4x LTF + 0.8us GI
	 * - 2x LTF + 1.6us GI, 4x LTF + 3.2us GI
	 * for HE_MU PPDU
	 * - 2x, 4x LTF + 0.8us GI
	 * - 2x LTF + 1.6us GI, 4x LTF + 3.2us GI
	 * for HE_TB PPDU
	 * - 1x, 2x LTF + 1.6us GI
	 * - 4x LTF + 3.2us GI
	 */
	if (mode >= MT_PHY_TYPE_HE_SU)
		val |= FIELD_PREP(MT_TXD6_HELTF, td->tx_ltf);

	if (td->tx_rate_ldpc)
		val |= MT_TXD6_LDPC;

	txwi[6] |= cpu_to_le32(val);
	txwi[7] |= cpu_to_le32(FIELD_PREP(MT_TXD7_SPE_IDX,
					  dev->test.spe_idx));
#endif
}

static void
static void
mt7915_mac_write_txwi_8023(struct mt7915_dev *dev, __le32 *txwi,
mt7915_mac_write_txwi_8023(struct mt7915_dev *dev, __le32 *txwi,
			   struct sk_buff *skb, struct mt76_wcid *wcid)
			   struct sk_buff *skb, struct mt76_wcid *wcid)
@@ -761,6 +863,9 @@ void mt7915_mac_write_txwi(struct mt7915_dev *dev, __le32 *txwi,
		txwi[6] |= cpu_to_le32(val);
		txwi[6] |= cpu_to_le32(val);
		txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
		txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
	}
	}

	if (mt76_testmode_enabled(&dev->mt76))
		mt7915_mac_write_txwi_tm(dev, mphy, txwi, skb);
}
}


int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
@@ -881,6 +986,18 @@ mt7915_tx_complete_status(struct mt76_dev *mdev, struct sk_buff *skb,


	hw = mt76_tx_status_get_hw(mdev, skb);
	hw = mt76_tx_status_get_hw(mdev, skb);


#ifdef CONFIG_NL80211_TESTMODE
	if (skb == mdev->test.tx_skb) {
		struct mt7915_phy *phy = mt7915_hw_phy(hw);
		struct ieee80211_vif *vif = phy->monitor_vif;
		struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;

		mt76_tx_complete_skb(mdev, mvif->sta.wcid.idx, skb);

		return;
	}
#endif

	if (info->flags & IEEE80211_TX_CTL_AMPDU)
	if (info->flags & IEEE80211_TX_CTL_AMPDU)
		info->flags |= IEEE80211_TX_STAT_AMPDU;
		info->flags |= IEEE80211_TX_STAT_AMPDU;


+34 −6
Original line number Original line Diff line number Diff line
@@ -44,11 +44,12 @@ static int mt7915_start(struct ieee80211_hw *hw)
		mt7915_mac_enable_nf(dev, 1);
		mt7915_mac_enable_nf(dev, 1);
	}
	}


	mt7915_mcu_set_sku_en(phy, true);
	mt7915_mcu_set_sku_en(phy, !mt76_testmode_enabled(&dev->mt76));
	mt7915_mcu_set_chan_info(phy, MCU_EXT_CMD_SET_RX_PATH);
	mt7915_mcu_set_chan_info(phy, MCU_EXT_CMD_SET_RX_PATH);


	set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
	set_bit(MT76_STATE_RUNNING, &phy->mt76->state);


	if (!mt76_testmode_enabled(&dev->mt76))
		ieee80211_queue_delayed_work(hw, &phy->mac_work,
		ieee80211_queue_delayed_work(hw, &phy->mac_work,
					     MT7915_WATCHDOG_TIME);
					     MT7915_WATCHDOG_TIME);


@@ -69,6 +70,8 @@ static void mt7915_stop(struct ieee80211_hw *hw)


	mutex_lock(&dev->mt76.mutex);
	mutex_lock(&dev->mt76.mutex);


	mt76_testmode_reset(&dev->mt76, true);

	clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
	clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);


	if (phy != &dev->phy) {
	if (phy != &dev->phy) {
@@ -150,6 +153,12 @@ static int mt7915_add_interface(struct ieee80211_hw *hw,


	mutex_lock(&dev->mt76.mutex);
	mutex_lock(&dev->mt76.mutex);


	mt76_testmode_reset(&dev->mt76, true);

	if (vif->type == NL80211_IFTYPE_MONITOR &&
	    is_zero_ether_addr(vif->addr))
		phy->monitor_vif = vif;

	mvif->idx = ffs(~phy->mt76->vif_mask) - 1;
	mvif->idx = ffs(~phy->mt76->vif_mask) - 1;
	if (mvif->idx >= MT7915_MAX_INTERFACES) {
	if (mvif->idx >= MT7915_MAX_INTERFACES) {
		ret = -ENOSPC;
		ret = -ENOSPC;
@@ -218,6 +227,13 @@ static void mt7915_remove_interface(struct ieee80211_hw *hw,


	/* TODO: disable beacon for the bss */
	/* TODO: disable beacon for the bss */


	mutex_lock(&dev->mt76.mutex);
	mt76_testmode_reset(&dev->mt76, true);
	mutex_unlock(&dev->mt76.mutex);

	if (vif == phy->monitor_vif)
		phy->monitor_vif = NULL;

	mt7915_mcu_add_dev_info(phy, vif, false);
	mt7915_mcu_add_dev_info(phy, vif, false);


	rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
	rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
@@ -252,7 +268,7 @@ static void mt7915_init_dfs_state(struct mt7915_phy *phy)
	phy->dfs_state = -1;
	phy->dfs_state = -1;
}
}


static int mt7915_set_channel(struct mt7915_phy *phy)
int mt7915_set_channel(struct mt7915_phy *phy)
{
{
	struct mt7915_dev *dev = phy->dev;
	struct mt7915_dev *dev = phy->dev;
	int ret;
	int ret;
@@ -281,6 +297,8 @@ static int mt7915_set_channel(struct mt7915_phy *phy)
	mutex_unlock(&dev->mt76.mutex);
	mutex_unlock(&dev->mt76.mutex);


	mt76_txq_schedule_all(phy->mt76);
	mt76_txq_schedule_all(phy->mt76);

	if (!mt76_testmode_enabled(&dev->mt76))
		ieee80211_queue_delayed_work(phy->mt76->hw, &phy->mac_work,
		ieee80211_queue_delayed_work(phy->mt76->hw, &phy->mac_work,
					     MT7915_WATCHDOG_TIME);
					     MT7915_WATCHDOG_TIME);


@@ -346,6 +364,13 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed)
	int ret;
	int ret;


	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
#ifdef CONFIG_NL80211_TESTMODE
		if (dev->mt76.test.state != MT76_TM_STATE_OFF) {
			mutex_lock(&dev->mt76.mutex);
			mt76_testmode_reset(&dev->mt76, false);
			mutex_unlock(&dev->mt76.mutex);
		}
#endif
		ieee80211_stop_queues(hw);
		ieee80211_stop_queues(hw);
		ret = mt7915_set_channel(phy);
		ret = mt7915_set_channel(phy);
		if (ret)
		if (ret)
@@ -370,6 +395,7 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed)
			phy->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC;
			phy->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC;


		mt76_rmw_field(dev, MT_DMA_DCR0, MT_DMA_DCR0_RXD_G5_EN, enabled);
		mt76_rmw_field(dev, MT_DMA_DCR0, MT_DMA_DCR0_RXD_G5_EN, enabled);
		mt76_testmode_reset(&dev->mt76, true);
		mt76_wr(dev, MT_WF_RFCR(band), phy->rxfilter);
		mt76_wr(dev, MT_WF_RFCR(band), phy->rxfilter);
	}
	}


@@ -887,6 +913,8 @@ const struct ieee80211_ops mt7915_ops = {
	.set_coverage_class = mt7915_set_coverage_class,
	.set_coverage_class = mt7915_set_coverage_class,
	.sta_statistics = mt7915_sta_statistics,
	.sta_statistics = mt7915_sta_statistics,
	.sta_set_4addr = mt7915_sta_set_4addr,
	.sta_set_4addr = mt7915_sta_set_4addr,
	CFG80211_TESTMODE_CMD(mt76_testmode_cmd)
	CFG80211_TESTMODE_DUMP(mt76_testmode_dump)
#ifdef CONFIG_MAC80211_DEBUGFS
#ifdef CONFIG_MAC80211_DEBUGFS
	.sta_add_debugfs = mt7915_sta_add_debugfs,
	.sta_add_debugfs = mt7915_sta_add_debugfs,
#endif
#endif
+31 −0
Original line number Original line Diff line number Diff line
@@ -3166,6 +3166,15 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd)
		.channel_band = chandef->chan->band,
		.channel_band = chandef->chan->band,
	};
	};


#ifdef CONFIG_NL80211_TESTMODE
	if (dev->mt76.test.tx_antenna_mask &&
	    (dev->mt76.test.state == MT76_TM_STATE_TX_FRAMES ||
	     dev->mt76.test.state == MT76_TM_STATE_RX_FRAMES)) {
		req.tx_streams_num = fls(dev->mt76.test.tx_antenna_mask);
		req.rx_streams = dev->mt76.test.tx_antenna_mask;
	}
#endif

	if (dev->mt76.hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
	if (dev->mt76.hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
		req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
		req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
	else if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) &&
	else if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) &&
@@ -3287,6 +3296,28 @@ int mt7915_mcu_set_sku(struct mt7915_phy *phy)
				 sizeof(req), true);
				 sizeof(req), true);
}
}


int mt7915_mcu_set_test_param(struct mt7915_dev *dev, u8 param, bool test_mode,
			      u8 en)
{
	struct {
		u8 test_mode_en;
		u8 param_idx;
		u8 _rsv[2];

		u8 enable;
		u8 _rsv2[3];

		u8 pad[8];
	} __packed req = {
		.test_mode_en = test_mode,
		.param_idx = param,
		.enable = en,
	};

	return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_ATE_CTRL, &req,
				 sizeof(req), false);
}

int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable)
int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable)
{
{
	struct mt7915_dev *dev = phy->dev;
	struct mt7915_dev *dev = phy->dev;
Loading