Search Linux Wireless

[RFC v1 184/256] cl8k: add stats.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Viktor Barna <viktor.barna@xxxxxxxxxx>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx>
---
 drivers/net/wireless/celeno/cl8k/stats.c | 1402 ++++++++++++++++++++++
 1 file changed, 1402 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/stats.c

diff --git a/drivers/net/wireless/celeno/cl8k/stats.c b/drivers/net/wireless/celeno/cl8k/stats.c
new file mode 100644
index 000000000000..f340f60ebab9
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/stats.c
@@ -0,0 +1,1402 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include <linux/string.h>
+#include "stats.h"
+#include "rx/rx.h"
+#include "reg/reg_access.h"
+#include "sta.h"
+#include "band.h"
+#include "mib.h"
+#include "rate_ctrl.h"
+#include "vif.h"
+#include "data_rates.h"
+
+typedef void (*stats_callback)(struct cl_hw *, struct cl_sta *);
+
+static const char *bw_str[CHNL_BW_MAX_OFDMA] = {
+       [CHNL_BW_2_5] = "2.5",
+       [CHNL_BW_5] = "5",
+       [CHNL_BW_10] = "10",
+       [CHNL_BW_20] = "20",
+       [CHNL_BW_40] = "40",
+       [CHNL_BW_80] = "80",
+       [CHNL_BW_160] = "160"
+};
+
+static const char *gi_he_str[WRS_GI_MAX_HE] = {
+       [WRS_GI_LONG] = "3.2",
+       [WRS_GI_SHORT] = "1.6",
+       [WRS_GI_VSHORT] = "0.8"
+};
+
+static const char *gi_ht_vht_str[WRS_GI_MAX_HT] = {
+       [WRS_GI_LONG] = "0.8",
+       [WRS_GI_SHORT] = "0.4",
+};
+
+static void cl_stats_sta_reset(struct cl_hw *cl_hw, struct cl_sta *cl_sta)
+{
+       struct cl_ps_stats *ps = &cl_sta->stats->ps;
+       bool is_ps = ps->is_ps;
+       unsigned long timestamp_sleep = ps->timestamp_sleep;
+
+       memset(cl_sta->stats, 0, sizeof(struct cl_stats));
+
+       /* Restore value of power-save state and timestamp */
+       ps->is_ps = is_ps;
+       ps->timestamp_sleep = timestamp_sleep;
+}
+
+static void cl_stats_print_per(struct cl_hw *cl_hw, u32 delay)
+{
+       u32 system_per = 0, air_per = 0, ampdu_all,
+               old_phy_error, old_fcs_error, old_overflow, old_complete,
+               old_ampdu_failed, old_real_fcs, old_ampdu_success,
+               new_phy_error, new_fcs_error, new_overflow, new_complete,
+               new_ampdu_failed, new_real_fcs, new_ampdu_success;
+       char *buf = NULL;
+       ssize_t buf_size;
+       int len = 0;
+
+       old_phy_error = cl_mib_cntr_read(cl_hw, MIB_DOT11_RX_PHY_ERROR_COUNT);
+       old_fcs_error = cl_mib_cntr_read(cl_hw, MIB_DOT11_FCS_ERROR_COUNT);
+       old_overflow = cl_mib_cntr_read(cl_hw, MIB_DOT11_RX_FIFO_OVERFLOW_COUNT);
+       old_complete = cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT0) +
+                      cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT1) +
+                      cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT2) +
+                      cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT3) +
+                      cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT4) +
+                      cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT5) +
+                      cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT6) +
+                      cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT7);
+       old_ampdu_failed = cl_mib_cntr_read(cl_hw, MIB_AMPDU_INCORRECT_RCVED_COUNT);
+       old_ampdu_success = cl_mib_cntr_read(cl_hw, MIB_RW_U_AMPDU_RECEIVED_COUNT);
+       old_real_fcs = old_fcs_error - old_overflow;
+
+       mdelay(delay);
+
+       new_phy_error = cl_mib_cntr_read(cl_hw, MIB_DOT11_RX_PHY_ERROR_COUNT);
+       new_fcs_error = cl_mib_cntr_read(cl_hw, MIB_DOT11_FCS_ERROR_COUNT);
+       new_overflow = cl_mib_cntr_read(cl_hw, MIB_DOT11_RX_FIFO_OVERFLOW_COUNT);
+       new_complete = cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT0) +
+               cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT1) +
+               cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT2) +
+               cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT3) +
+               cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT4) +
+               cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT5) +
+               cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT6) +
+               cl_mib_cntr_read(cl_hw, MIB_RW_QOS_U_RECEIVED_MPDU_COUNT7);
+       new_ampdu_failed = cl_mib_cntr_read(cl_hw, MIB_AMPDU_INCORRECT_RCVED_COUNT);
+       new_ampdu_success = cl_mib_cntr_read(cl_hw, MIB_RW_U_AMPDU_RECEIVED_COUNT);
+       new_real_fcs = new_fcs_error - new_overflow;
+
+       /* Overflow handling */
+       if (old_complete > new_complete ||
+           old_overflow > new_overflow ||
+           old_fcs_error > new_fcs_error ||
+           old_phy_error > new_phy_error ||
+           old_ampdu_failed > new_ampdu_failed)
+               return;
+
+       ampdu_all = (new_ampdu_failed - old_ampdu_failed) +
+               (new_ampdu_success - old_ampdu_success);
+
+       if (new_complete - old_complete) {
+               system_per = ((new_overflow - old_overflow) * 100 /
+                             (new_complete - old_complete));
+       } else {
+               cl_snprintf(&buf, &len, &buf_size, "No successfully received packets\n");
+               goto out;
+       }
+
+       if ((new_fcs_error + new_phy_error + new_complete) -
+           (old_fcs_error + old_phy_error + old_complete))
+               air_per = (new_real_fcs - old_real_fcs) * 100 /
+               ((new_real_fcs + new_complete) - (old_real_fcs + old_complete));
+
+       cl_snprintf(&buf, &len, &buf_size,
+                   "Air PER = [%u%%]\n"
+                   "System PER = [%u%%]\n"
+                   "Successfully received packets = [%u]\n"
+                   "Number of phy errors received: [%u]\n"
+                   "Aggregation failure ratio: [%u/%u]\n",
+                   air_per, system_per, new_complete - old_complete,
+                   new_phy_error - old_phy_error,
+                   new_ampdu_failed - old_ampdu_failed, ampdu_all);
+
+out:
+       cl_vendor_reply(cl_hw, buf, len);
+       kfree(buf);
+}
+
+static void cl_stats_print_rssi(struct cl_hw *cl_hw, struct cl_sta *cl_sta)
+{
+       struct cl_stats *stats = cl_sta->stats;
+       u32 i = 0, j = 0;
+       u64 avg_rssi[MAX_ANTENNAS] = {0};
+       u64 sum_rssi[MAX_ANTENNAS] = {0};
+       u64 total_rssi = 0;
+       char *buf = NULL;
+       ssize_t buf_size;
+       int len = 0;
+
+       cl_snprintf(&buf, &len, &buf_size,
+                   "STA #%u, MAC %pM\n", cl_sta->sta_idx, cl_sta->addr);
+
+       cl_snprintf(&buf, &len, &buf_size, "|----");
+       for (j = 0; j < cl_hw->num_antennas; j++)
+               cl_snprintf(&buf, &len, &buf_size, "-----------");
+       cl_snprintf(&buf, &len, &buf_size, "|\n");
+
+       cl_snprintf(&buf, &len, &buf_size, "|RSSI");
+       for (j = 0; j < cl_hw->num_antennas; j++)
+               cl_snprintf(&buf, &len, &buf_size, "|   Ant%u   ", j + 1);
+       cl_snprintf(&buf, &len, &buf_size, "|\n");
+
+       cl_snprintf(&buf, &len, &buf_size, "|----");
+       for (j = 0; j < cl_hw->num_antennas; j++)
+               cl_snprintf(&buf, &len, &buf_size, "+----------");
+       cl_snprintf(&buf, &len, &buf_size, "|\n");
+
+       for (i = 0; i < RSSI_ARR_SIZE; i++) {
+               total_rssi = 0;
+
+               for (j = 0; j < cl_hw->num_antennas; j++) {
+                       total_rssi += stats->rssi[i][j];
+                       sum_rssi[j] += stats->rssi[i][j];
+                       avg_rssi[j] += (i * stats->rssi[i][j]);
+               }
+
+               /* Does not print rssi entries with 0 packets */
+               if (total_rssi == 0)
+                       continue;
+
+               cl_snprintf(&buf, &len, &buf_size, "|%3d ", -i);
+               for (j = 0; j < cl_hw->num_antennas; j++)
+                       cl_snprintf(&buf, &len, &buf_size,
+                                   "|%10u", stats->rssi[i][j]);
+               cl_snprintf(&buf, &len, &buf_size, "|\n");
+       }
+
+       total_rssi = 0;
+
+       for (j = 0; j < cl_hw->num_antennas; j++) {
+               if (sum_rssi[j] == 0)
+                       goto out;
+
+               avg_rssi[j] = div64_u64(avg_rssi[j], sum_rssi[j]);
+       }
+
+       cl_snprintf(&buf, &len, &buf_size, "|----");
+       for (j = 0; j < cl_hw->num_antennas; j++)
+               cl_snprintf(&buf, &len, &buf_size, "+----------");
+       cl_snprintf(&buf, &len, &buf_size, "|\n");
+
+       cl_snprintf(&buf, &len, &buf_size, "|AVG ");
+       for (j = 0; j < cl_hw->num_antennas; j++)
+               cl_snprintf(&buf, &len, &buf_size, "|%10lld", -avg_rssi[j]);
+       cl_snprintf(&buf, &len, &buf_size, "|\n");
+
+       cl_snprintf(&buf, &len, &buf_size, "|----");
+       for (j = 0; j < cl_hw->num_antennas; j++)
+               cl_snprintf(&buf, &len, &buf_size, "-----------");
+       cl_snprintf(&buf, &len, &buf_size, "|\n");
+
+out:
+       cl_vendor_reply(cl_hw, buf, len);
+       kfree(buf);
+}
+
+static void cl_stats_print_fec_coding(struct cl_hw *cl_hw, struct cl_sta *cl_sta)
+{
+       struct cl_stats *stats = cl_sta->stats;
+       char *buf = NULL;
+       ssize_t buf_size;
+       int len = 0;
+
+       cl_snprintf(&buf, &len, &buf_size,
+                   "\nSTA #%u, MAC %pM\n", cl_sta->sta_idx, cl_sta->addr);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "BCC  = %u\n", stats->fec_coding[CL_FEC_CODING_BCC]);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "LDPC = %u\n", stats->fec_coding[CL_FEC_CODING_LDPC]);
+
+       cl_vendor_reply(cl_hw, buf, len);
+       kfree(buf);
+}
+
+static void cl_stats_print_power_save(struct cl_hw *cl_hw, struct cl_sta *cl_sta)
+{
+       struct cl_ps_stats *ps_stats = &cl_sta->stats->ps;
+       char *buf = NULL;
+       ssize_t buf_size;
+       int len = 0;
+
+       cl_snprintf(&buf, &len, &buf_size,
+                   "\n");
+       cl_snprintf(&buf, &len, &buf_size,
+                   "STA #%u, MAC %pM\n", cl_sta->sta_idx, cl_sta->addr);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "Current state = %s\n", ps_stats->is_ps ? "SLEEP" : "AWAKE");
+       cl_snprintf(&buf, &len, &buf_size,
+                   "|-------------------------|\n");
+       cl_snprintf(&buf, &len, &buf_size,
+                   "| Period     | Counter    |\n");
+       cl_snprintf(&buf, &len, &buf_size,
+                   "|-------------------------|\n");
+       cl_snprintf(&buf, &len, &buf_size,
+                   "| <= 50ms    | %10u |\n", ps_stats->period[PS_PERIOD_50MS]);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "| <= 100ms   | %10u |\n", ps_stats->period[PS_PERIOD_100MS]);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "| <= 250ms   | %10u |\n", ps_stats->period[PS_PERIOD_250MS]);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "| <= 500ms   | %10u |\n", ps_stats->period[PS_PERIOD_500MS]);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "| <= 750ms   | %10u |\n", ps_stats->period[PS_PERIOD_750MS]);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "| <= 1000ms  | %10u |\n", ps_stats->period[PS_PERIOD_1000MS]);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "| <= 2000ms  | %10u |\n", ps_stats->period[PS_PERIOD_2000MS]);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "| <= 5000ms  | %10u |\n", ps_stats->period[PS_PERIOD_5000MS]);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "| <= 10000ms | %10u |\n", ps_stats->period[PS_PERIOD_10000MS]);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "|  > 10000ms | %10u |\n", ps_stats->period[PS_PERIOD_ABOVE]);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "|-------------------------|\n");
+
+       cl_vendor_reply(cl_hw, buf, len);
+       kfree(buf);
+}
+
+static void _cl_stats_print_tx(char **buf, int *len, ssize_t *buf_size,
+                              struct cl_tx_cntrs *cntrs,
+                              const char *mode_str, const char *bw_str, const char *gi_str,
+                              u8 nss, u8 mcs, u8 bf,
+                              u64 *total_success, u64 *total_fail)
+{
+       u8 per = 0;
+
+       if (cntrs->fail == 0 && cntrs->success == 0)
+               return;
+
+       per = (u8)div64_u64(100 * (u64)cntrs->fail, cntrs->fail + cntrs->success);
+
+       cl_snprintf(buf, len, buf_size,
+                   "|%-8s|%-3s|%2u|%3u|%3s|%2u|%10u|%10u|%3u|\n",
+                   mode_str, bw_str, nss, mcs, gi_str, bf, cntrs->success, cntrs->fail, per);
+
+       *total_success += cntrs->success;
+       *total_fail += cntrs->fail;
+}
+
+static void cl_stats_print_tx_he(char **buf, int *len, ssize_t *buf_size,
+                                struct cl_stats *stats, u64 *total_success, u64 *total_fail)
+{
+       struct cl_tx_cntrs *cntrs;
+       u8 bw = 0, nss = 0, mcs = 0, gi = 0, bf = 0;
+
+       for (bw = 0; bw < CHNL_BW_MAX; bw++)
+               for (nss = 0; nss < WRS_SS_MAX; nss++)
+                       for (mcs = 0; mcs < WRS_MCS_MAX; mcs++)
+                               for (gi = 0; gi < WRS_GI_MAX; gi++)
+                                       for (bf = 0; bf < BF_IDX_MAX; bf++) {
+                                               cntrs = &stats->tx.he[bw][nss][mcs][gi][bf];
+                                               _cl_stats_print_tx(buf, len, buf_size,
+                                                                  cntrs,
+                                                                  "HE",
+                                                                  bw_str[bw], gi_he_str[gi],
+                                                                  nss, mcs, bf,
+                                                                  total_success, total_fail);
+                                       }
+}
+
+static void cl_stats_print_tx_vht(char **buf, int *len, ssize_t *buf_size,
+                                 struct cl_stats *stats, u64 *total_success, u64 *total_fail)
+{
+       struct cl_tx_cntrs *cntrs;
+       u8 bw = 0, nss = 0, mcs = 0, gi = 0, bf = 0;
+
+       for (bw = 0; bw < CHNL_BW_MAX_VHT; bw++)
+               for (nss = 0; nss < WRS_SS_MAX; nss++)
+                       for (mcs = 0; mcs < WRS_MCS_MAX_VHT; mcs++)
+                               for (gi = 0; gi < WRS_GI_MAX_VHT; gi++)
+                                       for (bf = 0; bf < BF_IDX_MAX; bf++) {
+                                               cntrs = &stats->tx.vht[bw][nss][mcs][gi][bf];
+                                               _cl_stats_print_tx(buf, len, buf_size,
+                                                                  cntrs,
+                                                                  "VHT",
+                                                                  bw_str[bw], gi_ht_vht_str[gi],
+                                                                  nss, mcs, bf,
+                                                                  total_success, total_fail);
+                                       }
+}
+
+static void cl_stats_print_tx_ht(char **buf, int *len, ssize_t *buf_size,
+                                struct cl_stats *stats, u64 *total_success, u64 *total_fail)
+{
+       struct cl_tx_cntrs *cntrs;
+       u8 bw = 0, nss = 0, mcs = 0, gi = 0;
+
+       for (bw = 0; bw < CHNL_BW_MAX_HT; bw++)
+               for (nss = 0; nss < WRS_SS_MAX; nss++)
+                       for (mcs = 0; mcs < WRS_MCS_MAX_HT; mcs++)
+                               for (gi = 0; gi < WRS_GI_MAX_HT; gi++) {
+                                       cntrs = &stats->tx.ht[bw][nss][mcs][gi];
+                                       _cl_stats_print_tx(buf, len, buf_size,
+                                                          cntrs,
+                                                          "HT",
+                                                          bw_str[bw], gi_ht_vht_str[gi],
+                                                          nss, mcs, 0,
+                                                          total_success, total_fail);
+                               }
+}
+
+static void cl_stats_print_tx_ofdm(char **buf, int *len, ssize_t *buf_size,
+                                  struct cl_stats *stats, u64 *total_success, u64 *total_fail)
+{
+       struct cl_tx_cntrs *cntrs;
+       u8 mcs;
+
+       for (mcs = 0; mcs < WRS_MCS_MAX_OFDM; mcs++) {
+               cntrs = &stats->tx.ofdm[mcs];
+               _cl_stats_print_tx(buf, len, buf_size,
+                                  cntrs,
+                                  "OFDM", bw_str[0], "0",
+                                  0, mcs, 0,
+                                  total_success, total_fail);
+       }
+}
+
+static void cl_stats_print_tx_cck(char **buf, int *len, ssize_t *buf_size,
+                                 struct cl_stats *stats, u64 *total_success, u64 *total_fail)
+{
+       struct cl_tx_cntrs *cntrs;
+       u8 mcs;
+
+       for (mcs = 0; mcs < WRS_MCS_MAX_CCK; mcs++) {
+               cntrs = &stats->tx.cck[mcs];
+               _cl_stats_print_tx(buf, len, buf_size,
+                                  cntrs,
+                                  "CCK", bw_str[0], "0",
+                                  0, mcs, 0,
+                                  total_success, total_fail);
+       }
+}
+
+static void cl_stats_print_tx(struct cl_hw *cl_hw, struct cl_sta *cl_sta)
+{
+       struct cl_stats *stats = cl_sta->stats;
+       u64 total_success = 0, total_fail = 0;
+       char *buf = NULL;
+       ssize_t buf_size;
+       int len = 0;
+
+       cl_snprintf(&buf, &len, &buf_size,
+                   "\nSTA #%u, MAC %pM\n", cl_sta->sta_idx, cl_sta->addr);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "------------------------------------------------------\n");
+       cl_snprintf(&buf, &len, &buf_size,
+                   "|MODE    |BW |SS|MCS|GI |BF| Success  |   Fail   |PER|\n");
+       cl_snprintf(&buf, &len, &buf_size,
+                   "|--------+---+--+---+---+--+----------+----------+---|\n");
+
+       cl_stats_print_tx_he(&buf, &len, &buf_size, stats, &total_success, &total_fail);
+       cl_stats_print_tx_vht(&buf, &len, &buf_size, stats, &total_success, &total_fail);
+       cl_stats_print_tx_ht(&buf, &len, &buf_size, stats, &total_success, &total_fail);
+       cl_stats_print_tx_ofdm(&buf, &len, &buf_size, stats, &total_success, &total_fail);
+       cl_stats_print_tx_cck(&buf, &len, &buf_size, stats, &total_success, &total_fail);
+
+       cl_snprintf(&buf, &len, &buf_size,
+                   "------------------------------------------------------\n");
+       cl_snprintf(&buf, &len, &buf_size,
+                   "                           |%10llu|%10llu|\n", total_success, total_fail);
+       cl_snprintf(&buf, &len, &buf_size,
+                   "                           -----------------------\n");
+       cl_snprintf(&buf, &len, &buf_size,
+                   "\n");
+
+       cl_vendor_reply(cl_hw, buf, len);
+       kfree(buf);
+}
+
+static void _cl_stats_print_rx(struct cl_hw *cl_hw, struct cl_rx_stats *rx_stats,
+                              struct cl_sta *cl_sta)
+{
+       u8 mode = 0, mcs = 0, nss = 0, bw = 0, gi = 0, flag = rx_stats->flag;
+       u16 data_rate = 0, data_rate_div_10 = 0, data_rate_mod_10 = 0;
+       u64 remainder = 0, packets = 0, total_packets[WRS_MODE_MAX] = {0},
+               total_data_rate[WRS_MODE_MAX] = {0}, equivalent_data_rate = 0;
+       char *buf = NULL;
+       ssize_t buf_size;
+       int len = 0;
+
+       if (cl_sta)
+               cl_snprintf(&buf, &len, &buf_size,
+                           "\nSTA #%u, MAC %pM\n",
+                           cl_sta->sta_idx, cl_sta->addr);
+
+       cl_snprintf(&buf, &len, &buf_size,
+                   "-------------------------------------------------------\n");
+       cl_snprintf(&buf, &len, &buf_size,
+                   "|  MODE   | BW | SS | MCS | GI | Data-Rate | #Packets |\n");
+       cl_snprintf(&buf, &len, &buf_size,
+                   "|---------+----+----+-----+----+-----------+----------|\n");
+
+       if ((flag & RX_STATS_HE_TRIG) == 0)
+               goto stats_he_ext;
+
+       for (bw = 0; bw < CHNL_BW_MAX_HE; bw++) {
+               for (nss = 0; nss < WRS_SS_MAX; nss++) {
+                       for (mcs = 0; mcs < WRS_MCS_MAX_HE; mcs++) {
+                               for (gi = 0; gi < WRS_GI_MAX_HE; gi++) {
+                                       packets = rx_stats->he_trig[bw][nss][mcs][gi];
+
+                                       if (packets == 0)
+                                               continue;
+
+                                       data_rate = cl_data_rates_get_x10(WRS_MODE_HE, bw,
+                                                                         nss, mcs, gi);
+                                       data_rate_div_10 = data_rate / 10;
+                                       data_rate_mod_10 = data_rate % 10;
+
+                                       total_packets[WRS_MODE_HE] += packets;
+                                       total_data_rate[WRS_MODE_HE] += (packets * data_rate);
+
+                                       cl_snprintf(&buf, &len, &buf_size,
+                                                   "| HE_TRIG |%4u|%4u|%5u|%4s| %7u.%u |%10llu|\n",
+                                                   BW_TO_MHZ(bw), nss, mcs, gi_he_str[gi],
+                                                   data_rate_div_10, data_rate_mod_10, packets);
+                               }
+                       }
+               }
+       }
+
+stats_he_ext:
+       if ((flag & RX_STATS_HE_EXT) == 0)
+               goto stats_he_mu;
+
+       for (bw = 0; bw < CHNL_BW_MAX_HE; bw++) {
+               for (nss = 0; nss < WRS_SS_MAX; nss++) {
+                       for (mcs = 0; mcs < WRS_MCS_MAX_HE; mcs++) {
+                               for (gi = 0; gi < WRS_GI_MAX_HE; gi++) {
+                                       packets = rx_stats->he_ext[bw][nss][mcs][gi];
+
+                                       if (packets == 0)
+                                               continue;
+
+                                       data_rate = cl_data_rates_get_x10(WRS_MODE_HE, bw,
+                                                                         nss, mcs, gi);
+                                       data_rate_div_10 = data_rate / 10;
+                                       data_rate_mod_10 = data_rate % 10;
+
+                                       total_packets[WRS_MODE_HE] += packets;
+                                       total_data_rate[WRS_MODE_HE] += (packets * data_rate);
+
+                                       cl_snprintf(&buf, &len, &buf_size,
+                                                   "| HE_EXT  |%4u|%4u|%5u|%4s| %7u.%u |%10llu|\n",
+                                                   BW_TO_MHZ(bw), nss, mcs, gi_he_str[gi],
+                                                   data_rate_div_10, data_rate_mod_10, packets);
+                               }
+                       }
+               }
+       }
+
+stats_he_mu:
+       if ((flag & RX_STATS_HE_MU) == 0)
+               goto stats_he_su;
+
+       for (bw = 0; bw < CHNL_BW_MAX_HE; bw++) {
+               for (nss = 0; nss < WRS_SS_MAX; nss++) {
+                       for (mcs = 0; mcs < WRS_MCS_MAX_HE; mcs++) {
+                               for (gi = 0; gi < WRS_GI_MAX_HE; gi++) {
+                                       packets = rx_stats->he_mu[bw][nss][mcs][gi];
+
+                                       if (packets == 0)
+                                               continue;
+
+                                       data_rate = cl_data_rates_get_x10(WRS_MODE_HE, bw,
+                                                                         nss, mcs, gi);
+                                       data_rate_div_10 = data_rate / 10;
+                                       data_rate_mod_10 = data_rate % 10;
+
+                                       total_packets[WRS_MODE_HE] += packets;
+                                       total_data_rate[WRS_MODE_HE] += (packets * data_rate);
+
+                                       cl_snprintf(&buf, &len, &buf_size,
+                                                   "| HE_MU   |%4u|%4u|%5u|%4s| %7u.%u |%10llu|\n",
+                                                   BW_TO_MHZ(bw), nss, mcs, gi_he_str[gi],
+                                                   data_rate_div_10, data_rate_mod_10, packets);
+                               }
+                       }
+               }
+       }
+
+stats_he_su:
+       if ((flag & RX_STATS_HE_SU) == 0)
+               goto stats_vht;
+
+       for (bw = 0; bw < CHNL_BW_MAX_HE; bw++) {
+               for (nss = 0; nss < WRS_SS_MAX; nss++) {
+                       for (mcs = 0; mcs < WRS_MCS_MAX_HE; mcs++) {
+                               for (gi = 0; gi < WRS_GI_MAX_HE; gi++) {
+                                       packets = rx_stats->he_su[bw][nss][mcs][gi];
+
+                                       if (packets == 0)
+                                               continue;
+
+                                       data_rate = cl_data_rates_get_x10(WRS_MODE_HE, bw,
+                                                                         nss, mcs, gi);
+                                       data_rate_div_10 = data_rate / 10;
+                                       data_rate_mod_10 = data_rate % 10;
+
+                                       total_packets[WRS_MODE_HE] += packets;
+                                       total_data_rate[WRS_MODE_HE] += (packets * data_rate);
+
+                                       cl_snprintf(&buf, &len, &buf_size,
+                                                   "| HE_SU   |%4u|%4u|%5u|%4s| %7u.%u |%10llu|\n",
+                                                   BW_TO_MHZ(bw), nss, mcs, gi_he_str[gi],
+                                                   data_rate_div_10, data_rate_mod_10, packets);
+                               }
+                       }
+               }
+       }
+
+stats_vht:
+       if ((flag & RX_STATS_VHT) == 0)
+               goto stats_ht;
+
+       for (bw = 0; bw < CHNL_BW_MAX_VHT; bw++) {
+               for (nss = 0; nss < WRS_SS_MAX; nss++) {
+                       for (mcs = 0; mcs < WRS_MCS_MAX_VHT; mcs++) {
+                               for (gi = 0; gi < WRS_GI_MAX_VHT; gi++) {
+                                       packets = rx_stats->vht[bw][nss][mcs][gi];
+
+                                       if (packets == 0)
+                                               continue;
+
+                                       data_rate = cl_data_rates_get_x10(WRS_MODE_VHT,
+                                                                         bw, nss, mcs, gi);
+                                       data_rate_div_10 = data_rate / 10;
+                                       data_rate_mod_10 = data_rate % 10;
+
+                                       total_packets[WRS_MODE_VHT] += packets;
+                                       total_data_rate[WRS_MODE_VHT] += (packets * data_rate);
+
+                                       cl_snprintf(&buf, &len, &buf_size,
+                                                   "| VHT     |%4u|%4u|%5u|%4s| %7u.%u |%10llu|\n",
+                                                   BW_TO_MHZ(bw), nss, mcs, gi_ht_vht_str[gi],
+                                                   data_rate_div_10, data_rate_mod_10, packets);
+                               }
+                       }
+               }
+       }
+
+stats_ht:
+       if ((flag & RX_STATS_HT) == 0)
+               goto stats_ofdm;
+
+       for (bw = 0; bw < CHNL_BW_MAX_HT; bw++) {
+               for (nss = 0; nss < WRS_SS_MAX; nss++) {
+                       for (mcs = 0; mcs < WRS_MCS_MAX_HT; mcs++) {
+                               for (gi = 0; gi < WRS_GI_MAX_HT; gi++) {
+                                       packets = rx_stats->ht[bw][nss][mcs][gi];
+
+                                       if (packets == 0)
+                                               continue;
+
+                                       data_rate = cl_data_rates_get_x10(WRS_MODE_HT,
+                                                                         bw, nss, mcs, gi);
+                                       data_rate_div_10 = data_rate / 10;
+                                       data_rate_mod_10 = data_rate % 10;
+
+                                       total_packets[WRS_MODE_HT] += packets;
+                                       total_data_rate[WRS_MODE_HT] += (packets * data_rate);
+
+                                       cl_snprintf(&buf, &len, &buf_size,
+                                                   "| HT      |%4u|%4u|%5u|%4s| %7u.%u |%10llu|\n",
+                                                   BW_TO_MHZ(bw), nss, mcs, gi_ht_vht_str[gi],
+                                                   data_rate_div_10, data_rate_mod_10, packets);
+                               }
+                       }
+               }
+       }
+
+stats_ofdm:
+       if ((flag & RX_STATS_OFDM) == 0)
+               goto stats_cck;
+
+       for (mcs = 0; mcs < WRS_MCS_MAX_OFDM; mcs++) {
+               packets = rx_stats->ofdm[mcs];
+
+               if (packets == 0)
+                       continue;
+
+               data_rate = cl_data_rates_get_x10(WRS_MODE_OFDM, 0, 0, mcs, 0);
+               data_rate_div_10 = data_rate / 10;
+               data_rate_mod_10 = data_rate % 10;
+
+               total_packets[WRS_MODE_OFDM] += packets;
+               total_data_rate[WRS_MODE_OFDM] += (packets * data_rate);
+
+               cl_snprintf(&buf, &len, &buf_size,
+                           "| OFDM    |  20|   0|%5u|   0| %7u.%u |%10llu|\n",
+                           mcs, data_rate_div_10, data_rate_mod_10, packets);
+       }
+
+stats_cck:
+       if ((flag & RX_STATS_CCK) == 0)
+               goto stats_end;
+
+       for (mcs = 0; mcs < WRS_MCS_MAX_CCK; mcs++) {
+               packets = rx_stats->cck[mcs];
+
+               if (packets == 0)
+                       continue;
+
+               data_rate = cl_data_rates_get_x10(WRS_MODE_CCK, 0, 0, mcs, 0);
+               data_rate_div_10 = data_rate / 10;
+               data_rate_mod_10 = data_rate % 10;
+
+               total_packets[WRS_MODE_CCK] += packets;
+               total_data_rate[WRS_MODE_CCK] += (packets * data_rate);
+
+               cl_snprintf(&buf, &len, &buf_size,
+                           "| CCK     |  20|   0|%5u|   0| %7u.%u |%10llu|\n",
+                           mcs, data_rate_div_10, data_rate_mod_10, packets);
+       }
+
+stats_end:
+       cl_snprintf(&buf, &len, &buf_size,
+                   "-------------------------------------------------------\n");
+
+       for (mode = 0; mode < WRS_MODE_MAX; mode++) {
+               if (total_packets[mode] == 0)
+                       continue;
+
+               equivalent_data_rate = div64_u64(total_data_rate[mode], total_packets[mode]);
+               data_rate_div_10 = (u16)div64_u64_rem(equivalent_data_rate, 10, &remainder);
+
+               cl_snprintf(&buf, &len, &buf_size,
+                           "%s: Equivalent data rate = %u.%u Mbps\n\n",
+                           WRS_MODE_STR(mode), data_rate_div_10, (u16)remainder);
+       }
+
+       cl_vendor_reply(cl_hw, buf, len);
+       kfree(buf);
+}
+
+static void cl_stats_print_rx(struct cl_hw *cl_hw, struct cl_sta *cl_sta)
+{
+       if (cl_hw->chip->conf->ce_production_mode)
+               _cl_stats_print_rx(cl_hw, cl_hw->rx_stats, NULL);
+       else
+               _cl_stats_print_rx(cl_hw, &cl_sta->stats->rx, cl_sta);
+}
+
+static void cl_stats_print_cpu(struct cl_hw *cl_hw)
+{
+       u32 cpu = 0;
+       char *buf = NULL;
+       ssize_t buf_size;
+       int len = 0;
+       struct cl_cpu_cntr *cpu_cntr = &cl_hw->cpu_cntr;
+
+       cl_snprintf(&buf, &len, &buf_size, "|-------------------------|\n");
+       cl_snprintf(&buf, &len, &buf_size, "|CPU|Tx Agg    |Tx Single |\n");
+       cl_snprintf(&buf, &len, &buf_size, "|---+----------+----------|\n");
+
+       for (cpu = 0; cpu < CPU_MAX_NUM; cpu++) {
+               if (cpu_cntr->tx_agg[cpu] == 0 && cpu_cntr->tx_single[cpu] == 0)
+                       continue;
+
+               cl_snprintf(&buf, &len, &buf_size, "| %u |%10u|%10u|\n",
+                           cpu,
+                           cpu_cntr->tx_agg[cpu],
+                           cpu_cntr->tx_single[cpu]);
+       }
+
+       cl_snprintf(&buf, &len, &buf_size, "|-------------------------|\n");
+
+       cl_vendor_reply(cl_hw, buf, len);
+       kfree(buf);
+}
+
+static void cl_sta_print_do(struct cl_hw *cl_hw, u8 sta_idx, stats_callback callback)
+{
+       struct cl_sta *cl_sta;
+
+       cl_sta_lock(cl_hw);
+
+       cl_sta = cl_sta_get(cl_hw, sta_idx);
+       if (cl_sta)
+               callback(cl_hw, cl_sta);
+
+       cl_sta_unlock(cl_hw);
+}
+
+static void cl_stats_cli_cpu(struct cl_hw *cl_hw, bool action)
+{
+       if (action)
+               cl_stats_print_cpu(cl_hw);
+       else
+               memset(&cl_hw->cpu_cntr, 0, sizeof(struct cl_cpu_cntr));
+}
+
+static void cl_stats_cli_rx_info(struct cl_hw *cl_hw, bool action)
+{
+       if (action)
+               cl_rx_info_print(cl_hw);
+       else
+               cl_rx_info_reset(cl_hw);
+}
+
+static void cll_stats_config_ps(struct cl_sta *cl_sta)
+{
+       cl_sta->stats->ps.timestamp_sleep = jiffies;
+       cl_sta->stats->ps.is_ps = test_sta_flag(cl_sta->stainfo, WLAN_STA_PS_STA) ? true : false;
+}
+
+static void cl_stats_free(struct cl_hw *cl_hw, struct cl_sta *cl_sta)
+{
+       kfree(cl_sta->stats);
+       cl_sta->stats = NULL;
+}
+
+static void cl_stats_disable(struct cl_hw *cl_hw)
+{
+       pr_debug("Statistics disabled\n");
+       cl_hw->conf->ci_stats_en = false;
+       cl_sta_loop(cl_hw, cl_stats_free);
+
+       if (cl_hw->chip->conf->ce_production_mode) {
+               kfree(cl_hw->rx_stats);
+               cl_hw->rx_stats = NULL;
+       }
+}
+
+static void cl_stats_enable(struct cl_hw *cl_hw)
+{
+       /*
+        * Allocate for all existing stations.
+        * If one of the allocations fails disable ci_stats_en (and free the
+        * stations that were already allocated).
+        * In production mode also allocate cl_hw->rx_stats
+        */
+       struct cl_sta *cl_sta = NULL;
+       bool success = true;
+
+       if (cl_hw->chip->conf->ce_production_mode) {
+               cl_hw->rx_stats = kzalloc(sizeof(*cl_hw->rx_stats), GFP_ATOMIC);
+
+               if (!cl_hw->rx_stats)
+                       goto out;
+       }
+
+       read_lock(&cl_hw->cl_sta_db.lock);
+
+       list_for_each_entry(cl_sta, &cl_hw->cl_sta_db.head, list) {
+               cl_sta->stats = kzalloc(sizeof(*cl_sta->stats), GFP_ATOMIC);
+
+               if (cl_sta->stats) {
+                       cll_stats_config_ps(cl_sta);
+               } else {
+                       success = false;
+                       break;
+               }
+       }
+
+       if (success) {
+               pr_debug("Statistics enabled\n");
+               cl_hw->conf->ci_stats_en = true;
+       } else {
+               list_for_each_entry(cl_sta, &cl_hw->cl_sta_db.head, list)
+                       cl_stats_free(cl_hw, cl_sta);
+       }
+
+out:
+       read_unlock(&cl_hw->cl_sta_db.lock);
+}
+
+static void cl_stats_cli_enable(struct cl_hw *cl_hw, bool enable)
+{
+       spin_lock_bh(&cl_hw->lock_stats);
+
+       if (enable == cl_hw->conf->ci_stats_en) {
+               pr_debug("Statistics are already %s\n", enable ? "Enabled" : "Disabled");
+               goto out;
+       }
+
+       if (enable)
+               cl_stats_enable(cl_hw);
+       else
+               cl_stats_disable(cl_hw);
+
+out:
+       spin_unlock_bh(&cl_hw->lock_stats);
+}
+
+static void cl_stats_cli_rx(struct cl_hw *cl_hw, u8 sta_idx)
+{
+       spin_lock_bh(&cl_hw->lock_stats);
+
+       if (!cl_hw->conf->ci_stats_en)
+               goto out;
+
+       if (cl_hw->chip->conf->ce_production_mode) {
+               cl_stats_print_rx(cl_hw, NULL);
+       } else {
+               if (sta_idx == STA_IDX_INVALID)
+                       cl_sta_loop(cl_hw, cl_stats_print_rx);
+               else
+                       cl_sta_print_do(cl_hw, sta_idx, cl_stats_print_rx);
+       }
+
+out:
+       spin_unlock_bh(&cl_hw->lock_stats);
+}
+
+static void cl_stats_cli_rssi(struct cl_hw *cl_hw, u8 sta_idx)
+{
+       spin_lock_bh(&cl_hw->lock_stats);
+
+       if (!cl_hw->conf->ci_stats_en)
+               goto out;
+
+       if (sta_idx == STA_IDX_INVALID)
+               cl_sta_loop(cl_hw, cl_stats_print_rssi);
+       else
+               cl_sta_print_do(cl_hw, sta_idx, cl_stats_print_rssi);
+
+out:
+       spin_unlock_bh(&cl_hw->lock_stats);
+}
+
+static void cl_stats_cli_fec_coding(struct cl_hw *cl_hw, u8 sta_idx)
+{
+       spin_lock_bh(&cl_hw->lock_stats);
+
+       if (!cl_hw->conf->ci_stats_en)
+               goto out;
+
+       if (sta_idx == STA_IDX_INVALID)
+               cl_sta_loop(cl_hw, cl_stats_print_fec_coding);
+       else
+               cl_sta_print_do(cl_hw, sta_idx, cl_stats_print_fec_coding);
+
+out:
+       spin_unlock_bh(&cl_hw->lock_stats);
+}
+
+static void cl_stats_cli_ps(struct cl_hw *cl_hw, u8 sta_idx)
+{
+       spin_lock_bh(&cl_hw->lock_stats);
+
+       if (!cl_hw->conf->ci_stats_en)
+               goto out;
+
+       if (sta_idx == STA_IDX_INVALID)
+               cl_sta_loop(cl_hw, cl_stats_print_power_save);
+       else
+               cl_sta_print_do(cl_hw, sta_idx, cl_stats_print_power_save);
+
+out:
+       spin_unlock_bh(&cl_hw->lock_stats);
+}
+
+static void cl_stats_cli_tx(struct cl_hw *cl_hw, u8 sta_idx)
+{
+       spin_lock_bh(&cl_hw->lock_stats);
+
+       if (!cl_hw->conf->ci_stats_en)
+               goto out;
+
+       if (sta_idx == STA_IDX_INVALID)
+               cl_sta_loop(cl_hw, cl_stats_print_tx);
+       else
+               cl_sta_print_do(cl_hw, sta_idx, cl_stats_print_tx);
+
+out:
+       spin_unlock_bh(&cl_hw->lock_stats);
+}
+
+static void cl_stats_cli_reset(struct cl_hw *cl_hw, u8 sta_idx)
+{
+       spin_lock_bh(&cl_hw->lock_stats);
+
+       if (!cl_hw->conf->ci_stats_en)
+               goto out;
+
+       if (cl_hw->chip->conf->ce_production_mode) {
+               memset(cl_hw->rx_stats, 0, sizeof(struct cl_rx_stats));
+       } else {
+               if (sta_idx == STA_IDX_INVALID)
+                       cl_sta_loop(cl_hw, cl_stats_sta_reset);
+               else
+                       cl_sta_print_do(cl_hw, sta_idx, cl_stats_sta_reset);
+       }
+
+out:
+       spin_unlock_bh(&cl_hw->lock_stats);
+}
+
+static int cl_stats_cli_help(struct cl_hw *cl_hw)
+{
+       char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       int err = 0;
+
+       if (!buf)
+               return -ENOMEM;
+
+       snprintf(buf, PAGE_SIZE,
+                "stats usage:\n"
+                "-a : Print air PER and system PER [delay-ms)]\n"
+                "-b : CPU stats [0-reset, 1-print]\n"
+                "-e : Enable/Disable statistics [0-dis, 1-en]\n"
+                "-f : Print RX FEC coding counters [sta_idx]\n"
+                "-p : Print power-save statistics [sta_idx]\n"
+                "-r : Print RX stats table [sta_idx]\n"
+                "-s : Print RSSI stats tables [sta_idx]\n"
+                "-t : Print TX stats table [sta_idx]\n"
+                "-u : Uplink stats [0-reset, 1-print]\n"
+                "-z : Reset stats tables [sta_idx]\n");
+
+       err = cl_vendor_reply(cl_hw, buf, strlen(buf));
+       kfree(buf);
+
+       return err;
+}
+
+static void _cl_stats_update_tx(struct cl_hw *cl_hw, struct cl_sta *cl_sta,
+                               struct cl_agg_tx_report *agg_report)
+{
+       struct cl_stats *stats = cl_sta->stats;
+       struct cl_tx_cntrs *cntrs;
+       union cl_rate_ctrl_info rate_ctrl_info = {.word = agg_report->rate_cntrl_info};
+       u8 bw, nss, mcs, gi, bf;
+
+       switch (rate_ctrl_info.field.format_mod) {
+       case WRS_MODE_HE:
+               nss = (rate_ctrl_info.field.mcs_index >> 4);
+               mcs = (rate_ctrl_info.field.mcs_index & 0xF);
+               gi = rate_ctrl_info.field.gi;
+               bw = rate_ctrl_info.field.bw;
+               bf = agg_report->bf;
+               cntrs = &stats->tx.he[bw][nss][mcs][gi][bf];
+               break;
+       case WRS_MODE_VHT:
+               bw = rate_ctrl_info.field.bw;
+               nss = (rate_ctrl_info.field.mcs_index >> 4);
+               mcs = (rate_ctrl_info.field.mcs_index & 0xF);
+               gi = rate_ctrl_info.field.gi;
+               bf = agg_report->bf;
+               cntrs = &stats->tx.vht[bw][nss][mcs][gi][bf];
+               break;
+       case WRS_MODE_HT:
+               bw = rate_ctrl_info.field.bw;
+               nss = (rate_ctrl_info.field.mcs_index >> 3);
+               mcs = (rate_ctrl_info.field.mcs_index & 0x7);
+               gi = rate_ctrl_info.field.gi;
+               cntrs = &stats->tx.ht[bw][nss][mcs][gi];
+               break;
+       case WRS_MODE_OFDM:
+               mcs = rate_ctrl_info.field.mcs_index - RATE_CTRL_OFFSET_OFDM;
+               cntrs = &stats->tx.ofdm[mcs];
+               break;
+       case WRS_MODE_CCK:
+               mcs = rate_ctrl_info.field.mcs_index;
+               cntrs = &stats->tx.cck[mcs];
+               break;
+       default:
+               return;
+       }
+
+       cntrs->success += agg_report->success;
+       cntrs->fail += agg_report->fail;
+}
+
+void cl_stats_init(struct cl_hw *cl_hw)
+{
+       spin_lock_init(&cl_hw->lock_stats);
+
+       if (cl_hw->conf->ci_stats_en && cl_hw->chip->conf->ce_production_mode) {
+               cl_hw->rx_stats = kzalloc(sizeof(*cl_hw->rx_stats), GFP_ATOMIC);
+
+               if (!cl_hw->rx_stats)
+                       cl_hw->conf->ci_stats_en = false;
+       }
+}
+
+void cl_stats_deinit(struct cl_hw *cl_hw)
+{
+       spin_lock_bh(&cl_hw->lock_stats);
+
+       if (cl_hw->conf->ci_stats_en && cl_hw->chip->conf->ce_production_mode) {
+               cl_hw->conf->ci_stats_en = false;
+
+               kfree(cl_hw->rx_stats);
+               cl_hw->rx_stats = NULL;
+       }
+
+       spin_unlock_bh(&cl_hw->lock_stats);
+}
+
+void cl_stats_sta_add(struct cl_hw *cl_hw, struct cl_sta *cl_sta)
+{
+       /* If allocation failed disable ci_stats_en, and free the memory of all other stations */
+       bool disable = false;
+
+       /* Take regular lock and not BH, because cl_sta_add_to_lut() already disables BH */
+       spin_lock(&cl_hw->lock_stats);
+
+       if (cl_hw->conf->ci_stats_en) {
+               cl_sta->stats = kzalloc(sizeof(*cl_sta->stats), GFP_ATOMIC);
+
+               if (cl_sta->stats)
+                       cll_stats_config_ps(cl_sta);
+               else
+                       disable = true;
+       }
+
+       spin_unlock(&cl_hw->lock_stats);
+
+       if (disable)
+               cl_stats_cli_enable(cl_hw, false);
+}
+
+void cl_stats_sta_remove(struct cl_hw *cl_hw, struct cl_sta *cl_sta)
+{
+       spin_lock_bh(&cl_hw->lock_stats);
+
+       if (cl_hw->conf->ci_stats_en)
+               cl_stats_free(cl_hw, cl_sta);
+
+       spin_unlock_bh(&cl_hw->lock_stats);
+}
+
+void cl_stats_update_tx_agg(struct cl_hw *cl_hw, struct cl_sta *cl_sta,
+                           struct cl_agg_tx_report *agg_report)
+{
+       spin_lock(&cl_hw->lock_stats);
+
+       if (cl_hw->conf->ci_stats_en) {
+               struct cl_stats *stats = cl_sta->stats;
+
+               stats->tx.agg_cntr++;
+               stats->tx.fail_cntr += agg_report->fail;
+               _cl_stats_update_tx(cl_hw, cl_sta, agg_report);
+       }
+
+       spin_unlock(&cl_hw->lock_stats);
+}
+
+void cl_stats_update_tx_single(struct cl_hw *cl_hw, struct cl_sta *cl_sta,
+                              struct cl_agg_tx_report *agg_report)
+{
+       spin_lock(&cl_hw->lock_stats);
+
+       if (cl_hw->conf->ci_stats_en) {
+               cl_sta->stats->tx.fail_cntr += agg_report->fail;
+               _cl_stats_update_tx(cl_hw, cl_sta, agg_report);
+       }
+
+       spin_unlock(&cl_hw->lock_stats);
+}
+
+void cl_stats_update_rx_rssi(struct cl_hw *cl_hw, struct cl_sta *cl_sta, s8 rssi[MAX_ANTENNAS])
+{
+       int i;
+       s8 rx_rssi;
+
+       spin_lock(&cl_hw->lock_stats);
+
+       if (!cl_hw->conf->ci_stats_en)
+               goto out;
+
+       for (i = 0; i < cl_hw->num_antennas; i++) {
+               rx_rssi = rssi[i] * -1;
+
+               if (rx_rssi >= 0 && rx_rssi < RSSI_ARR_SIZE)
+                       cl_sta->stats->rssi[rx_rssi][i]++;
+       }
+
+out:
+       spin_unlock(&cl_hw->lock_stats);
+}
+
+void _cl_stats_update_rx_rate(struct cl_hw *cl_hw, struct cl_rx_stats *rx_stats,
+                             struct hw_rxhdr *rxhdr)
+{
+       u8 bw, nss, mcs, gi;
+
+       switch (rxhdr->format_mod) {
+       case FORMATMOD_HE_TRIG:
+               nss = rxhdr->n_sts & 0x3;
+               mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_HE);
+               gi = min_t(u8, rxhdr->gi_type, WRS_GI_MAX_HE);
+               rx_stats->he_trig[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx;
+               rx_stats->flag |= RX_STATS_HE_TRIG;
+               break;
+       case FORMATMOD_HE_EXT:
+               nss = rxhdr->n_sts & 0x3;
+               mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_HE);
+               gi = min_t(u8, rxhdr->gi_type, WRS_GI_MAX_HE);
+               rx_stats->he_ext[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx;
+               rx_stats->flag |= RX_STATS_HE_EXT;
+               break;
+       case FORMATMOD_HE_MU:
+               nss = rxhdr->n_sts & 0x3;
+               mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_HE);
+               gi = min_t(u8, rxhdr->gi_type, WRS_GI_MAX_HE);
+               rx_stats->he_mu[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx;
+               rx_stats->flag |= RX_STATS_HE_MU;
+               break;
+       case FORMATMOD_HE_SU:
+               nss = rxhdr->n_sts & 0x3;
+               mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_HE);
+               gi = min_t(u8, rxhdr->gi_type, WRS_GI_MAX_HE);
+               rx_stats->he_su[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx;
+               rx_stats->flag |= RX_STATS_HE_SU;
+               break;
+       case FORMATMOD_VHT:
+               nss = rxhdr->n_sts & 0x3;
+               mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_VHT);
+               gi = rxhdr->gi_type & 0x1;
+               rx_stats->vht[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx;
+               rx_stats->flag |= RX_STATS_VHT;
+               break;
+       case FORMATMOD_HT_MF:
+       case FORMATMOD_HT_GF:
+               bw = rxhdr->ch_bw & 0x1;
+               nss = (rxhdr->mcs >> 3) & 0x3;
+               mcs = rxhdr->mcs & 0x7;
+               gi = rxhdr->gi_type & 0x1;
+               rx_stats->ht[bw][nss][mcs][gi] += rxhdr->frm_successful_rx;
+               rx_stats->flag |= RX_STATS_HT;
+               break;
+       case FORMATMOD_NON_HT:
+               if (rxhdr->mcs >= RATE_CTRL_OFFSET_OFDM) {
+                       mcs = (rxhdr->mcs - RATE_CTRL_OFFSET_OFDM) & 0x7;
+                       rx_stats->ofdm[mcs] += rxhdr->frm_successful_rx;
+                       rx_stats->flag |= RX_STATS_OFDM;
+               } else if (cl_band_is_24g(cl_hw)) {
+                       mcs = rxhdr->mcs & 0x3;
+                       rx_stats->cck[mcs] += rxhdr->frm_successful_rx;
+                       rx_stats->flag |= RX_STATS_CCK;
+               }
+               break;
+       }
+}
+
+void cl_stats_update_rx_rate(struct cl_hw *cl_hw, struct cl_sta *cl_sta, struct hw_rxhdr *rxhdr)
+{
+       spin_lock(&cl_hw->lock_stats);
+
+       if (cl_hw->conf->ci_stats_en) {
+               _cl_stats_update_rx_rate(cl_hw, &cl_sta->stats->rx, rxhdr);
+               cl_sta->stats->fec_coding[rxhdr->fec_coding]++;
+       }
+
+       spin_unlock(&cl_hw->lock_stats);
+}
+
+void cl_stats_update_rx_rate_production(struct cl_hw *cl_hw, struct hw_rxhdr *rxhdr)
+{
+       spin_lock(&cl_hw->lock_stats);
+
+       if (cl_hw->conf->ci_stats_en)
+               _cl_stats_update_rx_rate(cl_hw, cl_hw->rx_stats, rxhdr);
+
+       spin_unlock(&cl_hw->lock_stats);
+}
+
+void cl_stats_update_ps(struct cl_hw *cl_hw, struct cl_sta *cl_sta, bool is_ps)
+{
+       struct cl_ps_stats *ps;
+
+       spin_lock(&cl_hw->lock_stats);
+
+       if (!cl_hw->conf->ci_stats_en)
+               goto out;
+
+       ps = &cl_sta->stats->ps;
+
+       if (ps->is_ps == is_ps)
+               goto out;
+
+       ps->is_ps = is_ps;
+
+       if (is_ps) {
+               ps->timestamp_sleep = jiffies;
+       } else {
+               unsigned long sleep_time = jiffies_to_msecs(jiffies - ps->timestamp_sleep);
+
+               if (sleep_time <= 50)
+                       ps->period[PS_PERIOD_50MS]++;
+               else if (sleep_time <= 100)
+                       ps->period[PS_PERIOD_100MS]++;
+               else if (sleep_time <= 250)
+                       ps->period[PS_PERIOD_250MS]++;
+               else if (sleep_time <= 500)
+                       ps->period[PS_PERIOD_500MS]++;
+               else if (sleep_time <= 750)
+                       ps->period[PS_PERIOD_750MS]++;
+               else if (sleep_time <= 1000)
+                       ps->period[PS_PERIOD_1000MS]++;
+               else if (sleep_time <= 2000)
+                       ps->period[PS_PERIOD_2000MS]++;
+               else if (sleep_time <= 5000)
+                       ps->period[PS_PERIOD_5000MS]++;
+               else if (sleep_time <= 10000)
+                       ps->period[PS_PERIOD_10000MS]++;
+               else
+                       ps->period[PS_PERIOD_ABOVE]++;
+       }
+
+out:
+       spin_unlock(&cl_hw->lock_stats);
+}
+
+int cl_stats_cli(struct cl_hw *cl_hw, struct cl_vif *cl_vif, struct cli_params *cli_params)
+{
+       bool print_per = false;
+       bool cpu_stats = false;
+       bool enable_tx_rx_rssi = false;
+       bool print_rx = false;
+       bool print_rssi = false;
+       bool print_fec_coding = false;
+       bool print_power_save = false;
+       bool print_tx = false;
+       bool uplink_stats = false;
+       bool reset_stats = false;
+       u32 expected_params = -1;
+
+       switch (cli_params->option) {
+       case 'a':
+               print_per = true;
+               expected_params = 1;
+               break;
+       case 'b':
+               cpu_stats = true;
+               expected_params = 1;
+               break;
+       case 'e':
+               enable_tx_rx_rssi = true;
+               expected_params = 1;
+               break;
+       case 'f':
+               print_fec_coding = true;
+               expected_params = 1;
+               break;
+       case 'p':
+               print_power_save = true;
+               expected_params = 1;
+               break;
+       case 't':
+               print_tx = true;
+               expected_params = 1;
+               break;
+       case 'r':
+               print_rx = true;
+               expected_params = cl_hw->chip->conf->ce_production_mode ? 0 : 1;
+               break;
+       case 'z':
+               reset_stats = true;
+               expected_params = cl_hw->chip->conf->ce_production_mode ? 0 : 1;
+               break;
+       case 's':
+               print_rssi = true;
+               expected_params = 1;
+               break;
+       case 'u':
+               uplink_stats = true;
+               expected_params = 1;
+               break;
+       case '?':
+               return cl_stats_cli_help(cl_hw);
+       default:
+               cl_dbg_err(cl_hw, "Illegal option (%c) - try '?' for help\n", cli_params->option);
+               goto out_err;
+       }
+
+       if (expected_params != cli_params->num_params) {
+               cl_dbg_err(cl_hw, "Wrong number of arguments (expected %u) (actual %u)\n",
+                          expected_params, cli_params->num_params);
+               goto out_err;
+       }
+
+       if (print_per) {
+               cl_stats_print_per(cl_hw, (u32)cli_params->params[0]);
+               return 0;
+       }
+
+       if (cpu_stats) {
+               cl_stats_cli_cpu(cl_hw, (bool)cli_params->params[0]);
+               return 0;
+       }
+
+       if (uplink_stats) {
+               cl_stats_cli_rx_info(cl_hw, (bool)cli_params->params[0]);
+               return 0;
+       }
+
+       if (enable_tx_rx_rssi) {
+               cl_stats_cli_enable(cl_hw, (bool)cli_params->params[0]);
+               return 0;
+       }
+
+       if (!cl_hw->conf->ci_stats_en) {
+               pr_debug("Statistics are disabled!\n"
+                        "To enable them type: "
+                        "'iwcl <interface> cecli stats -e.1'\n");
+               return 0;
+       }
+
+       if (print_rx) {
+               u8 sta_idx = (u8)cli_params->params[0];
+
+               cl_stats_cli_rx(cl_hw, sta_idx);
+               return 0;
+       }
+
+       if (print_rssi) {
+               u8 sta_idx = (u8)cli_params->params[0];
+
+               cl_stats_cli_rssi(cl_hw, sta_idx);
+               return 0;
+       }
+
+       if (print_fec_coding) {
+               u8 sta_idx = (u8)cli_params->params[0];
+
+               cl_stats_cli_fec_coding(cl_hw, sta_idx);
+               return 0;
+       }
+
+       if (print_power_save) {
+               u8 sta_idx = (u8)cli_params->params[0];
+
+               cl_stats_cli_ps(cl_hw, sta_idx);
+               return 0;
+       }
+
+       if (print_tx) {
+               u8 sta_idx = (u8)cli_params->params[0];
+
+               cl_stats_cli_tx(cl_hw, sta_idx);
+               return 0;
+       }
+
+       if (reset_stats) {
+               u8 sta_idx = (u8)cli_params->params[0];
+
+               cl_stats_cli_reset(cl_hw, sta_idx);
+               return 0;
+       }
+
+out_err:
+       return -EIO;
+}
--
2.30.0

________________________________
The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any retransmission, dissemination, copying or other use of, or taking of any action in reliance upon this information is prohibited. If you received this in error, please contact the sender and delete the material from any computer. Nothing contained herein shall be deemed as a representation, warranty or a commitment by Celeno. No warranties are expressed or implied, including, but not limited to, any implied warranties of non-infringement, merchantability and fitness for a particular purpose.
________________________________






[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux