1f0efa862SFelix Fietkau // SPDX-License-Identifier: ISC 2f0efa862SFelix Fietkau /* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */ 360c45a78SShayne Chen 460c45a78SShayne Chen #include <linux/random.h> 5f0efa862SFelix Fietkau #include "mt76.h" 6f0efa862SFelix Fietkau 7bce84458SLorenzo Bianconi const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = { 8f0efa862SFelix Fietkau [MT76_TM_ATTR_RESET] = { .type = NLA_FLAG }, 9f0efa862SFelix Fietkau [MT76_TM_ATTR_STATE] = { .type = NLA_U8 }, 10f0efa862SFelix Fietkau [MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 }, 1174f12d51SLin Ma [MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 }, 12f0efa862SFelix Fietkau [MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 }, 13f0efa862SFelix Fietkau [MT76_TM_ATTR_TX_RATE_NSS] = { .type = NLA_U8 }, 14f0efa862SFelix Fietkau [MT76_TM_ATTR_TX_RATE_IDX] = { .type = NLA_U8 }, 15f0efa862SFelix Fietkau [MT76_TM_ATTR_TX_RATE_SGI] = { .type = NLA_U8 }, 16f0efa862SFelix Fietkau [MT76_TM_ATTR_TX_RATE_LDPC] = { .type = NLA_U8 }, 177f54c742SShayne Chen [MT76_TM_ATTR_TX_RATE_STBC] = { .type = NLA_U8 }, 181a38c2f5SShayne Chen [MT76_TM_ATTR_TX_LTF] = { .type = NLA_U8 }, 19f0efa862SFelix Fietkau [MT76_TM_ATTR_TX_ANTENNA] = { .type = NLA_U8 }, 20fdc9c18eSShayne Chen [MT76_TM_ATTR_TX_SPE_IDX] = { .type = NLA_U8 }, 21f0efa862SFelix Fietkau [MT76_TM_ATTR_TX_POWER_CONTROL] = { .type = NLA_U8 }, 22f0efa862SFelix Fietkau [MT76_TM_ATTR_TX_POWER] = { .type = NLA_NESTED }, 23b8cbdb97SShayne Chen [MT76_TM_ATTR_TX_DUTY_CYCLE] = { .type = NLA_U8 }, 24b8cbdb97SShayne Chen [MT76_TM_ATTR_TX_IPG] = { .type = NLA_U32 }, 25b8cbdb97SShayne Chen [MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 }, 26f0efa862SFelix Fietkau [MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 }, 27bce84458SLorenzo Bianconi [MT76_TM_ATTR_DRV_DATA] = { .type = NLA_NESTED }, 28f0efa862SFelix Fietkau }; 29bce84458SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76_tm_policy); 30f0efa862SFelix Fietkau 31c918c74dSShayne Chen void mt76_testmode_tx_pending(struct mt76_phy *phy) 32f0efa862SFelix Fietkau { 33c918c74dSShayne Chen struct mt76_testmode_data *td = &phy->test; 34c918c74dSShayne Chen struct mt76_dev *dev = phy->dev; 35f0efa862SFelix Fietkau struct mt76_wcid *wcid = &dev->global_wcid; 36f0efa862SFelix Fietkau struct sk_buff *skb = td->tx_skb; 37f0efa862SFelix Fietkau struct mt76_queue *q; 38ba459094SShayne Chen u16 tx_queued_limit; 39f0efa862SFelix Fietkau int qid; 40f0efa862SFelix Fietkau 41f0efa862SFelix Fietkau if (!skb || !td->tx_pending) 42f0efa862SFelix Fietkau return; 43f0efa862SFelix Fietkau 44f0efa862SFelix Fietkau qid = skb_get_queue_mapping(skb); 4591990519SLorenzo Bianconi q = phy->q_tx[qid]; 46f0efa862SFelix Fietkau 47ba459094SShayne Chen tx_queued_limit = td->tx_queued_limit ? td->tx_queued_limit : 1000; 48ba459094SShayne Chen 49f0efa862SFelix Fietkau spin_lock_bh(&q->lock); 50f0efa862SFelix Fietkau 51ba459094SShayne Chen while (td->tx_pending > 0 && 52ba459094SShayne Chen td->tx_queued - td->tx_done < tx_queued_limit && 539729ff4cSFelix Fietkau q->queued < q->ndesc / 2) { 54f0efa862SFelix Fietkau int ret; 55f0efa862SFelix Fietkau 56*5d581c33SFelix Fietkau ret = dev->queue_ops->tx_queue_skb(phy, q, qid, skb_get(skb), 57d08295f5SFelix Fietkau wcid, NULL); 58f0efa862SFelix Fietkau if (ret < 0) 59f0efa862SFelix Fietkau break; 60f0efa862SFelix Fietkau 61f0efa862SFelix Fietkau td->tx_pending--; 62f0efa862SFelix Fietkau td->tx_queued++; 63f0efa862SFelix Fietkau } 64f0efa862SFelix Fietkau 65f0efa862SFelix Fietkau dev->queue_ops->kick(dev, q); 66f0efa862SFelix Fietkau 67f0efa862SFelix Fietkau spin_unlock_bh(&q->lock); 68f0efa862SFelix Fietkau } 69f0efa862SFelix Fietkau 702601dda8SShayne Chen static u32 712601dda8SShayne Chen mt76_testmode_max_mpdu_len(struct mt76_phy *phy, u8 tx_rate_mode) 722601dda8SShayne Chen { 732601dda8SShayne Chen switch (tx_rate_mode) { 742601dda8SShayne Chen case MT76_TM_TX_MODE_HT: 752601dda8SShayne Chen return IEEE80211_MAX_MPDU_LEN_HT_7935; 762601dda8SShayne Chen case MT76_TM_TX_MODE_VHT: 772601dda8SShayne Chen case MT76_TM_TX_MODE_HE_SU: 782601dda8SShayne Chen case MT76_TM_TX_MODE_HE_EXT_SU: 792601dda8SShayne Chen case MT76_TM_TX_MODE_HE_TB: 802601dda8SShayne Chen case MT76_TM_TX_MODE_HE_MU: 812601dda8SShayne Chen if (phy->sband_5g.sband.vht_cap.cap & 822601dda8SShayne Chen IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991) 832601dda8SShayne Chen return IEEE80211_MAX_MPDU_LEN_VHT_7991; 842601dda8SShayne Chen return IEEE80211_MAX_MPDU_LEN_VHT_11454; 852601dda8SShayne Chen case MT76_TM_TX_MODE_CCK: 862601dda8SShayne Chen case MT76_TM_TX_MODE_OFDM: 872601dda8SShayne Chen default: 882601dda8SShayne Chen return IEEE80211_MAX_FRAME_LEN; 892601dda8SShayne Chen } 902601dda8SShayne Chen } 91f0efa862SFelix Fietkau 922601dda8SShayne Chen static void 932601dda8SShayne Chen mt76_testmode_free_skb(struct mt76_phy *phy) 94f0efa862SFelix Fietkau { 95c918c74dSShayne Chen struct mt76_testmode_data *td = &phy->test; 962601dda8SShayne Chen 97d705ae86SLorenzo Bianconi dev_kfree_skb(td->tx_skb); 982601dda8SShayne Chen td->tx_skb = NULL; 992601dda8SShayne Chen } 1002601dda8SShayne Chen 1012601dda8SShayne Chen int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len) 1022601dda8SShayne Chen { 1032601dda8SShayne Chen #define MT_TXP_MAX_LEN 4095 104f0efa862SFelix Fietkau u16 fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA | 105f0efa862SFelix Fietkau IEEE80211_FCTL_FROMDS; 1062601dda8SShayne Chen struct mt76_testmode_data *td = &phy->test; 1072601dda8SShayne Chen struct sk_buff **frag_tail, *head; 1082601dda8SShayne Chen struct ieee80211_tx_info *info; 1092601dda8SShayne Chen struct ieee80211_hdr *hdr; 1102601dda8SShayne Chen u32 max_len, head_len; 1112601dda8SShayne Chen int nfrags, i; 112f0efa862SFelix Fietkau 1132601dda8SShayne Chen max_len = mt76_testmode_max_mpdu_len(phy, td->tx_rate_mode); 1142601dda8SShayne Chen if (len > max_len) 1152601dda8SShayne Chen len = max_len; 1162601dda8SShayne Chen else if (len < sizeof(struct ieee80211_hdr)) 1172601dda8SShayne Chen len = sizeof(struct ieee80211_hdr); 118f0efa862SFelix Fietkau 1192601dda8SShayne Chen nfrags = len / MT_TXP_MAX_LEN; 1202601dda8SShayne Chen head_len = nfrags ? MT_TXP_MAX_LEN : len; 1212601dda8SShayne Chen 1222601dda8SShayne Chen if (len > IEEE80211_MAX_FRAME_LEN) 1232601dda8SShayne Chen fc |= IEEE80211_STYPE_QOS_DATA; 1242601dda8SShayne Chen 1252601dda8SShayne Chen head = alloc_skb(head_len, GFP_KERNEL); 1262601dda8SShayne Chen if (!head) 127f0efa862SFelix Fietkau return -ENOMEM; 128f0efa862SFelix Fietkau 12960c45a78SShayne Chen hdr = __skb_put_zero(head, sizeof(*hdr)); 130f0efa862SFelix Fietkau hdr->frame_control = cpu_to_le16(fc); 131c40b42c2SShayne Chen memcpy(hdr->addr1, td->addr[0], ETH_ALEN); 132c40b42c2SShayne Chen memcpy(hdr->addr2, td->addr[1], ETH_ALEN); 133c40b42c2SShayne Chen memcpy(hdr->addr3, td->addr[2], ETH_ALEN); 1342601dda8SShayne Chen skb_set_queue_mapping(head, IEEE80211_AC_BE); 13560c45a78SShayne Chen get_random_bytes(__skb_put(head, head_len - sizeof(*hdr)), 13660c45a78SShayne Chen head_len - sizeof(*hdr)); 137f0efa862SFelix Fietkau 1382601dda8SShayne Chen info = IEEE80211_SKB_CB(head); 139f0efa862SFelix Fietkau info->flags = IEEE80211_TX_CTL_INJECTED | 140f0efa862SFelix Fietkau IEEE80211_TX_CTL_NO_ACK | 141f0efa862SFelix Fietkau IEEE80211_TX_CTL_NO_PS_BUFFER; 14261fe7357SShayne Chen 143a062f001SLorenzo Bianconi info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->band_idx); 1442601dda8SShayne Chen frag_tail = &skb_shinfo(head)->frag_list; 1452601dda8SShayne Chen 1462601dda8SShayne Chen for (i = 0; i < nfrags; i++) { 1472601dda8SShayne Chen struct sk_buff *frag; 1482601dda8SShayne Chen u16 frag_len; 1492601dda8SShayne Chen 1502601dda8SShayne Chen if (i == nfrags - 1) 1512601dda8SShayne Chen frag_len = len % MT_TXP_MAX_LEN; 1522601dda8SShayne Chen else 1532601dda8SShayne Chen frag_len = MT_TXP_MAX_LEN; 1542601dda8SShayne Chen 1552601dda8SShayne Chen frag = alloc_skb(frag_len, GFP_KERNEL); 156fe2c3b1fSLorenzo Bianconi if (!frag) { 157fe2c3b1fSLorenzo Bianconi mt76_testmode_free_skb(phy); 158fe2c3b1fSLorenzo Bianconi dev_kfree_skb(head); 1592601dda8SShayne Chen return -ENOMEM; 160fe2c3b1fSLorenzo Bianconi } 1612601dda8SShayne Chen 16260c45a78SShayne Chen get_random_bytes(__skb_put(frag, frag_len), frag_len); 1632601dda8SShayne Chen head->len += frag->len; 1642601dda8SShayne Chen head->data_len += frag->len; 1652601dda8SShayne Chen 1662601dda8SShayne Chen *frag_tail = frag; 167223cea6dSLorenzo Bianconi frag_tail = &(*frag_tail)->next; 1682601dda8SShayne Chen } 1692601dda8SShayne Chen 1702601dda8SShayne Chen mt76_testmode_free_skb(phy); 1712601dda8SShayne Chen td->tx_skb = head; 1722601dda8SShayne Chen 1732601dda8SShayne Chen return 0; 1742601dda8SShayne Chen } 1752601dda8SShayne Chen EXPORT_SYMBOL(mt76_testmode_alloc_skb); 1762601dda8SShayne Chen 1772601dda8SShayne Chen static int 1782601dda8SShayne Chen mt76_testmode_tx_init(struct mt76_phy *phy) 1792601dda8SShayne Chen { 1802601dda8SShayne Chen struct mt76_testmode_data *td = &phy->test; 1812601dda8SShayne Chen struct ieee80211_tx_info *info; 1822601dda8SShayne Chen struct ieee80211_tx_rate *rate; 1832601dda8SShayne Chen u8 max_nss = hweight8(phy->antenna_mask); 1842601dda8SShayne Chen int ret; 1852601dda8SShayne Chen 1862601dda8SShayne Chen ret = mt76_testmode_alloc_skb(phy, td->tx_mpdu_len); 1872601dda8SShayne Chen if (ret) 1882601dda8SShayne Chen return ret; 1892601dda8SShayne Chen 19061fe7357SShayne Chen if (td->tx_rate_mode > MT76_TM_TX_MODE_VHT) 19161fe7357SShayne Chen goto out; 19261fe7357SShayne Chen 1932601dda8SShayne Chen if (td->tx_antenna_mask) 1942601dda8SShayne Chen max_nss = min_t(u8, max_nss, hweight8(td->tx_antenna_mask)); 1952601dda8SShayne Chen 1962601dda8SShayne Chen info = IEEE80211_SKB_CB(td->tx_skb); 197f0efa862SFelix Fietkau rate = &info->control.rates[0]; 198f0efa862SFelix Fietkau rate->count = 1; 199f0efa862SFelix Fietkau rate->idx = td->tx_rate_idx; 200f0efa862SFelix Fietkau 201f0efa862SFelix Fietkau switch (td->tx_rate_mode) { 202f0efa862SFelix Fietkau case MT76_TM_TX_MODE_CCK: 20398df2baeSLorenzo Bianconi if (phy->chandef.chan->band != NL80211_BAND_2GHZ) 204f0efa862SFelix Fietkau return -EINVAL; 205f0efa862SFelix Fietkau 206f0efa862SFelix Fietkau if (rate->idx > 4) 207f0efa862SFelix Fietkau return -EINVAL; 208f0efa862SFelix Fietkau break; 209f0efa862SFelix Fietkau case MT76_TM_TX_MODE_OFDM: 21098df2baeSLorenzo Bianconi if (phy->chandef.chan->band != NL80211_BAND_2GHZ) 211f0efa862SFelix Fietkau break; 212f0efa862SFelix Fietkau 213f0efa862SFelix Fietkau if (rate->idx > 8) 214f0efa862SFelix Fietkau return -EINVAL; 215f0efa862SFelix Fietkau 216f0efa862SFelix Fietkau rate->idx += 4; 217f0efa862SFelix Fietkau break; 218f0efa862SFelix Fietkau case MT76_TM_TX_MODE_HT: 219f0efa862SFelix Fietkau if (rate->idx > 8 * max_nss && 220f0efa862SFelix Fietkau !(rate->idx == 32 && 22198df2baeSLorenzo Bianconi phy->chandef.width >= NL80211_CHAN_WIDTH_40)) 222f0efa862SFelix Fietkau return -EINVAL; 223f0efa862SFelix Fietkau 224f0efa862SFelix Fietkau rate->flags |= IEEE80211_TX_RC_MCS; 225f0efa862SFelix Fietkau break; 226f0efa862SFelix Fietkau case MT76_TM_TX_MODE_VHT: 227f0efa862SFelix Fietkau if (rate->idx > 9) 228f0efa862SFelix Fietkau return -EINVAL; 229f0efa862SFelix Fietkau 230f0efa862SFelix Fietkau if (td->tx_rate_nss > max_nss) 231f0efa862SFelix Fietkau return -EINVAL; 232f0efa862SFelix Fietkau 233f0efa862SFelix Fietkau ieee80211_rate_set_vht(rate, td->tx_rate_idx, td->tx_rate_nss); 234f0efa862SFelix Fietkau rate->flags |= IEEE80211_TX_RC_VHT_MCS; 235f0efa862SFelix Fietkau break; 236f0efa862SFelix Fietkau default: 237f0efa862SFelix Fietkau break; 238f0efa862SFelix Fietkau } 239f0efa862SFelix Fietkau 240f0efa862SFelix Fietkau if (td->tx_rate_sgi) 241f0efa862SFelix Fietkau rate->flags |= IEEE80211_TX_RC_SHORT_GI; 242f0efa862SFelix Fietkau 243f0efa862SFelix Fietkau if (td->tx_rate_ldpc) 244f0efa862SFelix Fietkau info->flags |= IEEE80211_TX_CTL_LDPC; 245f0efa862SFelix Fietkau 2467f54c742SShayne Chen if (td->tx_rate_stbc) 2477f54c742SShayne Chen info->flags |= IEEE80211_TX_CTL_STBC; 2487f54c742SShayne Chen 249f0efa862SFelix Fietkau if (td->tx_rate_mode >= MT76_TM_TX_MODE_HT) { 25098df2baeSLorenzo Bianconi switch (phy->chandef.width) { 251f0efa862SFelix Fietkau case NL80211_CHAN_WIDTH_40: 252f0efa862SFelix Fietkau rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; 253f0efa862SFelix Fietkau break; 254f0efa862SFelix Fietkau case NL80211_CHAN_WIDTH_80: 255f0efa862SFelix Fietkau rate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH; 256f0efa862SFelix Fietkau break; 257f0efa862SFelix Fietkau case NL80211_CHAN_WIDTH_80P80: 258f0efa862SFelix Fietkau case NL80211_CHAN_WIDTH_160: 259f0efa862SFelix Fietkau rate->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH; 260f0efa862SFelix Fietkau break; 261f0efa862SFelix Fietkau default: 262f0efa862SFelix Fietkau break; 263f0efa862SFelix Fietkau } 264f0efa862SFelix Fietkau } 26561fe7357SShayne Chen out: 266f0efa862SFelix Fietkau return 0; 267f0efa862SFelix Fietkau } 268f0efa862SFelix Fietkau 269f0efa862SFelix Fietkau static void 270c918c74dSShayne Chen mt76_testmode_tx_start(struct mt76_phy *phy) 271f0efa862SFelix Fietkau { 272c918c74dSShayne Chen struct mt76_testmode_data *td = &phy->test; 273c918c74dSShayne Chen struct mt76_dev *dev = phy->dev; 274f0efa862SFelix Fietkau 275f0efa862SFelix Fietkau td->tx_queued = 0; 276f0efa862SFelix Fietkau td->tx_done = 0; 277f0efa862SFelix Fietkau td->tx_pending = td->tx_count; 278781eef5bSFelix Fietkau mt76_worker_schedule(&dev->tx_worker); 279f0efa862SFelix Fietkau } 280f0efa862SFelix Fietkau 281f0efa862SFelix Fietkau static void 282c918c74dSShayne Chen mt76_testmode_tx_stop(struct mt76_phy *phy) 283f0efa862SFelix Fietkau { 284c918c74dSShayne Chen struct mt76_testmode_data *td = &phy->test; 285c918c74dSShayne Chen struct mt76_dev *dev = phy->dev; 286f0efa862SFelix Fietkau 287781eef5bSFelix Fietkau mt76_worker_disable(&dev->tx_worker); 288f0efa862SFelix Fietkau 289f0efa862SFelix Fietkau td->tx_pending = 0; 290f0efa862SFelix Fietkau 291781eef5bSFelix Fietkau mt76_worker_enable(&dev->tx_worker); 292f0efa862SFelix Fietkau 293ba459094SShayne Chen wait_event_timeout(dev->tx_wait, td->tx_done == td->tx_queued, 294ba459094SShayne Chen MT76_TM_TIMEOUT * HZ); 295f0efa862SFelix Fietkau 2962601dda8SShayne Chen mt76_testmode_free_skb(phy); 297f0efa862SFelix Fietkau } 298f0efa862SFelix Fietkau 299f0efa862SFelix Fietkau static inline void 300f0efa862SFelix Fietkau mt76_testmode_param_set(struct mt76_testmode_data *td, u16 idx) 301f0efa862SFelix Fietkau { 302f0efa862SFelix Fietkau td->param_set[idx / 32] |= BIT(idx % 32); 303f0efa862SFelix Fietkau } 304f0efa862SFelix Fietkau 305f0efa862SFelix Fietkau static inline bool 306f0efa862SFelix Fietkau mt76_testmode_param_present(struct mt76_testmode_data *td, u16 idx) 307f0efa862SFelix Fietkau { 308f0efa862SFelix Fietkau return td->param_set[idx / 32] & BIT(idx % 32); 309f0efa862SFelix Fietkau } 310f0efa862SFelix Fietkau 311f0efa862SFelix Fietkau static void 312c918c74dSShayne Chen mt76_testmode_init_defaults(struct mt76_phy *phy) 313f0efa862SFelix Fietkau { 314c918c74dSShayne Chen struct mt76_testmode_data *td = &phy->test; 315f0efa862SFelix Fietkau 3162601dda8SShayne Chen if (td->tx_mpdu_len > 0) 317f0efa862SFelix Fietkau return; 318f0efa862SFelix Fietkau 3192601dda8SShayne Chen td->tx_mpdu_len = 1024; 320f0efa862SFelix Fietkau td->tx_count = 1; 321f0efa862SFelix Fietkau td->tx_rate_mode = MT76_TM_TX_MODE_OFDM; 322f0efa862SFelix Fietkau td->tx_rate_nss = 1; 323c40b42c2SShayne Chen 324c40b42c2SShayne Chen memcpy(td->addr[0], phy->macaddr, ETH_ALEN); 325c40b42c2SShayne Chen memcpy(td->addr[1], phy->macaddr, ETH_ALEN); 326c40b42c2SShayne Chen memcpy(td->addr[2], phy->macaddr, ETH_ALEN); 327f0efa862SFelix Fietkau } 328f0efa862SFelix Fietkau 329f0efa862SFelix Fietkau static int 330c918c74dSShayne Chen __mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state) 331f0efa862SFelix Fietkau { 332c918c74dSShayne Chen enum mt76_testmode_state prev_state = phy->test.state; 333c918c74dSShayne Chen struct mt76_dev *dev = phy->dev; 334f0efa862SFelix Fietkau int err; 335f0efa862SFelix Fietkau 336f0efa862SFelix Fietkau if (prev_state == MT76_TM_STATE_TX_FRAMES) 337c918c74dSShayne Chen mt76_testmode_tx_stop(phy); 338f0efa862SFelix Fietkau 339f0efa862SFelix Fietkau if (state == MT76_TM_STATE_TX_FRAMES) { 340c918c74dSShayne Chen err = mt76_testmode_tx_init(phy); 341f0efa862SFelix Fietkau if (err) 342f0efa862SFelix Fietkau return err; 343f0efa862SFelix Fietkau } 344f0efa862SFelix Fietkau 345c918c74dSShayne Chen err = dev->test_ops->set_state(phy, state); 346f0efa862SFelix Fietkau if (err) { 347f0efa862SFelix Fietkau if (state == MT76_TM_STATE_TX_FRAMES) 348c918c74dSShayne Chen mt76_testmode_tx_stop(phy); 349f0efa862SFelix Fietkau 350f0efa862SFelix Fietkau return err; 351f0efa862SFelix Fietkau } 352f0efa862SFelix Fietkau 353f0efa862SFelix Fietkau if (state == MT76_TM_STATE_TX_FRAMES) 354c918c74dSShayne Chen mt76_testmode_tx_start(phy); 355f0efa862SFelix Fietkau else if (state == MT76_TM_STATE_RX_FRAMES) { 356c918c74dSShayne Chen memset(&phy->test.rx_stats, 0, sizeof(phy->test.rx_stats)); 357f0efa862SFelix Fietkau } 358f0efa862SFelix Fietkau 359c918c74dSShayne Chen phy->test.state = state; 360f0efa862SFelix Fietkau 361f0efa862SFelix Fietkau return 0; 362f0efa862SFelix Fietkau } 363f0efa862SFelix Fietkau 364c918c74dSShayne Chen int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state) 365f0efa862SFelix Fietkau { 366c918c74dSShayne Chen struct mt76_testmode_data *td = &phy->test; 367c918c74dSShayne Chen struct ieee80211_hw *hw = phy->hw; 368f0efa862SFelix Fietkau 369f0efa862SFelix Fietkau if (state == td->state && state == MT76_TM_STATE_OFF) 370f0efa862SFelix Fietkau return 0; 371f0efa862SFelix Fietkau 372f0efa862SFelix Fietkau if (state > MT76_TM_STATE_OFF && 373c918c74dSShayne Chen (!test_bit(MT76_STATE_RUNNING, &phy->state) || 374f0efa862SFelix Fietkau !(hw->conf.flags & IEEE80211_CONF_MONITOR))) 375f0efa862SFelix Fietkau return -ENOTCONN; 376f0efa862SFelix Fietkau 377f0efa862SFelix Fietkau if (state != MT76_TM_STATE_IDLE && 378f0efa862SFelix Fietkau td->state != MT76_TM_STATE_IDLE) { 379f0efa862SFelix Fietkau int ret; 380f0efa862SFelix Fietkau 381c918c74dSShayne Chen ret = __mt76_testmode_set_state(phy, MT76_TM_STATE_IDLE); 382f0efa862SFelix Fietkau if (ret) 383f0efa862SFelix Fietkau return ret; 384f0efa862SFelix Fietkau } 385f0efa862SFelix Fietkau 386c918c74dSShayne Chen return __mt76_testmode_set_state(phy, state); 387f0efa862SFelix Fietkau 388f0efa862SFelix Fietkau } 389f0efa862SFelix Fietkau EXPORT_SYMBOL(mt76_testmode_set_state); 390f0efa862SFelix Fietkau 391f0efa862SFelix Fietkau static int 392f0efa862SFelix Fietkau mt76_tm_get_u8(struct nlattr *attr, u8 *dest, u8 min, u8 max) 393f0efa862SFelix Fietkau { 394f0efa862SFelix Fietkau u8 val; 395f0efa862SFelix Fietkau 396f0efa862SFelix Fietkau if (!attr) 397f0efa862SFelix Fietkau return 0; 398f0efa862SFelix Fietkau 399f0efa862SFelix Fietkau val = nla_get_u8(attr); 400f0efa862SFelix Fietkau if (val < min || val > max) 401f0efa862SFelix Fietkau return -EINVAL; 402f0efa862SFelix Fietkau 403f0efa862SFelix Fietkau *dest = val; 404f0efa862SFelix Fietkau return 0; 405f0efa862SFelix Fietkau } 406f0efa862SFelix Fietkau 407f0efa862SFelix Fietkau int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 408f0efa862SFelix Fietkau void *data, int len) 409f0efa862SFelix Fietkau { 410f0efa862SFelix Fietkau struct mt76_phy *phy = hw->priv; 411f0efa862SFelix Fietkau struct mt76_dev *dev = phy->dev; 412c918c74dSShayne Chen struct mt76_testmode_data *td = &phy->test; 413f0efa862SFelix Fietkau struct nlattr *tb[NUM_MT76_TM_ATTRS]; 414f0efa862SFelix Fietkau u32 state; 415f0efa862SFelix Fietkau int err; 416f0efa862SFelix Fietkau int i; 417f0efa862SFelix Fietkau 418f0efa862SFelix Fietkau if (!dev->test_ops) 419f0efa862SFelix Fietkau return -EOPNOTSUPP; 420f0efa862SFelix Fietkau 421f0efa862SFelix Fietkau err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, 422f0efa862SFelix Fietkau mt76_tm_policy, NULL); 423f0efa862SFelix Fietkau if (err) 424f0efa862SFelix Fietkau return err; 425f0efa862SFelix Fietkau 426f0efa862SFelix Fietkau err = -EINVAL; 427f0efa862SFelix Fietkau 428f0efa862SFelix Fietkau mutex_lock(&dev->mutex); 429f0efa862SFelix Fietkau 430f0efa862SFelix Fietkau if (tb[MT76_TM_ATTR_RESET]) { 431c918c74dSShayne Chen mt76_testmode_set_state(phy, MT76_TM_STATE_OFF); 432f0efa862SFelix Fietkau memset(td, 0, sizeof(*td)); 433f0efa862SFelix Fietkau } 434f0efa862SFelix Fietkau 435c918c74dSShayne Chen mt76_testmode_init_defaults(phy); 436f0efa862SFelix Fietkau 437f0efa862SFelix Fietkau if (tb[MT76_TM_ATTR_TX_COUNT]) 438f0efa862SFelix Fietkau td->tx_count = nla_get_u32(tb[MT76_TM_ATTR_TX_COUNT]); 439f0efa862SFelix Fietkau 440f0efa862SFelix Fietkau if (tb[MT76_TM_ATTR_TX_RATE_IDX]) 441f0efa862SFelix Fietkau td->tx_rate_idx = nla_get_u8(tb[MT76_TM_ATTR_TX_RATE_IDX]); 442f0efa862SFelix Fietkau 443f0efa862SFelix Fietkau if (mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_MODE], &td->tx_rate_mode, 444f0efa862SFelix Fietkau 0, MT76_TM_TX_MODE_MAX) || 445f0efa862SFelix Fietkau mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_NSS], &td->tx_rate_nss, 446f0efa862SFelix Fietkau 1, hweight8(phy->antenna_mask)) || 4471a38c2f5SShayne Chen mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_SGI], &td->tx_rate_sgi, 0, 2) || 448f0efa862SFelix Fietkau mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_LDPC], &td->tx_rate_ldpc, 0, 1) || 4497f54c742SShayne Chen mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_STBC], &td->tx_rate_stbc, 0, 1) || 4501a38c2f5SShayne Chen mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_LTF], &td->tx_ltf, 0, 2) || 45199ad32a4SBo Jiao mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_ANTENNA], 45299ad32a4SBo Jiao &td->tx_antenna_mask, 0, 0xff) || 453fdc9c18eSShayne Chen mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_SPE_IDX], &td->tx_spe_idx, 0, 27) || 454b8cbdb97SShayne Chen mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_DUTY_CYCLE], 455b8cbdb97SShayne Chen &td->tx_duty_cycle, 0, 99) || 456f0efa862SFelix Fietkau mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL], 457f0efa862SFelix Fietkau &td->tx_power_control, 0, 1)) 458f0efa862SFelix Fietkau goto out; 459f0efa862SFelix Fietkau 4602601dda8SShayne Chen if (tb[MT76_TM_ATTR_TX_LENGTH]) { 4612601dda8SShayne Chen u32 val = nla_get_u32(tb[MT76_TM_ATTR_TX_LENGTH]); 4622601dda8SShayne Chen 4632601dda8SShayne Chen if (val > mt76_testmode_max_mpdu_len(phy, td->tx_rate_mode) || 4642601dda8SShayne Chen val < sizeof(struct ieee80211_hdr)) 4652601dda8SShayne Chen goto out; 4662601dda8SShayne Chen 4672601dda8SShayne Chen td->tx_mpdu_len = val; 4682601dda8SShayne Chen } 4692601dda8SShayne Chen 470b8cbdb97SShayne Chen if (tb[MT76_TM_ATTR_TX_IPG]) 471b8cbdb97SShayne Chen td->tx_ipg = nla_get_u32(tb[MT76_TM_ATTR_TX_IPG]); 472b8cbdb97SShayne Chen 473b8cbdb97SShayne Chen if (tb[MT76_TM_ATTR_TX_TIME]) 474b8cbdb97SShayne Chen td->tx_time = nla_get_u32(tb[MT76_TM_ATTR_TX_TIME]); 475b8cbdb97SShayne Chen 476f0efa862SFelix Fietkau if (tb[MT76_TM_ATTR_FREQ_OFFSET]) 477f0efa862SFelix Fietkau td->freq_offset = nla_get_u32(tb[MT76_TM_ATTR_FREQ_OFFSET]); 478f0efa862SFelix Fietkau 479f0efa862SFelix Fietkau if (tb[MT76_TM_ATTR_STATE]) { 480f0efa862SFelix Fietkau state = nla_get_u32(tb[MT76_TM_ATTR_STATE]); 481f0efa862SFelix Fietkau if (state > MT76_TM_STATE_MAX) 482f0efa862SFelix Fietkau goto out; 483f0efa862SFelix Fietkau } else { 484f0efa862SFelix Fietkau state = td->state; 485f0efa862SFelix Fietkau } 486f0efa862SFelix Fietkau 487f0efa862SFelix Fietkau if (tb[MT76_TM_ATTR_TX_POWER]) { 488f0efa862SFelix Fietkau struct nlattr *cur; 489f0efa862SFelix Fietkau int idx = 0; 490f0efa862SFelix Fietkau int rem; 491f0efa862SFelix Fietkau 492f0efa862SFelix Fietkau nla_for_each_nested(cur, tb[MT76_TM_ATTR_TX_POWER], rem) { 493f0efa862SFelix Fietkau if (nla_len(cur) != 1 || 494f0efa862SFelix Fietkau idx >= ARRAY_SIZE(td->tx_power)) 495f0efa862SFelix Fietkau goto out; 496f0efa862SFelix Fietkau 497f0efa862SFelix Fietkau td->tx_power[idx++] = nla_get_u8(cur); 498f0efa862SFelix Fietkau } 499f0efa862SFelix Fietkau } 500f0efa862SFelix Fietkau 501c40b42c2SShayne Chen if (tb[MT76_TM_ATTR_MAC_ADDRS]) { 502c40b42c2SShayne Chen struct nlattr *cur; 503c40b42c2SShayne Chen int idx = 0; 504c40b42c2SShayne Chen int rem; 505c40b42c2SShayne Chen 506c40b42c2SShayne Chen nla_for_each_nested(cur, tb[MT76_TM_ATTR_MAC_ADDRS], rem) { 507c40b42c2SShayne Chen if (nla_len(cur) != ETH_ALEN || idx >= 3) 508c40b42c2SShayne Chen goto out; 509c40b42c2SShayne Chen 510c40b42c2SShayne Chen memcpy(td->addr[idx], nla_data(cur), ETH_ALEN); 511c40b42c2SShayne Chen idx++; 512c40b42c2SShayne Chen } 513c40b42c2SShayne Chen } 514c40b42c2SShayne Chen 515f0efa862SFelix Fietkau if (dev->test_ops->set_params) { 516c918c74dSShayne Chen err = dev->test_ops->set_params(phy, tb, state); 517f0efa862SFelix Fietkau if (err) 518f0efa862SFelix Fietkau goto out; 519f0efa862SFelix Fietkau } 520f0efa862SFelix Fietkau 521f0efa862SFelix Fietkau for (i = MT76_TM_ATTR_STATE; i < ARRAY_SIZE(tb); i++) 522f0efa862SFelix Fietkau if (tb[i]) 523f0efa862SFelix Fietkau mt76_testmode_param_set(td, i); 524f0efa862SFelix Fietkau 525f0efa862SFelix Fietkau err = 0; 526f0efa862SFelix Fietkau if (tb[MT76_TM_ATTR_STATE]) 527c918c74dSShayne Chen err = mt76_testmode_set_state(phy, state); 528f0efa862SFelix Fietkau 529f0efa862SFelix Fietkau out: 530f0efa862SFelix Fietkau mutex_unlock(&dev->mutex); 531f0efa862SFelix Fietkau 532f0efa862SFelix Fietkau return err; 533f0efa862SFelix Fietkau } 534f0efa862SFelix Fietkau EXPORT_SYMBOL(mt76_testmode_cmd); 535f0efa862SFelix Fietkau 536f0efa862SFelix Fietkau static int 537c918c74dSShayne Chen mt76_testmode_dump_stats(struct mt76_phy *phy, struct sk_buff *msg) 538f0efa862SFelix Fietkau { 539c918c74dSShayne Chen struct mt76_testmode_data *td = &phy->test; 540c918c74dSShayne Chen struct mt76_dev *dev = phy->dev; 541f0efa862SFelix Fietkau u64 rx_packets = 0; 542f0efa862SFelix Fietkau u64 rx_fcs_error = 0; 543f0efa862SFelix Fietkau int i; 544f0efa862SFelix Fietkau 545a0d65f62SShayne Chen if (dev->test_ops->dump_stats) { 546a0d65f62SShayne Chen int ret; 547a0d65f62SShayne Chen 548a0d65f62SShayne Chen ret = dev->test_ops->dump_stats(phy, msg); 549a0d65f62SShayne Chen if (ret) 550a0d65f62SShayne Chen return ret; 551a0d65f62SShayne Chen } 552a0d65f62SShayne Chen 553f0efa862SFelix Fietkau for (i = 0; i < ARRAY_SIZE(td->rx_stats.packets); i++) { 554f0efa862SFelix Fietkau rx_packets += td->rx_stats.packets[i]; 555f0efa862SFelix Fietkau rx_fcs_error += td->rx_stats.fcs_error[i]; 556f0efa862SFelix Fietkau } 557f0efa862SFelix Fietkau 558f0efa862SFelix Fietkau if (nla_put_u32(msg, MT76_TM_STATS_ATTR_TX_PENDING, td->tx_pending) || 559f0efa862SFelix Fietkau nla_put_u32(msg, MT76_TM_STATS_ATTR_TX_QUEUED, td->tx_queued) || 560f0efa862SFelix Fietkau nla_put_u32(msg, MT76_TM_STATS_ATTR_TX_DONE, td->tx_done) || 561f0efa862SFelix Fietkau nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_PACKETS, rx_packets, 562f0efa862SFelix Fietkau MT76_TM_STATS_ATTR_PAD) || 563f0efa862SFelix Fietkau nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_FCS_ERROR, rx_fcs_error, 564f0efa862SFelix Fietkau MT76_TM_STATS_ATTR_PAD)) 565f0efa862SFelix Fietkau return -EMSGSIZE; 566f0efa862SFelix Fietkau 567f0efa862SFelix Fietkau return 0; 568f0efa862SFelix Fietkau } 569f0efa862SFelix Fietkau 570f0efa862SFelix Fietkau int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg, 571f0efa862SFelix Fietkau struct netlink_callback *cb, void *data, int len) 572f0efa862SFelix Fietkau { 573f0efa862SFelix Fietkau struct mt76_phy *phy = hw->priv; 574f0efa862SFelix Fietkau struct mt76_dev *dev = phy->dev; 575c918c74dSShayne Chen struct mt76_testmode_data *td = &phy->test; 576f0efa862SFelix Fietkau struct nlattr *tb[NUM_MT76_TM_ATTRS] = {}; 577f0efa862SFelix Fietkau int err = 0; 578f0efa862SFelix Fietkau void *a; 579f0efa862SFelix Fietkau int i; 580f0efa862SFelix Fietkau 581f0efa862SFelix Fietkau if (!dev->test_ops) 582f0efa862SFelix Fietkau return -EOPNOTSUPP; 583f0efa862SFelix Fietkau 584f0efa862SFelix Fietkau if (cb->args[2]++ > 0) 585f0efa862SFelix Fietkau return -ENOENT; 586f0efa862SFelix Fietkau 587f0efa862SFelix Fietkau if (data) { 588f0efa862SFelix Fietkau err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, 589f0efa862SFelix Fietkau mt76_tm_policy, NULL); 590f0efa862SFelix Fietkau if (err) 591f0efa862SFelix Fietkau return err; 592f0efa862SFelix Fietkau } 593f0efa862SFelix Fietkau 594f0efa862SFelix Fietkau mutex_lock(&dev->mutex); 595f0efa862SFelix Fietkau 596f0efa862SFelix Fietkau if (tb[MT76_TM_ATTR_STATS]) { 597ce8463a7SLorenzo Bianconi err = -EINVAL; 598ce8463a7SLorenzo Bianconi 599f0efa862SFelix Fietkau a = nla_nest_start(msg, MT76_TM_ATTR_STATS); 600ce8463a7SLorenzo Bianconi if (a) { 601c918c74dSShayne Chen err = mt76_testmode_dump_stats(phy, msg); 602f0efa862SFelix Fietkau nla_nest_end(msg, a); 603ce8463a7SLorenzo Bianconi } 604f0efa862SFelix Fietkau 605f0efa862SFelix Fietkau goto out; 606f0efa862SFelix Fietkau } 607f0efa862SFelix Fietkau 608c918c74dSShayne Chen mt76_testmode_init_defaults(phy); 609f0efa862SFelix Fietkau 610f0efa862SFelix Fietkau err = -EMSGSIZE; 611f0efa862SFelix Fietkau if (nla_put_u32(msg, MT76_TM_ATTR_STATE, td->state)) 612f0efa862SFelix Fietkau goto out; 613f0efa862SFelix Fietkau 614e7a6a044SShayne Chen if (dev->test_mtd.name && 615e7a6a044SShayne Chen (nla_put_string(msg, MT76_TM_ATTR_MTD_PART, dev->test_mtd.name) || 616e7a6a044SShayne Chen nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset))) 617f0efa862SFelix Fietkau goto out; 618f0efa862SFelix Fietkau 619f0efa862SFelix Fietkau if (nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, td->tx_count) || 6202601dda8SShayne Chen nla_put_u32(msg, MT76_TM_ATTR_TX_LENGTH, td->tx_mpdu_len) || 621f0efa862SFelix Fietkau nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_MODE, td->tx_rate_mode) || 622f0efa862SFelix Fietkau nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_NSS, td->tx_rate_nss) || 623f0efa862SFelix Fietkau nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, td->tx_rate_idx) || 624f0efa862SFelix Fietkau nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_SGI, td->tx_rate_sgi) || 625f0efa862SFelix Fietkau nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, td->tx_rate_ldpc) || 6267f54c742SShayne Chen nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) || 6271a38c2f5SShayne Chen (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) && 6281a38c2f5SShayne Chen nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) || 629f0efa862SFelix Fietkau (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) && 630f0efa862SFelix Fietkau nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA, td->tx_antenna_mask)) || 631fdc9c18eSShayne Chen (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_SPE_IDX) && 632fdc9c18eSShayne Chen nla_put_u8(msg, MT76_TM_ATTR_TX_SPE_IDX, td->tx_spe_idx)) || 633b8cbdb97SShayne Chen (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_DUTY_CYCLE) && 634b8cbdb97SShayne Chen nla_put_u8(msg, MT76_TM_ATTR_TX_DUTY_CYCLE, td->tx_duty_cycle)) || 635b8cbdb97SShayne Chen (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_IPG) && 636b8cbdb97SShayne Chen nla_put_u32(msg, MT76_TM_ATTR_TX_IPG, td->tx_ipg)) || 637b8cbdb97SShayne Chen (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_TIME) && 638b8cbdb97SShayne Chen nla_put_u32(msg, MT76_TM_ATTR_TX_TIME, td->tx_time)) || 639f0efa862SFelix Fietkau (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER_CONTROL) && 640f0efa862SFelix Fietkau nla_put_u8(msg, MT76_TM_ATTR_TX_POWER_CONTROL, td->tx_power_control)) || 641f0efa862SFelix Fietkau (mt76_testmode_param_present(td, MT76_TM_ATTR_FREQ_OFFSET) && 642f0efa862SFelix Fietkau nla_put_u8(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset))) 643f0efa862SFelix Fietkau goto out; 644f0efa862SFelix Fietkau 645f0efa862SFelix Fietkau if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER)) { 646f0efa862SFelix Fietkau a = nla_nest_start(msg, MT76_TM_ATTR_TX_POWER); 647f0efa862SFelix Fietkau if (!a) 648f0efa862SFelix Fietkau goto out; 649f0efa862SFelix Fietkau 650f0efa862SFelix Fietkau for (i = 0; i < ARRAY_SIZE(td->tx_power); i++) 651f0efa862SFelix Fietkau if (nla_put_u8(msg, i, td->tx_power[i])) 652f0efa862SFelix Fietkau goto out; 653f0efa862SFelix Fietkau 654f0efa862SFelix Fietkau nla_nest_end(msg, a); 655f0efa862SFelix Fietkau } 656f0efa862SFelix Fietkau 657c40b42c2SShayne Chen if (mt76_testmode_param_present(td, MT76_TM_ATTR_MAC_ADDRS)) { 658c40b42c2SShayne Chen a = nla_nest_start(msg, MT76_TM_ATTR_MAC_ADDRS); 659c40b42c2SShayne Chen if (!a) 660c40b42c2SShayne Chen goto out; 661c40b42c2SShayne Chen 662c40b42c2SShayne Chen for (i = 0; i < 3; i++) 663c40b42c2SShayne Chen if (nla_put(msg, i, ETH_ALEN, td->addr[i])) 664c40b42c2SShayne Chen goto out; 665c40b42c2SShayne Chen 666c40b42c2SShayne Chen nla_nest_end(msg, a); 667c40b42c2SShayne Chen } 668c40b42c2SShayne Chen 669f0efa862SFelix Fietkau err = 0; 670f0efa862SFelix Fietkau 671f0efa862SFelix Fietkau out: 672f0efa862SFelix Fietkau mutex_unlock(&dev->mutex); 673f0efa862SFelix Fietkau 674f0efa862SFelix Fietkau return err; 675f0efa862SFelix Fietkau } 676f0efa862SFelix Fietkau EXPORT_SYMBOL(mt76_testmode_dump); 677