blob: 3337917f6ddb627479bd37fd6cbd1327cb5ffffb [file] [log] [blame]
developer05f3b2b2024-08-19 19:17:34 +08001From c051b319e725ca566a990b375c089bf74d53e021 Mon Sep 17 00:00:00 2001
developer66e89bc2024-04-23 14:50:01 +08002From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
3Date: Wed, 28 Dec 2022 22:24:25 +0800
developer05f3b2b2024-08-19 19:17:34 +08004Subject: [PATCH 027/199] mtk: mt76: mt7996: add basic testmode support
developer66e89bc2024-04-23 14:50:01 +08005
6Add testmode eeprom buffer mode support
7
developer66e89bc2024-04-23 14:50:01 +08008Fix power & freq offset issue for iTest power cal & tx/rx verifcation
91. Wait for fw to tx. Otherwise, iTest testing tool cannot get the
10accurate tx power.
112. In crystal mode, freq offset is set in 6G band and forwarded to 5G
12and 2G band. Therefore, we should avoid reseting freq offset to 0 when
136G interface is off.
14
developer66e89bc2024-04-23 14:50:01 +080015edcca return err in testmode; therefore, bypass it when we are in testmode idle state or testmode bf is on
16
developer66e89bc2024-04-23 14:50:01 +080017Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
18---
19 eeprom.c | 6 +-
20 mac80211.c | 3 +-
21 mt76.h | 36 +++
22 mt76_connac_mcu.h | 2 +
23 mt7996/Makefile | 1 +
24 mt7996/eeprom.c | 37 ++-
25 mt7996/eeprom.h | 1 +
26 mt7996/init.c | 8 +
27 mt7996/mac.c | 3 +-
28 mt7996/main.c | 27 ++
29 mt7996/mcu.c | 59 +++-
30 mt7996/mcu.h | 33 +++
developer05f3b2b2024-08-19 19:17:34 +080031 mt7996/mt7996.h | 27 +-
developer66e89bc2024-04-23 14:50:01 +080032 mt7996/testmode.c | 740 ++++++++++++++++++++++++++++++++++++++++++++++
33 mt7996/testmode.h | 299 +++++++++++++++++++
34 testmode.c | 126 ++++++--
35 testmode.h | 87 +++++-
36 tools/fields.c | 102 ++++++-
developer05f3b2b2024-08-19 19:17:34 +080037 18 files changed, 1547 insertions(+), 50 deletions(-)
developer66e89bc2024-04-23 14:50:01 +080038 create mode 100644 mt7996/testmode.c
39 create mode 100644 mt7996/testmode.h
40
41diff --git a/eeprom.c b/eeprom.c
developer05f3b2b2024-08-19 19:17:34 +080042index 0bc66cc1..a0047d79 100644
developer66e89bc2024-04-23 14:50:01 +080043--- a/eeprom.c
44+++ b/eeprom.c
45@@ -94,8 +94,10 @@ int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int l
46 }
47
48 #ifdef CONFIG_NL80211_TESTMODE
49- dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
50- dev->test_mtd.offset = offset;
51+ if (len == dev->eeprom.size) {
52+ dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
53+ dev->test_mtd.offset = offset;
54+ }
55 #endif
56
57 out_put_node:
58diff --git a/mac80211.c b/mac80211.c
developer05f3b2b2024-08-19 19:17:34 +080059index 9df9109f..7e4ee83f 100644
developer66e89bc2024-04-23 14:50:01 +080060--- a/mac80211.c
61+++ b/mac80211.c
developer9237f442024-06-14 17:13:04 +080062@@ -846,7 +846,8 @@ void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
developer66e89bc2024-04-23 14:50:01 +080063 }
64
65 #ifdef CONFIG_NL80211_TESTMODE
66- if (phy->test.state == MT76_TM_STATE_RX_FRAMES) {
67+ if (!(phy->test.flag & MT_TM_FW_RX_COUNT) &&
68+ phy->test.state == MT76_TM_STATE_RX_FRAMES) {
69 phy->test.rx_stats.packets[q]++;
70 if (status->flag & RX_FLAG_FAILED_FCS_CRC)
71 phy->test.rx_stats.fcs_error[q]++;
72diff --git a/mt76.h b/mt76.h
developer05f3b2b2024-08-19 19:17:34 +080073index beba1d91..27635c49 100644
developer66e89bc2024-04-23 14:50:01 +080074--- a/mt76.h
75+++ b/mt76.h
developer05f3b2b2024-08-19 19:17:34 +080076@@ -704,14 +704,21 @@ struct mt76_testmode_ops {
developer66e89bc2024-04-23 14:50:01 +080077 int (*set_params)(struct mt76_phy *phy, struct nlattr **tb,
78 enum mt76_testmode_state new_state);
79 int (*dump_stats)(struct mt76_phy *phy, struct sk_buff *msg);
80+ void (*reset_rx_stats)(struct mt76_phy *phy);
81+ void (*tx_stop)(struct mt76_phy *phy);
82+ int (*set_eeprom)(struct mt76_phy *phy, u32 offset, u8 *val, u8 action);
83 };
84
85+#define MT_TM_FW_RX_COUNT BIT(0)
86+
87 struct mt76_testmode_data {
88 enum mt76_testmode_state state;
89
90 u32 param_set[DIV_ROUND_UP(NUM_MT76_TM_ATTRS, 32)];
91 struct sk_buff *tx_skb;
92
93+ u8 sku_en;
94+
95 u32 tx_count;
96 u16 tx_mpdu_len;
97
developer05f3b2b2024-08-19 19:17:34 +080098@@ -721,6 +728,7 @@ struct mt76_testmode_data {
developer66e89bc2024-04-23 14:50:01 +080099 u8 tx_rate_sgi;
100 u8 tx_rate_ldpc;
101 u8 tx_rate_stbc;
102+ u16 tx_preamble_puncture;
103 u8 tx_ltf;
104
105 u8 tx_antenna_mask;
developer05f3b2b2024-08-19 19:17:34 +0800106@@ -730,6 +738,9 @@ struct mt76_testmode_data {
developer66e89bc2024-04-23 14:50:01 +0800107 u32 tx_time;
108 u32 tx_ipg;
109
110+ bool ibf;
111+ bool ebf;
112+
113 u32 freq_offset;
114
115 u8 tx_power[4];
developer05f3b2b2024-08-19 19:17:34 +0800116@@ -744,7 +755,16 @@ struct mt76_testmode_data {
developer66e89bc2024-04-23 14:50:01 +0800117 struct {
118 u64 packets[__MT_RXQ_MAX];
119 u64 fcs_error[__MT_RXQ_MAX];
120+ u64 len_mismatch;
121 } rx_stats;
122+ u8 flag;
123+
124+ struct {
125+ u8 type;
126+ u8 enable;
127+ } cfg;
128+
129+ u8 aid;
130 };
131
132 struct mt76_vif {
developer05f3b2b2024-08-19 19:17:34 +0800133@@ -1453,6 +1473,22 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
developer66e89bc2024-04-23 14:50:01 +0800134 int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state);
135 int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len);
136
137+static inline void
138+mt76_testmode_param_set(struct mt76_testmode_data *td, u16 idx)
139+{
140+#ifdef CONFIG_NL80211_TESTMODE
141+ td->param_set[idx / 32] |= BIT(idx % 32);
142+#endif
143+}
144+
145+static inline bool
146+mt76_testmode_param_present(struct mt76_testmode_data *td, u16 idx)
147+{
148+#ifdef CONFIG_NL80211_TESTMODE
149+ return td->param_set[idx / 32] & BIT(idx % 32);
150+#endif
151+}
152+
153 static inline void mt76_testmode_reset(struct mt76_phy *phy, bool disable)
154 {
155 #ifdef CONFIG_NL80211_TESTMODE
156diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
developer05f3b2b2024-08-19 19:17:34 +0800157index a7cb33d2..57e0e9de 100644
developer66e89bc2024-04-23 14:50:01 +0800158--- a/mt76_connac_mcu.h
159+++ b/mt76_connac_mcu.h
developer05f3b2b2024-08-19 19:17:34 +0800160@@ -1279,12 +1279,14 @@ enum {
developer66e89bc2024-04-23 14:50:01 +0800161 MCU_UNI_CMD_EFUSE_CTRL = 0x2d,
162 MCU_UNI_CMD_RA = 0x2f,
163 MCU_UNI_CMD_MURU = 0x31,
164+ MCU_UNI_CMD_TESTMODE_RX_STAT = 0x32,
165 MCU_UNI_CMD_BF = 0x33,
166 MCU_UNI_CMD_CHANNEL_SWITCH = 0x34,
167 MCU_UNI_CMD_THERMAL = 0x35,
168 MCU_UNI_CMD_VOW = 0x37,
169 MCU_UNI_CMD_PP = 0x38,
170 MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
171+ MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
172 MCU_UNI_CMD_RRO = 0x57,
173 MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
174 MCU_UNI_CMD_PER_STA_INFO = 0x6d,
175diff --git a/mt7996/Makefile b/mt7996/Makefile
developer05f3b2b2024-08-19 19:17:34 +0800176index a056b40e..7bb17f44 100644
developer66e89bc2024-04-23 14:50:01 +0800177--- a/mt7996/Makefile
178+++ b/mt7996/Makefile
179@@ -8,5 +8,6 @@ mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
180 debugfs.o mmio.o
181
182 mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
183+mt7996e-$(CONFIG_NL80211_TESTMODE) += testmode.o
184
185 mt7996e-y += mtk_debugfs.o mtk_mcu.o
186diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
developer05f3b2b2024-08-19 19:17:34 +0800187index bd02eb00..e2790109 100644
developer66e89bc2024-04-23 14:50:01 +0800188--- a/mt7996/eeprom.c
189+++ b/mt7996/eeprom.c
190@@ -6,6 +6,11 @@
191 #include <linux/firmware.h>
192 #include "mt7996.h"
193 #include "eeprom.h"
194+#include <linux/moduleparam.h>
195+
196+static bool testmode_enable;
197+module_param(testmode_enable, bool, 0644);
198+MODULE_PARM_DESC(testmode_enable, "Enable testmode");
199
200 static int mt7996_check_eeprom(struct mt7996_dev *dev)
201 {
202@@ -43,6 +48,9 @@ static int mt7996_check_eeprom(struct mt7996_dev *dev)
203
204 static char *mt7996_eeprom_name(struct mt7996_dev *dev)
205 {
206+ if (dev->testmode_enable)
207+ return MT7996_EEPROM_DEFAULT_TM;
208+
209 switch (mt76_chip(&dev->mt76)) {
210 case 0x7990:
211 if (dev->chip_sku == MT7996_SKU_404)
212@@ -92,21 +100,36 @@ out:
213 return ret;
214 }
215
216-static int mt7996_eeprom_load(struct mt7996_dev *dev)
217+int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev)
218 {
219+ u8 *eeprom;
220 int ret;
221
222+ /* load eeprom in flash or bin file mode to determine fw mode */
223 ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
224 if (ret < 0)
225 return ret;
226
227 if (ret) {
228 dev->flash_mode = true;
229- } else {
230- u8 free_block_num;
231- u32 block_num, i;
232- u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
233+ eeprom = dev->mt76.eeprom.data;
234+ /* testmode enable priority: eeprom field > module parameter */
235+ dev->testmode_enable = !mt7996_check_eeprom(dev) ? eeprom[MT_EE_TESTMODE_EN] :
236+ testmode_enable;
237+ }
238+
239+ return ret;
240+}
241+
242+static int mt7996_eeprom_load(struct mt7996_dev *dev)
243+{
244+ int ret;
245+ u8 free_block_num;
246+ u32 block_num, i;
247+ u32 eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE;
248
249+ /* flash or bin file mode eeprom is loaded before mcu init */
250+ if (!dev->flash_mode) {
251 ret = mt7996_mcu_get_eeprom_free_block(dev, &free_block_num);
252 if (ret < 0)
253 return ret;
254@@ -118,8 +141,8 @@ static int mt7996_eeprom_load(struct mt7996_dev *dev)
255 /* read eeprom data from efuse */
256 block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size);
257 for (i = 0; i < block_num; i++) {
258- ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size);
259- if (ret < 0)
260+ ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size, NULL);
261+ if (ret && ret != -EINVAL)
262 return ret;
263 }
264 }
265diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
developer05f3b2b2024-08-19 19:17:34 +0800266index 72c38ad3..de3ff4e2 100644
developer66e89bc2024-04-23 14:50:01 +0800267--- a/mt7996/eeprom.h
268+++ b/mt7996/eeprom.h
269@@ -14,6 +14,7 @@ enum mt7996_eeprom_field {
270 MT_EE_MAC_ADDR = 0x004,
271 MT_EE_MAC_ADDR2 = 0x00a,
272 MT_EE_WIFI_CONF = 0x190,
273+ MT_EE_TESTMODE_EN = 0x1af,
274 MT_EE_MAC_ADDR3 = 0x2c0,
275 MT_EE_RATE_DELTA_2G = 0x1400,
276 MT_EE_RATE_DELTA_5G = 0x147d,
277diff --git a/mt7996/init.c b/mt7996/init.c
developer05f3b2b2024-08-19 19:17:34 +0800278index f80fba28..ed7c7040 100644
developer66e89bc2024-04-23 14:50:01 +0800279--- a/mt7996/init.c
280+++ b/mt7996/init.c
developer05f3b2b2024-08-19 19:17:34 +0800281@@ -971,6 +971,10 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
developer66e89bc2024-04-23 14:50:01 +0800282
283 set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state);
284
285+ ret = mt7996_eeprom_check_fw_mode(dev);
286+ if (ret < 0)
287+ return ret;
288+
289 ret = mt7996_mcu_init(dev);
290 if (ret)
291 return ret;
developer05f3b2b2024-08-19 19:17:34 +0800292@@ -1420,6 +1424,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
developer66e89bc2024-04-23 14:50:01 +0800293
294 mt7996_init_wiphy(hw, &dev->mt76.mmio.wed);
295
296+#ifdef CONFIG_NL80211_TESTMODE
297+ dev->mt76.test_ops = &mt7996_testmode_ops;
298+#endif
299+
300 ret = mt76_register_device(&dev->mt76, true, mt76_rates,
301 ARRAY_SIZE(mt76_rates));
302 if (ret)
303diff --git a/mt7996/mac.c b/mt7996/mac.c
developer05f3b2b2024-08-19 19:17:34 +0800304index 52ea6796..a0fcd8aa 100644
developer66e89bc2024-04-23 14:50:01 +0800305--- a/mt7996/mac.c
306+++ b/mt7996/mac.c
307@@ -685,7 +685,8 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
308 *info);
309 }
310
311- if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023))
312+ if (rxv && mode >= MT_PHY_TYPE_HE_SU && mode < MT_PHY_TYPE_EHT_SU &&
313+ !(status->flag & RX_FLAG_8023))
314 mt76_connac3_mac_decode_he_radiotap(skb, rxv, mode);
315
316 if (!status->wcid || !ieee80211_is_data_qos(fc) || hw_aggr)
317diff --git a/mt7996/main.c b/mt7996/main.c
developer05f3b2b2024-08-19 19:17:34 +0800318index 72232994..be50f7f3 100644
developer66e89bc2024-04-23 14:50:01 +0800319--- a/mt7996/main.c
320+++ b/mt7996/main.c
321@@ -23,6 +23,18 @@ static bool mt7996_dev_running(struct mt7996_dev *dev)
322 return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
323 }
324
325+static void mt7996_testmode_disable_all(struct mt7996_dev *dev)
326+{
327+ struct mt7996_phy *phy;
328+ int i;
329+
330+ for (i = 0; i < __MT_MAX_BAND; i++) {
331+ phy = __mt7996_phy(dev, i);
332+ if (phy)
333+ mt76_testmode_set_state(phy->mt76, MT76_TM_STATE_OFF);
334+ }
335+}
336+
337 int mt7996_run(struct ieee80211_hw *hw)
338 {
339 struct mt7996_dev *dev = mt7996_hw_dev(hw);
340@@ -45,6 +57,8 @@ int mt7996_run(struct ieee80211_hw *hw)
341 }
342 }
343
344+ mt7996_testmode_disable_all(dev);
345+
346 mt7996_mac_enable_nf(dev, phy->mt76->band_idx);
347
348 ret = mt7996_mcu_set_rts_thresh(phy, 0x92b);
349@@ -303,6 +317,11 @@ int mt7996_set_channel(struct mt7996_phy *phy)
350
351 mt76_set_channel(phy->mt76);
352
353+ if (mt76_testmode_enabled(phy->mt76) || phy->mt76->test.bf_en) {
354+ mt7996_tm_update_channel(phy);
355+ goto out;
356+ }
357+
358 ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
359 if (ret)
360 goto out;
developer05f3b2b2024-08-19 19:17:34 +0800361@@ -415,6 +434,12 @@ static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
developer66e89bc2024-04-23 14:50:01 +0800362 int ret;
363
364 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
365+ if (!mt76_testmode_enabled(phy->mt76) && !phy->mt76->test.bf_en) {
366+ ret = mt7996_mcu_edcca_enable(phy, true);
367+ if (ret)
368+ return ret;
369+ }
370+
371 ret = mt7996_mcu_set_pp_en(phy, PP_USR_MODE,
372 phy->mt76->chandef.punctured);
373 if (ret)
developer05f3b2b2024-08-19 19:17:34 +0800374@@ -1531,6 +1556,8 @@ const struct ieee80211_ops mt7996_ops = {
developer66e89bc2024-04-23 14:50:01 +0800375 .sta_set_decap_offload = mt7996_sta_set_decap_offload,
376 .add_twt_setup = mt7996_mac_add_twt_setup,
377 .twt_teardown_request = mt7996_twt_teardown_request,
378+ CFG80211_TESTMODE_CMD(mt76_testmode_cmd)
379+ CFG80211_TESTMODE_DUMP(mt76_testmode_dump)
380 #ifdef CONFIG_MAC80211_DEBUGFS
381 .sta_add_debugfs = mt7996_sta_add_debugfs,
382 #endif
383diff --git a/mt7996/mcu.c b/mt7996/mcu.c
developer05f3b2b2024-08-19 19:17:34 +0800384index 4ff1af78..61b46370 100644
developer66e89bc2024-04-23 14:50:01 +0800385--- a/mt7996/mcu.c
386+++ b/mt7996/mcu.c
developer05f3b2b2024-08-19 19:17:34 +0800387@@ -2868,8 +2868,12 @@ static int mt7996_load_ram(struct mt7996_dev *dev)
developer66e89bc2024-04-23 14:50:01 +0800388 {
389 int ret;
390
391- ret = __mt7996_load_ram(dev, "WM", fw_name(dev, FIRMWARE_WM),
392- MT7996_RAM_TYPE_WM);
393+ if (dev->testmode_enable)
394+ ret = __mt7996_load_ram(dev, "WM_TM", fw_name(dev, FIRMWARE_WM_TM),
395+ MT7996_RAM_TYPE_WM_TM);
396+ else
397+ ret = __mt7996_load_ram(dev, "WM", fw_name(dev, FIRMWARE_WM),
398+ MT7996_RAM_TYPE_WM);
399 if (ret)
400 return ret;
401
developer05f3b2b2024-08-19 19:17:34 +0800402@@ -3560,17 +3564,9 @@ int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
developer66e89bc2024-04-23 14:50:01 +0800403 &req, sizeof(req), true);
404 }
405
406-int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
407+int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf)
408 {
409- struct {
410- u8 _rsv[4];
411-
412- __le16 tag;
413- __le16 len;
414- __le32 addr;
415- __le32 valid;
416- u8 data[16];
417- } __packed req = {
418+ struct mt7996_mcu_eeprom_info req = {
419 .tag = cpu_to_le16(UNI_EFUSE_ACCESS),
420 .len = cpu_to_le16(sizeof(req) - 4),
421 .addr = cpu_to_le32(round_down(offset,
developer05f3b2b2024-08-19 19:17:34 +0800422@@ -3579,6 +3575,7 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
developer66e89bc2024-04-23 14:50:01 +0800423 struct sk_buff *skb;
424 bool valid;
425 int ret;
426+ u8 *buf = read_buf;
427
428 ret = mt76_mcu_send_and_get_msg(&dev->mt76,
429 MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL),
developer05f3b2b2024-08-19 19:17:34 +0800430@@ -3589,7 +3586,9 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
developer66e89bc2024-04-23 14:50:01 +0800431 valid = le32_to_cpu(*(__le32 *)(skb->data + 16));
432 if (valid) {
433 u32 addr = le32_to_cpu(*(__le32 *)(skb->data + 12));
434- u8 *buf = (u8 *)dev->mt76.eeprom.data + addr;
435+
436+ if (!buf)
437+ buf = (u8 *)dev->mt76.eeprom.data + addr;
438
439 skb_pull(skb, 48);
440 memcpy(buf, skb->data, MT7996_EEPROM_BLOCK_SIZE);
developer05f3b2b2024-08-19 19:17:34 +0800441@@ -4612,3 +4611,37 @@ int mt7996_mcu_set_pp_en(struct mt7996_phy *phy, u8 mode, u16 bitmap)
developer66e89bc2024-04-23 14:50:01 +0800442 return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(PP),
443 &req, sizeof(req), false);
444 }
445+
446+int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data)
447+{
448+ struct mt7996_dev *dev = phy->dev;
449+ struct tx_power_ctrl req = {
450+ .tag = cpu_to_le16(power_ctrl_id),
451+ .len = cpu_to_le16(sizeof(req) - 4),
452+ .power_ctrl_id = power_ctrl_id,
453+ .band_idx = phy->mt76->band_idx,
454+ };
455+
456+ switch (power_ctrl_id) {
457+ case UNI_TXPOWER_SKU_POWER_LIMIT_CTRL:
458+ req.sku_enable = !!data;
459+ break;
460+ case UNI_TXPOWER_PERCENTAGE_CTRL:
461+ req.percentage_ctrl_enable = !!data;
462+ break;
463+ case UNI_TXPOWER_PERCENTAGE_DROP_CTRL:
464+ req.power_drop_level = data;
465+ break;
466+ case UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL:
467+ req.bf_backoff_enable = !!data;
468+ break;
469+ case UNI_TXPOWER_ATE_MODE_CTRL:
470+ req.ate_mode_enable = !!data;
471+ break;
472+ default:
473+ req.sku_enable = !!data;
474+ }
475+
476+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TXPOWER),
477+ &req, sizeof(req), false);
478+}
479diff --git a/mt7996/mcu.h b/mt7996/mcu.h
developer05f3b2b2024-08-19 19:17:34 +0800480index df42c0f8..abf8ef48 100644
developer66e89bc2024-04-23 14:50:01 +0800481--- a/mt7996/mcu.h
482+++ b/mt7996/mcu.h
483@@ -157,6 +157,16 @@ struct mt7996_mcu_eeprom {
484 __le16 buf_len;
485 } __packed;
486
487+struct mt7996_mcu_eeprom_info {
488+ u8 _rsv[4];
489+
490+ __le16 tag;
491+ __le16 len;
492+ __le32 addr;
493+ __le32 valid;
494+ u8 data[MT7996_EEPROM_BLOCK_SIZE];
495+} __packed;
496+
497 struct mt7996_mcu_phy_rx_info {
498 u8 category;
499 u8 rate;
500@@ -889,8 +899,31 @@ enum {
501 UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG,
502 };
503
504+struct tx_power_ctrl {
505+ u8 _rsv[4];
506+
507+ __le16 tag;
508+ __le16 len;
509+
510+ u8 power_ctrl_id;
511+ union {
512+ bool sku_enable;
513+ bool ate_mode_enable;
514+ bool percentage_ctrl_enable;
515+ bool bf_backoff_enable;
516+ u8 power_drop_level;
517+ };
518+ u8 band_idx;
519+ u8 rsv[1];
520+} __packed;
521+
522 enum {
523+ UNI_TXPOWER_SKU_POWER_LIMIT_CTRL = 0,
524+ UNI_TXPOWER_PERCENTAGE_CTRL = 1,
525+ UNI_TXPOWER_PERCENTAGE_DROP_CTRL = 2,
526+ UNI_TXPOWER_BACKOFF_POWER_LIMIT_CTRL = 3,
527 UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL = 4,
528+ UNI_TXPOWER_ATE_MODE_CTRL = 6,
529 };
530
531 enum {
532diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
developer05f3b2b2024-08-19 19:17:34 +0800533index bb7536ff..1e1e638a 100644
developer66e89bc2024-04-23 14:50:01 +0800534--- a/mt7996/mt7996.h
535+++ b/mt7996/mt7996.h
developer05f3b2b2024-08-19 19:17:34 +0800536@@ -32,21 +32,25 @@
developer66e89bc2024-04-23 14:50:01 +0800537 #define MT7996_FIRMWARE_WA "mediatek/mt7996/mt7996_wa.bin"
538 #define MT7996_FIRMWARE_WM "mediatek/mt7996/mt7996_wm.bin"
539 #define MT7996_FIRMWARE_DSP "mediatek/mt7996/mt7996_dsp.bin"
540+#define MT7996_FIRMWARE_WM_TM "mediatek/mt7996/mt7996_wm_tm.bin"
541 #define MT7996_ROM_PATCH "mediatek/mt7996/mt7996_rom_patch.bin"
542
543 #define MT7992_FIRMWARE_WA "mediatek/mt7996/mt7992_wa.bin"
544 #define MT7992_FIRMWARE_WM "mediatek/mt7996/mt7992_wm.bin"
545 #define MT7992_FIRMWARE_DSP "mediatek/mt7996/mt7992_dsp.bin"
546+#define MT7992_FIRMWARE_WM_TM "mediatek/mt7996/mt7992_wm_tm.bin"
547 #define MT7992_ROM_PATCH "mediatek/mt7996/mt7992_rom_patch.bin"
548
549 #define MT7992_FIRMWARE_WA_24 "mediatek/mt7996/mt7992_wa_24.bin"
550 #define MT7992_FIRMWARE_WM_24 "mediatek/mt7996/mt7992_wm_24.bin"
551 #define MT7992_FIRMWARE_DSP_24 "mediatek/mt7996/mt7992_dsp_24.bin"
552+#define MT7992_FIRMWARE_WM_TM_24 "mediatek/mt7996/mt7992_wm_tm_24.bin"
553 #define MT7992_ROM_PATCH_24 "mediatek/mt7996/mt7992_rom_patch_24.bin"
554
555 #define MT7992_FIRMWARE_WA_23 "mediatek/mt7996/mt7992_wa_23.bin"
556 #define MT7992_FIRMWARE_WM_23 "mediatek/mt7996/mt7992_wm_23.bin"
557 #define MT7992_FIRMWARE_DSP_23 "mediatek/mt7996/mt7992_dsp_23.bin"
558+#define MT7992_FIRMWARE_WM_TM_23 "mediatek/mt7996/mt7992_wm_tm_23.bin"
559 #define MT7992_ROM_PATCH_23 "mediatek/mt7996/mt7992_rom_patch_23.bin"
560
561 #define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin"
developer05f3b2b2024-08-19 19:17:34 +0800562@@ -127,6 +131,7 @@ enum mt7992_sku_type {
developer66e89bc2024-04-23 14:50:01 +0800563
564 enum mt7996_ram_type {
565 MT7996_RAM_TYPE_WM,
566+ MT7996_RAM_TYPE_WM_TM = MT7996_RAM_TYPE_WM,
567 MT7996_RAM_TYPE_WA,
568 MT7996_RAM_TYPE_DSP,
569 __MT7996_RAM_TYPE_MAX,
developer05f3b2b2024-08-19 19:17:34 +0800570@@ -277,6 +282,21 @@ struct mt7996_phy {
developer66e89bc2024-04-23 14:50:01 +0800571
572 u8 pp_mode;
573 u16 punct_bitmap;
574+
575+#ifdef CONFIG_NL80211_TESTMODE
576+ struct {
577+ u32 *reg_backup;
578+
579+ s32 last_freq_offset;
580+ u8 last_rcpi[4];
581+ s8 last_rssi[4];
582+ s8 last_ib_rssi[4];
583+ s8 last_wb_rssi[4];
584+ u8 last_snr;
585+
586+ u8 spe_idx;
587+ } test;
588+#endif
589 };
590
591 struct mt7996_dev {
developer05f3b2b2024-08-19 19:17:34 +0800592@@ -357,6 +377,8 @@ struct mt7996_dev {
developer66e89bc2024-04-23 14:50:01 +0800593 spinlock_t lock;
594 } wed_rro;
595
596+ bool testmode_enable;
597+
598 bool ibf;
599 u8 fw_debug_wm;
600 u8 fw_debug_wa;
developer05f3b2b2024-08-19 19:17:34 +0800601@@ -472,6 +494,7 @@ mt7996_band_valid(struct mt7996_dev *dev, u8 band)
developer66e89bc2024-04-23 14:50:01 +0800602 extern const struct ieee80211_ops mt7996_ops;
603 extern struct pci_driver mt7996_pci_driver;
604 extern struct pci_driver mt7996_hif_driver;
605+extern const struct mt76_testmode_ops mt7996_testmode_ops;
606
607 struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
608 void __iomem *mem_base, u32 device_id);
developer05f3b2b2024-08-19 19:17:34 +0800609@@ -481,6 +504,7 @@ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif);
developer66e89bc2024-04-23 14:50:01 +0800610 int mt7996_register_device(struct mt7996_dev *dev);
611 void mt7996_unregister_device(struct mt7996_dev *dev);
612 int mt7996_eeprom_init(struct mt7996_dev *dev);
613+int mt7996_eeprom_check_fw_mode(struct mt7996_dev *dev);
614 int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy);
615 int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
616 struct ieee80211_channel *chan);
developer05f3b2b2024-08-19 19:17:34 +0800617@@ -533,7 +557,7 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
developer66e89bc2024-04-23 14:50:01 +0800618 int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif,
619 struct ieee80211_sta *sta, void *data, u32 field);
620 int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
621-int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset);
622+int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *read_buf);
623 int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num);
624 int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap);
625 int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 set, u8 band);
developer05f3b2b2024-08-19 19:17:34 +0800626@@ -568,6 +592,7 @@ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
developer66e89bc2024-04-23 14:50:01 +0800627 void mt7996_mcu_exit(struct mt7996_dev *dev);
628 int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
629 int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
630+int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
631
632 static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
633 {
634diff --git a/mt7996/testmode.c b/mt7996/testmode.c
635new file mode 100644
developer05f3b2b2024-08-19 19:17:34 +0800636index 00000000..98eebcee
developer66e89bc2024-04-23 14:50:01 +0800637--- /dev/null
638+++ b/mt7996/testmode.c
639@@ -0,0 +1,740 @@
640+// SPDX-License-Identifier: ISC
641+/*
642+ * Copyright (C) 2022 MediaTek Inc.
643+ */
644+
645+#include "mt7996.h"
646+#include "mac.h"
647+#include "mcu.h"
648+#include "testmode.h"
649+
650+enum {
651+ TM_CHANGED_TXPOWER,
652+ TM_CHANGED_FREQ_OFFSET,
653+ TM_CHANGED_SKU_EN,
654+ TM_CHANGED_TX_LENGTH,
655+ TM_CHANGED_TX_TIME,
656+ TM_CHANGED_CFG,
657+
658+ /* must be last */
659+ NUM_TM_CHANGED
660+};
661+
662+static const u8 tm_change_map[] = {
663+ [TM_CHANGED_TXPOWER] = MT76_TM_ATTR_TX_POWER,
664+ [TM_CHANGED_FREQ_OFFSET] = MT76_TM_ATTR_FREQ_OFFSET,
665+ [TM_CHANGED_SKU_EN] = MT76_TM_ATTR_SKU_EN,
666+ [TM_CHANGED_TX_LENGTH] = MT76_TM_ATTR_TX_LENGTH,
667+ [TM_CHANGED_TX_TIME] = MT76_TM_ATTR_TX_TIME,
668+ [TM_CHANGED_CFG] = MT76_TM_ATTR_CFG,
669+};
670+
671+static u8 mt7996_tm_bw_mapping(enum nl80211_chan_width width, enum bw_mapping_method method)
672+{
673+ static const u8 width_to_bw[][NUM_BW_MAP] = {
674+ [NL80211_CHAN_WIDTH_40] = {FW_CDBW_40MHZ, TM_CBW_40MHZ},
675+ [NL80211_CHAN_WIDTH_80] = {FW_CDBW_80MHZ, TM_CBW_80MHZ},
676+ [NL80211_CHAN_WIDTH_80P80] = {FW_CDBW_8080MHZ, TM_CBW_8080MHZ},
677+ [NL80211_CHAN_WIDTH_160] = {FW_CDBW_160MHZ, TM_CBW_160MHZ},
678+ [NL80211_CHAN_WIDTH_5] = {FW_CDBW_5MHZ, TM_CBW_5MHZ},
679+ [NL80211_CHAN_WIDTH_10] = {FW_CDBW_10MHZ, TM_CBW_10MHZ},
680+ [NL80211_CHAN_WIDTH_20] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
681+ [NL80211_CHAN_WIDTH_20_NOHT] = {FW_CDBW_20MHZ, TM_CBW_20MHZ},
682+ [NL80211_CHAN_WIDTH_320] = {FW_CDBW_320MHZ, TM_CBW_320MHZ},
683+ };
684+
685+ if (width >= ARRAY_SIZE(width_to_bw))
686+ return 0;
687+
688+ return width_to_bw[width][method];
689+}
690+
691+static u8 mt7996_tm_rate_to_phy(u8 tx_rate_mode)
692+{
693+ static const u8 rate_to_phy[] = {
694+ [MT76_TM_TX_MODE_CCK] = MT_PHY_TYPE_CCK,
695+ [MT76_TM_TX_MODE_OFDM] = MT_PHY_TYPE_OFDM,
696+ [MT76_TM_TX_MODE_HT] = MT_PHY_TYPE_HT,
697+ [MT76_TM_TX_MODE_VHT] = MT_PHY_TYPE_VHT,
698+ [MT76_TM_TX_MODE_HE_SU] = MT_PHY_TYPE_HE_SU,
699+ [MT76_TM_TX_MODE_HE_EXT_SU] = MT_PHY_TYPE_HE_EXT_SU,
700+ [MT76_TM_TX_MODE_HE_TB] = MT_PHY_TYPE_HE_TB,
701+ [MT76_TM_TX_MODE_HE_MU] = MT_PHY_TYPE_HE_MU,
702+ [MT76_TM_TX_MODE_EHT_SU] = MT_PHY_TYPE_EHT_SU,
703+ [MT76_TM_TX_MODE_EHT_TRIG] = MT_PHY_TYPE_EHT_TRIG,
704+ [MT76_TM_TX_MODE_EHT_MU] = MT_PHY_TYPE_EHT_MU,
705+ };
706+
707+ if (tx_rate_mode > MT76_TM_TX_MODE_MAX)
708+ return -EINVAL;
709+
710+ return rate_to_phy[tx_rate_mode];
711+}
712+
713+static int
714+mt7996_tm_check_antenna(struct mt7996_phy *phy)
715+{
716+ struct mt76_testmode_data *td = &phy->mt76->test;
717+ struct mt7996_dev *dev = phy->dev;
718+ u8 band_idx = phy->mt76->band_idx;
719+ u32 chainmask = phy->mt76->chainmask;
720+ u32 aux_rx_mask;
721+
722+ chainmask = chainmask >> dev->chainshift[band_idx];
723+ aux_rx_mask = BIT(fls(chainmask)) * phy->has_aux_rx;
724+ if (td->tx_antenna_mask & ~(chainmask | aux_rx_mask)) {
725+ dev_err(dev->mt76.dev,
726+ "tx antenna mask 0x%x exceeds hw limit (chainmask 0x%x, has aux rx: %s)\n",
727+ td->tx_antenna_mask, chainmask, phy->has_aux_rx ? "yes" : "no");
728+ return -EINVAL;
729+ }
730+
731+ return 0;
732+}
733+
734+static int
735+mt7996_tm_set(struct mt7996_dev *dev, u32 func_idx, u32 data)
736+{
737+ struct mt7996_tm_req req = {
738+ .rf_test = {
739+ .tag = cpu_to_le16(UNI_RF_TEST_CTRL),
740+ .len = cpu_to_le16(sizeof(req.rf_test)),
741+ .action = RF_ACTION_SET,
742+ .op.rf.func_idx = func_idx,
743+ .op.rf.param.func_data = cpu_to_le32(data),
744+ },
745+ };
746+ bool wait = (data == RF_CMD(START_TX)) ? true : false;
747+
748+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
749+ sizeof(req), wait);
750+}
751+
752+static int
753+mt7996_tm_get(struct mt7996_dev *dev, u32 func_idx, u32 data, u32 *result)
754+{
755+ struct mt7996_tm_req req = {
756+ .rf_test = {
757+ .tag = cpu_to_le16(UNI_RF_TEST_CTRL),
758+ .len = cpu_to_le16(sizeof(req.rf_test)),
759+ .action = RF_ACTION_GET,
760+ .op.rf.func_idx = func_idx,
761+ .op.rf.param.func_data = cpu_to_le32(data),
762+ },
763+ };
764+ struct mt7996_tm_event *event;
765+ struct sk_buff *skb;
766+ int ret;
767+
768+ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(TESTMODE_CTRL),
769+ &req, sizeof(req), true, &skb);
770+ if (ret)
771+ return ret;
772+
773+ event = (struct mt7996_tm_event *)skb->data;
774+ *result = event->result.payload_length;
775+
776+ dev_kfree_skb(skb);
777+
778+ return ret;
779+}
780+
781+static void
782+mt7996_tm_set_antenna(struct mt7996_phy *phy, u32 func_idx)
783+{
784+#define SPE_INDEX_MASK BIT(31)
785+#define TX_ANTENNA_MASK GENMASK(3, 0)
786+#define RX_ANTENNA_MASK GENMASK(20, 16) /* RX antenna mask at most 5 bit */
787+ struct mt7996_dev *dev = phy->dev;
788+ struct mt76_testmode_data *td = &phy->mt76->test;
789+ u32 antenna_mask;
790+
791+ if (!mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA))
792+ return;
793+
794+ if (func_idx == SET_ID(TX_PATH))
795+ antenna_mask = td->tx_spe_idx ? (SPE_INDEX_MASK | td->tx_spe_idx) :
796+ td->tx_antenna_mask & TX_ANTENNA_MASK;
797+ else if (func_idx == SET_ID(RX_PATH))
798+ antenna_mask = u32_encode_bits(td->tx_antenna_mask, RX_ANTENNA_MASK);
799+ else
800+ return;
801+
802+ mt7996_tm_set(dev, func_idx, antenna_mask);
803+}
804+
805+static void
806+mt7996_tm_set_mac_addr(struct mt7996_dev *dev, u8 *addr, u32 func_idx)
807+{
808+#define REMAIN_PART_TAG BIT(18)
809+ u32 own_mac_first = 0, own_mac_remain = 0;
810+ int len = sizeof(u32);
811+
812+ memcpy(&own_mac_first, addr, len);
813+ mt7996_tm_set(dev, func_idx, own_mac_first);
814+ /* Set the remain part of mac address */
815+ memcpy(&own_mac_remain, addr + len, ETH_ALEN - len);
816+ mt7996_tm_set(dev, func_idx | REMAIN_PART_TAG, own_mac_remain);
817+}
818+
819+static int
820+mt7996_tm_rf_switch_mode(struct mt7996_dev *dev, u32 op_mode)
821+{
822+ struct mt7996_tm_req req = {
823+ .rf_test = {
824+ .tag = cpu_to_le16(UNI_RF_TEST_CTRL),
825+ .len = cpu_to_le16(sizeof(req.rf_test)),
826+ .action = RF_ACTION_SWITCH_TO_RF_TEST,
827+ .op.op_mode = cpu_to_le32(op_mode),
828+ },
829+ };
830+
831+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_CTRL), &req,
832+ sizeof(req), false);
833+}
834+
835+static void
836+mt7996_tm_init(struct mt7996_phy *phy, bool en)
837+{
838+ struct mt7996_dev *dev = phy->dev;
839+ u8 rf_test_mode = en ? RF_OPER_RF_TEST : RF_OPER_NORMAL;
840+
841+ if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
842+ return;
843+
844+ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(ATE_MODE), en);
845+ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(SKU_POWER_LIMIT), !en);
846+ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(BACKOFF_POWER_LIMIT), !en);
847+
848+ mt7996_tm_rf_switch_mode(dev, rf_test_mode);
849+
850+ mt7996_mcu_add_bss_info(phy, phy->monitor_vif, en);
851+ mt7996_mcu_add_sta(dev, phy->monitor_vif, NULL, en, false);
852+
853+ mt7996_tm_set(dev, SET_ID(BAND_IDX), phy->mt76->band_idx);
854+
855+ /* use firmware counter for RX stats */
856+ phy->mt76->test.flag |= MT_TM_FW_RX_COUNT;
857+}
858+
859+static void
860+mt7996_tm_update_channel(struct mt7996_phy *phy)
861+{
862+#define CHAN_FREQ_BW_80P80_TAG (SET_ID(CHAN_FREQ) | BIT(16))
863+ struct mt7996_dev *dev = phy->dev;
864+ struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
865+ struct ieee80211_channel *chan = chandef->chan;
866+ u8 width = chandef->width;
867+ static const u8 ch_band[] = {
868+ [NL80211_BAND_2GHZ] = 0,
869+ [NL80211_BAND_5GHZ] = 1,
870+ [NL80211_BAND_6GHZ] = 2,
871+ };
872+
873+ if (!chan || !chandef) {
874+ dev_info(dev->mt76.dev, "chandef not found, channel update failed!\n");
875+ return;
876+ }
877+
878+ /* system bw */
879+ mt7996_tm_set(dev, SET_ID(CBW), mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
880+
881+ if (width == NL80211_CHAN_WIDTH_80P80) {
882+ width = NL80211_CHAN_WIDTH_160;
883+ mt7996_tm_set(dev, CHAN_FREQ_BW_80P80_TAG, chandef->center_freq2 * 1000);
884+ }
885+
886+ /* TODO: define per-packet bw */
887+ /* per-packet bw */
888+ mt7996_tm_set(dev, SET_ID(DBW), mt7996_tm_bw_mapping(width, BW_MAP_NL_TO_FW));
889+
890+ /* control channel selection index */
891+ mt7996_tm_set(dev, SET_ID(PRIMARY_CH), 0);
892+ mt7996_tm_set(dev, SET_ID(BAND), ch_band[chan->band]);
893+
894+ /* trigger switch channel calibration */
895+ mt7996_tm_set(dev, SET_ID(CHAN_FREQ), chandef->center_freq1 * 1000);
896+
897+ // TODO: update power limit table
898+}
899+
900+static void
901+mt7996_tm_tx_stop(struct mt76_phy *mphy)
902+{
903+ struct mt76_testmode_data *td = &mphy->test;
904+ struct mt7996_phy *phy = mphy->priv;
905+ struct mt7996_dev *dev = phy->dev;
906+
907+ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
908+ td->tx_pending = 0;
909+}
910+
911+static void
912+mt7996_tm_set_tx_frames(struct mt7996_phy *phy, bool en)
913+{
914+#define FRAME_CONTROL 0x88
915+ struct mt76_testmode_data *td = &phy->mt76->test;
916+ struct mt7996_dev *dev = phy->dev;
917+
918+ //TODO: RU operation, replace mcs, nss, and ldpc
919+ if (en) {
920+ mt7996_tm_set(dev, SET_ID(MAC_HEADER), FRAME_CONTROL);
921+ mt7996_tm_set(dev, SET_ID(SEQ_CTRL), 0);
922+ mt7996_tm_set(dev, SET_ID(TX_COUNT), td->tx_count);
923+ mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
924+ mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
925+
926+ if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER))
927+ mt7996_tm_set(dev, SET_ID(POWER), td->tx_power[0]);
928+
929+ if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_TIME)) {
930+ mt7996_tm_set(dev, SET_ID(TX_LEN), 0);
931+ mt7996_tm_set(dev, SET_ID(TX_TIME), td->tx_time);
932+ } else {
933+ mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
934+ mt7996_tm_set(dev, SET_ID(TX_TIME), 0);
935+ }
936+
937+ mt7996_tm_set_antenna(phy, SET_ID(TX_PATH));
938+ mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
939+ mt7996_tm_set(dev, SET_ID(STBC), td->tx_rate_stbc);
940+ mt7996_tm_set(dev, SET_ID(ENCODE_MODE), td->tx_rate_ldpc);
941+ mt7996_tm_set(dev, SET_ID(IBF_ENABLE), td->ibf);
942+ mt7996_tm_set(dev, SET_ID(EBF_ENABLE), td->ebf);
943+ mt7996_tm_set(dev, SET_ID(IPG), td->tx_ipg);
944+ mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
945+ mt7996_tm_set(dev, SET_ID(NSS), td->tx_rate_nss);
946+ mt7996_tm_set(dev, SET_ID(AID_OFFSET), 0);
947+ mt7996_tm_set(dev, SET_ID(PUNCTURE), td->tx_preamble_puncture);
948+
949+ mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
950+ mt7996_tm_set(dev, SET_ID(HW_TX_MODE), 0);
951+ mt7996_tm_update_channel(phy);
952+
953+ /* trigger firmware to start TX */
954+ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_TX));
955+ } else {
956+ mt7996_tm_tx_stop(phy->mt76);
957+ }
958+}
959+
960+static int
961+mt7996_tm_rx_stats_user_ctrl(struct mt7996_phy *phy, u16 user_idx)
962+{
963+ struct mt7996_dev *dev = phy->dev;
964+ struct mt7996_tm_rx_req req = {
965+ .band = phy->mt76->band_idx,
966+ .user_ctrl = {
967+ .tag = cpu_to_le16(UNI_TM_RX_STAT_SET_USER_CTRL),
968+ .len = cpu_to_le16(sizeof(req.user_ctrl)),
969+ .band_idx = phy->mt76->band_idx,
970+ .user_idx = cpu_to_le16(user_idx),
971+ },
972+ };
973+
974+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TESTMODE_RX_STAT), &req,
975+ sizeof(req), false);
976+}
977+
978+static void
979+mt7996_tm_set_rx_frames(struct mt7996_phy *phy, bool en)
980+{
981+#define RX_MU_DISABLE 0xf800
982+ struct mt76_testmode_data *td = &phy->mt76->test;
983+ struct mt7996_dev *dev = phy->dev;
984+ int ret;
985+
986+ if (en) {
987+ ret = mt7996_tm_rx_stats_user_ctrl(phy, td->aid);
988+ if (ret) {
989+ dev_info(dev->mt76.dev, "Set RX stats user control failed!\n");
990+ return;
991+ }
992+
993+ mt7996_tm_update_channel(phy);
994+
995+ if (td->tx_rate_mode >= MT76_TM_TX_MODE_HE_MU) {
996+ if (td->aid)
997+ ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), td->aid);
998+ else
999+ ret = mt7996_tm_set(dev, SET_ID(RX_MU_AID), RX_MU_DISABLE);
1000+ }
1001+ mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
1002+ mt7996_tm_set(dev, SET_ID(GI), td->tx_rate_sgi);
1003+ mt7996_tm_set_antenna(phy, SET_ID(RX_PATH));
1004+ mt7996_tm_set(dev, SET_ID(MAX_PE), 2);
1005+
1006+ mt7996_tm_set_mac_addr(dev, td->addr[1], SET_ID(SA));
1007+
1008+ /* trigger firmware to start RX */
1009+ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(START_RX));
1010+ } else {
1011+ /* trigger firmware to stop RX */
1012+ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
1013+ }
1014+}
1015+
1016+static void
1017+mt7996_tm_set_tx_cont(struct mt7996_phy *phy, bool en)
1018+{
1019+#define CONT_WAVE_MODE_OFDM 3
1020+ struct mt76_testmode_data *td = &phy->mt76->test;
1021+ struct mt7996_dev *dev = phy->dev;
1022+
1023+ if (en) {
1024+ mt7996_tm_update_channel(phy);
1025+ mt7996_tm_set(dev, SET_ID(TX_MODE), mt7996_tm_rate_to_phy(td->tx_rate_mode));
1026+ mt7996_tm_set(dev, SET_ID(TX_RATE), td->tx_rate_idx);
1027+ /* fix payload is OFDM */
1028+ mt7996_tm_set(dev, SET_ID(CONT_WAVE_MODE), CONT_WAVE_MODE_OFDM);
1029+ mt7996_tm_set(dev, SET_ID(ANT_MASK), td->tx_antenna_mask);
1030+
1031+ /* trigger firmware to start CONT TX */
1032+ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(CONT_WAVE));
1033+ } else {
1034+ /* trigger firmware to stop CONT TX */
1035+ mt7996_tm_set(dev, SET_ID(COMMAND), RF_CMD(STOP_TEST));
1036+ }
1037+}
1038+
1039+static void
1040+mt7996_tm_update_params(struct mt7996_phy *phy, u32 changed)
1041+{
1042+ struct mt76_testmode_data *td = &phy->mt76->test;
1043+ struct mt7996_dev *dev = phy->dev;
1044+
1045+ if (changed & BIT(TM_CHANGED_FREQ_OFFSET)) {
1046+ mt7996_tm_set(dev, SET_ID(FREQ_OFFSET), td->freq_offset);
1047+ mt7996_tm_set(dev, SET_ID(FREQ_OFFSET_C2), td->freq_offset);
1048+ }
1049+ if (changed & BIT(TM_CHANGED_TXPOWER))
1050+ mt7996_tm_set(dev, SET_ID(POWER), td->tx_power[0]);
1051+ if (changed & BIT(TM_CHANGED_SKU_EN)) {
1052+ mt7996_tm_update_channel(phy);
1053+ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(SKU_POWER_LIMIT), td->sku_en);
1054+ mt7996_mcu_set_tx_power_ctrl(phy, POWER_CTRL(BACKOFF_POWER_LIMIT), td->sku_en);
1055+ mt7996_mcu_set_txpower_sku(phy);
1056+ }
1057+ if (changed & BIT(TM_CHANGED_TX_LENGTH)) {
1058+ mt7996_tm_set(dev, SET_ID(TX_LEN), td->tx_mpdu_len);
1059+ mt7996_tm_set(dev, SET_ID(TX_TIME), 0);
1060+ }
1061+ if (changed & BIT(TM_CHANGED_TX_TIME)) {
1062+ mt7996_tm_set(dev, SET_ID(TX_LEN), 0);
1063+ mt7996_tm_set(dev, SET_ID(TX_TIME), td->tx_time);
1064+ }
1065+ if (changed & BIT(TM_CHANGED_CFG)) {
1066+ u32 func_idx = td->cfg.enable ? SET_ID(CFG_ON) : SET_ID(CFG_OFF);
1067+
1068+ mt7996_tm_set(dev, func_idx, td->cfg.type);
1069+ }
1070+}
1071+
1072+static int
1073+mt7996_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state)
1074+{
1075+ struct mt76_testmode_data *td = &mphy->test;
1076+ struct mt7996_phy *phy = mphy->priv;
1077+ enum mt76_testmode_state prev_state = td->state;
1078+
1079+ mphy->test.state = state;
1080+
1081+ if (prev_state != MT76_TM_STATE_OFF)
1082+ mt7996_tm_set(phy->dev, SET_ID(BAND_IDX), mphy->band_idx);
1083+
1084+ if (prev_state == MT76_TM_STATE_TX_FRAMES ||
1085+ state == MT76_TM_STATE_TX_FRAMES)
1086+ mt7996_tm_set_tx_frames(phy, state == MT76_TM_STATE_TX_FRAMES);
1087+ else if (prev_state == MT76_TM_STATE_RX_FRAMES ||
1088+ state == MT76_TM_STATE_RX_FRAMES)
1089+ mt7996_tm_set_rx_frames(phy, state == MT76_TM_STATE_RX_FRAMES);
1090+ else if (prev_state == MT76_TM_STATE_TX_CONT ||
1091+ state == MT76_TM_STATE_TX_CONT)
1092+ mt7996_tm_set_tx_cont(phy, state == MT76_TM_STATE_TX_CONT);
1093+ else if (prev_state == MT76_TM_STATE_OFF ||
1094+ state == MT76_TM_STATE_OFF)
1095+ mt7996_tm_init(phy, !(state == MT76_TM_STATE_OFF));
1096+
1097+ if ((state == MT76_TM_STATE_IDLE &&
1098+ prev_state == MT76_TM_STATE_OFF) ||
1099+ (state == MT76_TM_STATE_OFF &&
1100+ prev_state == MT76_TM_STATE_IDLE)) {
1101+ u32 changed = 0;
1102+ int i, ret;
1103+
1104+ for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
1105+ u16 cur = tm_change_map[i];
1106+
1107+ if (mt76_testmode_param_present(td, cur))
1108+ changed |= BIT(i);
1109+ }
1110+
1111+ ret = mt7996_tm_check_antenna(phy);
1112+ if (ret)
1113+ return ret;
1114+
1115+ mt7996_tm_update_params(phy, changed);
1116+ }
1117+
1118+ return 0;
1119+}
1120+
1121+static int
1122+mt7996_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
1123+ enum mt76_testmode_state new_state)
1124+{
1125+ struct mt76_testmode_data *td = &mphy->test;
1126+ struct mt7996_phy *phy = mphy->priv;
1127+ struct mt7996_dev *dev = phy->dev;
1128+ u32 changed = 0;
1129+ int i, ret;
1130+
1131+ BUILD_BUG_ON(NUM_TM_CHANGED >= 32);
1132+
1133+ if (new_state == MT76_TM_STATE_OFF ||
1134+ td->state == MT76_TM_STATE_OFF)
1135+ return 0;
1136+
1137+ ret = mt7996_tm_check_antenna(phy);
1138+ if (ret)
1139+ return ret;
1140+
1141+ for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
1142+ if (tb[tm_change_map[i]])
1143+ changed |= BIT(i);
1144+ }
1145+
1146+ mt7996_tm_set(dev, SET_ID(BAND_IDX), mphy->band_idx);
1147+ mt7996_tm_update_params(phy, changed);
1148+
1149+ return 0;
1150+}
1151+
1152+static int
1153+mt7996_tm_get_rx_stats(struct mt7996_phy *phy)
1154+{
1155+ struct mt7996_dev *dev = phy->dev;
1156+ struct mt7996_tm_rx_req req = {
1157+ .band = phy->mt76->band_idx,
1158+ .rx_stat_all = {
1159+ .tag = cpu_to_le16(UNI_TM_RX_STAT_GET_ALL_V2),
1160+ .len = cpu_to_le16(sizeof(req.rx_stat_all)),
1161+ .band_idx = phy->mt76->band_idx,
1162+ },
1163+ };
1164+ struct mt76_testmode_data *td = &phy->mt76->test;
1165+ struct mt7996_tm_rx_event *rx_stats;
1166+ struct mt7996_tm_rx_event_stat_all *rx_stats_all;
1167+ struct sk_buff *skb;
1168+ enum mt76_rxq_id qid;
1169+ int i, ret = 0;
1170+ u32 mac_rx_mdrdy_cnt;
1171+ u16 mac_rx_len_mismatch, fcs_err_count;
1172+
1173+ if (td->state != MT76_TM_STATE_RX_FRAMES)
1174+ return 0;
1175+
1176+ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(TESTMODE_RX_STAT),
1177+ &req, sizeof(req), true, &skb);
1178+
1179+ if (ret)
1180+ return ret;
1181+
1182+ rx_stats = (struct mt7996_tm_rx_event *)skb->data;
1183+ rx_stats_all = &rx_stats->rx_stat_all;
1184+
1185+ phy->test.last_freq_offset = le32_to_cpu(rx_stats_all->user_info[0].freq_offset);
1186+ phy->test.last_snr = le32_to_cpu(rx_stats_all->user_info[0].snr);
1187+ for (i = 0; i < ARRAY_SIZE(phy->test.last_rcpi); i++) {
1188+ phy->test.last_rcpi[i] = le16_to_cpu(rx_stats_all->rxv_info[i].rcpi);
1189+ phy->test.last_rssi[i] = le16_to_cpu(rx_stats_all->rxv_info[i].rssi);
1190+ phy->test.last_ib_rssi[i] = rx_stats_all->fagc[i].ib_rssi;
1191+ phy->test.last_wb_rssi[i] = rx_stats_all->fagc[i].wb_rssi;
1192+ }
1193+
1194+ if (phy->mt76->band_idx == 2)
1195+ qid = MT_RXQ_BAND2;
1196+ else if (phy->mt76->band_idx == 1)
1197+ qid = MT_RXQ_BAND1;
1198+ else
1199+ qid = MT_RXQ_MAIN;
1200+
1201+ fcs_err_count = le16_to_cpu(rx_stats_all->band_info.mac_rx_fcs_err_cnt);
1202+ mac_rx_len_mismatch = le16_to_cpu(rx_stats_all->band_info.mac_rx_len_mismatch);
1203+ mac_rx_mdrdy_cnt = le32_to_cpu(rx_stats_all->band_info.mac_rx_mdrdy_cnt);
1204+ td->rx_stats.packets[qid] += mac_rx_mdrdy_cnt;
1205+ td->rx_stats.packets[qid] += fcs_err_count;
1206+ td->rx_stats.fcs_error[qid] += fcs_err_count;
1207+ td->rx_stats.len_mismatch += mac_rx_len_mismatch;
1208+
1209+ dev_kfree_skb(skb);
1210+
1211+ return ret;
1212+}
1213+
1214+static void
1215+mt7996_tm_reset_trx_stats(struct mt76_phy *mphy)
1216+{
1217+ struct mt7996_phy *phy = mphy->priv;
1218+ struct mt7996_dev *dev = phy->dev;
1219+
1220+ memset(&mphy->test.rx_stats, 0, sizeof(mphy->test.rx_stats));
1221+ mt7996_tm_set(dev, SET_ID(TRX_COUNTER_RESET), 0);
1222+}
1223+
1224+static int
1225+mt7996_tm_get_tx_stats(struct mt7996_phy *phy)
1226+{
1227+ struct mt7996_dev *dev = phy->dev;
1228+ struct mt76_testmode_data *td = &phy->mt76->test;
1229+ int ret;
1230+
1231+ if (td->state != MT76_TM_STATE_TX_FRAMES)
1232+ return 0;
1233+
1234+ ret = mt7996_tm_get(dev, GET_ID(TXED_COUNT), 0, &td->tx_done);
1235+ if (ret)
1236+ return ret;
1237+
1238+ td->tx_pending = td->tx_count - td->tx_done;
1239+
1240+ return ret;
1241+}
1242+
1243+static int
1244+mt7996_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
1245+{
1246+ struct mt7996_phy *phy = mphy->priv;
1247+ void *rx, *rssi;
1248+ int i;
1249+
1250+ mt7996_tm_set(phy->dev, SET_ID(BAND_IDX), mphy->band_idx);
1251+ mt7996_tm_get_rx_stats(phy);
1252+ mt7996_tm_get_tx_stats(phy);
1253+
1254+ rx = nla_nest_start(msg, MT76_TM_STATS_ATTR_LAST_RX);
1255+ if (!rx)
1256+ return -ENOMEM;
1257+
1258+ if (nla_put_s32(msg, MT76_TM_RX_ATTR_FREQ_OFFSET, phy->test.last_freq_offset))
1259+ return -ENOMEM;
1260+
1261+ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RCPI);
1262+ if (!rssi)
1263+ return -ENOMEM;
1264+
1265+ for (i = 0; i < ARRAY_SIZE(phy->test.last_rcpi); i++)
1266+ if (nla_put_u8(msg, i, phy->test.last_rcpi[i]))
1267+ return -ENOMEM;
1268+
1269+ nla_nest_end(msg, rssi);
1270+
1271+ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RSSI);
1272+ if (!rssi)
1273+ return -ENOMEM;
1274+
1275+ for (i = 0; i < ARRAY_SIZE(phy->test.last_rssi); i++)
1276+ if (nla_put_s8(msg, i, phy->test.last_rssi[i]))
1277+ return -ENOMEM;
1278+
1279+ nla_nest_end(msg, rssi);
1280+
1281+ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_IB_RSSI);
1282+ if (!rssi)
1283+ return -ENOMEM;
1284+
1285+ for (i = 0; i < ARRAY_SIZE(phy->test.last_ib_rssi); i++)
1286+ if (nla_put_s8(msg, i, phy->test.last_ib_rssi[i]))
1287+ return -ENOMEM;
1288+
1289+ nla_nest_end(msg, rssi);
1290+
1291+ rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_WB_RSSI);
1292+ if (!rssi)
1293+ return -ENOMEM;
1294+
1295+ for (i = 0; i < ARRAY_SIZE(phy->test.last_wb_rssi); i++)
1296+ if (nla_put_s8(msg, i, phy->test.last_wb_rssi[i]))
1297+ return -ENOMEM;
1298+
1299+ nla_nest_end(msg, rssi);
1300+
1301+ if (nla_put_u8(msg, MT76_TM_RX_ATTR_SNR, phy->test.last_snr))
1302+ return -ENOMEM;
1303+
1304+ nla_nest_end(msg, rx);
1305+
1306+ return 0;
1307+}
1308+
1309+static int
1310+mt7996_tm_write_back_to_efuse(struct mt7996_dev *dev)
1311+{
1312+ struct mt7996_mcu_eeprom_info req = {
1313+ .tag = cpu_to_le16(UNI_EFUSE_ACCESS),
1314+ .len = cpu_to_le16(sizeof(req) - 4),
1315+ };
1316+ u8 read_buf[MT76_TM_EEPROM_BLOCK_SIZE], *eeprom = dev->mt76.eeprom.data;
1317+ int i, ret = -EINVAL;
1318+
1319+ /* prevent from damaging chip id in efuse */
1320+ if (mt76_chip(&dev->mt76) != get_unaligned_le16(eeprom))
1321+ goto out;
1322+
1323+ for (i = 0; i < MT7996_EEPROM_SIZE; i += MT76_TM_EEPROM_BLOCK_SIZE) {
1324+ req.addr = cpu_to_le32(i);
1325+ memcpy(req.data, eeprom + i, MT76_TM_EEPROM_BLOCK_SIZE);
1326+
1327+ ret = mt7996_mcu_get_eeprom(dev, i, read_buf);
1328+ if (ret < 0)
1329+ return ret;
1330+
1331+ if (!memcmp(req.data, read_buf, MT76_TM_EEPROM_BLOCK_SIZE))
1332+ continue;
1333+
1334+ ret = mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL),
1335+ &req, sizeof(req), true);
1336+ if (ret)
1337+ return ret;
1338+ }
1339+
1340+out:
1341+ return ret;
1342+}
1343+
1344+static int
1345+mt7996_tm_set_eeprom(struct mt76_phy *mphy, u32 offset, u8 *val, u8 action)
1346+{
1347+ struct mt7996_phy *phy = mphy->priv;
1348+ struct mt7996_dev *dev = phy->dev;
1349+ u8 *eeprom = dev->mt76.eeprom.data;
1350+ int ret = 0;
1351+
1352+ if (offset >= MT7996_EEPROM_SIZE)
1353+ return -EINVAL;
1354+
1355+ switch (action) {
1356+ case MT76_TM_EEPROM_ACTION_UPDATE_DATA:
1357+ memcpy(eeprom + offset, val, MT76_TM_EEPROM_BLOCK_SIZE);
1358+ break;
1359+ case MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE:
1360+ ret = mt7996_mcu_set_eeprom(dev);
1361+ break;
1362+ case MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE:
1363+ ret = mt7996_tm_write_back_to_efuse(dev);
1364+ break;
1365+ default:
1366+ break;
1367+ }
1368+
1369+ return ret;
1370+}
1371+
1372+const struct mt76_testmode_ops mt7996_testmode_ops = {
1373+ .set_state = mt7996_tm_set_state,
1374+ .set_params = mt7996_tm_set_params,
1375+ .dump_stats = mt7996_tm_dump_stats,
1376+ .reset_rx_stats = mt7996_tm_reset_trx_stats,
1377+ .tx_stop = mt7996_tm_tx_stop,
1378+ .set_eeprom = mt7996_tm_set_eeprom,
1379+};
1380diff --git a/mt7996/testmode.h b/mt7996/testmode.h
1381new file mode 100644
developer05f3b2b2024-08-19 19:17:34 +08001382index 00000000..319ef257
developer66e89bc2024-04-23 14:50:01 +08001383--- /dev/null
1384+++ b/mt7996/testmode.h
1385@@ -0,0 +1,299 @@
1386+/* SPDX-License-Identifier: ISC */
1387+/* Copyright (C) 2020 MediaTek Inc. */
1388+
1389+#ifndef __MT7996_TESTMODE_H
1390+#define __MT7996_TESTMODE_H
1391+
1392+enum {
1393+ TM_CBW_20MHZ,
1394+ TM_CBW_40MHZ,
1395+ TM_CBW_80MHZ,
1396+ TM_CBW_10MHZ,
1397+ TM_CBW_5MHZ,
1398+ TM_CBW_160MHZ,
1399+ TM_CBW_8080MHZ,
1400+ TM_CBW_320MHZ = 12,
1401+};
1402+
1403+/* BW defined in FW hal_cal_flow_rom.h */
1404+enum {
1405+ FW_CDBW_20MHZ,
1406+ FW_CDBW_40MHZ,
1407+ FW_CDBW_80MHZ,
1408+ FW_CDBW_160MHZ,
1409+ FW_CDBW_320MHZ,
1410+ FW_CDBW_5MHZ,
1411+ FW_CDBW_10MHZ,
1412+ FW_CDBW_8080MHZ,
1413+};
1414+
1415+enum bw_mapping_method {
1416+ BW_MAP_NL_TO_FW,
1417+ BW_MAP_NL_TO_TM,
1418+
1419+ NUM_BW_MAP,
1420+};
1421+
1422+struct mt7996_tm_rf_test {
1423+ __le16 tag;
1424+ __le16 len;
1425+
1426+ u8 action;
1427+ u8 icap_len;
1428+ u8 _rsv[2];
1429+ union {
1430+ __le32 op_mode;
1431+ __le32 freq;
1432+
1433+ struct {
1434+ __le32 func_idx;
1435+ union {
1436+ __le32 func_data;
1437+ __le32 cal_dump;
1438+
1439+ u8 _pad[80];
1440+ } param;
1441+ } rf;
1442+ } op;
1443+} __packed;
1444+
1445+struct mt7996_tm_req {
1446+ u8 _rsv[4];
1447+
1448+ struct mt7996_tm_rf_test rf_test;
1449+} __packed;
1450+
1451+struct mt7996_tm_rf_test_result {
1452+ __le32 func_idx;
1453+ __le32 payload_length;
1454+ u8 event[0];
1455+};
1456+
1457+struct mt7996_tm_event {
1458+ u8 _rsv[4];
1459+
1460+ __le16 tag;
1461+ __le16 len;
1462+ struct mt7996_tm_rf_test_result result;
1463+} __packed;
1464+
1465+enum {
1466+ RF_ACTION_SWITCH_TO_RF_TEST,
1467+ RF_ACTION_IN_RF_TEST,
1468+ RF_ACTION_SET = 3,
1469+ RF_ACTION_GET,
1470+};
1471+
1472+enum {
1473+ RF_OPER_NORMAL,
1474+ RF_OPER_RF_TEST,
1475+ RF_OPER_ICAP,
1476+ RF_OPER_ICAP_OVERLAP,
1477+ RF_OPER_WIFI_SPECTRUM,
1478+};
1479+
1480+enum {
1481+ UNI_RF_TEST_CTRL,
1482+};
1483+
1484+#define RF_CMD(cmd) RF_TEST_CMD_##cmd
1485+
1486+enum {
1487+ RF_TEST_CMD_STOP_TEST = 0,
1488+ RF_TEST_CMD_START_TX = 1,
1489+ RF_TEST_CMD_START_RX = 2,
1490+ RF_TEST_CMD_CONT_WAVE = 10,
1491+ RF_TEST_CMD_TX_COMMIT = 18,
1492+ RF_TEST_CMD_RX_COMMIT = 19,
1493+};
1494+
1495+#define SET_ID(id) RF_TEST_ID_SET_##id
1496+#define GET_ID(id) RF_TEST_ID_GET_##id
1497+
1498+enum {
1499+ RF_TEST_ID_SET_COMMAND = 1,
1500+ RF_TEST_ID_SET_POWER = 2,
1501+ RF_TEST_ID_SET_TX_RATE = 3,
1502+ RF_TEST_ID_SET_TX_MODE = 4,
1503+ RF_TEST_ID_SET_TX_LEN = 6,
1504+ RF_TEST_ID_SET_TX_COUNT = 7,
1505+ RF_TEST_ID_SET_IPG = 8,
1506+ RF_TEST_ID_SET_GI = 16,
1507+ RF_TEST_ID_SET_STBC = 17,
1508+ RF_TEST_ID_SET_CHAN_FREQ = 18,
1509+ RF_TEST_ID_GET_TXED_COUNT = 32,
1510+ RF_TEST_ID_SET_CONT_WAVE_MODE = 65,
1511+ RF_TEST_ID_SET_DA = 68,
1512+ RF_TEST_ID_SET_SA = 69,
1513+ RF_TEST_ID_SET_CBW = 71,
1514+ RF_TEST_ID_SET_DBW = 72,
1515+ RF_TEST_ID_SET_PRIMARY_CH = 73,
1516+ RF_TEST_ID_SET_ENCODE_MODE = 74,
1517+ RF_TEST_ID_SET_BAND = 90,
1518+ RF_TEST_ID_SET_TRX_COUNTER_RESET = 91,
1519+ RF_TEST_ID_SET_MAC_HEADER = 101,
1520+ RF_TEST_ID_SET_SEQ_CTRL = 102,
1521+ RF_TEST_ID_SET_PAYLOAD = 103,
1522+ RF_TEST_ID_SET_BAND_IDX = 104,
1523+ RF_TEST_ID_SET_RX_PATH = 106,
1524+ RF_TEST_ID_SET_FREQ_OFFSET = 107,
1525+ RF_TEST_ID_GET_FREQ_OFFSET = 108,
1526+ RF_TEST_ID_SET_TX_PATH = 113,
1527+ RF_TEST_ID_SET_NSS = 114,
1528+ RF_TEST_ID_SET_ANT_MASK = 115,
1529+ RF_TEST_ID_SET_IBF_ENABLE = 126,
1530+ RF_TEST_ID_SET_EBF_ENABLE = 127,
1531+ RF_TEST_ID_GET_TX_POWER = 136,
1532+ RF_TEST_ID_SET_RX_MU_AID = 157,
1533+ RF_TEST_ID_SET_HW_TX_MODE = 167,
1534+ RF_TEST_ID_SET_PUNCTURE = 168,
1535+ RF_TEST_ID_SET_FREQ_OFFSET_C2 = 171,
1536+ RF_TEST_ID_GET_FREQ_OFFSET_C2 = 172,
1537+ RF_TEST_ID_SET_CFG_ON = 176,
1538+ RF_TEST_ID_SET_CFG_OFF = 177,
1539+ RF_TEST_ID_SET_BSSID = 189,
1540+ RF_TEST_ID_SET_TX_TIME = 190,
1541+ RF_TEST_ID_SET_MAX_PE = 191,
1542+ RF_TEST_ID_SET_AID_OFFSET = 204,
1543+};
1544+
1545+#define POWER_CTRL(type) UNI_TXPOWER_##type##_CTRL
1546+
1547+struct mt7996_tm_rx_stat_user_ctrl {
1548+ __le16 tag;
1549+ __le16 len;
1550+
1551+ u8 band_idx;
1552+ u8 rsv;
1553+ __le16 user_idx;
1554+} __packed;
1555+
1556+struct mt7996_tm_rx_stat_all {
1557+ __le16 tag;
1558+ __le16 len;
1559+
1560+ u8 band_idx;
1561+ u8 rsv[3];
1562+} __packed;
1563+
1564+struct mt7996_tm_rx_req {
1565+ u8 band;
1566+ u8 _rsv[3];
1567+
1568+ union {
1569+ struct mt7996_tm_rx_stat_user_ctrl user_ctrl;
1570+ struct mt7996_tm_rx_stat_all rx_stat_all;
1571+ };
1572+} __packed;
1573+
1574+enum {
1575+ UNI_TM_RX_STAT_SET_USER_CTRL = 7,
1576+ UNI_TM_RX_STAT_GET_ALL_V2 = 9,
1577+};
1578+
1579+struct rx_band_info {
1580+ /* mac part */
1581+ __le16 mac_rx_fcs_err_cnt;
1582+ __le16 mac_rx_len_mismatch;
1583+ __le16 mac_rx_fcs_ok_cnt;
1584+ u8 rsv1[2];
1585+ __le32 mac_rx_mdrdy_cnt;
1586+
1587+ /* phy part */
1588+ __le16 phy_rx_fcs_err_cnt_cck;
1589+ __le16 phy_rx_fcs_err_cnt_ofdm;
1590+ __le16 phy_rx_pd_cck;
1591+ __le16 phy_rx_pd_ofdm;
1592+ __le16 phy_rx_sig_err_cck;
1593+ __le16 phy_rx_sfd_err_cck;
1594+ __le16 phy_rx_sig_err_ofdm;
1595+ __le16 phy_rx_tag_err_ofdm;
1596+ __le16 phy_rx_mdrdy_cnt_cck;
1597+ __le16 phy_rx_mdrdy_cnt_ofdm;
1598+} __packed;
1599+
1600+struct rx_band_info_ext {
1601+ /* mac part */
1602+ __le32 mac_rx_mpdu_cnt;
1603+
1604+ /* phy part */
1605+ u8 rsv[4];
1606+} __packed;
1607+
1608+struct rx_common_info {
1609+ __le16 rx_fifo_full;
1610+ u8 rsv[2];
1611+ __le32 aci_hit_low;
1612+ __le32 aci_hit_high;
1613+} __packed;
1614+
1615+struct rx_common_info_ext {
1616+ __le32 driver_rx_count;
1617+ __le32 sinr;
1618+ __le32 mu_pkt_count;
1619+
1620+ /* mac part */
1621+ u8 _rsv[4];
1622+
1623+ /* phy part */
1624+ u8 sig_mcs;
1625+ u8 rsv[3];
1626+} __packed;
1627+
1628+struct rx_rxv_info {
1629+ __le16 rcpi;
1630+ s16 rssi;
1631+ s16 snr;
1632+ s16 adc_rssi;
1633+} __packed;
1634+
1635+struct rx_rssi_info {
1636+ s8 ib_rssi;
1637+ s8 wb_rssi;
1638+ u8 rsv[2];
1639+} __packed;
1640+
1641+struct rx_user_info {
1642+ s32 freq_offset;
1643+ s32 snr;
1644+ __le32 fcs_err_count;
1645+} __packed;
1646+
1647+struct rx_user_info_ext {
1648+ s8 ne_var_db_all_user;
1649+ u8 rsv[3];
1650+} __packed;
1651+
1652+#define MAX_ANTENNA_NUM 8
1653+#define MAX_USER_NUM 16
1654+
1655+struct mt7996_tm_rx_event_stat_all {
1656+ __le16 tag;
1657+ __le16 len;
1658+
1659+ struct rx_band_info band_info;
1660+ struct rx_band_info_ext band_info_ext;
1661+ struct rx_common_info common_info;
1662+ struct rx_common_info_ext common_info_ext;
1663+
1664+ /* RXV info */
1665+ struct rx_rxv_info rxv_info[MAX_ANTENNA_NUM];
1666+
1667+ /* RSSI info */
1668+ struct rx_rssi_info fagc[MAX_ANTENNA_NUM];
1669+ struct rx_rssi_info inst[MAX_ANTENNA_NUM];
1670+
1671+ /* User info */
1672+ struct rx_user_info user_info[MAX_USER_NUM];
1673+ struct rx_user_info_ext user_info_ext[MAX_USER_NUM];
1674+} __packed;
1675+
1676+struct mt7996_tm_rx_event {
1677+ u8 _rsv[4];
1678+
1679+ union {
1680+ struct mt7996_tm_rx_event_stat_all rx_stat_all;
1681+ };
1682+} __packed;
1683+
1684+#endif
1685diff --git a/testmode.c b/testmode.c
developer05f3b2b2024-08-19 19:17:34 +08001686index ca4feccf..44f3a5bf 100644
developer66e89bc2024-04-23 14:50:01 +08001687--- a/testmode.c
1688+++ b/testmode.c
1689@@ -2,11 +2,13 @@
1690 /* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
1691
1692 #include <linux/random.h>
1693+#include "mt76_connac.h"
1694 #include "mt76.h"
1695
1696 const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = {
1697 [MT76_TM_ATTR_RESET] = { .type = NLA_FLAG },
1698 [MT76_TM_ATTR_STATE] = { .type = NLA_U8 },
1699+ [MT76_TM_ATTR_SKU_EN] = { .type = NLA_U8 },
1700 [MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 },
1701 [MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 },
1702 [MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 },
1703@@ -82,6 +84,11 @@ mt76_testmode_max_mpdu_len(struct mt76_phy *phy, u8 tx_rate_mode)
1704 IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991)
1705 return IEEE80211_MAX_MPDU_LEN_VHT_7991;
1706 return IEEE80211_MAX_MPDU_LEN_VHT_11454;
1707+ case MT76_TM_TX_MODE_EHT_SU:
1708+ case MT76_TM_TX_MODE_EHT_TRIG:
1709+ case MT76_TM_TX_MODE_EHT_MU:
1710+ /* TODO: check the limit */
1711+ return UINT_MAX;
1712 case MT76_TM_TX_MODE_CCK:
1713 case MT76_TM_TX_MODE_OFDM:
1714 default:
1715@@ -183,6 +190,9 @@ mt76_testmode_tx_init(struct mt76_phy *phy)
1716 u8 max_nss = hweight8(phy->antenna_mask);
1717 int ret;
1718
1719+ if (is_mt7996(phy->dev))
1720+ return 0;
1721+
1722 ret = mt76_testmode_alloc_skb(phy, td->tx_mpdu_len);
1723 if (ret)
1724 return ret;
1725@@ -275,7 +285,9 @@ mt76_testmode_tx_start(struct mt76_phy *phy)
1726 td->tx_queued = 0;
1727 td->tx_done = 0;
1728 td->tx_pending = td->tx_count;
1729- mt76_worker_schedule(&dev->tx_worker);
1730+
1731+ if (!is_mt7996(dev))
1732+ mt76_worker_schedule(&dev->tx_worker);
1733 }
1734
1735 static void
1736@@ -284,6 +296,11 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
1737 struct mt76_testmode_data *td = &phy->test;
1738 struct mt76_dev *dev = phy->dev;
1739
1740+ if (is_mt7996(dev) && dev->test_ops->tx_stop) {
1741+ dev->test_ops->tx_stop(phy);
1742+ return;
1743+ }
1744+
1745 mt76_worker_disable(&dev->tx_worker);
1746
1747 td->tx_pending = 0;
1748@@ -296,22 +313,11 @@ mt76_testmode_tx_stop(struct mt76_phy *phy)
1749 mt76_testmode_free_skb(phy);
1750 }
1751
1752-static inline void
1753-mt76_testmode_param_set(struct mt76_testmode_data *td, u16 idx)
1754-{
1755- td->param_set[idx / 32] |= BIT(idx % 32);
1756-}
1757-
1758-static inline bool
1759-mt76_testmode_param_present(struct mt76_testmode_data *td, u16 idx)
1760-{
1761- return td->param_set[idx / 32] & BIT(idx % 32);
1762-}
1763-
1764 static void
1765 mt76_testmode_init_defaults(struct mt76_phy *phy)
1766 {
1767 struct mt76_testmode_data *td = &phy->test;
1768+ u8 addr[ETH_ALEN] = {phy->band_idx, 0x11, 0x22, 0xaa, 0xbb, 0xcc};
1769
1770 if (td->tx_mpdu_len > 0)
1771 return;
1772@@ -319,11 +325,18 @@ mt76_testmode_init_defaults(struct mt76_phy *phy)
1773 td->tx_mpdu_len = 1024;
1774 td->tx_count = 1;
1775 td->tx_rate_mode = MT76_TM_TX_MODE_OFDM;
1776+ td->tx_rate_idx = 7;
1777 td->tx_rate_nss = 1;
1778+ /* 0xffff for OFDMA no puncture */
1779+ td->tx_preamble_puncture = ~(td->tx_preamble_puncture & 0);
1780+ td->tx_ipg = 50;
1781
1782- memcpy(td->addr[0], phy->macaddr, ETH_ALEN);
1783- memcpy(td->addr[1], phy->macaddr, ETH_ALEN);
1784- memcpy(td->addr[2], phy->macaddr, ETH_ALEN);
1785+ /* rx stat user config */
1786+ td->aid = 1;
1787+
1788+ memcpy(td->addr[0], addr, ETH_ALEN);
1789+ memcpy(td->addr[1], addr, ETH_ALEN);
1790+ memcpy(td->addr[2], addr, ETH_ALEN);
1791 }
1792
1793 static int
1794@@ -353,7 +366,7 @@ __mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state)
1795 if (state == MT76_TM_STATE_TX_FRAMES)
1796 mt76_testmode_tx_start(phy);
1797 else if (state == MT76_TM_STATE_RX_FRAMES) {
1798- memset(&phy->test.rx_stats, 0, sizeof(phy->test.rx_stats));
1799+ dev->test_ops->reset_rx_stats(phy);
1800 }
1801
1802 phy->test.state = state;
1803@@ -404,6 +417,44 @@ mt76_tm_get_u8(struct nlattr *attr, u8 *dest, u8 min, u8 max)
1804 return 0;
1805 }
1806
1807+static int
1808+mt76_testmode_set_eeprom(struct mt76_phy *phy, struct nlattr **tb)
1809+{
1810+ struct mt76_dev *dev = phy->dev;
1811+ u8 action, val[MT76_TM_EEPROM_BLOCK_SIZE];
1812+ u32 offset = 0;
1813+ int err = -EINVAL;
1814+
1815+ if (!dev->test_ops->set_eeprom)
1816+ return -EOPNOTSUPP;
1817+
1818+ if (mt76_tm_get_u8(tb[MT76_TM_ATTR_EEPROM_ACTION], &action,
1819+ 0, MT76_TM_EEPROM_ACTION_MAX))
1820+ goto out;
1821+
1822+ if (tb[MT76_TM_ATTR_EEPROM_OFFSET]) {
1823+ struct nlattr *cur;
1824+ int rem, idx = 0;
1825+
1826+ offset = nla_get_u32(tb[MT76_TM_ATTR_EEPROM_OFFSET]);
1827+ if (!!(offset % MT76_TM_EEPROM_BLOCK_SIZE) ||
1828+ !tb[MT76_TM_ATTR_EEPROM_VAL])
1829+ goto out;
1830+
1831+ nla_for_each_nested(cur, tb[MT76_TM_ATTR_EEPROM_VAL], rem) {
1832+ if (nla_len(cur) != 1 || idx >= ARRAY_SIZE(val))
1833+ goto out;
1834+
1835+ val[idx++] = nla_get_u8(cur);
1836+ }
1837+ }
1838+
1839+ err = dev->test_ops->set_eeprom(phy, offset, val, action);
1840+
1841+out:
1842+ return err;
1843+}
1844+
1845 int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1846 void *data, int len)
1847 {
1848@@ -427,6 +478,11 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1849
1850 mutex_lock(&dev->mutex);
1851
1852+ if (tb[MT76_TM_ATTR_EEPROM_ACTION]) {
1853+ err = mt76_testmode_set_eeprom(phy, tb);
1854+ goto out;
1855+ }
1856+
1857 if (tb[MT76_TM_ATTR_RESET]) {
1858 mt76_testmode_set_state(phy, MT76_TM_STATE_OFF);
1859 memset(td, 0, sizeof(*td));
1860@@ -434,6 +490,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1861
1862 mt76_testmode_init_defaults(phy);
1863
1864+ if (tb[MT76_TM_ATTR_SKU_EN])
1865+ td->sku_en = nla_get_u8(tb[MT76_TM_ATTR_SKU_EN]);
1866+
1867 if (tb[MT76_TM_ATTR_TX_COUNT])
1868 td->tx_count = nla_get_u32(tb[MT76_TM_ATTR_TX_COUNT]);
1869
1870@@ -454,7 +513,8 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1871 mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_DUTY_CYCLE],
1872 &td->tx_duty_cycle, 0, 99) ||
1873 mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL],
1874- &td->tx_power_control, 0, 1))
1875+ &td->tx_power_control, 0, 1) ||
1876+ mt76_tm_get_u8(tb[MT76_TM_ATTR_AID], &td->aid, 0, 16))
1877 goto out;
1878
1879 if (tb[MT76_TM_ATTR_TX_LENGTH]) {
1880@@ -494,7 +554,9 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1881 idx >= ARRAY_SIZE(td->tx_power))
1882 goto out;
1883
1884- td->tx_power[idx++] = nla_get_u8(cur);
1885+ err = mt76_tm_get_u8(cur, &td->tx_power[idx++], 0, 63);
1886+ if (err)
1887+ return err;
1888 }
1889 }
1890
1891@@ -512,6 +574,22 @@ int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1892 }
1893 }
1894
1895+ if (tb[MT76_TM_ATTR_CFG]) {
1896+ struct nlattr *cur;
1897+ int rem, idx = 0;
1898+
1899+ nla_for_each_nested(cur, tb[MT76_TM_ATTR_CFG], rem) {
1900+ if (nla_len(cur) != 1 || idx >= 2)
1901+ goto out;
1902+
1903+ if (idx == 0)
1904+ td->cfg.type = nla_get_u8(cur);
1905+ else
1906+ td->cfg.enable = nla_get_u8(cur);
1907+ idx++;
1908+ }
1909+ }
1910+
1911 if (dev->test_ops->set_params) {
1912 err = dev->test_ops->set_params(phy, tb, state);
1913 if (err)
1914@@ -561,6 +639,9 @@ mt76_testmode_dump_stats(struct mt76_phy *phy, struct sk_buff *msg)
1915 nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_PACKETS, rx_packets,
1916 MT76_TM_STATS_ATTR_PAD) ||
1917 nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_FCS_ERROR, rx_fcs_error,
1918+ MT76_TM_STATS_ATTR_PAD) ||
1919+ nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_LEN_MISMATCH,
1920+ td->rx_stats.len_mismatch,
1921 MT76_TM_STATS_ATTR_PAD))
1922 return -EMSGSIZE;
1923
1924@@ -613,7 +694,8 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
1925
1926 if (dev->test_mtd.name &&
1927 (nla_put_string(msg, MT76_TM_ATTR_MTD_PART, dev->test_mtd.name) ||
1928- nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset)))
1929+ nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset) ||
1930+ nla_put_u8(msg, MT76_TM_ATTR_BAND_IDX, phy->band_idx)))
1931 goto out;
1932
1933 if (nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, td->tx_count) ||
1934@@ -624,6 +706,8 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
1935 nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_SGI, td->tx_rate_sgi) ||
1936 nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, td->tx_rate_ldpc) ||
1937 nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) ||
1938+ nla_put_u8(msg, MT76_TM_ATTR_SKU_EN, td->sku_en) ||
1939+ nla_put_u8(msg, MT76_TM_ATTR_AID, td->aid) ||
1940 (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) &&
1941 nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) ||
1942 (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) &&
1943@@ -639,7 +723,7 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
1944 (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER_CONTROL) &&
1945 nla_put_u8(msg, MT76_TM_ATTR_TX_POWER_CONTROL, td->tx_power_control)) ||
1946 (mt76_testmode_param_present(td, MT76_TM_ATTR_FREQ_OFFSET) &&
1947- nla_put_u8(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
1948+ nla_put_u32(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset)))
1949 goto out;
1950
1951 if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER)) {
1952diff --git a/testmode.h b/testmode.h
developer05f3b2b2024-08-19 19:17:34 +08001953index 5e2792d8..96872e8c 100644
developer66e89bc2024-04-23 14:50:01 +08001954--- a/testmode.h
1955+++ b/testmode.h
1956@@ -5,7 +5,8 @@
1957 #ifndef __MT76_TESTMODE_H
1958 #define __MT76_TESTMODE_H
1959
1960-#define MT76_TM_TIMEOUT 10
1961+#define MT76_TM_TIMEOUT 10
1962+#define MT76_TM_EEPROM_BLOCK_SIZE 16
1963
1964 /**
1965 * enum mt76_testmode_attr - testmode attributes inside NL80211_ATTR_TESTDATA
1966@@ -17,7 +18,9 @@
1967 *
1968 * @MT76_TM_ATTR_MTD_PART: mtd partition used for eeprom data (string)
1969 * @MT76_TM_ATTR_MTD_OFFSET: offset of eeprom data within the partition (u32)
1970+ * @MT76_TM_ATTR_BAND_IDX: band idx of the chip (u8)
1971 *
1972+ * @MT76_TM_ATTR_SKU_EN: config txpower sku is enabled or disabled in testmode (u8)
1973 * @MT76_TM_ATTR_TX_COUNT: configured number of frames to send when setting
1974 * state to MT76_TM_STATE_TX_FRAMES (u32)
1975 * @MT76_TM_ATTR_TX_PENDING: pending frames during MT76_TM_STATE_TX_FRAMES (u32)
1976@@ -38,6 +41,11 @@
1977 *
1978 * @MT76_TM_ATTR_STATS: statistics (nested, see &enum mt76_testmode_stats_attr)
1979 *
1980+ * @MT76_TM_ATTR_PRECAL: Pre-cal data (u8)
1981+ * @MT76_TM_ATTR_PRECAL_INFO: group size, dpd size, dpd_info, transmit size,
1982+ * eeprom cal indicator (u32),
1983+ * dpd_info = [dpd_per_chan_size, chan_num_2g,
1984+ * chan_num_5g, chan_num_6g]
1985 * @MT76_TM_ATTR_TX_SPE_IDX: tx spatial extension index (u8)
1986 *
1987 * @MT76_TM_ATTR_TX_DUTY_CYCLE: packet tx duty cycle (u8)
1988@@ -47,6 +55,29 @@
1989 * @MT76_TM_ATTR_DRV_DATA: driver specific netlink attrs (nested)
1990 *
1991 * @MT76_TM_ATTR_MAC_ADDRS: array of nested MAC addresses (nested)
1992+ *
1993+ * @MT76_TM_ATTR_EEPROM_ACTION: eeprom setting actions
1994+ * (u8, see &enum mt76_testmode_eeprom_action)
1995+ * @MT76_TM_ATTR_EEPROM_OFFSET: offset of eeprom data block for writing (u32)
1996+ * @MT76_TM_ATTR_EEPROM_VAL: values for writing into a 16-byte data block
1997+ * (nested, u8 attrs)
1998+ *
1999+ * @MT76_TM_ATTR_CFG: config testmode rf feature (nested, see &mt76_testmode_cfg)
2000+ * @MT76_TM_ATTR_TXBF_ACT: txbf setting actions (u8)
2001+ * @MT76_TM_ATTR_TXBF_PARAM: txbf parameters (nested)
2002+ *
2003+ * @MT76_TM_ATTR_OFF_CH_SCAN_CH: config the channel of background chain (ZWDFS) (u8)
2004+ * @MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH: config the center channel of background chain (ZWDFS) (u8)
2005+ * @MT76_TM_ATTR_OFF_CH_SCAN_BW: config the bandwidth of background chain (ZWDFS) (u8)
2006+ * @MT76_TM_ATTR_OFF_CH_SCAN_PATH: config the tx path of background chain (ZWDFS) (u8)
2007+ *
2008+ * @MT76_TM_ATTR_IPI_THRESHOLD: config the IPI index you want to read (u8)
2009+ * @MT76_TM_ATTR_IPI_PERIOD: config the time period for reading
2010+ * the histogram of specific IPI index (u8)
2011+ * @MT76_TM_ATTR_IPI_ANTENNA_INDEX: config the antenna index for reading
2012+ * the histogram of specific IPI index (u8)
2013+ * @MT76_TM_ATTR_IPI_RESET: Reset the IPI counter
2014+ *
2015 */
2016 enum mt76_testmode_attr {
2017 MT76_TM_ATTR_UNSPEC,
2018@@ -56,7 +87,9 @@ enum mt76_testmode_attr {
2019
2020 MT76_TM_ATTR_MTD_PART,
2021 MT76_TM_ATTR_MTD_OFFSET,
2022+ MT76_TM_ATTR_BAND_IDX,
2023
2024+ MT76_TM_ATTR_SKU_EN,
2025 MT76_TM_ATTR_TX_COUNT,
2026 MT76_TM_ATTR_TX_LENGTH,
2027 MT76_TM_ATTR_TX_RATE_MODE,
2028@@ -74,6 +107,8 @@ enum mt76_testmode_attr {
2029 MT76_TM_ATTR_FREQ_OFFSET,
2030
2031 MT76_TM_ATTR_STATS,
2032+ MT76_TM_ATTR_PRECAL,
2033+ MT76_TM_ATTR_PRECAL_INFO,
2034
2035 MT76_TM_ATTR_TX_SPE_IDX,
2036
2037@@ -84,6 +119,27 @@ enum mt76_testmode_attr {
2038 MT76_TM_ATTR_DRV_DATA,
2039
2040 MT76_TM_ATTR_MAC_ADDRS,
2041+ MT76_TM_ATTR_AID,
2042+ MT76_TM_ATTR_RU_ALLOC,
2043+ MT76_TM_ATTR_RU_IDX,
2044+
2045+ MT76_TM_ATTR_EEPROM_ACTION,
2046+ MT76_TM_ATTR_EEPROM_OFFSET,
2047+ MT76_TM_ATTR_EEPROM_VAL,
2048+
2049+ MT76_TM_ATTR_CFG,
2050+ MT76_TM_ATTR_TXBF_ACT,
2051+ MT76_TM_ATTR_TXBF_PARAM,
2052+
2053+ MT76_TM_ATTR_OFF_CH_SCAN_CH,
2054+ MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH,
2055+ MT76_TM_ATTR_OFF_CH_SCAN_BW,
2056+ MT76_TM_ATTR_OFF_CH_SCAN_PATH,
2057+
2058+ MT76_TM_ATTR_IPI_THRESHOLD,
2059+ MT76_TM_ATTR_IPI_PERIOD,
2060+ MT76_TM_ATTR_IPI_ANTENNA_INDEX,
2061+ MT76_TM_ATTR_IPI_RESET,
2062
2063 /* keep last */
2064 NUM_MT76_TM_ATTRS,
2065@@ -101,6 +157,8 @@ enum mt76_testmode_attr {
2066 * @MT76_TM_STATS_ATTR_RX_FCS_ERROR: number of rx packets with FCS error (u64)
2067 * @MT76_TM_STATS_ATTR_LAST_RX: information about the last received packet
2068 * see &enum mt76_testmode_rx_attr
2069+ * @MT76_TM_STATS_ATTR_RX_LEN_MISMATCH: number of rx packets with length
2070+ * mismatch error (u64)
2071 */
2072 enum mt76_testmode_stats_attr {
2073 MT76_TM_STATS_ATTR_UNSPEC,
2074@@ -113,6 +171,7 @@ enum mt76_testmode_stats_attr {
2075 MT76_TM_STATS_ATTR_RX_PACKETS,
2076 MT76_TM_STATS_ATTR_RX_FCS_ERROR,
2077 MT76_TM_STATS_ATTR_LAST_RX,
2078+ MT76_TM_STATS_ATTR_RX_LEN_MISMATCH,
2079
2080 /* keep last */
2081 NUM_MT76_TM_STATS_ATTRS,
2082@@ -125,6 +184,7 @@ enum mt76_testmode_stats_attr {
2083 *
2084 * @MT76_TM_RX_ATTR_FREQ_OFFSET: frequency offset (s32)
2085 * @MT76_TM_RX_ATTR_RCPI: received channel power indicator (array, u8)
2086+ * @MT76_TM_RX_ATTR_RSSI: received signal strength indicator (array, s8)
2087 * @MT76_TM_RX_ATTR_IB_RSSI: internal inband RSSI (array, s8)
2088 * @MT76_TM_RX_ATTR_WB_RSSI: internal wideband RSSI (array, s8)
2089 * @MT76_TM_RX_ATTR_SNR: signal-to-noise ratio (u8)
2090@@ -134,6 +194,7 @@ enum mt76_testmode_rx_attr {
2091
2092 MT76_TM_RX_ATTR_FREQ_OFFSET,
2093 MT76_TM_RX_ATTR_RCPI,
2094+ MT76_TM_RX_ATTR_RSSI,
2095 MT76_TM_RX_ATTR_IB_RSSI,
2096 MT76_TM_RX_ATTR_WB_RSSI,
2097 MT76_TM_RX_ATTR_SNR,
2098@@ -177,6 +238,9 @@ enum mt76_testmode_state {
2099 * @MT76_TM_TX_MODE_HE_EXT_SU: 802.11ax extended-range SU
2100 * @MT76_TM_TX_MODE_HE_TB: 802.11ax trigger-based
2101 * @MT76_TM_TX_MODE_HE_MU: 802.11ax multi-user MIMO
2102+ * @MT76_TM_TX_MODE_EHT_SU: 802.11be single-user MIMO
2103+ * @MT76_TM_TX_MODE_EHT_TRIG: 802.11be trigger-based
2104+ * @MT76_TM_TX_MODE_EHT_MU: 802.11be multi-user MIMO
2105 */
2106 enum mt76_testmode_tx_mode {
2107 MT76_TM_TX_MODE_CCK,
2108@@ -187,12 +251,33 @@ enum mt76_testmode_tx_mode {
2109 MT76_TM_TX_MODE_HE_EXT_SU,
2110 MT76_TM_TX_MODE_HE_TB,
2111 MT76_TM_TX_MODE_HE_MU,
2112+ MT76_TM_TX_MODE_EHT_SU,
2113+ MT76_TM_TX_MODE_EHT_TRIG,
2114+ MT76_TM_TX_MODE_EHT_MU,
2115
2116 /* keep last */
2117 NUM_MT76_TM_TX_MODES,
2118 MT76_TM_TX_MODE_MAX = NUM_MT76_TM_TX_MODES - 1,
2119 };
2120
2121+/**
2122+ * enum mt76_testmode_eeprom_action - eeprom setting actions
2123+ *
2124+ * @MT76_TM_EEPROM_ACTION_UPDATE_DATA: update rf values to specific
2125+ * eeprom data block
2126+ * @MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE: send updated eeprom data to fw
2127+ * @MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE: write eeprom data back to efuse
2128+ */
2129+enum mt76_testmode_eeprom_action {
2130+ MT76_TM_EEPROM_ACTION_UPDATE_DATA,
2131+ MT76_TM_EEPROM_ACTION_UPDATE_BUFFER_MODE,
2132+ MT76_TM_EEPROM_ACTION_WRITE_TO_EFUSE,
2133+
2134+ /* keep last */
2135+ NUM_MT76_TM_EEPROM_ACTION,
2136+ MT76_TM_EEPROM_ACTION_MAX = NUM_MT76_TM_EEPROM_ACTION - 1,
2137+};
2138+
2139 extern const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS];
2140
2141 #endif
2142diff --git a/tools/fields.c b/tools/fields.c
developer05f3b2b2024-08-19 19:17:34 +08002143index e3f69089..055f90f3 100644
developer66e89bc2024-04-23 14:50:01 +08002144--- a/tools/fields.c
2145+++ b/tools/fields.c
2146@@ -10,6 +10,7 @@ static const char * const testmode_state[] = {
2147 [MT76_TM_STATE_IDLE] = "idle",
2148 [MT76_TM_STATE_TX_FRAMES] = "tx_frames",
2149 [MT76_TM_STATE_RX_FRAMES] = "rx_frames",
2150+ [MT76_TM_STATE_TX_CONT] = "tx_cont",
2151 };
2152
2153 static const char * const testmode_tx_mode[] = {
2154@@ -21,6 +22,9 @@ static const char * const testmode_tx_mode[] = {
2155 [MT76_TM_TX_MODE_HE_EXT_SU] = "he_ext_su",
2156 [MT76_TM_TX_MODE_HE_TB] = "he_tb",
2157 [MT76_TM_TX_MODE_HE_MU] = "he_mu",
2158+ [MT76_TM_TX_MODE_EHT_SU] = "eht_su",
2159+ [MT76_TM_TX_MODE_EHT_TRIG] = "eht_tb",
2160+ [MT76_TM_TX_MODE_EHT_MU] = "eht_mu",
2161 };
2162
2163 static void print_enum(const struct tm_field *field, struct nlattr *attr)
2164@@ -65,7 +69,7 @@ static bool parse_u8(const struct tm_field *field, int idx,
2165
2166 static void print_u8(const struct tm_field *field, struct nlattr *attr)
2167 {
2168- printf("%d", nla_get_u8(attr));
2169+ printf("%u", nla_get_u8(attr));
2170 }
2171
2172 static void print_s8(const struct tm_field *field, struct nlattr *attr)
2173@@ -86,12 +90,12 @@ static void print_s32(const struct tm_field *field, struct nlattr *attr)
2174
2175 static void print_u32(const struct tm_field *field, struct nlattr *attr)
2176 {
2177- printf("%d", nla_get_u32(attr));
2178+ printf("%u", nla_get_u32(attr));
2179 }
2180
2181 static void print_u64(const struct tm_field *field, struct nlattr *attr)
2182 {
2183- printf("%lld", (unsigned long long)nla_get_u64(attr));
2184+ printf("%llu", (unsigned long long)nla_get_u64(attr));
2185 }
2186
2187 static bool parse_flag(const struct tm_field *field, int idx,
2188@@ -201,6 +205,62 @@ static void print_extra_stats(const struct tm_field *field, struct nlattr **tb)
2189 printf("%srx_per=%.02f%%\n", prefix, 100 * failed / total);
2190 }
2191
2192+static bool parse_mac(const struct tm_field *field, int idx,
2193+ struct nl_msg *msg, const char *val)
2194+{
2195+#define ETH_ALEN 6
2196+ bool ret = true;
2197+ char *str, *cur, *ap;
2198+ void *a;
2199+
2200+ str = strdup(val);
2201+ ap = str;
2202+
2203+ a = nla_nest_start(msg, idx);
2204+
2205+ idx = 0;
2206+ while ((cur = strsep(&ap, ",")) != NULL) {
2207+ unsigned char addr[ETH_ALEN];
2208+ char *val, *tmp = cur;
2209+ int i = 0;
2210+
2211+ while ((val = strsep(&tmp, ":")) != NULL) {
2212+ if (i >= ETH_ALEN)
2213+ break;
2214+
2215+ addr[i++] = strtoul(val, NULL, 16);
2216+ }
2217+
2218+ nla_put(msg, idx, ETH_ALEN, addr);
2219+
2220+ idx++;
2221+ }
2222+
2223+ nla_nest_end(msg, a);
2224+
2225+ free(str);
2226+
2227+ return ret;
2228+}
2229+
2230+static void print_mac(const struct tm_field *field, struct nlattr *attr)
2231+{
2232+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
2233+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
2234+ unsigned char addr[3][6];
2235+ struct nlattr *cur;
2236+ int idx = 0;
2237+ int rem;
2238+
2239+ nla_for_each_nested(cur, attr, rem) {
2240+ if (nla_len(cur) != 6)
2241+ continue;
2242+ memcpy(addr[idx++], nla_data(cur), 6);
2243+ }
2244+
2245+ printf("" MACSTR "," MACSTR "," MACSTR "",
2246+ MAC2STR(addr[0]), MAC2STR(addr[1]), MAC2STR(addr[2]));
2247+}
2248
2249 #define FIELD_GENERIC(_field, _name, ...) \
2250 [FIELD_NAME(_field)] = { \
2251@@ -250,10 +310,18 @@ static void print_extra_stats(const struct tm_field *field, struct nlattr **tb)
2252 ##__VA_ARGS__ \
2253 )
2254
2255+#define FIELD_MAC(_field, _name) \
2256+ [FIELD_NAME(_field)] = { \
2257+ .name = _name, \
2258+ .parse = parse_mac, \
2259+ .print = print_mac \
2260+ }
2261+
2262 #define FIELD_NAME(_field) MT76_TM_RX_ATTR_##_field
2263 static const struct tm_field rx_fields[NUM_MT76_TM_RX_ATTRS] = {
2264 FIELD_RO(s32, FREQ_OFFSET, "freq_offset"),
2265 FIELD_ARRAY_RO(u8, RCPI, "rcpi"),
2266+ FIELD_ARRAY_RO(s8, RSSI, "rssi"),
2267 FIELD_ARRAY_RO(s8, IB_RSSI, "ib_rssi"),
2268 FIELD_ARRAY_RO(s8, WB_RSSI, "wb_rssi"),
2269 FIELD_RO(s8, SNR, "snr"),
2270@@ -261,6 +329,7 @@ static const struct tm_field rx_fields[NUM_MT76_TM_RX_ATTRS] = {
2271 static struct nla_policy rx_policy[NUM_MT76_TM_RX_ATTRS] = {
2272 [MT76_TM_RX_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
2273 [MT76_TM_RX_ATTR_RCPI] = { .type = NLA_NESTED },
2274+ [MT76_TM_RX_ATTR_RSSI] = { .type = NLA_NESTED },
2275 [MT76_TM_RX_ATTR_IB_RSSI] = { .type = NLA_NESTED },
2276 [MT76_TM_RX_ATTR_WB_RSSI] = { .type = NLA_NESTED },
2277 [MT76_TM_RX_ATTR_SNR] = { .type = NLA_U8 },
2278@@ -274,6 +343,7 @@ static const struct tm_field stats_fields[NUM_MT76_TM_STATS_ATTRS] = {
2279 FIELD_RO(u32, TX_DONE, "tx_done"),
2280 FIELD_RO(u64, RX_PACKETS, "rx_packets"),
2281 FIELD_RO(u64, RX_FCS_ERROR, "rx_fcs_error"),
2282+ FIELD_RO(u64, RX_LEN_MISMATCH, "rx_len_mismatch"),
2283 FIELD_NESTED_RO(LAST_RX, rx, "last_"),
2284 };
2285 static struct nla_policy stats_policy[NUM_MT76_TM_STATS_ATTRS] = {
2286@@ -282,6 +352,7 @@ static struct nla_policy stats_policy[NUM_MT76_TM_STATS_ATTRS] = {
2287 [MT76_TM_STATS_ATTR_TX_DONE] = { .type = NLA_U32 },
2288 [MT76_TM_STATS_ATTR_RX_PACKETS] = { .type = NLA_U64 },
2289 [MT76_TM_STATS_ATTR_RX_FCS_ERROR] = { .type = NLA_U64 },
2290+ [MT76_TM_STATS_ATTR_RX_LEN_MISMATCH] = { .type = NLA_U64 },
2291 };
2292 #undef FIELD_NAME
2293
2294@@ -291,6 +362,7 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
2295 FIELD_ENUM(STATE, "state", testmode_state),
2296 FIELD_RO(string, MTD_PART, "mtd_part"),
2297 FIELD_RO(u32, MTD_OFFSET, "mtd_offset"),
2298+ FIELD(u8, SKU_EN, "sku_en"),
2299 FIELD(u32, TX_COUNT, "tx_count"),
2300 FIELD(u32, TX_LENGTH, "tx_length"),
2301 FIELD_ENUM(TX_RATE_MODE, "tx_rate_mode", testmode_tx_mode),
2302@@ -300,12 +372,20 @@ static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
2303 FIELD(u8, TX_RATE_LDPC, "tx_rate_ldpc"),
2304 FIELD(u8, TX_RATE_STBC, "tx_rate_stbc"),
2305 FIELD(u8, TX_LTF, "tx_ltf"),
2306+ FIELD(u8, TX_DUTY_CYCLE, "tx_duty_cycle"),
2307+ FIELD(u32, TX_IPG, "tx_ipg"),
2308+ FIELD(u32, TX_TIME, "tx_time"),
2309 FIELD(u8, TX_POWER_CONTROL, "tx_power_control"),
2310 FIELD_ARRAY(u8, TX_POWER, "tx_power"),
2311 FIELD(u8, TX_ANTENNA, "tx_antenna"),
2312 FIELD(u32, FREQ_OFFSET, "freq_offset"),
2313+ FIELD(u8, AID, "aid"),
2314+ FIELD(u8, RU_ALLOC, "ru_alloc"),
2315+ FIELD(u8, RU_IDX, "ru_idx"),
2316+ FIELD_MAC(MAC_ADDRS, "mac_addrs"),
2317 FIELD_NESTED_RO(STATS, stats, "",
2318 .print_extra = print_extra_stats),
2319+
2320 };
2321 #undef FIELD_NAME
2322
2323@@ -313,6 +393,7 @@ static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
2324 [MT76_TM_ATTR_STATE] = { .type = NLA_U8 },
2325 [MT76_TM_ATTR_MTD_PART] = { .type = NLA_STRING },
2326 [MT76_TM_ATTR_MTD_OFFSET] = { .type = NLA_U32 },
2327+ [MT76_TM_ATTR_SKU_EN] = { .type = NLA_U8 },
2328 [MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 },
2329 [MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 },
2330 [MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 },
2331@@ -322,10 +403,25 @@ static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
2332 [MT76_TM_ATTR_TX_RATE_LDPC] = { .type = NLA_U8 },
2333 [MT76_TM_ATTR_TX_RATE_STBC] = { .type = NLA_U8 },
2334 [MT76_TM_ATTR_TX_LTF] = { .type = NLA_U8 },
2335+ [MT76_TM_ATTR_TX_DUTY_CYCLE] = { .type = NLA_U8 },
2336+ [MT76_TM_ATTR_TX_IPG] = { .type = NLA_U32 },
2337+ [MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 },
2338 [MT76_TM_ATTR_TX_POWER_CONTROL] = { .type = NLA_U8 },
2339 [MT76_TM_ATTR_TX_ANTENNA] = { .type = NLA_U8 },
2340+ [MT76_TM_ATTR_TX_SPE_IDX] = { .type = NLA_U8 },
2341 [MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
2342+ [MT76_TM_ATTR_AID] = { .type = NLA_U8 },
2343+ [MT76_TM_ATTR_RU_ALLOC] = { .type = NLA_U8 },
2344+ [MT76_TM_ATTR_RU_IDX] = { .type = NLA_U8 },
2345 [MT76_TM_ATTR_STATS] = { .type = NLA_NESTED },
2346+ [MT76_TM_ATTR_TXBF_ACT] = { .type = NLA_U8 },
2347+ [MT76_TM_ATTR_OFF_CH_SCAN_CH] = { .type = NLA_U8 },
2348+ [MT76_TM_ATTR_OFF_CH_SCAN_CENTER_CH] = { .type = NLA_U8 },
2349+ [MT76_TM_ATTR_OFF_CH_SCAN_BW] = { .type = NLA_U8 },
2350+ [MT76_TM_ATTR_IPI_THRESHOLD] = { .type = NLA_U8 },
2351+ [MT76_TM_ATTR_IPI_PERIOD] = { .type = NLA_U32 },
2352+ [MT76_TM_ATTR_IPI_ANTENNA_INDEX] = { .type = NLA_U8 },
2353+ [MT76_TM_ATTR_IPI_RESET] = { .type = NLA_U8 },
2354 };
2355
2356 const struct tm_field msg_field = {
2357--
developer9237f442024-06-14 17:13:04 +080023582.18.0
developer66e89bc2024-04-23 14:50:01 +08002359