@@ -4886,6 +4886,9 @@ struct rate_control_ops {
void (*remove_sta_debugfs)(void *priv, void *priv_sta);
u32 (*get_expected_throughput)(void *priv_sta);
+
+ void (*tx_ratestats)(void *priv, void *priv_sta,
+ enum cfg80211_ratestats_ops op);
};
static inline int rate_supported(struct ieee80211_sta *sta,
@@ -4966,6 +4969,23 @@ int rate_control_set_rates(struct ieee80211_hw *hw,
int ieee80211_rate_control_register(const struct rate_control_ops *ops);
void ieee80211_rate_control_unregister(const struct rate_control_ops *ops);
+/**
+ * ieee80211_report_ratestats - report (TX) rate statistics for a station
+ * @hw: the hardware pointer
+ * @pubsta: the station
+ * @n_stats: number of statistics entries in array
+ * @stats: statistics array
+ * @gfp: allocation flags
+ *
+ * Note that RX statistics are implemented by mac80211 directly, not by the
+ * rate control algorithm, so this must only be used by the latter to report
+ * TX statistics.
+ */
+void ieee80211_report_ratestats(struct ieee80211_hw *hw,
+ struct ieee80211_sta *pubsta,
+ unsigned int n_stats,
+ struct cfg80211_ratestats *stats, gfp_t gfp);
+
static inline bool
conf_is_ht20(struct ieee80211_conf *conf)
{
@@ -3702,8 +3702,10 @@ static void ieee80211_ratestats(struct wiphy *wiphy,
switch (op) {
case CFG80211_RATESTATS_START:
local->ratestats_active = true;
- list_for_each_entry(sta, &local->sta_list, list)
+ list_for_each_entry(sta, &local->sta_list, list) {
ieee80211_sta_start_ratestats(sta);
+ rate_control_tx_ratestats(sta, op);
+ }
/* list stays empty */
break;
case CFG80211_RATESTATS_DUMP:
@@ -3711,6 +3713,7 @@ static void ieee80211_ratestats(struct wiphy *wiphy,
stats = ieee80211_sta_reset_ratestats(sta);
if (stats)
list_add_tail(&stats->list, &list);
+ rate_control_tx_ratestats(sta, op);
}
break;
case CFG80211_RATESTATS_STOP:
@@ -3719,6 +3722,7 @@ static void ieee80211_ratestats(struct wiphy *wiphy,
stats = ieee80211_sta_stop_ratestats(sta);
if (stats)
list_add_tail(&stats->list, &list);
+ rate_control_tx_ratestats(sta, op);
}
break;
}
@@ -1037,6 +1037,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
goto fail_rate;
}
+ if (WARN_ON(wiphy_ext_feature_isset(hw->wiphy,
+ NL80211_EXT_FEATURE_RATESTATS) &&
+ (!local->rate_ctrl ||
+ !local->rate_ctrl->ops->tx_ratestats)))
+ hw->wiphy->ext_features[NL80211_EXT_FEATURE_RATESTATS / 8] &=
+ ~BIT(NL80211_EXT_FEATURE_RATESTATS % 8);
+
/* add one default STA interface if supported */
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
!(hw->flags & IEEE80211_HW_NO_AUTO_VIF)) {
@@ -762,3 +762,22 @@ void rate_control_deinitialize(struct ieee80211_local *local)
rate_control_free(ref);
}
+void ieee80211_report_ratestats(struct ieee80211_hw *hw,
+ struct ieee80211_sta *pubsta,
+ unsigned int n_stats,
+ struct cfg80211_ratestats *stats, gfp_t gfp)
+{
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+ int i;
+
+ for (i = 0; i < n_stats; i++) {
+ struct cfg80211_tid_stats *s = &stats[i].stats;
+
+ if (WARN_ON(s->filled & BIT(NL80211_TID_STATS_RX_MSDU)))
+ s->filled &= ~BIT(NL80211_TID_STATS_RX_MSDU);
+ }
+
+ cfg80211_report_ratestats(hw->wiphy, &sta->sdata->wdev, sta->sta.addr,
+ n_stats, stats, gfp);
+}
+EXPORT_SYMBOL_GPL(ieee80211_report_ratestats);
@@ -157,6 +157,15 @@ static inline void rate_control_remove_sta_debugfs(struct sta_info *sta)
#endif
}
+static inline void rate_control_tx_ratestats(struct sta_info *sta,
+ enum cfg80211_ratestats_ops op)
+{
+ struct rate_control_ref *ref = sta->rate_ctrl;
+
+ if (ref && ref->ops->tx_ratestats)
+ ref->ops->tx_ratestats(ref->priv, sta->rate_ctrl_priv, op);
+}
+
/* Get a reference to the rate control algorithm. If `name' is NULL, get the
* first available algorithm. */
int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,