Skip to content

Commit

Permalink
mt76: mt7915: implement testmode tx support
Browse files Browse the repository at this point in the history
Support testmode tx for MT7915A, including tx streams and HE rate
settings.

Reviewed-by: Ryder Lee <ryder.lee@mediatek.com>
Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
  • Loading branch information
Shayne Chen authored and Felix Fietkau committed Dec 4, 2020
1 parent 61fe735 commit aadf095
Show file tree
Hide file tree
Showing 10 changed files with 453 additions and 9 deletions.
2 changes: 2 additions & 0 deletions drivers/net/wireless/mediatek/mt76/mt7915/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
debugfs.o

mt7915e-$(CONFIG_NL80211_TESTMODE) += testmode.o
8 changes: 5 additions & 3 deletions drivers/net/wireless/mediatek/mt76/mt7915/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ mt7915_init_wiphy(struct ieee80211_hw *hw)

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

hw->max_tx_fragments = 4;
}
Expand Down Expand Up @@ -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] ^= 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);
if (ret)
ieee80211_free_hw(mphy->hw);
Expand Down Expand Up @@ -678,6 +676,10 @@ int mt7915_register_device(struct mt7915_dev *dev)
mt7915_cap_dbdc_disable(dev);
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,
ARRAY_SIZE(mt7915_rates));
if (ret)
Expand Down
117 changes: 117 additions & 0 deletions drivers/net/wireless/mediatek/mt76/mt7915/mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,108 @@ int mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb)
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
mt7915_mac_write_txwi_8023(struct mt7915_dev *dev, __le32 *txwi,
struct sk_buff *skb, struct mt76_wcid *wcid)
Expand Down Expand Up @@ -761,6 +863,9 @@ void mt7915_mac_write_txwi(struct mt7915_dev *dev, __le32 *txwi,
txwi[6] |= cpu_to_le32(val);
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,
Expand Down Expand Up @@ -881,6 +986,18 @@ mt7915_tx_complete_status(struct mt76_dev *mdev, struct sk_buff *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)
info->flags |= IEEE80211_TX_STAT_AMPDU;

Expand Down
40 changes: 34 additions & 6 deletions drivers/net/wireless/mediatek/mt76/mt7915/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,14 @@ static int mt7915_start(struct ieee80211_hw *hw)
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);

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

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

if (!running)
mt7915_mac_reset_counters(phy);
Expand All @@ -69,6 +70,8 @@ static void mt7915_stop(struct ieee80211_hw *hw)

mutex_lock(&dev->mt76.mutex);

mt76_testmode_reset(&dev->mt76, true);

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

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

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;
if (mvif->idx >= MT7915_MAX_INTERFACES) {
ret = -ENOSPC;
Expand Down Expand Up @@ -218,6 +227,13 @@ static void mt7915_remove_interface(struct ieee80211_hw *hw,

/* 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);

rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
Expand Down Expand Up @@ -252,7 +268,7 @@ static void mt7915_init_dfs_state(struct mt7915_phy *phy)
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;
int ret;
Expand Down Expand Up @@ -281,8 +297,10 @@ static int mt7915_set_channel(struct mt7915_phy *phy)
mutex_unlock(&dev->mt76.mutex);

mt76_txq_schedule_all(phy->mt76);
ieee80211_queue_delayed_work(phy->mt76->hw, &phy->mac_work,
MT7915_WATCHDOG_TIME);

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

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

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);
ret = mt7915_set_channel(phy);
if (ret)
Expand All @@ -370,6 +395,7 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed)
phy->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC;

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

Expand Down Expand Up @@ -887,6 +913,8 @@ const struct ieee80211_ops mt7915_ops = {
.set_coverage_class = mt7915_set_coverage_class,
.sta_statistics = mt7915_sta_statistics,
.sta_set_4addr = mt7915_sta_set_4addr,
CFG80211_TESTMODE_CMD(mt76_testmode_cmd)
CFG80211_TESTMODE_DUMP(mt76_testmode_dump)
#ifdef CONFIG_MAC80211_DEBUGFS
.sta_add_debugfs = mt7915_sta_add_debugfs,
#endif
Expand Down
31 changes: 31 additions & 0 deletions drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
Original file line number Diff line number Diff line change
Expand Up @@ -3166,6 +3166,15 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd)
.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)
req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
else if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) &&
Expand Down Expand Up @@ -3287,6 +3296,28 @@ int mt7915_mcu_set_sku(struct mt7915_phy *phy)
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)
{
struct mt7915_dev *dev = phy->dev;
Expand Down
5 changes: 5 additions & 0 deletions drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ enum {
MCU_EXT_EVENT_RATE_REPORT = 0x87,
};

enum {
MCU_ATE_SET_TRX = 0x1,
};

struct mt7915_mcu_rxd {
__le32 rxd[6];

Expand Down Expand Up @@ -216,6 +220,7 @@ enum {
MCU_EXT_CMD_WTBL_UPDATE = 0x32,
MCU_EXT_CMD_SET_DRR_CTRL = 0x36,
MCU_EXT_CMD_SET_RDD_CTRL = 0x3a,
MCU_EXT_CMD_ATE_CTRL = 0x3d,
MCU_EXT_CMD_PROTECT_CTRL = 0x3e,
MCU_EXT_CMD_MAC_INIT_CTRL = 0x46,
MCU_EXT_CMD_RX_HDR_TRANS = 0x47,
Expand Down
14 changes: 14 additions & 0 deletions drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ struct mt7915_phy {

struct ieee80211_sband_iftype_data iftype[2][NUM_NL80211_IFTYPES];

struct ieee80211_vif *monitor_vif;

u32 rxfilter;
u64 omac_mask;

Expand Down Expand Up @@ -164,6 +166,14 @@ struct mt7915_dev {
s8 **rate_power; /* TODO: use mt76_rate_power */

bool fw_debug;

#ifdef CONFIG_NL80211_TESTMODE
struct {
u32 *reg_backup;

u8 spe_idx;
} test;
#endif
};

enum {
Expand Down Expand Up @@ -253,6 +263,7 @@ static inline u8 mt7915_lmac_mapping(struct mt7915_dev *dev, u8 ac)

extern const struct ieee80211_ops mt7915_ops;
extern struct pci_driver mt7915_pci_driver;
extern const struct mt76_testmode_ops mt7915_testmode_ops;

u32 mt7915_reg_map(struct mt7915_dev *dev, u32 addr);

Expand Down Expand Up @@ -298,6 +309,7 @@ int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
int mt7915_mcu_add_smps(struct mt7915_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
int mt7915_set_channel(struct mt7915_phy *phy);
int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd);
int mt7915_mcu_set_tx(struct mt7915_dev *dev, struct ieee80211_vif *vif);
int mt7915_mcu_set_fixed_rate(struct mt7915_dev *dev,
Expand All @@ -306,6 +318,8 @@ int mt7915_mcu_set_eeprom(struct mt7915_dev *dev);
int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset);
int mt7915_mcu_set_mac(struct mt7915_dev *dev, int band, bool enable,
bool hdr_trans);
int mt7915_mcu_set_test_param(struct mt7915_dev *dev, u8 param, bool test_mode,
u8 en);
int mt7915_mcu_set_scs(struct mt7915_dev *dev, u8 band, bool enable);
int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band);
int mt7915_mcu_set_rts_thresh(struct mt7915_phy *phy, u32 val);
Expand Down
Loading

0 comments on commit aadf095

Please sign in to comment.