Change ib_rssi and wb_rssi into array type, since they could be reported
by per-antenna.
Reviewed-by: Ryder Lee <[email protected]>
Signed-off-by: Shayne Chen <[email protected]>
---
v2: move Reviewed-by before s-o-b
.../net/wireless/mediatek/mt76/mt7615/mac.c | 4 ++--
.../wireless/mediatek/mt76/mt7615/mt7615.h | 4 ++--
.../wireless/mediatek/mt76/mt7615/testmode.c | 24 ++++++++++++++++---
drivers/net/wireless/mediatek/mt76/testmode.h | 4 ++--
4 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
index 8dc645e..9aad2ee 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
@@ -215,8 +215,8 @@ static void mt7615_mac_fill_tm_rx(struct mt7615_dev *dev, __le32 *rxv)
dev->test.last_rcpi[1] = FIELD_GET(MT_RXV4_RCPI1, rxv4);
dev->test.last_rcpi[2] = FIELD_GET(MT_RXV4_RCPI2, rxv4);
dev->test.last_rcpi[3] = FIELD_GET(MT_RXV4_RCPI3, rxv4);
- dev->test.last_ib_rssi = FIELD_GET(MT_RXV3_IB_RSSI, rxv3);
- dev->test.last_wb_rssi = FIELD_GET(MT_RXV3_WB_RSSI, rxv3);
+ dev->test.last_ib_rssi[0] = FIELD_GET(MT_RXV3_IB_RSSI, rxv3);
+ dev->test.last_wb_rssi[0] = FIELD_GET(MT_RXV3_WB_RSSI, rxv3);
#endif
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
index f72506d..50ea9ef 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
@@ -303,8 +303,8 @@ struct mt7615_dev {
s16 last_freq_offset;
u8 last_rcpi[4];
- s8 last_ib_rssi;
- s8 last_wb_rssi;
+ s8 last_ib_rssi[4];
+ s8 last_wb_rssi[4];
} test;
#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7615/testmode.c
index 1f33628..8fc97a5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/testmode.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/testmode.c
@@ -335,9 +335,7 @@ mt7615_tm_dump_stats(struct mt76_dev *mdev, struct sk_buff *msg)
if (!rx)
return -ENOMEM;
- if (nla_put_s32(msg, MT76_TM_RX_ATTR_FREQ_OFFSET, dev->test.last_freq_offset) ||
- nla_put_s32(msg, MT76_TM_RX_ATTR_IB_RSSI, dev->test.last_ib_rssi) ||
- nla_put_s32(msg, MT76_TM_RX_ATTR_WB_RSSI, dev->test.last_wb_rssi))
+ if (nla_put_s32(msg, MT76_TM_RX_ATTR_FREQ_OFFSET, dev->test.last_freq_offset))
return -ENOMEM;
rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RCPI);
@@ -350,6 +348,26 @@ mt7615_tm_dump_stats(struct mt76_dev *mdev, struct sk_buff *msg)
nla_nest_end(msg, rssi);
+ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_IB_RSSI);
+ if (!rssi)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(dev->test.last_ib_rssi); i++)
+ if (nla_put_s8(msg, i, dev->test.last_ib_rssi[i]))
+ return -ENOMEM;
+
+ nla_nest_end(msg, rssi);
+
+ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_WB_RSSI);
+ if (!rssi)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(dev->test.last_wb_rssi); i++)
+ if (nla_put_s8(msg, i, dev->test.last_wb_rssi[i]))
+ return -ENOMEM;
+
+ nla_nest_end(msg, rssi);
+
nla_nest_end(msg, rx);
return 0;
diff --git a/drivers/net/wireless/mediatek/mt76/testmode.h b/drivers/net/wireless/mediatek/mt76/testmode.h
index 691fe57..02c9495 100644
--- a/drivers/net/wireless/mediatek/mt76/testmode.h
+++ b/drivers/net/wireless/mediatek/mt76/testmode.h
@@ -99,8 +99,8 @@ enum mt76_testmode_stats_attr {
*
* @MT76_TM_RX_ATTR_FREQ_OFFSET: frequency offset (s32)
* @MT76_TM_RX_ATTR_RCPI: received channel power indicator (array, u8)
- * @MT76_TM_RX_ATTR_IB_RSSI: internal inband RSSI (s8)
- * @MT76_TM_RX_ATTR_WB_RSSI: internal wideband RSSI (s8)
+ * @MT76_TM_RX_ATTR_IB_RSSI: internal inband RSSI (array, s8)
+ * @MT76_TM_RX_ATTR_WB_RSSI: internal wideband RSSI (array, s8)
*/
enum mt76_testmode_rx_attr {
MT76_TM_RX_ATTR_UNSPEC,
--
2.17.1
Support testmode rx and display rx statistic by parsing RXV packet
type, which is currently only enabled in testmode.
Reviewed-by: Ryder Lee <[email protected]>
Signed-off-by: Shayne Chen <[email protected]>
---
v2: change last_snr to u8
v3: use nla_put_u8 for MT76_TM_RX_ATTR_SNR
change snr variable type to u8 in mt7915_mac_fill_rx_vector
.../net/wireless/mediatek/mt76/mt7915/dma.c | 3 +
.../net/wireless/mediatek/mt76/mt7915/mac.c | 38 ++++++++
.../net/wireless/mediatek/mt76/mt7915/mac.h | 5 +
.../net/wireless/mediatek/mt76/mt7915/mcu.h | 1 +
.../wireless/mediatek/mt76/mt7915/mt7915.h | 7 ++
.../wireless/mediatek/mt76/mt7915/testmode.c | 97 +++++++++++++++++++
.../wireless/mediatek/mt76/mt7915/testmode.h | 30 ++++++
7 files changed, 181 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
index cfa12c4..e14814d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
@@ -61,6 +61,9 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
case PKT_TYPE_RX_EVENT:
mt7915_mcu_rx_event(dev, skb);
break;
+ case PKT_TYPE_TXRXV:
+ mt7915_mac_fill_rx_vector(dev, skb);
+ break;
case PKT_TYPE_NORMAL:
if (!mt7915_mac_fill_rx(dev, skb)) {
mt76_rx(&dev->mt76, q, skb);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index e03e12f..7bfdf54 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -562,6 +562,44 @@ int mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb)
return 0;
}
+void mt7915_mac_fill_rx_vector(struct mt7915_dev *dev, struct sk_buff *skb)
+{
+#ifdef CONFIG_NL80211_TESTMODE
+ __le32 *rxd = (__le32 *)skb->data;
+ __le32 *rxv = rxd + 4;
+ u32 rcpi, ib_rssi, wb_rssi, v20, v21;
+ s32 foe;
+ u8 snr;
+ int i;
+
+ rcpi = le32_to_cpu(rxv[6]);
+ ib_rssi = le32_to_cpu(rxv[7]);
+ wb_rssi = le32_to_cpu(rxv[8]) >> 5;
+
+ for (i = 0; i < 4; i++, rcpi >>= 8, ib_rssi >>= 8, wb_rssi >>= 9) {
+ if (i == 3)
+ wb_rssi = le32_to_cpu(rxv[9]);
+
+ dev->test.last_rcpi[i] = rcpi & 0xff;
+ dev->test.last_ib_rssi[i] = ib_rssi & 0xff;
+ dev->test.last_wb_rssi[i] = wb_rssi & 0xff;
+ }
+
+ v20 = le32_to_cpu(rxv[20]);
+ v21 = le32_to_cpu(rxv[21]);
+
+ foe = FIELD_GET(MT_CRXV_FOE_LO, v20) |
+ (FIELD_GET(MT_CRXV_FOE_HI, v21) << MT_CRXV_FOE_SHIFT);
+
+ snr = FIELD_GET(MT_CRXV_SNR, v20) - 16;
+
+ dev->test.last_freq_offset = foe;
+ dev->test.last_snr = snr;
+
+ dev_kfree_skb(skb);
+#endif
+}
+
static u16
mt7915_mac_tx_rate_val(struct mt76_phy *mphy, u8 mode, u8 rate_idx,
u8 nss, u8 stbc, u8 *bw)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
index 0921b6f..d420392 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
@@ -128,6 +128,11 @@ enum rx_pkt_type {
#define MT_CRXV_HE_BEAM_CHNG BIT(13)
#define MT_CRXV_HE_DOPPLER BIT(16)
+#define MT_CRXV_SNR GENMASK(18, 13)
+#define MT_CRXV_FOE_LO GENMASK(31, 19)
+#define MT_CRXV_FOE_HI GENMASK(6, 0)
+#define MT_CRXV_FOE_SHIFT 13
+
enum tx_header_format {
MT_HDR_FORMAT_802_3,
MT_HDR_FORMAT_CMD,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
index 0a7e9d2..89453a6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
@@ -48,6 +48,7 @@ enum {
enum {
MCU_ATE_SET_TRX = 0x1,
+ MCU_ATE_SET_RX_FILTER = 0x3,
};
struct mt7915_mcu_rxd {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
index 6735915..fd7de79 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
@@ -165,6 +165,12 @@ struct mt7915_dev {
struct {
u32 *reg_backup;
+ s32 last_freq_offset;
+ u8 last_rcpi[4];
+ s8 last_ib_rssi[4];
+ s8 last_wb_rssi[4];
+ u8 last_snr;
+
u8 spe_idx;
} test;
#endif
@@ -436,6 +442,7 @@ void mt7915_mac_write_txwi(struct mt7915_dev *dev, __le32 *txwi,
struct ieee80211_key_conf *key, bool beacon);
void mt7915_mac_set_timing(struct mt7915_phy *phy);
int mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb);
+void mt7915_mac_fill_rx_vector(struct mt7915_dev *dev, struct sk_buff *skb);
void mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb);
int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
index 5d95766..acab268 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
@@ -113,6 +113,31 @@ mt7915_tm_reg_backup_restore(struct mt7915_dev *dev, struct mt7915_phy *phy)
mt76_clear(dev, MT_TMAC_TCR0(0), MT_TMAC_TCR0_TBTT_STOP_CTRL);
}
+static int
+mt7915_tm_config_rx_filter(struct mt7915_dev *dev, bool en)
+{
+ struct mt7915_tm_cmd req = {
+ .testmode_en = 1,
+ .param_idx = MCU_ATE_SET_RX_FILTER,
+ .param.filter.report_en = en,
+ .param.filter.band = 0, /* TODO: support dbdc */
+ };
+ __le32 mask = RX_FILTER_NOT_OWN_BTIM |
+ RX_FILTER_NOT_OWN_UCAST |
+ RX_FILTER_RTS | RX_FILTER_CTS |
+ RX_FILTER_CTRL_RSV |
+ RX_FILTER_BC_MC_BSSID_A2 |
+ RX_FILTER_BC_MC_BSSID_A3 |
+ RX_FILTER_BC_MC_OMAC_A3 |
+ RX_FILTER_PROTOCOL_VERSION |
+ RX_FILTER_FCS_ERR;
+
+ req.param.filter.mask = cpu_to_le32(mask);
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_ATE_CTRL, &req,
+ sizeof(req), false);
+}
+
static void
mt7915_tm_init(struct mt7915_dev *dev)
{
@@ -124,6 +149,7 @@ mt7915_tm_init(struct mt7915_dev *dev)
mt7915_tm_mode_ctrl(dev, en);
mt7915_tm_reg_backup_restore(dev, &dev->phy);
mt7915_tm_set_trx(dev, &dev->phy, TM_MAC_TXRX, !en);
+ mt7915_tm_config_rx_filter(dev, en);
}
static void
@@ -156,6 +182,20 @@ mt7915_tm_set_tx_frames(struct mt7915_dev *dev, bool en)
info->control.vif = dev->phy.monitor_vif;
}
+static void
+mt7915_tm_set_rx_frames(struct mt7915_dev *dev, bool en)
+{
+ if (en) {
+ mutex_unlock(&dev->mt76.mutex);
+ mt7915_set_channel(&dev->phy);
+ mutex_lock(&dev->mt76.mutex);
+
+ mt7915_mcu_set_chan_info(&dev->phy, MCU_EXT_CMD_SET_RX_PATH);
+ }
+
+ mt7915_tm_set_trx(dev, &dev->phy, TM_MAC_RX_RXV, en);
+}
+
static int
mt7915_tm_set_state(struct mt76_dev *mdev, enum mt76_testmode_state state)
{
@@ -169,12 +209,69 @@ mt7915_tm_set_state(struct mt76_dev *mdev, enum mt76_testmode_state state)
mt7915_tm_set_tx_frames(dev, false);
else if (state == MT76_TM_STATE_TX_FRAMES)
mt7915_tm_set_tx_frames(dev, true);
+ else if (prev_state == MT76_TM_STATE_RX_FRAMES)
+ mt7915_tm_set_rx_frames(dev, false);
+ else if (state == MT76_TM_STATE_RX_FRAMES)
+ mt7915_tm_set_rx_frames(dev, true);
else if (prev_state == MT76_TM_STATE_OFF || state == MT76_TM_STATE_OFF)
mt7915_tm_init(dev);
return 0;
}
+static int
+mt7915_tm_dump_stats(struct mt76_dev *mdev, struct sk_buff *msg)
+{
+ struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
+ void *rx, *rssi;
+ int i;
+
+ rx = nla_nest_start(msg, MT76_TM_STATS_ATTR_LAST_RX);
+ if (!rx)
+ return -ENOMEM;
+
+ if (nla_put_s32(msg, MT76_TM_RX_ATTR_FREQ_OFFSET, dev->test.last_freq_offset))
+ return -ENOMEM;
+
+ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RCPI);
+ if (!rssi)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(dev->test.last_rcpi); i++)
+ if (nla_put_u8(msg, i, dev->test.last_rcpi[i]))
+ return -ENOMEM;
+
+ nla_nest_end(msg, rssi);
+
+ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_IB_RSSI);
+ if (!rssi)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(dev->test.last_ib_rssi); i++)
+ if (nla_put_s8(msg, i, dev->test.last_ib_rssi[i]))
+ return -ENOMEM;
+
+ nla_nest_end(msg, rssi);
+
+ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_WB_RSSI);
+ if (!rssi)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(dev->test.last_wb_rssi); i++)
+ if (nla_put_s8(msg, i, dev->test.last_wb_rssi[i]))
+ return -ENOMEM;
+
+ nla_nest_end(msg, rssi);
+
+ if (nla_put_u8(msg, MT76_TM_RX_ATTR_SNR, dev->test.last_snr))
+ return -ENOMEM;
+
+ nla_nest_end(msg, rx);
+
+ return 0;
+}
+
const struct mt76_testmode_ops mt7915_testmode_ops = {
.set_state = mt7915_tm_set_state,
+ .dump_stats = mt7915_tm_dump_stats,
};
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h
index 04f4a2c..b344a64 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h
@@ -11,6 +11,15 @@ struct mt7915_tm_trx {
u8 rsv;
};
+struct mt7915_tm_rx_filter {
+ u8 promiscuous;
+ u8 report_en;
+ u8 band;
+ u8 _rsv;
+ __le32 mask;
+ u8 _rsv1[4];
+};
+
struct mt7915_tm_cmd {
u8 testmode_en;
u8 param_idx;
@@ -18,6 +27,7 @@ struct mt7915_tm_cmd {
union {
__le32 data;
struct mt7915_tm_trx trx;
+ struct mt7915_tm_rx_filter filter;
u8 test[72];
} param;
} __packed;
@@ -31,4 +41,24 @@ enum {
TM_MAC_RX_RXV,
};
+#define RX_FILTER_STBC_BCN_BC_MC BIT(0)
+#define RX_FILTER_FCS_ERR BIT(1)
+#define RX_FILTER_PROTOCOL_VERSION BIT(2)
+#define RX_FILTER_PROB_REQ BIT(3)
+#define RX_FILTER_MCAST BIT(4)
+#define RX_FILTER_BCAST BIT(5)
+#define RX_FILTER_MCAST_TABLE BIT(6)
+#define RX_FILTER_BC_MC_OMAC_A3 BIT(7)
+#define RX_FILTER_BC_MC_BSSID_A3 BIT(8)
+#define RX_FILTER_BC_MC_BSSID_A2 BIT(9)
+#define RX_FILTER_BCN_BSSID BIT(10)
+#define RX_FILTER_CTRL_RSV BIT(11)
+#define RX_FILTER_CTS BIT(12)
+#define RX_FILTER_RTS BIT(13)
+#define RX_FILTER_DUPLICATE BIT(14)
+#define RX_FILTER_NOT_OWN_BSSID BIT(15)
+#define RX_FILTER_NOT_OWN_UCAST BIT(16)
+#define RX_FILTER_NOT_OWN_BTIM BIT(17)
+#define RX_FILTER_NDPA BIT(18)
+
#endif
--
2.17.1
Support to display snr value when dumping rx statistics.
Reviewed-by: Ryder Lee <[email protected]>
Signed-off-by: Shayne Chen <[email protected]>
---
v2: change snr attribute description to u8
drivers/net/wireless/mediatek/mt76/testmode.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/testmode.h b/drivers/net/wireless/mediatek/mt76/testmode.h
index 02c9495..4d22627 100644
--- a/drivers/net/wireless/mediatek/mt76/testmode.h
+++ b/drivers/net/wireless/mediatek/mt76/testmode.h
@@ -101,6 +101,7 @@ enum mt76_testmode_stats_attr {
* @MT76_TM_RX_ATTR_RCPI: received channel power indicator (array, u8)
* @MT76_TM_RX_ATTR_IB_RSSI: internal inband RSSI (array, s8)
* @MT76_TM_RX_ATTR_WB_RSSI: internal wideband RSSI (array, s8)
+ * @MT76_TM_RX_ATTR_SNR: signal-to-noise ratio (u8)
*/
enum mt76_testmode_rx_attr {
MT76_TM_RX_ATTR_UNSPEC,
@@ -109,6 +110,7 @@ enum mt76_testmode_rx_attr {
MT76_TM_RX_ATTR_RCPI,
MT76_TM_RX_ATTR_IB_RSSI,
MT76_TM_RX_ATTR_WB_RSSI,
+ MT76_TM_RX_ATTR_SNR,
/* keep last */
NUM_MT76_TM_RX_ATTRS,
--
2.17.1
Prepare for setting different LTF (1x, 2x, and 4x) and GI (0.8us,
1.6us, 3.2us) combinations in HE rate mode.
Reviewed-by: Ryder Lee <[email protected]>
Signed-off-by: Shayne Chen <[email protected]>
---
drivers/net/wireless/mediatek/mt76/mt76.h | 1 +
drivers/net/wireless/mediatek/mt76/testmode.c | 6 +++++-
drivers/net/wireless/mediatek/mt76/testmode.h | 2 ++
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 9ff249e..a0f225c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -528,6 +528,7 @@ struct mt76_testmode_data {
u8 tx_rate_sgi;
u8 tx_rate_ldpc;
u8 tx_rate_stbc;
+ u8 tx_ltf;
u8 tx_antenna_mask;
diff --git a/drivers/net/wireless/mediatek/mt76/testmode.c b/drivers/net/wireless/mediatek/mt76/testmode.c
index 6b59e08..f37a460 100644
--- a/drivers/net/wireless/mediatek/mt76/testmode.c
+++ b/drivers/net/wireless/mediatek/mt76/testmode.c
@@ -12,6 +12,7 @@ static const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
[MT76_TM_ATTR_TX_RATE_SGI] = { .type = NLA_U8 },
[MT76_TM_ATTR_TX_RATE_LDPC] = { .type = NLA_U8 },
[MT76_TM_ATTR_TX_RATE_STBC] = { .type = NLA_U8 },
+ [MT76_TM_ATTR_TX_LTF] = { .type = NLA_U8 },
[MT76_TM_ATTR_TX_ANTENNA] = { .type = NLA_U8 },
[MT76_TM_ATTR_TX_POWER_CONTROL] = { .type = NLA_U8 },
[MT76_TM_ATTR_TX_POWER] = { .type = NLA_NESTED },
@@ -338,9 +339,10 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
0, MT76_TM_TX_MODE_MAX) ||
mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_NSS], &td->tx_rate_nss,
1, hweight8(phy->antenna_mask)) ||
- mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_SGI], &td->tx_rate_sgi, 0, 1) ||
+ mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_SGI], &td->tx_rate_sgi, 0, 2) ||
mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_LDPC], &td->tx_rate_ldpc, 0, 1) ||
mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_STBC], &td->tx_rate_stbc, 0, 1) ||
+ mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_LTF], &td->tx_ltf, 0, 2) ||
mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_ANTENNA], &td->tx_antenna_mask, 1,
phy->antenna_mask) ||
mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL],
@@ -478,6 +480,8 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_SGI, td->tx_rate_sgi) ||
nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, td->tx_rate_ldpc) ||
nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) ||
+ (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) &&
+ nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
(mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA, td->tx_antenna_mask)) ||
(mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER_CONTROL) &&
diff --git a/drivers/net/wireless/mediatek/mt76/testmode.h b/drivers/net/wireless/mediatek/mt76/testmode.h
index 00c1659..3c09aa5 100644
--- a/drivers/net/wireless/mediatek/mt76/testmode.h
+++ b/drivers/net/wireless/mediatek/mt76/testmode.h
@@ -26,6 +26,7 @@
* @MT76_TM_ATTR_TX_RATE_SGI: packet tx use short guard interval (u8)
* @MT76_TM_ATTR_TX_RATE_LDPC: packet tx enable LDPC (u8)
* @MT76_TM_ATTR_TX_RATE_STBC: packet tx enable STBC (u8)
+ * @MT76_TM_ATTR_TX_LTF: packet tx LTF, set 0 to 2 for 1x, 2x, and 4x LTF (u8)
*
* @MT76_TM_ATTR_TX_ANTENNA: tx antenna mask (u8)
* @MT76_TM_ATTR_TX_POWER_CONTROL: enable tx power control (u8)
@@ -52,6 +53,7 @@ enum mt76_testmode_attr {
MT76_TM_ATTR_TX_RATE_SGI,
MT76_TM_ATTR_TX_RATE_LDPC,
MT76_TM_ATTR_TX_RATE_STBC,
+ MT76_TM_ATTR_TX_LTF,
MT76_TM_ATTR_TX_ANTENNA,
MT76_TM_ATTR_TX_POWER_CONTROL,
--
2.17.1
Extend enum mt76_testmode_tx_mode to support various HE rate modes.
Reviewed-by: Ryder Lee <[email protected]>
Signed-off-by: Shayne Chen <[email protected]>
---
drivers/net/wireless/mediatek/mt76/testmode.c | 6 +++++-
drivers/net/wireless/mediatek/mt76/testmode.h | 8 ++++++++
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/testmode.c b/drivers/net/wireless/mediatek/mt76/testmode.c
index f37a460..d1b1716 100644
--- a/drivers/net/wireless/mediatek/mt76/testmode.c
+++ b/drivers/net/wireless/mediatek/mt76/testmode.c
@@ -84,6 +84,10 @@ mt76_testmode_tx_init(struct mt76_dev *dev)
info->flags = IEEE80211_TX_CTL_INJECTED |
IEEE80211_TX_CTL_NO_ACK |
IEEE80211_TX_CTL_NO_PS_BUFFER;
+
+ if (td->tx_rate_mode > MT76_TM_TX_MODE_VHT)
+ goto out;
+
rate = &info->control.rates[0];
rate->count = 1;
rate->idx = td->tx_rate_idx;
@@ -152,7 +156,7 @@ mt76_testmode_tx_init(struct mt76_dev *dev)
break;
}
}
-
+out:
skb_set_queue_mapping(skb, IEEE80211_AC_BE);
return 0;
diff --git a/drivers/net/wireless/mediatek/mt76/testmode.h b/drivers/net/wireless/mediatek/mt76/testmode.h
index 3c09aa5..7efad68 100644
--- a/drivers/net/wireless/mediatek/mt76/testmode.h
+++ b/drivers/net/wireless/mediatek/mt76/testmode.h
@@ -147,12 +147,20 @@ enum mt76_testmode_state {
* @MT76_TM_TX_MODE_OFDM: legacy OFDM mode
* @MT76_TM_TX_MODE_HT: 802.11n MCS
* @MT76_TM_TX_MODE_VHT: 802.11ac MCS
+ * @MT76_TM_TX_MODE_HE_SU: 802.11ax single-user MIMO
+ * @MT76_TM_TX_MODE_HE_EXT_SU: 802.11ax extended-range SU
+ * @MT76_TM_TX_MODE_HE_TB: 802.11ax trigger-based
+ * @MT76_TM_TX_MODE_HE_MU: 802.11ax multi-user MIMO
*/
enum mt76_testmode_tx_mode {
MT76_TM_TX_MODE_CCK,
MT76_TM_TX_MODE_OFDM,
MT76_TM_TX_MODE_HT,
MT76_TM_TX_MODE_VHT,
+ MT76_TM_TX_MODE_HE_SU,
+ MT76_TM_TX_MODE_HE_EXT_SU,
+ MT76_TM_TX_MODE_HE_TB,
+ MT76_TM_TX_MODE_HE_MU,
/* keep last */
NUM_MT76_TM_TX_MODES,
--
2.17.1
Support testmode tx for MT7915A, including tx streams and HE rate
settings.
Reviewed-by: Ryder Lee <[email protected]>
Signed-off-by: Shayne Chen <[email protected]>
---
v3: add __packed for req struct in mt7915_tm_mode_ctrl
move the location of setting WANT_MONITOR_VIF flag
.../wireless/mediatek/mt76/mt7915/Makefile | 2 +
.../net/wireless/mediatek/mt76/mt7915/init.c | 8 +-
.../net/wireless/mediatek/mt76/mt7915/mac.c | 127 ++++++++++++
.../net/wireless/mediatek/mt76/mt7915/main.c | 40 +++-
.../net/wireless/mediatek/mt76/mt7915/mcu.c | 31 +++
.../net/wireless/mediatek/mt76/mt7915/mcu.h | 5 +
.../wireless/mediatek/mt76/mt7915/mt7915.h | 14 ++
.../net/wireless/mediatek/mt76/mt7915/regs.h | 31 +++
.../wireless/mediatek/mt76/mt7915/testmode.c | 180 ++++++++++++++++++
.../wireless/mediatek/mt76/mt7915/testmode.h | 34 ++++
10 files changed, 463 insertions(+), 9 deletions(-)
create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/testmode.h
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile
index 57fe726..cc2054d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile
@@ -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
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index ee69fe4..890c2bb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -270,6 +270,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;
}
@@ -637,9 +638,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);
@@ -695,6 +693,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)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index a7118df..e03e12f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -562,6 +562,118 @@ int mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb)
return 0;
}
+static u16
+mt7915_mac_tx_rate_val(struct mt76_phy *mphy, u8 mode, u8 rate_idx,
+ u8 nss, u8 stbc, u8 *bw)
+{
+ u16 rateval;
+
+ 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 (mode == MT_PHY_TYPE_HT || mode == MT_PHY_TYPE_HT_GF)
+ nss = 1 + (rate_idx >> 3);
+
+ rateval = FIELD_PREP(MT_TX_RATE_IDX, rate_idx) |
+ FIELD_PREP(MT_TX_RATE_MODE, mode) |
+ FIELD_PREP(MT_TX_RATE_NSS, nss - 1);
+
+ if (stbc && nss == 1) {
+ nss++;
+ rateval |= MT_TX_RATE_STBC;
+ }
+
+ return rateval;
+}
+
+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 bw, mode;
+ u16 rateval;
+ 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:
+ 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;
+ }
+
+ rateval = mt7915_mac_tx_rate_val(mphy, mode, td->tx_rate_idx,
+ td->tx_rate_nss, td->tx_rate_stbc, &bw);
+
+ txwi[2] |= cpu_to_le32(MT_TXD2_FIX_RATE);
+ 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)
@@ -761,6 +873,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,
@@ -881,6 +996,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;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
index 1262fb3..9ebe5af 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
@@ -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);
@@ -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) {
@@ -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;
@@ -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);
@@ -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;
@@ -281,8 +297,10 @@ out:
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;
}
@@ -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)
@@ -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);
}
@@ -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
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
index 5ccde54..372f45b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
@@ -3186,6 +3186,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) &&
@@ -3330,6 +3339,28 @@ int mt7915_mcu_set_txpower_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;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
index 5f23f27..0a7e9d2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
@@ -46,6 +46,10 @@ enum {
MCU_EXT_EVENT_RATE_REPORT = 0x87,
};
+enum {
+ MCU_ATE_SET_TRX = 0x1,
+};
+
struct mt7915_mcu_rxd {
__le32 rxd[6];
@@ -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,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
index 4292153..6735915 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
@@ -108,6 +108,8 @@ struct mt7915_phy {
struct ieee80211_sband_iftype_data iftype[2][NUM_NL80211_IFTYPES];
+ struct ieee80211_vif *monitor_vif;
+
u32 rxfilter;
u64 omac_mask;
@@ -158,6 +160,14 @@ struct mt7915_dev {
struct idr token;
bool fw_debug;
+
+#ifdef CONFIG_NL80211_TESTMODE
+ struct {
+ u32 *reg_backup;
+
+ u8 spe_idx;
+ } test;
+#endif
};
enum {
@@ -247,6 +257,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);
@@ -292,6 +303,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,
@@ -300,6 +312,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);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
index e4252c8..503c9c8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
@@ -51,6 +51,9 @@
#define MT_WF_TMAC_BASE(_band) ((_band) ? 0xa1000 : 0x21000)
#define MT_WF_TMAC(_band, ofs) (MT_WF_TMAC_BASE(_band) + (ofs))
+#define MT_TMAC_TCR0(_band) MT_WF_TMAC(_band, 0)
+#define MT_TMAC_TCR0_TBTT_STOP_CTRL BIT(25)
+
#define MT_TMAC_CDTR(_band) MT_WF_TMAC(_band, 0x090)
#define MT_TMAC_ODTR(_band) MT_WF_TMAC(_band, 0x094)
#define MT_TIMEOUT_VAL_PLCP GENMASK(15, 0)
@@ -72,6 +75,9 @@
#define MT_TMAC_FP0R18(_band) MT_WF_TMAC(_band, 0x270)
#define MT_TMAC_FP_MASK GENMASK(7, 0)
+#define MT_TMAC_TRCR0(_band) MT_WF_TMAC(_band, 0x09c)
+#define MT_TMAC_TFCR0(_band) MT_WF_TMAC(_band, 0x1e0)
+
/* DMA Band 0 */
#define MT_WF_DMA_BASE 0x21e00
#define MT_WF_DMA(ofs) (MT_WF_DMA_BASE + (ofs))
@@ -171,10 +177,33 @@
#define MT_WF_AGG_BASE(_band) ((_band) ? 0xa0800 : 0x20800)
#define MT_WF_AGG(_band, ofs) (MT_WF_AGG_BASE(_band) + (ofs))
+#define MT_AGG_AWSCR0(_band, _n) MT_WF_AGG(_band, 0x05c + (_n) * 4)
+#define MT_AGG_PCR0(_band, _n) MT_WF_AGG(_band, 0x06c + (_n) * 4)
+#define MT_AGG_PCR0_MM_PROT BIT(0)
+#define MT_AGG_PCR0_GF_PROT BIT(1)
+#define MT_AGG_PCR0_BW20_PROT BIT(2)
+#define MT_AGG_PCR0_BW40_PROT BIT(4)
+#define MT_AGG_PCR0_BW80_PROT BIT(6)
+#define MT_AGG_PCR0_ERP_PROT GENMASK(12, 8)
+#define MT_AGG_PCR0_VHT_PROT BIT(13)
+#define MT_AGG_PCR0_PTA_WIN_DIS BIT(15)
+
+#define MT_AGG_PCR1_RTS0_NUM_THRES GENMASK(31, 23)
+#define MT_AGG_PCR1_RTS0_LEN_THRES GENMASK(19, 0)
+
#define MT_AGG_ACR0(_band) MT_WF_AGG(_band, 0x084)
#define MT_AGG_ACR_CFEND_RATE GENMASK(13, 0)
#define MT_AGG_ACR_BAR_RATE GENMASK(29, 16)
+#define MT_AGG_MRCR(_band) MT_WF_AGG(_band, 0x098)
+#define MT_AGG_MRCR_BAR_CNT_LIMIT GENMASK(15, 12)
+#define MT_AGG_MRCR_LAST_RTS_CTS_RN BIT(6)
+#define MT_AGG_MRCR_RTS_FAIL_LIMIT GENMASK(11, 7)
+#define MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT GENMASK(28, 24)
+
+#define MT_AGG_ATCR1(_band) MT_WF_AGG(_band, 0x0f0)
+#define MT_AGG_ATCR3(_band) MT_WF_AGG(_band, 0x0f4)
+
/* ARB: band 0(0x20c00), band 1(0xa0c00) */
#define MT_WF_ARB_BASE(_band) ((_band) ? 0xa0c00 : 0x20c00)
#define MT_WF_ARB(_band, ofs) (MT_WF_ARB_BASE(_band) + (ofs))
@@ -183,6 +212,8 @@
#define MT_ARB_SCR_TX_DISABLE BIT(8)
#define MT_ARB_SCR_RX_DISABLE BIT(9)
+#define MT_ARB_DRNGR0(_band, _n) MT_WF_ARB(_band, 0x194 + (_n) * 4)
+
/* RMAC: band 0(0x21400), band 1(0xa1400) */
#define MT_WF_RMAC_BASE(_band) ((_band) ? 0xa1400 : 0x21400)
#define MT_WF_RMAC(_band, ofs) (MT_WF_RMAC_BASE(_band) + (ofs))
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
new file mode 100644
index 0000000..5d95766
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: ISC
+/* Copyright (C) 2020 MediaTek Inc. */
+
+#include "mt7915.h"
+#include "mac.h"
+#include "mcu.h"
+#include "testmode.h"
+
+struct reg_band {
+ u32 band[2];
+};
+
+#define REG_BAND(_reg) \
+ { .band[0] = MT_##_reg(0), .band[1] = MT_##_reg(1) }
+#define REG_BAND_IDX(_reg, _idx) \
+ { .band[0] = MT_##_reg(0, _idx), .band[1] = MT_##_reg(1, _idx) }
+
+static const struct reg_band reg_backup_list[] = {
+ REG_BAND_IDX(AGG_PCR0, 0),
+ REG_BAND_IDX(AGG_PCR0, 1),
+ REG_BAND_IDX(AGG_AWSCR0, 0),
+ REG_BAND_IDX(AGG_AWSCR0, 1),
+ REG_BAND_IDX(AGG_AWSCR0, 2),
+ REG_BAND_IDX(AGG_AWSCR0, 3),
+ REG_BAND(AGG_MRCR),
+ REG_BAND(TMAC_TFCR0),
+ REG_BAND(TMAC_TCR0),
+ REG_BAND(AGG_ATCR1),
+ REG_BAND(AGG_ATCR3),
+ REG_BAND(TMAC_TRCR0),
+ REG_BAND(TMAC_ICR0),
+ REG_BAND_IDX(ARB_DRNGR0, 0),
+ REG_BAND_IDX(ARB_DRNGR0, 1),
+};
+
+static int
+mt7915_tm_mode_ctrl(struct mt7915_dev *dev, bool enable)
+{
+ struct {
+ u8 format_id;
+ bool enable;
+ u8 rsv[2];
+ } __packed req = {
+ .format_id = 0x6,
+ .enable = enable,
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76,
+ MCU_EXT_CMD_TX_POWER_FEATURE_CTRL,
+ &req, sizeof(req), false);
+}
+
+static int
+mt7915_tm_set_trx(struct mt7915_dev *dev, struct mt7915_phy *phy,
+ int type, bool en)
+{
+ struct mt7915_tm_cmd req = {
+ .testmode_en = 1,
+ .param_idx = MCU_ATE_SET_TRX,
+ .param.trx.type = type,
+ .param.trx.enable = en,
+ .param.trx.band = phy != &dev->phy,
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_ATE_CTRL, &req,
+ sizeof(req), false);
+}
+
+static void
+mt7915_tm_reg_backup_restore(struct mt7915_dev *dev, struct mt7915_phy *phy)
+{
+ int n_regs = ARRAY_SIZE(reg_backup_list);
+ bool ext_phy = phy != &dev->phy;
+ u32 *b = dev->test.reg_backup;
+ int i;
+
+ if (dev->mt76.test.state == MT76_TM_STATE_OFF) {
+ for (i = 0; i < n_regs; i++)
+ mt76_wr(dev, reg_backup_list[i].band[ext_phy], b[i]);
+ return;
+ }
+
+ if (b)
+ return;
+
+ b = devm_kzalloc(dev->mt76.dev, 4 * n_regs, GFP_KERNEL);
+ if (!b)
+ return;
+
+ dev->test.reg_backup = b;
+ for (i = 0; i < n_regs; i++)
+ b[i] = mt76_rr(dev, reg_backup_list[i].band[ext_phy]);
+
+ mt76_clear(dev, MT_AGG_PCR0(ext_phy, 0), MT_AGG_PCR0_MM_PROT |
+ MT_AGG_PCR0_GF_PROT | MT_AGG_PCR0_ERP_PROT |
+ MT_AGG_PCR0_VHT_PROT | MT_AGG_PCR0_BW20_PROT |
+ MT_AGG_PCR0_BW40_PROT | MT_AGG_PCR0_BW80_PROT);
+ mt76_set(dev, MT_AGG_PCR0(ext_phy, 0), MT_AGG_PCR0_PTA_WIN_DIS);
+
+ mt76_wr(dev, MT_AGG_PCR0(ext_phy, 1), MT_AGG_PCR1_RTS0_NUM_THRES |
+ MT_AGG_PCR1_RTS0_LEN_THRES);
+
+ mt76_clear(dev, MT_AGG_MRCR(0), MT_AGG_MRCR_BAR_CNT_LIMIT |
+ MT_AGG_MRCR_LAST_RTS_CTS_RN | MT_AGG_MRCR_RTS_FAIL_LIMIT |
+ MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT);
+
+ mt76_rmw(dev, MT_AGG_MRCR(0), MT_AGG_MRCR_RTS_FAIL_LIMIT |
+ MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT,
+ FIELD_PREP(MT_AGG_MRCR_RTS_FAIL_LIMIT, 1) |
+ FIELD_PREP(MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT, 1));
+
+ mt76_wr(dev, MT_TMAC_TFCR0(0), 0);
+ mt76_clear(dev, MT_TMAC_TCR0(0), MT_TMAC_TCR0_TBTT_STOP_CTRL);
+}
+
+static void
+mt7915_tm_init(struct mt7915_dev *dev)
+{
+ bool en = !(dev->mt76.test.state == MT76_TM_STATE_OFF);
+
+ if (!test_bit(MT76_STATE_RUNNING, &dev->phy.mt76->state))
+ return;
+
+ mt7915_tm_mode_ctrl(dev, en);
+ mt7915_tm_reg_backup_restore(dev, &dev->phy);
+ mt7915_tm_set_trx(dev, &dev->phy, TM_MAC_TXRX, !en);
+}
+
+static void
+mt7915_tm_set_tx_frames(struct mt7915_dev *dev, bool en)
+{
+ static const u8 spe_idx_map[] = {0, 0, 1, 0, 3, 2, 4, 0,
+ 9, 8, 6, 10, 16, 12, 18, 0};
+ struct sk_buff *skb = dev->mt76.test.tx_skb;
+ struct ieee80211_tx_info *info;
+
+ mt7915_tm_set_trx(dev, &dev->phy, TM_MAC_RX_RXV, false);
+
+ if (en) {
+ u8 tx_ant = dev->mt76.test.tx_antenna_mask;
+
+ mutex_unlock(&dev->mt76.mutex);
+ mt7915_set_channel(&dev->phy);
+ mutex_lock(&dev->mt76.mutex);
+
+ mt7915_mcu_set_chan_info(&dev->phy, MCU_EXT_CMD_SET_RX_PATH);
+ dev->test.spe_idx = spe_idx_map[tx_ant];
+ }
+
+ mt7915_tm_set_trx(dev, &dev->phy, TM_MAC_TX, en);
+
+ if (!en || !skb)
+ return;
+
+ info = IEEE80211_SKB_CB(skb);
+ info->control.vif = dev->phy.monitor_vif;
+}
+
+static int
+mt7915_tm_set_state(struct mt76_dev *mdev, enum mt76_testmode_state state)
+{
+ struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
+ struct mt76_testmode_data *td = &mdev->test;
+ enum mt76_testmode_state prev_state = td->state;
+
+ mdev->test.state = state;
+
+ if (prev_state == MT76_TM_STATE_TX_FRAMES)
+ mt7915_tm_set_tx_frames(dev, false);
+ else if (state == MT76_TM_STATE_TX_FRAMES)
+ mt7915_tm_set_tx_frames(dev, true);
+ else if (prev_state == MT76_TM_STATE_OFF || state == MT76_TM_STATE_OFF)
+ mt7915_tm_init(dev);
+
+ return 0;
+}
+
+const struct mt76_testmode_ops mt7915_testmode_ops = {
+ .set_state = mt7915_tm_set_state,
+};
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h
new file mode 100644
index 0000000..04f4a2c
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: ISC
+/* Copyright (C) 2020 MediaTek Inc. */
+
+#ifndef __MT7915_TESTMODE_H
+#define __MT7915_TESTMODE_H
+
+struct mt7915_tm_trx {
+ u8 type;
+ u8 enable;
+ u8 band;
+ u8 rsv;
+};
+
+struct mt7915_tm_cmd {
+ u8 testmode_en;
+ u8 param_idx;
+ u8 _rsv[2];
+ union {
+ __le32 data;
+ struct mt7915_tm_trx trx;
+ u8 test[72];
+ } param;
+} __packed;
+
+enum {
+ TM_MAC_TX = 1,
+ TM_MAC_RX,
+ TM_MAC_TXRX,
+ TM_MAC_TXRX_RXV,
+ TM_MAC_RXV,
+ TM_MAC_RX_RXV,
+};
+
+#endif
--
2.17.1
On 2020-10-12 14:54, Shayne Chen wrote:
> Support testmode rx and display rx statistic by parsing RXV packet
> type, which is currently only enabled in testmode.
>
> Reviewed-by: Ryder Lee <[email protected]>
> Signed-off-by: Shayne Chen <[email protected]>
> ---
> v2: change last_snr to u8
> v3: use nla_put_u8 for MT76_TM_RX_ATTR_SNR
> change snr variable type to u8 in mt7915_mac_fill_rx_vector
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
> index 5d95766..acab268 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
> @@ -113,6 +113,31 @@ mt7915_tm_reg_backup_restore(struct mt7915_dev *dev, struct mt7915_phy *phy)
> mt76_clear(dev, MT_TMAC_TCR0(0), MT_TMAC_TCR0_TBTT_STOP_CTRL);
> }
>
> +static int
> +mt7915_tm_config_rx_filter(struct mt7915_dev *dev, bool en)
> +{
> + struct mt7915_tm_cmd req = {
> + .testmode_en = 1,
> + .param_idx = MCU_ATE_SET_RX_FILTER,
> + .param.filter.report_en = en,
> + .param.filter.band = 0, /* TODO: support dbdc */
> + };
> + __le32 mask = RX_FILTER_NOT_OWN_BTIM |
> + RX_FILTER_NOT_OWN_UCAST |
> + RX_FILTER_RTS | RX_FILTER_CTS |
> + RX_FILTER_CTRL_RSV |
> + RX_FILTER_BC_MC_BSSID_A2 |
> + RX_FILTER_BC_MC_BSSID_A3 |
> + RX_FILTER_BC_MC_OMAC_A3 |
> + RX_FILTER_PROTOCOL_VERSION |
> + RX_FILTER_FCS_ERR;
> +
> + req.param.filter.mask = cpu_to_le32(mask);
> +
> + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_ATE_CTRL, &req,
> + sizeof(req), false);
Why do we need this?
In order to use testmode you have to set up a monitor mode interface,
which should configure the rx filter settings properly already.
- Felix