wlcore_hw_sta_rc_update(wl, wlvif, sta, changed);
 }
 
-static int wlcore_op_get_rssi(struct ieee80211_hw *hw,
-                              struct ieee80211_vif *vif,
-                              struct ieee80211_sta *sta,
-                              s8 *rssi_dbm)
+static void wlcore_op_sta_statistics(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif,
+                                    struct ieee80211_sta *sta,
+                                    struct station_info *sinfo)
 {
        struct wl1271 *wl = hw->priv;
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-       int ret = 0;
+       s8 rssi_dbm;
+       int ret;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi");
 
        if (ret < 0)
                goto out_sleep;
 
-       ret = wlcore_acx_average_rssi(wl, wlvif, rssi_dbm);
+       ret = wlcore_acx_average_rssi(wl, wlvif, &rssi_dbm);
        if (ret < 0)
                goto out_sleep;
 
+       sinfo->filled |= STATION_INFO_SIGNAL;
+       sinfo->signal = rssi_dbm;
+
 out_sleep:
        wl1271_ps_elp_sleep(wl);
 
 out:
        mutex_unlock(&wl->mutex);
-
-       return ret;
 }
 
 static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
        .assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
        .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
        .sta_rc_update = wlcore_op_sta_rc_update,
-       .get_rssi = wlcore_op_get_rssi,
+       .sta_statistics = wlcore_op_sta_statistics,
        CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
 };
 
 
  *     is only used if the configured rate control algorithm actually uses
  *     the new rate table API, and is therefore optional. Must be atomic.
  *
+ * @sta_statistics: Get statistics for this station. For example with beacon
+ *     filtering, the statistics kept by mac80211 might not be accurate, so
+ *     let the driver pre-fill the statistics. The driver can fill most of
+ *     the values (indicating which by setting the filled bitmap), but not
+ *     all of them make sense - see the source for which ones are possible.
+ *     Statistics that the driver doesn't fill will be filled by mac80211.
+ *     The callback can sleep.
+ *
  * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
  *     bursting) for a hardware TX queue.
  *     Returns a negative error code on failure.
  * @get_et_strings:  Ethtool API to get a set of strings to describe stats
  *     and perhaps other supported types of ethtool data-sets.
  *
- * @get_rssi: Get current signal strength in dBm, the function is optional
- *     and can sleep.
- *
  * @mgd_prepare_tx: Prepare for transmitting a management frame for association
  *     before associated. In multi-channel scenarios, a virtual interface is
  *     bound to a channel before it is associated, but as it isn't associated
        void (*sta_rate_tbl_update)(struct ieee80211_hw *hw,
                                    struct ieee80211_vif *vif,
                                    struct ieee80211_sta *sta);
+       void (*sta_statistics)(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif,
+                              struct ieee80211_sta *sta,
+                              struct station_info *sinfo);
        int (*conf_tx)(struct ieee80211_hw *hw,
                       struct ieee80211_vif *vif, u16 ac,
                       const struct ieee80211_tx_queue_params *params);
        void    (*get_et_strings)(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif,
                                  u32 sset, u8 *data);
-       int     (*get_rssi)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                           struct ieee80211_sta *sta, s8 *rssi_dbm);
 
        void    (*mgd_prepare_tx)(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif);
 
        trace_drv_return_void(local);
 }
 
+static inline void drv_sta_statistics(struct ieee80211_local *local,
+                                     struct ieee80211_sub_if_data *sdata,
+                                     struct ieee80211_sta *sta,
+                                     struct station_info *sinfo)
+{
+       sdata = get_bss_sdata(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
+
+       trace_drv_sta_statistics(local, sdata, sta);
+       if (local->ops->sta_statistics)
+               local->ops->sta_statistics(&local->hw, &sdata->vif, sta, sinfo);
+       trace_drv_return_void(local);
+}
+
 static inline int drv_conf_tx(struct ieee80211_local *local,
                              struct ieee80211_sub_if_data *sdata, u16 ac,
                              const struct ieee80211_tx_queue_params *params)
        trace_drv_return_void(local);
 }
 
-static inline int drv_get_rssi(struct ieee80211_local *local,
-                               struct ieee80211_sub_if_data *sdata,
-                               struct ieee80211_sta *sta,
-                               s8 *rssi_dbm)
-{
-       int ret;
-
-       might_sleep();
-
-       ret = local->ops->get_rssi(&local->hw, &sdata->vif, sta, rssi_dbm);
-       trace_drv_get_rssi(local, sta, *rssi_dbm, ret);
-
-       return ret;
-}
-
 static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
                                      struct ieee80211_sub_if_data *sdata)
 {
 
        struct ieee80211_local *local = sdata->local;
        struct rate_control_ref *ref = NULL;
        struct timespec uptime;
-       u64 packets = 0;
        u32 thr = 0;
        int i, ac;
 
 
        sinfo->generation = sdata->local->sta_generation;
 
-       sinfo->filled = STATION_INFO_INACTIVE_TIME |
-                       STATION_INFO_RX_BYTES64 |
-                       STATION_INFO_TX_BYTES64 |
-                       STATION_INFO_RX_PACKETS |
-                       STATION_INFO_TX_PACKETS |
-                       STATION_INFO_TX_RETRIES |
-                       STATION_INFO_TX_FAILED |
-                       STATION_INFO_TX_BITRATE |
-                       STATION_INFO_RX_BITRATE |
-                       STATION_INFO_RX_DROP_MISC |
-                       STATION_INFO_BSS_PARAM |
-                       STATION_INFO_CONNECTED_TIME |
-                       STATION_INFO_STA_FLAGS |
-                       STATION_INFO_BEACON_LOSS_COUNT;
+       drv_sta_statistics(local, sdata, &sta->sta, sinfo);
+
+       sinfo->filled |= STATION_INFO_INACTIVE_TIME |
+                        STATION_INFO_STA_FLAGS |
+                        STATION_INFO_BSS_PARAM |
+                        STATION_INFO_CONNECTED_TIME |
+                        STATION_INFO_RX_DROP_MISC |
+                        STATION_INFO_BEACON_LOSS_COUNT;
 
        ktime_get_ts(&uptime);
        sinfo->connected_time = uptime.tv_sec - sta->last_connected;
-
        sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
-       sinfo->tx_bytes = 0;
-       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-               sinfo->tx_bytes += sta->tx_bytes[ac];
-               packets += sta->tx_packets[ac];
+
+       if (!(sinfo->filled & (STATION_INFO_TX_BYTES64 |
+                              STATION_INFO_TX_BYTES))) {
+               sinfo->tx_bytes = 0;
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       sinfo->tx_bytes += sta->tx_bytes[ac];
+               sinfo->filled |= STATION_INFO_TX_BYTES64;
+       }
+
+       if (!(sinfo->filled & STATION_INFO_TX_PACKETS)) {
+               sinfo->tx_packets = 0;
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       sinfo->tx_packets += sta->tx_packets[ac];
+               sinfo->filled |= STATION_INFO_TX_PACKETS;
+       }
+
+       if (!(sinfo->filled & (STATION_INFO_RX_BYTES64 |
+                              STATION_INFO_RX_BYTES))) {
+               sinfo->rx_bytes = sta->rx_bytes;
+               sinfo->filled |= STATION_INFO_RX_BYTES64;
+       }
+
+       if (!(sinfo->filled & STATION_INFO_RX_PACKETS)) {
+               sinfo->rx_packets = sta->rx_packets;
+               sinfo->filled |= STATION_INFO_RX_PACKETS;
+       }
+
+       if (!(sinfo->filled & STATION_INFO_TX_RETRIES)) {
+               sinfo->tx_retries = sta->tx_retry_count;
+               sinfo->filled |= STATION_INFO_TX_RETRIES;
+       }
+
+       if (!(sinfo->filled & STATION_INFO_TX_FAILED)) {
+               sinfo->tx_failed = sta->tx_retry_failed;
+               sinfo->filled |= STATION_INFO_TX_FAILED;
        }
-       sinfo->tx_packets = packets;
-       sinfo->rx_bytes = sta->rx_bytes;
-       sinfo->rx_packets = sta->rx_packets;
-       sinfo->tx_retries = sta->tx_retry_count;
-       sinfo->tx_failed = sta->tx_retry_failed;
+
        sinfo->rx_dropped_misc = sta->rx_dropped;
        sinfo->beacon_loss_count = sta->beacon_loss_count;
 
        if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
            (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
-               sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
-               if (!local->ops->get_rssi ||
-                   drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal))
+               if (!(sinfo->filled & STATION_INFO_SIGNAL)) {
                        sinfo->signal = (s8)sta->last_signal;
-               sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
+                       sinfo->filled |= STATION_INFO_SIGNAL;
+               }
+
+               if (!(sinfo->filled & STATION_INFO_SIGNAL_AVG)) {
+                       sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
+                       sinfo->filled |= STATION_INFO_SIGNAL_AVG;
+               }
        }
-       if (sta->chains) {
+
+       if (sta->chains &&
+           !(sinfo->filled & (STATION_INFO_CHAIN_SIGNAL |
+                              STATION_INFO_CHAIN_SIGNAL_AVG))) {
                sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
                                 STATION_INFO_CHAIN_SIGNAL_AVG;
 
                }
        }
 
-       sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
-       sta_set_rate_info_rx(sta, &sinfo->rxrate);
+       if (!(sinfo->filled & STATION_INFO_TX_BITRATE)) {
+               sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
+               sinfo->filled |= STATION_INFO_TX_BITRATE;
+       }
+
+       if (!(sinfo->filled & STATION_INFO_RX_BITRATE)) {
+               sta_set_rate_info_rx(sta, &sinfo->rxrate);
+               sinfo->filled |= STATION_INFO_RX_BITRATE;
+       }
 
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
 #ifdef CONFIG_MAC80211_MESH
 
        )
 );
 
+DEFINE_EVENT(sta_event, drv_sta_statistics,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                struct ieee80211_sta *sta),
+       TP_ARGS(local, sdata, sta)
+);
+
 DEFINE_EVENT(sta_event, drv_sta_add,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sub_if_data *sdata,
        TP_ARGS(local, sta, tids, num_frames, reason, more_data)
 );
 
-TRACE_EVENT(drv_get_rssi,
-       TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta,
-                s8 rssi, int ret),
-
-       TP_ARGS(local, sta, rssi, ret),
-
-       TP_STRUCT__entry(
-               LOCAL_ENTRY
-               STA_ENTRY
-               __field(s8, rssi)
-               __field(int, ret)
-       ),
-
-       TP_fast_assign(
-               LOCAL_ASSIGN;
-               STA_ASSIGN;
-               __entry->rssi = rssi;
-               __entry->ret = ret;
-       ),
-
-       TP_printk(
-               LOCAL_PR_FMT STA_PR_FMT " rssi:%d ret:%d",
-               LOCAL_PR_ARG, STA_PR_ARG, __entry->rssi, __entry->ret
-       )
-);
-
 DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sub_if_data *sdata),