@@ -217,6 +217,7 @@ mt7915_init_wiphy(struct ieee80211_hw *hw)
struct wiphy *wiphy = hw->wiphy;
hw->queues = 4;
+ hw->max_report_rates = 1;
hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
hw->netdev_features = NETIF_F_RXCSUM;
@@ -1202,17 +1202,22 @@ mt7915_txp_skb_unmap(struct mt76_dev *dev, struct mt76_txwi_cache *t)
static void
mt7915_txwi_free(struct mt7915_dev *dev, struct mt76_txwi_cache *t,
- struct ieee80211_sta *sta, struct list_head *free_list)
+ struct ieee80211_sta *sta, struct list_head *free_list,
+ u32 tx_cnt, u32 tx_status, u32 ampdu)
{
struct mt76_dev *mdev = &dev->mt76;
struct mt76_wcid *wcid;
__le32 *txwi;
u16 wcid_idx;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_tx_rate *rate;
mt7915_txp_skb_unmap(mdev, t);
if (!t->skb)
goto out;
+ rcu_read_lock(); /* protect wcid access */
+
txwi = (__le32 *)mt76_get_txwi_ptr(mdev, t);
if (sta) {
wcid = (struct mt76_wcid *)sta->drv_priv;
@@ -1222,6 +1227,75 @@ mt7915_txwi_free(struct mt7915_dev *dev, struct mt76_txwi_cache *t,
mt7915_tx_check_aggr(sta, txwi);
} else {
wcid_idx = FIELD_GET(MT_TXD1_WLAN_IDX, le32_to_cpu(txwi[1]));
+ wcid = rcu_dereference(mdev->wcid[wcid_idx]);
+ }
+
+ info = IEEE80211_SKB_CB(t->skb);
+
+ /* Cannot clear all of info->status, we need the driver private
+ * status intact.
+ */
+ info->status.is_valid_ack_signal = 0;
+
+ rate = &info->status.rates[0];
+ rate->idx = -1; /* will over-write below if we found wcid */
+ info->status.rates[1].idx = -1; /* terminate rate list */
+
+ /* force TX_STAT_AMPDU to be set, or mac80211 will ignore status */
+ if (ampdu || (info->flags & IEEE80211_TX_CTL_AMPDU)) {
+ info->flags |= IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_CTL_AMPDU;
+ info->status.ampdu_len = 1;
+ }
+
+ /* update info status based on cached wcid rate info since
+ * txfree path doesn't give us a lot of info.
+ */
+ if (wcid) {
+ struct mt7915_sta *msta = container_of(wcid, struct mt7915_sta, wcid);
+ struct mt7915_sta_stats *stats = &msta->stats;
+
+ if (wcid->rate.flags & RATE_INFO_FLAGS_MCS) {
+ rate->flags |= IEEE80211_TX_RC_MCS;
+ rate->idx = wcid->rate.mcs + wcid->rate.nss * 8;
+ } else if (wcid->rate.flags & RATE_INFO_FLAGS_VHT_MCS) {
+ rate->flags |= IEEE80211_TX_RC_VHT_MCS;
+ rate->idx = (wcid->rate.nss << 4) | wcid->rate.mcs;
+ } else if (wcid->rate.flags & RATE_INFO_FLAGS_HE_MCS) {
+ rate->idx = (wcid->rate.nss << 4) | wcid->rate.mcs;
+ } else {
+ rate->idx = wcid->rate.mcs;
+ }
+
+ switch (wcid->rate.bw) {
+ case RATE_INFO_BW_160:
+ rate->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH;
+ break;
+ case RATE_INFO_BW_80:
+ rate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH;
+ break;
+ case RATE_INFO_BW_40:
+ rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+ break;
+ }
+
+ stats->tx_mpdu_attempts += tx_cnt;
+ stats->tx_mpdu_retry += tx_cnt - 1;
+
+ if (tx_status == 0)
+ stats->tx_mpdu_ok++;
+ else
+ stats->tx_mpdu_fail++;
+ }
+
+ rcu_read_unlock();
+
+ /* Apply the values that this txfree path reports */
+ rate->count = tx_cnt;
+ if (tx_status == 0) {
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ info->status.ampdu_ack_len = 1;
+ } else {
+ info->flags &= ~IEEE80211_TX_STAT_ACK;
}
__mt76_tx_complete_skb(mdev, wcid_idx, t->skb, free_list);
@@ -1241,7 +1315,8 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb)
struct ieee80211_sta *sta = NULL;
LIST_HEAD(free_list);
struct sk_buff *tmp;
- u8 i, count;
+ u8 i;
+ u16 count;
bool wake = false;
/* clean DMA queues and unmap buffers first */
@@ -1257,9 +1332,12 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb)
* to the time ack is received or dropped by hw (air + hw queue time).
* Should avoid accessing WTBL to get Tx airtime, and use it instead.
*/
+ /* free->ctrl is high u16 of first DW in the txfree struct */
count = FIELD_GET(MT_TX_FREE_MSDU_CNT, le16_to_cpu(free->ctrl));
for (i = 0; i < count; i++) {
- u32 msdu, info = le32_to_cpu(free->info[i]);
+ u32 msdu, tx_cnt, tx_status;
+ u32 info = le32_to_cpu(free->info[i]); /* DW3+ */
+ u32 ampdu;
/*
* 1'b1: new wcid pair.
@@ -1290,7 +1368,12 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb)
if (!txwi)
continue;
- mt7915_txwi_free(dev, txwi, sta, &free_list);
+ tx_cnt = FIELD_GET(MT_TX_FREE_TXCNT, info);
+ /* 0 = success, 1 dropped-by-hw, 2 dropped-by-cpu */
+ tx_status = FIELD_GET(MT_TX_FREE_STATUS, info);
+ ampdu = FIELD_GET(MT_TX_FREE_HEAD_OF_PAGE, info);
+
+ mt7915_txwi_free(dev, txwi, sta, &free_list, tx_cnt, tx_status, ampdu);
}
mt7915_mac_sta_poll(dev);
@@ -1817,7 +1900,7 @@ void mt7915_tx_token_put(struct mt7915_dev *dev)
spin_lock_bh(&dev->mt76.token_lock);
idr_for_each_entry(&dev->mt76.token, txwi, id) {
- mt7915_txwi_free(dev, txwi, NULL, NULL);
+ mt7915_txwi_free(dev, txwi, NULL, NULL, 0, 1, 0);
dev->mt76.token_count--;
}
spin_unlock_bh(&dev->mt76.token_lock);
@@ -299,7 +299,7 @@ struct mt7915_tx_free {
__le16 ctrl;
u8 txd_cnt;
u8 rsv[3];
- __le32 info[];
+ __le32 info[]; /* DW3+ */
} __packed __aligned(4);
#define MT_TX_FREE_MSDU_CNT GENMASK(9, 0)
@@ -311,6 +311,8 @@ struct mt7915_tx_free {
/* when configured for txcount mode. See MT_PLE_HOST_RPT0_TX_LATENCY. */
#define MT_TX_FREE_TXCNT GENMASK(12, 0)
#define MT_TX_FREE_STATUS GENMASK(14, 13)
+/* 0: not MPDU, 1: MSDU is head pkt of TXD page (MPDU) */
+#define MT_TX_FREE_HEAD_OF_PAGE BIT(15)
#define MT_TX_FREE_MSDU_ID GENMASK(30, 16)
#define MT_TX_FREE_PAIR BIT(31)
@@ -1093,6 +1093,10 @@ static const char mt7915_gstrings_stats[][ETH_GSTRING_LEN] = {
"rx_ba_cnt",
/* per vif counters */
+ "v_tx_mpdu_attempts", /* counting any retries */
+ "v_tx_mpdu_fail", /* frames that failed even after retry */
+ "v_tx_mpdu_retry", /* number of times frames were retried */
+ "v_tx_mpdu_ok", /* frames that succeeded, perhaps after retry */
"v_tx_mode_cck",
"v_tx_mode_ofdm",
"v_tx_mode_ht",
@@ -1165,6 +1169,10 @@ static void mt7915_ethtool_worker(void *wi_data, struct ieee80211_sta *sta)
wi->sta_count++;
+ data[ei++] += mstats->tx_mpdu_attempts;
+ data[ei++] += mstats->tx_mpdu_fail;
+ data[ei++] += mstats->tx_mpdu_retry;
+ data[ei++] += mstats->tx_mpdu_ok;
data[ei++] += mstats->tx_mode[MT_PHY_TYPE_CCK];
data[ei++] += mstats->tx_mode[MT_PHY_TYPE_OFDM];
data[ei++] += mstats->tx_mode[MT_PHY_TYPE_HT];
@@ -65,6 +65,10 @@ enum mt7915_rxq_id {
};
struct mt7915_sta_stats {
+ unsigned long tx_mpdu_attempts; /* counting any retries */
+ unsigned long tx_mpdu_fail; /* frames that failed even after retry */
+ unsigned long tx_mpdu_ok; /* frames that succeeded, perhaps after retry */
+ unsigned long tx_mpdu_retry; /* number of times frames were retried */
unsigned long tx_mode[MT_PHY_TYPE_HE_LAST]; /* See mt76_phy_type */
unsigned long tx_bw[4]; /* 20, 40, 80, 160 */
unsigned long tx_nss[4]; /* 1, 2, 3, 4 */
@@ -276,6 +276,7 @@ void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *
struct ieee80211_tx_status status = {
.skb = skb,
.free_list = free_list,
+ .info = IEEE80211_SKB_CB(skb),
};
struct mt76_wcid *wcid = NULL;
struct ieee80211_hw *hw;
@@ -283,8 +284,11 @@ void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *
rcu_read_lock();
- if (wcid_idx < ARRAY_SIZE(dev->wcid))
+ if (wcid_idx < ARRAY_SIZE(dev->wcid)) {
wcid = rcu_dereference(dev->wcid[wcid_idx]);
+ if (wcid)
+ status.rate = &wcid->rate;
+ }
mt76_tx_check_non_aql(dev, wcid, skb);