16c92544dSBjoern A. Zeeb // SPDX-License-Identifier: ISC 26c92544dSBjoern A. Zeeb /* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */ 36c92544dSBjoern A. Zeeb 46c92544dSBjoern A. Zeeb #include <linux/random.h> 56c92544dSBjoern A. Zeeb #include "mt76.h" 66c92544dSBjoern A. Zeeb 76c92544dSBjoern A. Zeeb const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = { 86c92544dSBjoern A. Zeeb [MT76_TM_ATTR_RESET] = { .type = NLA_FLAG }, 96c92544dSBjoern A. Zeeb [MT76_TM_ATTR_STATE] = { .type = NLA_U8 }, 106c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 }, 116c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 }, 126c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_RATE_NSS] = { .type = NLA_U8 }, 136c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_RATE_IDX] = { .type = NLA_U8 }, 146c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_RATE_SGI] = { .type = NLA_U8 }, 156c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_RATE_LDPC] = { .type = NLA_U8 }, 166c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_RATE_STBC] = { .type = NLA_U8 }, 176c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_LTF] = { .type = NLA_U8 }, 186c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_ANTENNA] = { .type = NLA_U8 }, 196c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_SPE_IDX] = { .type = NLA_U8 }, 206c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_POWER_CONTROL] = { .type = NLA_U8 }, 216c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_POWER] = { .type = NLA_NESTED }, 226c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_DUTY_CYCLE] = { .type = NLA_U8 }, 236c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_IPG] = { .type = NLA_U32 }, 246c92544dSBjoern A. Zeeb [MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 }, 256c92544dSBjoern A. Zeeb [MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 }, 266c92544dSBjoern A. Zeeb [MT76_TM_ATTR_DRV_DATA] = { .type = NLA_NESTED }, 276c92544dSBjoern A. Zeeb }; 286c92544dSBjoern A. Zeeb EXPORT_SYMBOL_GPL(mt76_tm_policy); 296c92544dSBjoern A. Zeeb 306c92544dSBjoern A. Zeeb void mt76_testmode_tx_pending(struct mt76_phy *phy) 316c92544dSBjoern A. Zeeb { 326c92544dSBjoern A. Zeeb struct mt76_testmode_data *td = &phy->test; 336c92544dSBjoern A. Zeeb struct mt76_dev *dev = phy->dev; 346c92544dSBjoern A. Zeeb struct mt76_wcid *wcid = &dev->global_wcid; 356c92544dSBjoern A. Zeeb struct sk_buff *skb = td->tx_skb; 366c92544dSBjoern A. Zeeb struct mt76_queue *q; 376c92544dSBjoern A. Zeeb u16 tx_queued_limit; 386c92544dSBjoern A. Zeeb int qid; 396c92544dSBjoern A. Zeeb 406c92544dSBjoern A. Zeeb if (!skb || !td->tx_pending) 416c92544dSBjoern A. Zeeb return; 426c92544dSBjoern A. Zeeb 436c92544dSBjoern A. Zeeb qid = skb_get_queue_mapping(skb); 446c92544dSBjoern A. Zeeb q = phy->q_tx[qid]; 456c92544dSBjoern A. Zeeb 466c92544dSBjoern A. Zeeb tx_queued_limit = td->tx_queued_limit ? td->tx_queued_limit : 1000; 476c92544dSBjoern A. Zeeb 486c92544dSBjoern A. Zeeb spin_lock_bh(&q->lock); 496c92544dSBjoern A. Zeeb 506c92544dSBjoern A. Zeeb while (td->tx_pending > 0 && 516c92544dSBjoern A. Zeeb td->tx_queued - td->tx_done < tx_queued_limit && 526c92544dSBjoern A. Zeeb q->queued < q->ndesc / 2) { 536c92544dSBjoern A. Zeeb int ret; 546c92544dSBjoern A. Zeeb 556c92544dSBjoern A. Zeeb ret = dev->queue_ops->tx_queue_skb(dev, q, qid, skb_get(skb), 566c92544dSBjoern A. Zeeb wcid, NULL); 576c92544dSBjoern A. Zeeb if (ret < 0) 586c92544dSBjoern A. Zeeb break; 596c92544dSBjoern A. Zeeb 606c92544dSBjoern A. Zeeb td->tx_pending--; 616c92544dSBjoern A. Zeeb td->tx_queued++; 626c92544dSBjoern A. Zeeb } 636c92544dSBjoern A. Zeeb 646c92544dSBjoern A. Zeeb dev->queue_ops->kick(dev, q); 656c92544dSBjoern A. Zeeb 666c92544dSBjoern A. Zeeb spin_unlock_bh(&q->lock); 676c92544dSBjoern A. Zeeb } 686c92544dSBjoern A. Zeeb 696c92544dSBjoern A. Zeeb static u32 706c92544dSBjoern A. Zeeb mt76_testmode_max_mpdu_len(struct mt76_phy *phy, u8 tx_rate_mode) 716c92544dSBjoern A. Zeeb { 726c92544dSBjoern A. Zeeb switch (tx_rate_mode) { 736c92544dSBjoern A. Zeeb case MT76_TM_TX_MODE_HT: 746c92544dSBjoern A. Zeeb return IEEE80211_MAX_MPDU_LEN_HT_7935; 756c92544dSBjoern A. Zeeb case MT76_TM_TX_MODE_VHT: 766c92544dSBjoern A. Zeeb case MT76_TM_TX_MODE_HE_SU: 776c92544dSBjoern A. Zeeb case MT76_TM_TX_MODE_HE_EXT_SU: 786c92544dSBjoern A. Zeeb case MT76_TM_TX_MODE_HE_TB: 796c92544dSBjoern A. Zeeb case MT76_TM_TX_MODE_HE_MU: 806c92544dSBjoern A. Zeeb if (phy->sband_5g.sband.vht_cap.cap & 816c92544dSBjoern A. Zeeb IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991) 826c92544dSBjoern A. Zeeb return IEEE80211_MAX_MPDU_LEN_VHT_7991; 836c92544dSBjoern A. Zeeb return IEEE80211_MAX_MPDU_LEN_VHT_11454; 846c92544dSBjoern A. Zeeb case MT76_TM_TX_MODE_CCK: 856c92544dSBjoern A. Zeeb case MT76_TM_TX_MODE_OFDM: 866c92544dSBjoern A. Zeeb default: 876c92544dSBjoern A. Zeeb return IEEE80211_MAX_FRAME_LEN; 886c92544dSBjoern A. Zeeb } 896c92544dSBjoern A. Zeeb } 906c92544dSBjoern A. Zeeb 916c92544dSBjoern A. Zeeb static void 926c92544dSBjoern A. Zeeb mt76_testmode_free_skb(struct mt76_phy *phy) 936c92544dSBjoern A. Zeeb { 946c92544dSBjoern A. Zeeb struct mt76_testmode_data *td = &phy->test; 956c92544dSBjoern A. Zeeb 966c92544dSBjoern A. Zeeb dev_kfree_skb(td->tx_skb); 976c92544dSBjoern A. Zeeb td->tx_skb = NULL; 986c92544dSBjoern A. Zeeb } 996c92544dSBjoern A. Zeeb 1006c92544dSBjoern A. Zeeb int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len) 1016c92544dSBjoern A. Zeeb { 1026c92544dSBjoern A. Zeeb #define MT_TXP_MAX_LEN 4095 1036c92544dSBjoern A. Zeeb u16 fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA | 1046c92544dSBjoern A. Zeeb IEEE80211_FCTL_FROMDS; 1056c92544dSBjoern A. Zeeb struct mt76_testmode_data *td = &phy->test; 1066c92544dSBjoern A. Zeeb struct sk_buff **frag_tail, *head; 1076c92544dSBjoern A. Zeeb struct ieee80211_tx_info *info; 1086c92544dSBjoern A. Zeeb struct ieee80211_hdr *hdr; 1096c92544dSBjoern A. Zeeb u32 max_len, head_len; 1106c92544dSBjoern A. Zeeb int nfrags, i; 1116c92544dSBjoern A. Zeeb 1126c92544dSBjoern A. Zeeb max_len = mt76_testmode_max_mpdu_len(phy, td->tx_rate_mode); 1136c92544dSBjoern A. Zeeb if (len > max_len) 1146c92544dSBjoern A. Zeeb len = max_len; 1156c92544dSBjoern A. Zeeb else if (len < sizeof(struct ieee80211_hdr)) 1166c92544dSBjoern A. Zeeb len = sizeof(struct ieee80211_hdr); 1176c92544dSBjoern A. Zeeb 1186c92544dSBjoern A. Zeeb nfrags = len / MT_TXP_MAX_LEN; 1196c92544dSBjoern A. Zeeb head_len = nfrags ? MT_TXP_MAX_LEN : len; 1206c92544dSBjoern A. Zeeb 1216c92544dSBjoern A. Zeeb if (len > IEEE80211_MAX_FRAME_LEN) 1226c92544dSBjoern A. Zeeb fc |= IEEE80211_STYPE_QOS_DATA; 1236c92544dSBjoern A. Zeeb 1246c92544dSBjoern A. Zeeb head = alloc_skb(head_len, GFP_KERNEL); 1256c92544dSBjoern A. Zeeb if (!head) 1266c92544dSBjoern A. Zeeb return -ENOMEM; 1276c92544dSBjoern A. Zeeb 1286c92544dSBjoern A. Zeeb hdr = __skb_put_zero(head, sizeof(*hdr)); 1296c92544dSBjoern A. Zeeb hdr->frame_control = cpu_to_le16(fc); 1306c92544dSBjoern A. Zeeb memcpy(hdr->addr1, td->addr[0], ETH_ALEN); 1316c92544dSBjoern A. Zeeb memcpy(hdr->addr2, td->addr[1], ETH_ALEN); 1326c92544dSBjoern A. Zeeb memcpy(hdr->addr3, td->addr[2], ETH_ALEN); 1336c92544dSBjoern A. Zeeb skb_set_queue_mapping(head, IEEE80211_AC_BE); 1346c92544dSBjoern A. Zeeb get_random_bytes(__skb_put(head, head_len - sizeof(*hdr)), 1356c92544dSBjoern A. Zeeb head_len - sizeof(*hdr)); 1366c92544dSBjoern A. Zeeb 1376c92544dSBjoern A. Zeeb info = IEEE80211_SKB_CB(head); 1386c92544dSBjoern A. Zeeb info->flags = IEEE80211_TX_CTL_INJECTED | 1396c92544dSBjoern A. Zeeb IEEE80211_TX_CTL_NO_ACK | 1406c92544dSBjoern A. Zeeb IEEE80211_TX_CTL_NO_PS_BUFFER; 1416c92544dSBjoern A. Zeeb 1426c92544dSBjoern A. Zeeb info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->band_idx); 1436c92544dSBjoern A. Zeeb frag_tail = &skb_shinfo(head)->frag_list; 1446c92544dSBjoern A. Zeeb 1456c92544dSBjoern A. Zeeb for (i = 0; i < nfrags; i++) { 1466c92544dSBjoern A. Zeeb struct sk_buff *frag; 1476c92544dSBjoern A. Zeeb u16 frag_len; 1486c92544dSBjoern A. Zeeb 1496c92544dSBjoern A. Zeeb if (i == nfrags - 1) 1506c92544dSBjoern A. Zeeb frag_len = len % MT_TXP_MAX_LEN; 1516c92544dSBjoern A. Zeeb else 1526c92544dSBjoern A. Zeeb frag_len = MT_TXP_MAX_LEN; 1536c92544dSBjoern A. Zeeb 1546c92544dSBjoern A. Zeeb frag = alloc_skb(frag_len, GFP_KERNEL); 1556c92544dSBjoern A. Zeeb if (!frag) { 1566c92544dSBjoern A. Zeeb mt76_testmode_free_skb(phy); 1576c92544dSBjoern A. Zeeb dev_kfree_skb(head); 1586c92544dSBjoern A. Zeeb return -ENOMEM; 1596c92544dSBjoern A. Zeeb } 1606c92544dSBjoern A. Zeeb 1616c92544dSBjoern A. Zeeb get_random_bytes(__skb_put(frag, frag_len), frag_len); 1626c92544dSBjoern A. Zeeb head->len += frag->len; 1636c92544dSBjoern A. Zeeb head->data_len += frag->len; 1646c92544dSBjoern A. Zeeb 1656c92544dSBjoern A. Zeeb *frag_tail = frag; 1666c92544dSBjoern A. Zeeb frag_tail = &(*frag_tail)->next; 1676c92544dSBjoern A. Zeeb } 1686c92544dSBjoern A. Zeeb 1696c92544dSBjoern A. Zeeb mt76_testmode_free_skb(phy); 1706c92544dSBjoern A. Zeeb td->tx_skb = head; 1716c92544dSBjoern A. Zeeb 1726c92544dSBjoern A. Zeeb return 0; 1736c92544dSBjoern A. Zeeb } 1746c92544dSBjoern A. Zeeb EXPORT_SYMBOL(mt76_testmode_alloc_skb); 1756c92544dSBjoern A. Zeeb 1766c92544dSBjoern A. Zeeb static int 1776c92544dSBjoern A. Zeeb mt76_testmode_tx_init(struct mt76_phy *phy) 1786c92544dSBjoern A. Zeeb { 1796c92544dSBjoern A. Zeeb struct mt76_testmode_data *td = &phy->test; 1806c92544dSBjoern A. Zeeb struct ieee80211_tx_info *info; 1816c92544dSBjoern A. Zeeb struct ieee80211_tx_rate *rate; 1826c92544dSBjoern A. Zeeb u8 max_nss = hweight8(phy->antenna_mask); 1836c92544dSBjoern A. Zeeb int ret; 1846c92544dSBjoern A. Zeeb 1856c92544dSBjoern A. Zeeb ret = mt76_testmode_alloc_skb(phy, td->tx_mpdu_len); 1866c92544dSBjoern A. Zeeb if (ret) 1876c92544dSBjoern A. Zeeb return ret; 1886c92544dSBjoern A. Zeeb 1896c92544dSBjoern A. Zeeb if (td->tx_rate_mode > MT76_TM_TX_MODE_VHT) 1906c92544dSBjoern A. Zeeb goto out; 1916c92544dSBjoern A. Zeeb 1926c92544dSBjoern A. Zeeb if (td->tx_antenna_mask) 1936c92544dSBjoern A. Zeeb max_nss = min_t(u8, max_nss, hweight8(td->tx_antenna_mask)); 1946c92544dSBjoern A. Zeeb 1956c92544dSBjoern A. Zeeb info = IEEE80211_SKB_CB(td->tx_skb); 1966c92544dSBjoern A. Zeeb rate = &info->control.rates[0]; 1976c92544dSBjoern A. Zeeb rate->count = 1; 1986c92544dSBjoern A. Zeeb rate->idx = td->tx_rate_idx; 1996c92544dSBjoern A. Zeeb 2006c92544dSBjoern A. Zeeb switch (td->tx_rate_mode) { 2016c92544dSBjoern A. Zeeb case MT76_TM_TX_MODE_CCK: 2026c92544dSBjoern A. Zeeb if (phy->chandef.chan->band != NL80211_BAND_2GHZ) 2036c92544dSBjoern A. Zeeb return -EINVAL; 2046c92544dSBjoern A. Zeeb 2056c92544dSBjoern A. Zeeb if (rate->idx > 4) 2066c92544dSBjoern A. Zeeb return -EINVAL; 2076c92544dSBjoern A. Zeeb break; 2086c92544dSBjoern A. Zeeb case MT76_TM_TX_MODE_OFDM: 2096c92544dSBjoern A. Zeeb if (phy->chandef.chan->band != NL80211_BAND_2GHZ) 2106c92544dSBjoern A. Zeeb break; 2116c92544dSBjoern A. Zeeb 2126c92544dSBjoern A. Zeeb if (rate->idx > 8) 2136c92544dSBjoern A. Zeeb return -EINVAL; 2146c92544dSBjoern A. Zeeb 2156c92544dSBjoern A. Zeeb rate->idx += 4; 2166c92544dSBjoern A. Zeeb break; 2176c92544dSBjoern A. Zeeb case MT76_TM_TX_MODE_HT: 2186c92544dSBjoern A. Zeeb if (rate->idx > 8 * max_nss && 2196c92544dSBjoern A. Zeeb !(rate->idx == 32 && 2206c92544dSBjoern A. Zeeb phy->chandef.width >= NL80211_CHAN_WIDTH_40)) 2216c92544dSBjoern A. Zeeb return -EINVAL; 2226c92544dSBjoern A. Zeeb 2236c92544dSBjoern A. Zeeb rate->flags |= IEEE80211_TX_RC_MCS; 2246c92544dSBjoern A. Zeeb break; 2256c92544dSBjoern A. Zeeb case MT76_TM_TX_MODE_VHT: 2266c92544dSBjoern A. Zeeb if (rate->idx > 9) 2276c92544dSBjoern A. Zeeb return -EINVAL; 2286c92544dSBjoern A. Zeeb 2296c92544dSBjoern A. Zeeb if (td->tx_rate_nss > max_nss) 2306c92544dSBjoern A. Zeeb return -EINVAL; 2316c92544dSBjoern A. Zeeb 2326c92544dSBjoern A. Zeeb ieee80211_rate_set_vht(rate, td->tx_rate_idx, td->tx_rate_nss); 2336c92544dSBjoern A. Zeeb rate->flags |= IEEE80211_TX_RC_VHT_MCS; 2346c92544dSBjoern A. Zeeb break; 2356c92544dSBjoern A. Zeeb default: 2366c92544dSBjoern A. Zeeb break; 2376c92544dSBjoern A. Zeeb } 2386c92544dSBjoern A. Zeeb 2396c92544dSBjoern A. Zeeb if (td->tx_rate_sgi) 2406c92544dSBjoern A. Zeeb rate->flags |= IEEE80211_TX_RC_SHORT_GI; 2416c92544dSBjoern A. Zeeb 2426c92544dSBjoern A. Zeeb if (td->tx_rate_ldpc) 2436c92544dSBjoern A. Zeeb info->flags |= IEEE80211_TX_CTL_LDPC; 2446c92544dSBjoern A. Zeeb 2456c92544dSBjoern A. Zeeb if (td->tx_rate_stbc) 2466c92544dSBjoern A. Zeeb info->flags |= IEEE80211_TX_CTL_STBC; 2476c92544dSBjoern A. Zeeb 2486c92544dSBjoern A. Zeeb if (td->tx_rate_mode >= MT76_TM_TX_MODE_HT) { 2496c92544dSBjoern A. Zeeb switch (phy->chandef.width) { 2506c92544dSBjoern A. Zeeb case NL80211_CHAN_WIDTH_40: 2516c92544dSBjoern A. Zeeb rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; 2526c92544dSBjoern A. Zeeb break; 2536c92544dSBjoern A. Zeeb case NL80211_CHAN_WIDTH_80: 2546c92544dSBjoern A. Zeeb rate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH; 2556c92544dSBjoern A. Zeeb break; 2566c92544dSBjoern A. Zeeb case NL80211_CHAN_WIDTH_80P80: 2576c92544dSBjoern A. Zeeb case NL80211_CHAN_WIDTH_160: 2586c92544dSBjoern A. Zeeb rate->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH; 2596c92544dSBjoern A. Zeeb break; 2606c92544dSBjoern A. Zeeb default: 2616c92544dSBjoern A. Zeeb break; 2626c92544dSBjoern A. Zeeb } 2636c92544dSBjoern A. Zeeb } 2646c92544dSBjoern A. Zeeb out: 2656c92544dSBjoern A. Zeeb return 0; 2666c92544dSBjoern A. Zeeb } 2676c92544dSBjoern A. Zeeb 2686c92544dSBjoern A. Zeeb static void 2696c92544dSBjoern A. Zeeb mt76_testmode_tx_start(struct mt76_phy *phy) 2706c92544dSBjoern A. Zeeb { 2716c92544dSBjoern A. Zeeb struct mt76_testmode_data *td = &phy->test; 2726c92544dSBjoern A. Zeeb struct mt76_dev *dev = phy->dev; 2736c92544dSBjoern A. Zeeb 2746c92544dSBjoern A. Zeeb td->tx_queued = 0; 2756c92544dSBjoern A. Zeeb td->tx_done = 0; 2766c92544dSBjoern A. Zeeb td->tx_pending = td->tx_count; 2776c92544dSBjoern A. Zeeb mt76_worker_schedule(&dev->tx_worker); 2786c92544dSBjoern A. Zeeb } 2796c92544dSBjoern A. Zeeb 2806c92544dSBjoern A. Zeeb static void 2816c92544dSBjoern A. Zeeb mt76_testmode_tx_stop(struct mt76_phy *phy) 2826c92544dSBjoern A. Zeeb { 2836c92544dSBjoern A. Zeeb struct mt76_testmode_data *td = &phy->test; 2846c92544dSBjoern A. Zeeb struct mt76_dev *dev = phy->dev; 2856c92544dSBjoern A. Zeeb 2866c92544dSBjoern A. Zeeb mt76_worker_disable(&dev->tx_worker); 2876c92544dSBjoern A. Zeeb 2886c92544dSBjoern A. Zeeb td->tx_pending = 0; 2896c92544dSBjoern A. Zeeb 2906c92544dSBjoern A. Zeeb mt76_worker_enable(&dev->tx_worker); 2916c92544dSBjoern A. Zeeb 2926c92544dSBjoern A. Zeeb wait_event_timeout(dev->tx_wait, td->tx_done == td->tx_queued, 2936c92544dSBjoern A. Zeeb MT76_TM_TIMEOUT * HZ); 2946c92544dSBjoern A. Zeeb 2956c92544dSBjoern A. Zeeb mt76_testmode_free_skb(phy); 2966c92544dSBjoern A. Zeeb } 2976c92544dSBjoern A. Zeeb 2986c92544dSBjoern A. Zeeb static inline void 2996c92544dSBjoern A. Zeeb mt76_testmode_param_set(struct mt76_testmode_data *td, u16 idx) 3006c92544dSBjoern A. Zeeb { 3016c92544dSBjoern A. Zeeb td->param_set[idx / 32] |= BIT(idx % 32); 3026c92544dSBjoern A. Zeeb } 3036c92544dSBjoern A. Zeeb 3046c92544dSBjoern A. Zeeb static inline bool 3056c92544dSBjoern A. Zeeb mt76_testmode_param_present(struct mt76_testmode_data *td, u16 idx) 3066c92544dSBjoern A. Zeeb { 3076c92544dSBjoern A. Zeeb return td->param_set[idx / 32] & BIT(idx % 32); 3086c92544dSBjoern A. Zeeb } 3096c92544dSBjoern A. Zeeb 3106c92544dSBjoern A. Zeeb static void 3116c92544dSBjoern A. Zeeb mt76_testmode_init_defaults(struct mt76_phy *phy) 3126c92544dSBjoern A. Zeeb { 3136c92544dSBjoern A. Zeeb struct mt76_testmode_data *td = &phy->test; 3146c92544dSBjoern A. Zeeb 3156c92544dSBjoern A. Zeeb if (td->tx_mpdu_len > 0) 3166c92544dSBjoern A. Zeeb return; 3176c92544dSBjoern A. Zeeb 3186c92544dSBjoern A. Zeeb td->tx_mpdu_len = 1024; 3196c92544dSBjoern A. Zeeb td->tx_count = 1; 3206c92544dSBjoern A. Zeeb td->tx_rate_mode = MT76_TM_TX_MODE_OFDM; 3216c92544dSBjoern A. Zeeb td->tx_rate_nss = 1; 3226c92544dSBjoern A. Zeeb 3236c92544dSBjoern A. Zeeb memcpy(td->addr[0], phy->macaddr, ETH_ALEN); 3246c92544dSBjoern A. Zeeb memcpy(td->addr[1], phy->macaddr, ETH_ALEN); 3256c92544dSBjoern A. Zeeb memcpy(td->addr[2], phy->macaddr, ETH_ALEN); 3266c92544dSBjoern A. Zeeb } 3276c92544dSBjoern A. Zeeb 3286c92544dSBjoern A. Zeeb static int 3296c92544dSBjoern A. Zeeb __mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state) 3306c92544dSBjoern A. Zeeb { 3316c92544dSBjoern A. Zeeb enum mt76_testmode_state prev_state = phy->test.state; 3326c92544dSBjoern A. Zeeb struct mt76_dev *dev = phy->dev; 3336c92544dSBjoern A. Zeeb int err; 3346c92544dSBjoern A. Zeeb 3356c92544dSBjoern A. Zeeb if (prev_state == MT76_TM_STATE_TX_FRAMES) 3366c92544dSBjoern A. Zeeb mt76_testmode_tx_stop(phy); 3376c92544dSBjoern A. Zeeb 3386c92544dSBjoern A. Zeeb if (state == MT76_TM_STATE_TX_FRAMES) { 3396c92544dSBjoern A. Zeeb err = mt76_testmode_tx_init(phy); 3406c92544dSBjoern A. Zeeb if (err) 3416c92544dSBjoern A. Zeeb return err; 3426c92544dSBjoern A. Zeeb } 3436c92544dSBjoern A. Zeeb 3446c92544dSBjoern A. Zeeb err = dev->test_ops->set_state(phy, state); 3456c92544dSBjoern A. Zeeb if (err) { 3466c92544dSBjoern A. Zeeb if (state == MT76_TM_STATE_TX_FRAMES) 3476c92544dSBjoern A. Zeeb mt76_testmode_tx_stop(phy); 3486c92544dSBjoern A. Zeeb 3496c92544dSBjoern A. Zeeb return err; 3506c92544dSBjoern A. Zeeb } 3516c92544dSBjoern A. Zeeb 3526c92544dSBjoern A. Zeeb if (state == MT76_TM_STATE_TX_FRAMES) 3536c92544dSBjoern A. Zeeb mt76_testmode_tx_start(phy); 3546c92544dSBjoern A. Zeeb else if (state == MT76_TM_STATE_RX_FRAMES) { 3556c92544dSBjoern A. Zeeb memset(&phy->test.rx_stats, 0, sizeof(phy->test.rx_stats)); 3566c92544dSBjoern A. Zeeb } 3576c92544dSBjoern A. Zeeb 3586c92544dSBjoern A. Zeeb phy->test.state = state; 3596c92544dSBjoern A. Zeeb 3606c92544dSBjoern A. Zeeb return 0; 3616c92544dSBjoern A. Zeeb } 3626c92544dSBjoern A. Zeeb 3636c92544dSBjoern A. Zeeb int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state) 3646c92544dSBjoern A. Zeeb { 3656c92544dSBjoern A. Zeeb struct mt76_testmode_data *td = &phy->test; 3666c92544dSBjoern A. Zeeb struct ieee80211_hw *hw = phy->hw; 3676c92544dSBjoern A. Zeeb 3686c92544dSBjoern A. Zeeb if (state == td->state && state == MT76_TM_STATE_OFF) 3696c92544dSBjoern A. Zeeb return 0; 3706c92544dSBjoern A. Zeeb 3716c92544dSBjoern A. Zeeb if (state > MT76_TM_STATE_OFF && 3726c92544dSBjoern A. Zeeb (!test_bit(MT76_STATE_RUNNING, &phy->state) || 3736c92544dSBjoern A. Zeeb !(hw->conf.flags & IEEE80211_CONF_MONITOR))) 3746c92544dSBjoern A. Zeeb return -ENOTCONN; 3756c92544dSBjoern A. Zeeb 3766c92544dSBjoern A. Zeeb if (state != MT76_TM_STATE_IDLE && 3776c92544dSBjoern A. Zeeb td->state != MT76_TM_STATE_IDLE) { 3786c92544dSBjoern A. Zeeb int ret; 3796c92544dSBjoern A. Zeeb 3806c92544dSBjoern A. Zeeb ret = __mt76_testmode_set_state(phy, MT76_TM_STATE_IDLE); 3816c92544dSBjoern A. Zeeb if (ret) 3826c92544dSBjoern A. Zeeb return ret; 3836c92544dSBjoern A. Zeeb } 3846c92544dSBjoern A. Zeeb 3856c92544dSBjoern A. Zeeb return __mt76_testmode_set_state(phy, state); 3866c92544dSBjoern A. Zeeb 3876c92544dSBjoern A. Zeeb } 3886c92544dSBjoern A. Zeeb EXPORT_SYMBOL(mt76_testmode_set_state); 3896c92544dSBjoern A. Zeeb 3906c92544dSBjoern A. Zeeb static int 3916c92544dSBjoern A. Zeeb mt76_tm_get_u8(struct nlattr *attr, u8 *dest, u8 min, u8 max) 3926c92544dSBjoern A. Zeeb { 3936c92544dSBjoern A. Zeeb u8 val; 3946c92544dSBjoern A. Zeeb 3956c92544dSBjoern A. Zeeb if (!attr) 3966c92544dSBjoern A. Zeeb return 0; 3976c92544dSBjoern A. Zeeb 3986c92544dSBjoern A. Zeeb val = nla_get_u8(attr); 3996c92544dSBjoern A. Zeeb if (val < min || val > max) 4006c92544dSBjoern A. Zeeb return -EINVAL; 4016c92544dSBjoern A. Zeeb 4026c92544dSBjoern A. Zeeb *dest = val; 4036c92544dSBjoern A. Zeeb return 0; 4046c92544dSBjoern A. Zeeb } 4056c92544dSBjoern A. Zeeb 4066c92544dSBjoern A. Zeeb int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 4076c92544dSBjoern A. Zeeb void *data, int len) 4086c92544dSBjoern A. Zeeb { 4096c92544dSBjoern A. Zeeb struct mt76_phy *phy = hw->priv; 4106c92544dSBjoern A. Zeeb struct mt76_dev *dev = phy->dev; 4116c92544dSBjoern A. Zeeb struct mt76_testmode_data *td = &phy->test; 4126c92544dSBjoern A. Zeeb struct nlattr *tb[NUM_MT76_TM_ATTRS]; 4136c92544dSBjoern A. Zeeb u32 state; 4146c92544dSBjoern A. Zeeb int err; 4156c92544dSBjoern A. Zeeb int i; 4166c92544dSBjoern A. Zeeb 4176c92544dSBjoern A. Zeeb if (!dev->test_ops) 4186c92544dSBjoern A. Zeeb return -EOPNOTSUPP; 4196c92544dSBjoern A. Zeeb 4206c92544dSBjoern A. Zeeb err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, 4216c92544dSBjoern A. Zeeb mt76_tm_policy, NULL); 4226c92544dSBjoern A. Zeeb if (err) 4236c92544dSBjoern A. Zeeb return err; 4246c92544dSBjoern A. Zeeb 4256c92544dSBjoern A. Zeeb err = -EINVAL; 4266c92544dSBjoern A. Zeeb 4276c92544dSBjoern A. Zeeb mutex_lock(&dev->mutex); 4286c92544dSBjoern A. Zeeb 4296c92544dSBjoern A. Zeeb if (tb[MT76_TM_ATTR_RESET]) { 4306c92544dSBjoern A. Zeeb mt76_testmode_set_state(phy, MT76_TM_STATE_OFF); 4316c92544dSBjoern A. Zeeb memset(td, 0, sizeof(*td)); 4326c92544dSBjoern A. Zeeb } 4336c92544dSBjoern A. Zeeb 4346c92544dSBjoern A. Zeeb mt76_testmode_init_defaults(phy); 4356c92544dSBjoern A. Zeeb 4366c92544dSBjoern A. Zeeb if (tb[MT76_TM_ATTR_TX_COUNT]) 4376c92544dSBjoern A. Zeeb td->tx_count = nla_get_u32(tb[MT76_TM_ATTR_TX_COUNT]); 4386c92544dSBjoern A. Zeeb 4396c92544dSBjoern A. Zeeb if (tb[MT76_TM_ATTR_TX_RATE_IDX]) 4406c92544dSBjoern A. Zeeb td->tx_rate_idx = nla_get_u8(tb[MT76_TM_ATTR_TX_RATE_IDX]); 4416c92544dSBjoern A. Zeeb 4426c92544dSBjoern A. Zeeb if (mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_MODE], &td->tx_rate_mode, 4436c92544dSBjoern A. Zeeb 0, MT76_TM_TX_MODE_MAX) || 4446c92544dSBjoern A. Zeeb mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_NSS], &td->tx_rate_nss, 4456c92544dSBjoern A. Zeeb 1, hweight8(phy->antenna_mask)) || 4466c92544dSBjoern A. Zeeb mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_SGI], &td->tx_rate_sgi, 0, 2) || 4476c92544dSBjoern A. Zeeb mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_LDPC], &td->tx_rate_ldpc, 0, 1) || 4486c92544dSBjoern A. Zeeb mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_STBC], &td->tx_rate_stbc, 0, 1) || 4496c92544dSBjoern A. Zeeb mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_LTF], &td->tx_ltf, 0, 2) || 4506c92544dSBjoern A. Zeeb mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_ANTENNA], 4516c92544dSBjoern A. Zeeb &td->tx_antenna_mask, 0, 0xff) || 4526c92544dSBjoern A. Zeeb mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_SPE_IDX], &td->tx_spe_idx, 0, 27) || 4536c92544dSBjoern A. Zeeb mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_DUTY_CYCLE], 4546c92544dSBjoern A. Zeeb &td->tx_duty_cycle, 0, 99) || 4556c92544dSBjoern A. Zeeb mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL], 4566c92544dSBjoern A. Zeeb &td->tx_power_control, 0, 1)) 4576c92544dSBjoern A. Zeeb goto out; 4586c92544dSBjoern A. Zeeb 4596c92544dSBjoern A. Zeeb if (tb[MT76_TM_ATTR_TX_LENGTH]) { 4606c92544dSBjoern A. Zeeb u32 val = nla_get_u32(tb[MT76_TM_ATTR_TX_LENGTH]); 4616c92544dSBjoern A. Zeeb 4626c92544dSBjoern A. Zeeb if (val > mt76_testmode_max_mpdu_len(phy, td->tx_rate_mode) || 4636c92544dSBjoern A. Zeeb val < sizeof(struct ieee80211_hdr)) 4646c92544dSBjoern A. Zeeb goto out; 4656c92544dSBjoern A. Zeeb 4666c92544dSBjoern A. Zeeb td->tx_mpdu_len = val; 4676c92544dSBjoern A. Zeeb } 4686c92544dSBjoern A. Zeeb 4696c92544dSBjoern A. Zeeb if (tb[MT76_TM_ATTR_TX_IPG]) 4706c92544dSBjoern A. Zeeb td->tx_ipg = nla_get_u32(tb[MT76_TM_ATTR_TX_IPG]); 4716c92544dSBjoern A. Zeeb 4726c92544dSBjoern A. Zeeb if (tb[MT76_TM_ATTR_TX_TIME]) 4736c92544dSBjoern A. Zeeb td->tx_time = nla_get_u32(tb[MT76_TM_ATTR_TX_TIME]); 4746c92544dSBjoern A. Zeeb 4756c92544dSBjoern A. Zeeb if (tb[MT76_TM_ATTR_FREQ_OFFSET]) 4766c92544dSBjoern A. Zeeb td->freq_offset = nla_get_u32(tb[MT76_TM_ATTR_FREQ_OFFSET]); 4776c92544dSBjoern A. Zeeb 4786c92544dSBjoern A. Zeeb if (tb[MT76_TM_ATTR_STATE]) { 4796c92544dSBjoern A. Zeeb state = nla_get_u32(tb[MT76_TM_ATTR_STATE]); 4806c92544dSBjoern A. Zeeb if (state > MT76_TM_STATE_MAX) 4816c92544dSBjoern A. Zeeb goto out; 4826c92544dSBjoern A. Zeeb } else { 4836c92544dSBjoern A. Zeeb state = td->state; 4846c92544dSBjoern A. Zeeb } 4856c92544dSBjoern A. Zeeb 4866c92544dSBjoern A. Zeeb if (tb[MT76_TM_ATTR_TX_POWER]) { 4876c92544dSBjoern A. Zeeb struct nlattr *cur; 4886c92544dSBjoern A. Zeeb int idx = 0; 4896c92544dSBjoern A. Zeeb int rem; 4906c92544dSBjoern A. Zeeb 4916c92544dSBjoern A. Zeeb nla_for_each_nested(cur, tb[MT76_TM_ATTR_TX_POWER], rem) { 4926c92544dSBjoern A. Zeeb if (nla_len(cur) != 1 || 4936c92544dSBjoern A. Zeeb idx >= ARRAY_SIZE(td->tx_power)) 4946c92544dSBjoern A. Zeeb goto out; 4956c92544dSBjoern A. Zeeb 4966c92544dSBjoern A. Zeeb td->tx_power[idx++] = nla_get_u8(cur); 4976c92544dSBjoern A. Zeeb } 4986c92544dSBjoern A. Zeeb } 4996c92544dSBjoern A. Zeeb 5006c92544dSBjoern A. Zeeb if (tb[MT76_TM_ATTR_MAC_ADDRS]) { 5016c92544dSBjoern A. Zeeb struct nlattr *cur; 5026c92544dSBjoern A. Zeeb int idx = 0; 5036c92544dSBjoern A. Zeeb int rem; 5046c92544dSBjoern A. Zeeb 5056c92544dSBjoern A. Zeeb nla_for_each_nested(cur, tb[MT76_TM_ATTR_MAC_ADDRS], rem) { 5066c92544dSBjoern A. Zeeb if (nla_len(cur) != ETH_ALEN || idx >= 3) 5076c92544dSBjoern A. Zeeb goto out; 5086c92544dSBjoern A. Zeeb 5096c92544dSBjoern A. Zeeb memcpy(td->addr[idx], nla_data(cur), ETH_ALEN); 5106c92544dSBjoern A. Zeeb idx++; 5116c92544dSBjoern A. Zeeb } 5126c92544dSBjoern A. Zeeb } 5136c92544dSBjoern A. Zeeb 5146c92544dSBjoern A. Zeeb if (dev->test_ops->set_params) { 5156c92544dSBjoern A. Zeeb err = dev->test_ops->set_params(phy, tb, state); 5166c92544dSBjoern A. Zeeb if (err) 5176c92544dSBjoern A. Zeeb goto out; 5186c92544dSBjoern A. Zeeb } 5196c92544dSBjoern A. Zeeb 5206c92544dSBjoern A. Zeeb for (i = MT76_TM_ATTR_STATE; i < ARRAY_SIZE(tb); i++) 5216c92544dSBjoern A. Zeeb if (tb[i]) 5226c92544dSBjoern A. Zeeb mt76_testmode_param_set(td, i); 5236c92544dSBjoern A. Zeeb 5246c92544dSBjoern A. Zeeb err = 0; 5256c92544dSBjoern A. Zeeb if (tb[MT76_TM_ATTR_STATE]) 5266c92544dSBjoern A. Zeeb err = mt76_testmode_set_state(phy, state); 5276c92544dSBjoern A. Zeeb 5286c92544dSBjoern A. Zeeb out: 5296c92544dSBjoern A. Zeeb mutex_unlock(&dev->mutex); 5306c92544dSBjoern A. Zeeb 5316c92544dSBjoern A. Zeeb return err; 5326c92544dSBjoern A. Zeeb } 5336c92544dSBjoern A. Zeeb EXPORT_SYMBOL(mt76_testmode_cmd); 5346c92544dSBjoern A. Zeeb 5356c92544dSBjoern A. Zeeb static int 5366c92544dSBjoern A. Zeeb mt76_testmode_dump_stats(struct mt76_phy *phy, struct sk_buff *msg) 5376c92544dSBjoern A. Zeeb { 5386c92544dSBjoern A. Zeeb struct mt76_testmode_data *td = &phy->test; 5396c92544dSBjoern A. Zeeb struct mt76_dev *dev = phy->dev; 5406c92544dSBjoern A. Zeeb u64 rx_packets = 0; 5416c92544dSBjoern A. Zeeb u64 rx_fcs_error = 0; 5426c92544dSBjoern A. Zeeb int i; 5436c92544dSBjoern A. Zeeb 5446c92544dSBjoern A. Zeeb if (dev->test_ops->dump_stats) { 5456c92544dSBjoern A. Zeeb int ret; 5466c92544dSBjoern A. Zeeb 5476c92544dSBjoern A. Zeeb ret = dev->test_ops->dump_stats(phy, msg); 5486c92544dSBjoern A. Zeeb if (ret) 5496c92544dSBjoern A. Zeeb return ret; 5506c92544dSBjoern A. Zeeb } 5516c92544dSBjoern A. Zeeb 5526c92544dSBjoern A. Zeeb for (i = 0; i < ARRAY_SIZE(td->rx_stats.packets); i++) { 5536c92544dSBjoern A. Zeeb rx_packets += td->rx_stats.packets[i]; 5546c92544dSBjoern A. Zeeb rx_fcs_error += td->rx_stats.fcs_error[i]; 5556c92544dSBjoern A. Zeeb } 5566c92544dSBjoern A. Zeeb 5576c92544dSBjoern A. Zeeb if (nla_put_u32(msg, MT76_TM_STATS_ATTR_TX_PENDING, td->tx_pending) || 5586c92544dSBjoern A. Zeeb nla_put_u32(msg, MT76_TM_STATS_ATTR_TX_QUEUED, td->tx_queued) || 5596c92544dSBjoern A. Zeeb nla_put_u32(msg, MT76_TM_STATS_ATTR_TX_DONE, td->tx_done) || 5606c92544dSBjoern A. Zeeb nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_PACKETS, rx_packets, 5616c92544dSBjoern A. Zeeb MT76_TM_STATS_ATTR_PAD) || 5626c92544dSBjoern A. Zeeb nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_FCS_ERROR, rx_fcs_error, 5636c92544dSBjoern A. Zeeb MT76_TM_STATS_ATTR_PAD)) 5646c92544dSBjoern A. Zeeb return -EMSGSIZE; 5656c92544dSBjoern A. Zeeb 5666c92544dSBjoern A. Zeeb return 0; 5676c92544dSBjoern A. Zeeb } 5686c92544dSBjoern A. Zeeb 5696c92544dSBjoern A. Zeeb int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg, 5706c92544dSBjoern A. Zeeb struct netlink_callback *cb, void *data, int len) 5716c92544dSBjoern A. Zeeb { 5726c92544dSBjoern A. Zeeb struct mt76_phy *phy = hw->priv; 5736c92544dSBjoern A. Zeeb struct mt76_dev *dev = phy->dev; 5746c92544dSBjoern A. Zeeb struct mt76_testmode_data *td = &phy->test; 5756c92544dSBjoern A. Zeeb struct nlattr *tb[NUM_MT76_TM_ATTRS] = {}; 5766c92544dSBjoern A. Zeeb int err = 0; 5776c92544dSBjoern A. Zeeb void *a; 5786c92544dSBjoern A. Zeeb int i; 5796c92544dSBjoern A. Zeeb 5806c92544dSBjoern A. Zeeb if (!dev->test_ops) 5816c92544dSBjoern A. Zeeb return -EOPNOTSUPP; 5826c92544dSBjoern A. Zeeb 5836c92544dSBjoern A. Zeeb if (cb->args[2]++ > 0) 5846c92544dSBjoern A. Zeeb return -ENOENT; 5856c92544dSBjoern A. Zeeb 5866c92544dSBjoern A. Zeeb if (data) { 5876c92544dSBjoern A. Zeeb err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, 5886c92544dSBjoern A. Zeeb mt76_tm_policy, NULL); 5896c92544dSBjoern A. Zeeb if (err) 5906c92544dSBjoern A. Zeeb return err; 5916c92544dSBjoern A. Zeeb } 5926c92544dSBjoern A. Zeeb 5936c92544dSBjoern A. Zeeb mutex_lock(&dev->mutex); 5946c92544dSBjoern A. Zeeb 5956c92544dSBjoern A. Zeeb if (tb[MT76_TM_ATTR_STATS]) { 5966c92544dSBjoern A. Zeeb err = -EINVAL; 5976c92544dSBjoern A. Zeeb 5986c92544dSBjoern A. Zeeb a = nla_nest_start(msg, MT76_TM_ATTR_STATS); 5996c92544dSBjoern A. Zeeb if (a) { 6006c92544dSBjoern A. Zeeb err = mt76_testmode_dump_stats(phy, msg); 6016c92544dSBjoern A. Zeeb nla_nest_end(msg, a); 6026c92544dSBjoern A. Zeeb } 6036c92544dSBjoern A. Zeeb 6046c92544dSBjoern A. Zeeb goto out; 6056c92544dSBjoern A. Zeeb } 6066c92544dSBjoern A. Zeeb 6076c92544dSBjoern A. Zeeb mt76_testmode_init_defaults(phy); 6086c92544dSBjoern A. Zeeb 6096c92544dSBjoern A. Zeeb err = -EMSGSIZE; 6106c92544dSBjoern A. Zeeb if (nla_put_u32(msg, MT76_TM_ATTR_STATE, td->state)) 6116c92544dSBjoern A. Zeeb goto out; 6126c92544dSBjoern A. Zeeb 6136c92544dSBjoern A. Zeeb if (dev->test_mtd.name && 6146c92544dSBjoern A. Zeeb (nla_put_string(msg, MT76_TM_ATTR_MTD_PART, dev->test_mtd.name) || 6156c92544dSBjoern A. Zeeb nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset))) 6166c92544dSBjoern A. Zeeb goto out; 6176c92544dSBjoern A. Zeeb 6186c92544dSBjoern A. Zeeb if (nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, td->tx_count) || 6196c92544dSBjoern A. Zeeb nla_put_u32(msg, MT76_TM_ATTR_TX_LENGTH, td->tx_mpdu_len) || 6206c92544dSBjoern A. Zeeb nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_MODE, td->tx_rate_mode) || 6216c92544dSBjoern A. Zeeb nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_NSS, td->tx_rate_nss) || 6226c92544dSBjoern A. Zeeb nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, td->tx_rate_idx) || 6236c92544dSBjoern A. Zeeb nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_SGI, td->tx_rate_sgi) || 6246c92544dSBjoern A. Zeeb nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, td->tx_rate_ldpc) || 6256c92544dSBjoern A. Zeeb nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) || 6266c92544dSBjoern A. Zeeb (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) && 6276c92544dSBjoern A. Zeeb nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) || 6286c92544dSBjoern A. Zeeb (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) && 6296c92544dSBjoern A. Zeeb nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA, td->tx_antenna_mask)) || 6306c92544dSBjoern A. Zeeb (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_SPE_IDX) && 6316c92544dSBjoern A. Zeeb nla_put_u8(msg, MT76_TM_ATTR_TX_SPE_IDX, td->tx_spe_idx)) || 6326c92544dSBjoern A. Zeeb (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_DUTY_CYCLE) && 6336c92544dSBjoern A. Zeeb nla_put_u8(msg, MT76_TM_ATTR_TX_DUTY_CYCLE, td->tx_duty_cycle)) || 6346c92544dSBjoern A. Zeeb (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_IPG) && 6356c92544dSBjoern A. Zeeb nla_put_u32(msg, MT76_TM_ATTR_TX_IPG, td->tx_ipg)) || 6366c92544dSBjoern A. Zeeb (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_TIME) && 6376c92544dSBjoern A. Zeeb nla_put_u32(msg, MT76_TM_ATTR_TX_TIME, td->tx_time)) || 6386c92544dSBjoern A. Zeeb (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER_CONTROL) && 6396c92544dSBjoern A. Zeeb nla_put_u8(msg, MT76_TM_ATTR_TX_POWER_CONTROL, td->tx_power_control)) || 6406c92544dSBjoern A. Zeeb (mt76_testmode_param_present(td, MT76_TM_ATTR_FREQ_OFFSET) && 6416c92544dSBjoern A. Zeeb nla_put_u8(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset))) 6426c92544dSBjoern A. Zeeb goto out; 6436c92544dSBjoern A. Zeeb 6446c92544dSBjoern A. Zeeb if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER)) { 6456c92544dSBjoern A. Zeeb a = nla_nest_start(msg, MT76_TM_ATTR_TX_POWER); 6466c92544dSBjoern A. Zeeb if (!a) 6476c92544dSBjoern A. Zeeb goto out; 6486c92544dSBjoern A. Zeeb 6496c92544dSBjoern A. Zeeb for (i = 0; i < ARRAY_SIZE(td->tx_power); i++) 6506c92544dSBjoern A. Zeeb if (nla_put_u8(msg, i, td->tx_power[i])) 6516c92544dSBjoern A. Zeeb goto out; 6526c92544dSBjoern A. Zeeb 6536c92544dSBjoern A. Zeeb nla_nest_end(msg, a); 6546c92544dSBjoern A. Zeeb } 6556c92544dSBjoern A. Zeeb 6566c92544dSBjoern A. Zeeb if (mt76_testmode_param_present(td, MT76_TM_ATTR_MAC_ADDRS)) { 6576c92544dSBjoern A. Zeeb a = nla_nest_start(msg, MT76_TM_ATTR_MAC_ADDRS); 6586c92544dSBjoern A. Zeeb if (!a) 6596c92544dSBjoern A. Zeeb goto out; 6606c92544dSBjoern A. Zeeb 6616c92544dSBjoern A. Zeeb for (i = 0; i < 3; i++) 6626c92544dSBjoern A. Zeeb if (nla_put(msg, i, ETH_ALEN, td->addr[i])) 6636c92544dSBjoern A. Zeeb goto out; 6646c92544dSBjoern A. Zeeb 6656c92544dSBjoern A. Zeeb nla_nest_end(msg, a); 6666c92544dSBjoern A. Zeeb } 6676c92544dSBjoern A. Zeeb 6686c92544dSBjoern A. Zeeb err = 0; 6696c92544dSBjoern A. Zeeb 6706c92544dSBjoern A. Zeeb out: 6716c92544dSBjoern A. Zeeb mutex_unlock(&dev->mutex); 6726c92544dSBjoern A. Zeeb 6736c92544dSBjoern A. Zeeb return err; 6746c92544dSBjoern A. Zeeb } 6756c92544dSBjoern A. Zeeb EXPORT_SYMBOL(mt76_testmode_dump); 676