| /** |
| Copyright (c) 2008 - 2013 Quantenna Communications Inc |
| All Rights Reserved |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License |
| as published by the Free Software Foundation; either version 2 |
| of the License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| |
| **/ |
| #ifndef AUTOCONF_INCLUDED |
| #include <linux/config.h> |
| #endif |
| #include <linux/version.h> |
| #include <linux/device.h> |
| #include <linux/interrupt.h> |
| #include <linux/dma-mapping.h> |
| |
| #include "qdrv_features.h" |
| #include "qdrv_debug.h" |
| #include "qdrv_mac.h" |
| #include "qdrv_comm.h" |
| #include "qdrv_wlan.h" |
| #include "qdrv_vap.h" |
| #include "qdrv_txbf.h" |
| #include <qtn/qtn_math.h> |
| #include "qdrv_muc_stats.h" |
| #include <qtn/txbf_common.h> |
| #include <qtn/registers.h> |
| #include <qtn/muc_phy_stats.h> |
| |
| #define PHY_STATS_SUM_EVM 1 |
| #define TIME_AVERAGE_PHY_STATS 1 |
| #define PHY_STATS_PHY_DATE_RATE 1 |
| |
| /* valid only when Y is a power of 2, redefine more generally */ |
| #define MOD(X,Y) ((X)&(Y-1)) |
| |
| static struct qtn_stats_log host_log; |
| static unsigned int last_tstamp = 0; |
| #ifdef TIME_AVERAGE_PHY_STATS |
| |
| #define QDRV_MUC_STATS_RATE_TABLE_TO_MBPS 10 |
| |
| #endif |
| |
| #ifdef PHY_STATS_PHY_DATE_RATE |
| #define UNSUPPORTED_RATE 0 |
| #define MAX_BW_SIZE 2 |
| #define MAX_GI_SIZE 2 |
| #define MAX_MCS_SIZE 77 // index : 0~76 |
| |
| enum muc_stats_opt { |
| MSO_DEF = 0, |
| MSO_BOTH = 1, |
| MSO_SU = 2, |
| MSO_MU = 3, |
| }; |
| |
| const uint16_t mcs_rate_table[MAX_BW_SIZE * MAX_GI_SIZE * MAX_MCS_SIZE] = { |
| |
| //=========20Mhz with long GI from MCS 0 ~ 15=============== |
| 65, 130, 195, 260, 390, 520, 585, 650, |
| 130, 260, 390, 520, 780, 1040, 1170, 1300, |
| |
| //20Mhz with long GI from MCS 16 ~ 31 |
| 195, 390, 585, 780, 1170, 1560, 1755, 1950, |
| 260, 520, 780, 1040, 1560, 2080, 2340, 2600, |
| |
| //20Mhz with long GI from MCS 32 ~ 47 |
| 0, 390, 520, 650, 585, 780, 975, 520, |
| 650, 650, 780, 910, 910, 1040, 780, 975, |
| |
| //20Mhz with long GI from MCS 48 ~ 63 |
| 975, 1170, 1365, 1365, 1560, 650, 780, 910, |
| 780, 910, 1040, 1170, 1040, 1170, 1300, 1300, |
| |
| //20Mhz with long GI from MCS 64 ~ 76 |
| 1430, 975, 1170, 1365, 1170, 1365, 1560, 1755, 1560, 1755, 1950, 1950, 2145, |
| |
| |
| //==========20Mhz with short GI from MCS 0 ~ 15============== |
| 72, 144, 217, 289, 433, 578, 650, 722, |
| 144, 289, 433, 578, 867, 1156, 1300, 1444, |
| |
| //20Mhz with short GI from MCS 16 ~ 31 |
| 217, 433, 650, 867, 1300, 1733, 1950, 2167, |
| 289, 578, 867, 1156, 1733, 2311, 2600, 2889, |
| |
| //20Mhz with short GI from MCS 32 ~ 47 |
| 0, 433, 578, 722, 650, 867, 1083, 578, |
| 722, 722, 867, 1011, 1011, 1156, 867, 1083, |
| |
| //20Mhz with short GI from MCS 48 ~ 63 |
| 1083, 1300, 1517, 1517, 1733, 722, 867, 1011, |
| 867, 1011, 1156, 1300, 1156, 1300, 1444, 1444, |
| |
| //20Mhz with short GI from MCS 64 ~ 76 |
| 1589, 1083, 1300, 1517, 1300, 1517, 1733, 1950, 1733, 1950, 2167, 2167, 2383, |
| |
| |
| //======40Mhz with long GI from MCS 0 ~ 15================== |
| 135, 270, 405, 540, 810, 1080, 1215, 1350, |
| 270, 540, 810, 1080, 1620, 2160, 2430, 2700, |
| |
| //40Mhz with long GI from MCS 16 ~ 31 |
| 405, 810, 1215, 1620, 2430, 3240, 3645, 4050, |
| 540, 1080, 1620, 2160, 3240, 4320, 4860, 5400, |
| |
| //40Mhz with long GI from MCS 32 ~ 47 |
| 60, 810, 1080, 1350, 1215, 1620, 2025, 1080, |
| 1350, 1350, 1620, 1890, 1890, 2160, 1620, 2025, |
| |
| //40Mhz with long GI from MCS 48 ~ 63 |
| 2025, 2430, 2835, 2835, 3240, 1350, 1620, 1890, |
| 1620, 1890, 2160, 2430, 2160, 2430, 2700, 2700, |
| |
| //40Mhz with long GI from MCS 64 ~ 76 |
| 2970, 2025, 2430, 2835, 2430, 2835, 3240, 3645, 3240, 3645, 4050, 4050, 4455, |
| |
| |
| //========40Mhz with short GI from MCS 0 ~ 15================ |
| 150, 300, 450, 600, 900, 1200, 1350, 1500, |
| 300, 600, 900, 1200, 1800, 2400, 2700, 3000, |
| |
| //40Mhz with short GI from MCS 16 ~ 31 |
| 450, 900, 1350, 1800, 2700, 3600, 4050, 4500, |
| 600, 1200, 1800, 2400, 3600, 4800, 5400, 6000, |
| |
| //40Mhz with short GI from MCS 32 ~ 47 |
| 67, 900, 1200, 1500, 1350, 1800, 2250, 1200, |
| 1500, 1500, 1800, 2100, 2100, 2400, 1800, 2250, |
| |
| //40Mhz with short GI from MCS 48 ~ 63 |
| 2250, 2700, 3150, 3150, 3600, 1500, 1800, 2100, |
| 1800, 2100, 2400, 2700, 2400, 2700, 3000, 3000, |
| |
| //40Mhz with short GI from MCS 64 ~ 76 |
| 3300, 2250, 2700, 3150, 2700, 3150, 3600, 4050, 3600, 4050, 4500, 4500, 4950 |
| }; |
| |
| #ifdef QDRV_FEATURE_VHT |
| #define VHT_MAX_BW_SIZE 4 |
| #define VHT_MAX_GI_SIZE 2 |
| #define VHT_MAX_NSS_SIZE 4 |
| #define VHT_MAX_MCS_SIZE 10 |
| /* rate in uint 100kbps */ |
| const uint16_t vht_rate_table[VHT_MAX_BW_SIZE * VHT_MAX_GI_SIZE * |
| VHT_MAX_NSS_SIZE * VHT_MAX_MCS_SIZE] = { |
| /* 20MHz, Long GI, Nss = 1, MCS 0 ~ 9 */ |
| 65, 130, 195, 260, 390, 520, 585, 650, 780, UNSUPPORTED_RATE, |
| /* 20MHz, Long GI, Nss = 2, MCS 0 ~ 9 */ |
| 130, 260, 390, 520, 780, 1040, 1170, 1300, 1560, UNSUPPORTED_RATE, |
| /* 20MHz, Long GI, Nss = 3, MCS 0 ~ 9 */ |
| 195, 390, 585, 780, 1170, 1560, 1755, 1950, 2340, 2600, |
| /* 20MHz, Long GI, Nss = 4, MCS 0 ~ 9 */ |
| 260, 520, 780, 1040, 1560, 2080, 2340, 2600, 3120, UNSUPPORTED_RATE, |
| |
| /* 20MHz, Short GI, Nss = 1, MCS 0 ~ 9 */ |
| 72, 144, 217, 289, 433, 578, 650, 722, 867, UNSUPPORTED_RATE, |
| /* 20MHz, Short GI, Nss = 2, MCS 0 ~ 9 */ |
| 144, 289, 433, 578, 867, 1156, 1300, 1444, 1733, UNSUPPORTED_RATE, |
| /* 20MHz, Short GI, Nss = 3, MCS 0 ~ 9 */ |
| 217, 433, 650, 867, 1300, 1733, 1950, 2167, 2600, 2889, |
| /* 20MHz, Short GI, Nss = 4, MCS 0 ~ 9 */ |
| 289, 578, 867, 1156, 1733, 2311, 2600, 2889, 3467, UNSUPPORTED_RATE, |
| |
| /* 40MHz, Long GI, Nss = 1, MCS 0 ~ 9 */ |
| 135, 270, 405, 540, 810, 1080, 1215, 1350, 1620, 1800, |
| /* 40MHz, Long GI, Nss = 2, MCS 0 ~ 9 */ |
| 270, 540, 810, 1080, 1620, 2160, 2430, 2700, 3240, 3600, |
| /* 40MHz, Long GI, Nss = 3, MCS 0 ~ 9 */ |
| 405, 810, 1215, 1620, 2430, 3240, 3645, 4050, 4860, 5400, |
| /* 40MHz, Long GI, Nss = 4, MCS 0 ~ 9 */ |
| 540, 1080, 1620, 2160, 3240, 4320, 4860, 5400, 6480, 7200, |
| |
| /* 40MHz, Short GI, Nss = 1, MCS 0 ~ 9 */ |
| 150, 300, 450, 600, 900, 1200, 1350, 1500, 1800, 2000, |
| /* 40MHz, Short GI, Nss = 2, MCS 0 ~ 9 */ |
| 300, 600, 900, 1200, 1800, 2400, 2700, 3000, 3600, 4000, |
| /* 40MHz, Short GI, Nss = 3, MCS 0 ~ 9 */ |
| 450, 900, 1350, 1800, 2700, 3600, 4050, 4500, 5400, 6000, |
| /* 40MHz, Short GI, Nss = 4, MCS 0 ~ 9 */ |
| 600, 1200, 1800, 2400, 3600, 4800, 5400, 6000, 7200, 8000, |
| |
| /* 80MHz, Long GI, Nss = 1, MCS 0 ~ 9 */ |
| 293, 585, 878, 1170, 1755, 2340, 2633, 2925, 3510, 3900, |
| /* 80MHz, Long GI, Nss = 2, MCS 0 ~ 9 */ |
| 585, 1170, 1755, 2340, 3510, 4680, 5265, 5850, 7020, 7800, |
| /* 80MHz, Long GI, Nss = 3, MCS 0 ~ 9 */ |
| 878, 1755, 2633, 3510, 5265, 7020, UNSUPPORTED_RATE, 8775, 10530, 11700, |
| /* 80MHz, Long GI, Nss = 4, MCS 0 ~ 9 */ |
| 1170, 2340, 3510, 4680, 7020, 9360, 10530, 11700, 14040, 15600, |
| |
| /* 80MHz, Short GI, Nss = 1, MCS 0 ~ 9 */ |
| 325, 650, 975, 1300, 1950, 2600, 2925, 3250, 3900, 4333, |
| /* 80MHz, Short GI, Nss = 2, MCS 0 ~ 9 */ |
| 650, 1300, 1950, 2600, 3900, 5200, 5850, 6500, 7800, 8667, |
| /* 80MHz, Short GI, Nss = 3, MCS 0 ~ 9 */ |
| 975, 1950, 2925, 3900, 5850, 7800, UNSUPPORTED_RATE, 9750, 11700, 13000, |
| /* 80MHz, Short GI, Nss = 4, MCS 0 ~ 9 */ |
| 1300, 2600, 3900, 5200, 7800, 10400, 11700, 13000, 15600, 17333, |
| |
| /* 160MHz, Long GI, Nss = 1, MCS 0 ~ 9 */ |
| 585, 1170, 1755, 2340, 3510, 4680, 5265, 5850, 7020, 7800, |
| /* 160MHz, Long GI, Nss = 2, MCS 0 ~ 9 */ |
| 1170, 2340, 3510, 4680, 7020, 9360, 10530, 11700, 14040, 15600, |
| /* 160MHz, Long GI, Nss = 3, MCS 0 ~ 9 */ |
| 1755, 3510, 5265, 7020, 10530, 14040, 15795, 17550, 21060, UNSUPPORTED_RATE, |
| /* 160MHz, Long GI, Nss = 4, MCS 0 ~ 9 */ |
| 2340, 4680, 7020, 9360, 14040, 18720, 21060, 23400, 28080, 31200, |
| |
| /* 160MHz, Short GI, Nss = 1, MCS 0 ~ 9 */ |
| 650, 1300, 1950, 2600, 3900, 5200, 5850, 6500, 7800, 8667, |
| /* 160MHz, Short GI, Nss = 2, MCS 0 ~ 9 */ |
| 1300, 2600, 3900, 5200, 7800, 10400, 11700, 13000, 15600, 17333, |
| /* 160MHz, Short GI, Nss = 3, MCS 0 ~ 9 */ |
| 1950, 3900, 5850, 7800, 11700, 15600, 17550, 19500, 23400, UNSUPPORTED_RATE, |
| /* 160MHz, Short GI, Nss = 4, MCS 0 ~ 9 */ |
| 2600, 5200, 7800, 10400, 15600, 20800, 23400, 26000, 31200, 34667 |
| }; |
| #endif |
| |
| #endif |
| |
| |
| #define QDRV_MUC_STATS_DB_VALUE_LENGTH 8 |
| #define QDRV_MUC_STATS_DAGC_SHIFT_UP_M 0x00380000 |
| #define QDRV_MUC_STATS_DAGC_SHIFT_UP_S 19 |
| #define QDRV_MUC_STATS_DAGC_SHIFT_DOWN_M 0x00070000 |
| #define QDRV_MUC_STATS_DAGC_SHIFT_DOWN_S 16 |
| |
| #define QDRV_MUC_STATS_DAGC_SHIFT_FIELD_M 0x00ff0000 |
| #define QDRV_MUC_STATS_DAGC_SHIFT_FIELD_S 16 |
| |
| #define MIN_RCPI_VALUE -1000 |
| |
| /* Tag to filter(grep) statistics in csv format */ |
| #define STAT_CSV_TAG "*CSV_STAT*" |
| |
| static uint32_t qdrv_muc_stats_rate_lut(uint8_t bw, uint8_t sgi, uint8_t mcs) |
| { |
| int index; |
| uint32_t rate = 0; |
| |
| if (bw < MAX_BW_SIZE && sgi < MAX_GI_SIZE && mcs < MAX_MCS_SIZE) { |
| index = mcs + (sgi * MAX_MCS_SIZE) + (bw * MAX_GI_SIZE * MAX_MCS_SIZE); |
| rate = (uint32_t)mcs_rate_table[index]; |
| } |
| |
| return rate; |
| } |
| |
| static uint32_t qdrv_muc_stats_vhtrate_lut(uint8_t bw, uint8_t sgi, uint8_t mcs, uint8_t nss) |
| { |
| #ifdef QDRV_FEATURE_VHT |
| int index; |
| uint32_t rate = UNSUPPORTED_RATE; |
| |
| if (bw < VHT_MAX_BW_SIZE && sgi < VHT_MAX_GI_SIZE && nss < VHT_MAX_NSS_SIZE |
| && mcs < VHT_MAX_MCS_SIZE) { |
| index = mcs + (nss * VHT_MAX_MCS_SIZE) + (sgi * VHT_MAX_NSS_SIZE * VHT_MAX_MCS_SIZE) + |
| (bw * VHT_MAX_GI_SIZE * VHT_MAX_NSS_SIZE * VHT_MAX_MCS_SIZE); |
| rate = (uint32_t)vht_rate_table[index]; |
| } |
| |
| return rate; |
| #else |
| return UNSUPPORTED_RATE; |
| #endif |
| } |
| |
| /* The returned rate has unit kbps */ |
| uint32_t qdrv_muc_stats_mcs_to_phyrate(uint8_t bw, uint8_t sgi, uint8_t mcs, |
| uint8_t nss, uint8_t vht) |
| { |
| if (vht) |
| return qdrv_muc_stats_vhtrate_lut(bw, sgi, mcs, nss) * 100; |
| else |
| return qdrv_muc_stats_rate_lut(bw, sgi, mcs) * 100; |
| } |
| |
| enum qdrv_muc_stats_display_choice qdrv_muc_stats_get_display_choice(const struct qtn_stats *stats, |
| const struct ieee80211com *ic) |
| { |
| enum qdrv_muc_stats_display_choice display_choice = QDRV_MUC_STATS_SHOW_RSSI; |
| |
| if (ic->ic_mode_get_phy_stats == MUC_PHY_STATS_RSSI_RCPI_ONLY) { |
| display_choice = (stats->tstamp & 0x01) ? QDRV_MUC_STATS_SHOW_RSSI : QDRV_MUC_STATS_SHOW_RCPI; |
| } else { |
| if (qtn_select_rssi_over_error_sums(stats->tstamp, ic->ic_mode_get_phy_stats)) { |
| display_choice = QDRV_MUC_STATS_SHOW_RSSI; |
| } else { |
| display_choice = QDRV_MUC_STATS_SHOW_EVM; |
| } |
| } |
| |
| return(display_choice); |
| } |
| |
| static int qtn_muc_stats_get_dagc_shift_count(u_int32_t rx_gain_fields) |
| { |
| int dagc_shift_up = |
| (rx_gain_fields & QDRV_MUC_STATS_DAGC_SHIFT_UP_M) >> QDRV_MUC_STATS_DAGC_SHIFT_UP_S; |
| int dagc_shift_down = |
| (rx_gain_fields & QDRV_MUC_STATS_DAGC_SHIFT_DOWN_M) >> QDRV_MUC_STATS_DAGC_SHIFT_DOWN_S; |
| |
| return(dagc_shift_up - dagc_shift_down); |
| } |
| |
| /* |
| * Display RSSI/EVM stats of the node with the most packets |
| */ |
| static struct ieee80211_node *qtn_muc_stats_get_node(struct ieee80211com *ic, int mu) |
| { |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| struct ieee80211_node *ni; |
| struct ieee80211_node *found = NULL; |
| unsigned long max_pkts = 0; |
| |
| IEEE80211_NODE_LOCK_IRQ(nt); |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| unsigned long pkts = ni->ni_shared_stats->rx[mu].pkts + ni->ni_shared_stats->tx[mu].pkts; |
| if (pkts > max_pkts || found == NULL) { |
| found = ni; |
| max_pkts = pkts; |
| } |
| } |
| |
| if (found) |
| ieee80211_ref_node(found); |
| |
| IEEE80211_NODE_UNLOCK_IRQ(nt); |
| |
| return found; |
| } |
| |
| static void qtn_muc_stats_fmt_hw_noise(char *buf, const struct qtn_rx_stats *rxstats) |
| { |
| int noise_dbm; |
| int int_val; |
| int fract_val; |
| |
| if (rxstats->hw_noise != MIN_RCPI_VALUE) { |
| noise_dbm = rxstats->hw_noise; |
| int_val = noise_dbm / 10; |
| fract_val = ABS(noise_dbm) % 10; |
| |
| sprintf(buf, "%4d.%d", int_val, fract_val); |
| } else { |
| strcpy(buf, " -inf"); |
| } |
| } |
| |
| static const char g_header_line[] = KERN_DEBUG "Tstamp"" SU/MU"" RxMPDU"" AMSDU"" RxG"" CRC"" Noise"" TxFrame"" Defers"" Touts"" Retries"" ShPmbl"" LgPmbl"" Scale" " " " MCS-(TX/RX)"" RSSI / RCPI / EVM\n"; |
| static const char g_stat_comm_fmt[] = KERN_DEBUG "%6d" "%6s" "%7d" "%6d" "%4d" "%6d" "%7s" "%8d" "%7d" "%6d" "%8d" "%7d" "%8d" "%6d" " "; |
| static const char g_stat_comm_csv_fmt[] = "%d," "%s," "%d," "%d," "%d," "%d," "%s," "%d," "%d," "%d," "%d," "%d," "%d," "%d,"; |
| |
| static void qtn_muc_stats_display_common(const struct qtn_stats *stats, |
| int mu, int csv_format) |
| { |
| const struct qtn_rx_stats *rxstats[] = {&stats->rx_phy_stats, &stats->mu_rx_phy_stats}; |
| const struct qtn_tx_stats *txstats[] = {&stats->tx_phy_stats, &stats->mu_tx_phy_stats}; |
| |
| char noise_strs[QDRV_MUC_STATS_DB_VALUE_LENGTH]; |
| |
| qtn_muc_stats_fmt_hw_noise(noise_strs, rxstats[mu]); |
| |
| printk(csv_format ? g_stat_comm_csv_fmt : g_stat_comm_fmt, |
| stats->tstamp, |
| (mu) ? "MU" : "SU", |
| rxstats[mu]->num_pkts, |
| rxstats[mu]->num_amsdu, |
| rxstats[mu]->avg_rxgain, |
| rxstats[mu]->cnt_mac_crc, |
| noise_strs, |
| txstats[mu]->num_pkts, |
| txstats[mu]->num_defers, |
| txstats[mu]->num_timeouts, |
| txstats[mu]->num_retries, |
| rxstats[mu]->cnt_sp_fail, |
| rxstats[mu]->cnt_lp_fail, |
| txstats[mu]->last_tx_scale); |
| } |
| |
| static void qtn_muc_stats_display_rssi_only(struct ieee80211_node *ni, int mu, int csv_format) |
| { |
| int iter; |
| char db_strs[NUM_ANT + 1][QDRV_MUC_STATS_DB_VALUE_LENGTH]; |
| const struct qtn_node_shared_stats_rx *node_rxstats = &ni->ni_shared_stats->rx[mu]; |
| const struct qtn_node_shared_stats_tx *node_txstats= &ni->ni_shared_stats->tx[mu]; |
| |
| /* RSSIs in dBM (units are actually 0.1 dBM) ... */ |
| for (iter = 0; iter <= NUM_ANT; iter++) { |
| int rssi_dbm = node_rxstats->last_rssi_dbm[iter]; |
| if (rssi_dbm) { |
| int int_val = rssi_dbm / 10; |
| int fract_val = ABS(rssi_dbm) % 10; |
| snprintf(&db_strs[iter][0], |
| sizeof( db_strs[iter] ), |
| "%d.%d", |
| int_val, |
| fract_val); |
| } else { |
| strcpy(&db_strs[iter][0], "-inf"); |
| } |
| } |
| |
| #ifdef PHY_STATS_PHY_DATE_RATE |
| #define STAT_RSSI_FMT "%4dM %4dM %4s %4s %4s %4s dBm %4s avg RSSI\n" |
| #define STAT_RSSI_CSV_FMT "%d,%d,%s,%s,%s,%s,%s," |
| printk(csv_format ? STAT_RSSI_CSV_FMT : STAT_RSSI_FMT, |
| MS(node_txstats->last_mcs, QTN_PHY_STATS_MCS_PHYRATE), |
| MS(node_rxstats->last_mcs, QTN_PHY_STATS_MCS_PHYRATE), |
| db_strs[0], |
| db_strs[1], |
| db_strs[2], |
| db_strs[3], |
| db_strs[4]); |
| #undef STAT_RSSI_FMT |
| #undef STAT_RSSI_CSV_FMT |
| #else |
| unsigned int tx_mcs; |
| unsigned int rx_mcs; |
| rx_mcs = (node_rxstats->last_mcs & QTN_STATS_MCS_RATE_MASK) + |
| MS(node_rxstats->last_mcs, QTN_PHY_STATS_MCS_NSS) * 100; |
| tx_mcs = (node_txstats->last_mcs & QTN_STATS_MCS_RATE_MASK) + |
| MS(node_txstats->last_mcs, QTN_PHY_STATS_MCS_NSS) * 100; |
| |
| printk("%5d %5d %4s %4s %4s %4s dBm %4s avg RSSI\n", |
| tx_mcs, |
| rx_mcs, |
| db_strs[0], |
| db_strs[1], |
| db_strs[2], |
| db_strs[3], |
| db_strs[4]); |
| |
| #endif |
| } |
| |
| static void qtn_muc_stats_display_rcpi_only(struct ieee80211_node *ni, int mu) |
| { |
| int i; |
| char db_strs[NUM_ANT + 1][QDRV_MUC_STATS_DB_VALUE_LENGTH]; |
| |
| const struct qtn_node_shared_stats_rx *node_rxstats = &ni->ni_shared_stats->rx[mu]; |
| const struct qtn_node_shared_stats_tx *node_txstats= &ni->ni_shared_stats->tx[mu]; |
| unsigned int tx_mcs; |
| unsigned int rx_mcs; |
| |
| /* RSSIs in dBM (units are actually 0.1 dBM) ... */ |
| for (i = 0; i <= NUM_ANT; i++) { |
| int rcpi_dbm = node_rxstats->last_rcpi_dbm[i]; |
| if (rcpi_dbm) { |
| int int_val = rcpi_dbm / 10; |
| int fract_val = ABS(rcpi_dbm) % 10; |
| |
| snprintf(db_strs[i], |
| sizeof(db_strs[i]), |
| "%d.%d", |
| int_val, |
| fract_val); |
| } else { |
| strcpy(db_strs[i], "-inf"); |
| } |
| } |
| |
| rx_mcs = (node_rxstats->last_mcs & QTN_STATS_MCS_RATE_MASK) + |
| MS(node_rxstats->last_mcs, QTN_PHY_STATS_MCS_NSS) * 100; |
| tx_mcs = (node_txstats->last_mcs & QTN_STATS_MCS_RATE_MASK) + |
| MS(node_txstats->last_mcs, QTN_PHY_STATS_MCS_NSS) * 100; |
| |
| printk("%5d %5d %4s %4s %4s %4s dBm %4s max RCPI\n", |
| tx_mcs, |
| rx_mcs, |
| db_strs[0], |
| db_strs[1], |
| db_strs[2], |
| db_strs[3], |
| db_strs[4]); |
| } |
| |
| static void qtn_muc_stats_display_evm_only(struct ieee80211_node *ni, int mu, int csv_format) |
| { |
| char evm_strs[NUM_ANT+1][QDRV_MUC_STATS_DB_VALUE_LENGTH]; |
| int i; |
| int evm_int; |
| int evm_fract; |
| unsigned int tx_mcs; |
| unsigned int rx_mcs; |
| |
| const struct qtn_node_shared_stats_rx *node_rxstats = &ni->ni_shared_stats->rx[mu]; |
| const struct qtn_node_shared_stats_tx *node_txstats = &ni->ni_shared_stats->tx[mu]; |
| |
| for (i = 0; i <= NUM_ANT; i++) { |
| int v; |
| |
| v = node_rxstats->last_evm_dbm[i]; |
| |
| snprintf(evm_strs[i], |
| sizeof(evm_strs[i]), |
| "%d.%d", |
| v / 10, |
| ABS(v) % 10); |
| } |
| |
| evm_int = node_rxstats->last_evm_dbm[NUM_ANT] / 10; |
| evm_fract = ABS(node_rxstats->last_evm_dbm[NUM_ANT]) % 10; |
| |
| #ifdef PHY_STATS_SUM_EVM // doing the dB summation |
| int evm_sum=0; |
| |
| for (i = 0; i < NUM_ANT; i++) { |
| evm_sum += node_rxstats->last_evm_dbm[i]; |
| } |
| |
| evm_int = (int) evm_sum / 10; |
| evm_fract = (int) (ABS(evm_sum) % 10); |
| #endif |
| |
| snprintf(&evm_strs[NUM_ANT][0], |
| sizeof(evm_strs[NUM_ANT]), |
| "%d.%d", |
| evm_int, |
| evm_fract); |
| |
| rx_mcs = (node_rxstats->last_mcs & QTN_STATS_MCS_RATE_MASK) + |
| MS(node_rxstats->last_mcs, QTN_PHY_STATS_MCS_NSS) * 100; |
| tx_mcs = (node_txstats->last_mcs & QTN_STATS_MCS_RATE_MASK) + |
| MS(node_txstats->last_mcs, QTN_PHY_STATS_MCS_NSS) * 100; |
| |
| #define STAT_EVM_FMT "%5d %5d %3d %4s %4s %4s %4s dB %4s avg EVM\n" |
| #define STAT_EVM_CSV_FMT "%d,%d,%d,%s,%s,%s,%s,%s," |
| printk(csv_format ? STAT_EVM_CSV_FMT : STAT_EVM_FMT, |
| tx_mcs, |
| rx_mcs, |
| node_rxstats->last_rxsym, |
| &evm_strs[0][0], |
| &evm_strs[1][0], |
| &evm_strs[2][0], |
| &evm_strs[3][0], |
| &evm_strs[4][0]); |
| #undef STAT_EVM_FMT |
| #undef STAT_EVM_CSV_FMT |
| } |
| |
| static void display_log_node_info(struct ieee80211_node *ni, |
| enum qdrv_muc_stats_display_choice display_choice, int mu, int csv_format) |
| { |
| switch (display_choice) { |
| case QDRV_MUC_STATS_SHOW_RCPI: |
| qtn_muc_stats_display_rcpi_only(ni, mu); |
| break; |
| case QDRV_MUC_STATS_SHOW_EVM: |
| qtn_muc_stats_display_evm_only(ni, mu, csv_format); |
| break; |
| case QDRV_MUC_STATS_SHOW_RSSI: |
| default: |
| qtn_muc_stats_display_rssi_only(ni, mu, csv_format); |
| break; |
| } |
| } |
| |
| static void display_log_info(const struct qtn_stats *stats, struct ieee80211com *ic, enum muc_stats_opt opt, |
| int show_all_nodes, int csv_format) |
| { |
| struct ieee80211_node *main_ni; |
| enum qdrv_muc_stats_display_choice display_choice; |
| |
| static int start[] = {STATS_MIN, STATS_MIN, STATS_SU, STATS_MU }; |
| static int stop[] = {STATS_MAX, STATS_MAX, STATS_MU, STATS_MAX}; |
| int mu; |
| int ret = 1; |
| |
| display_choice = qdrv_muc_stats_get_display_choice(stats, ic); |
| |
| for (mu = start[opt]; mu < stop[opt]; mu++) { |
| main_ni = qtn_muc_stats_get_node(ic, mu); |
| |
| if (unlikely(main_ni == NULL)) { |
| continue; |
| } |
| ret = 0; |
| qtn_muc_stats_display_common(stats, mu, csv_format); |
| if (csv_format) { |
| display_log_node_info(main_ni, QDRV_MUC_STATS_SHOW_RSSI, mu, csv_format); |
| display_log_node_info(main_ni, QDRV_MUC_STATS_SHOW_EVM, mu, csv_format); |
| } else { |
| display_log_node_info(main_ni, display_choice, mu, csv_format); |
| } |
| ieee80211_free_node(main_ni); |
| } |
| |
| if (ret) goto exit; |
| |
| if (show_all_nodes) { |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| struct ieee80211_node *ni; |
| |
| IEEE80211_NODE_LOCK_IRQ(nt); |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| for (mu = start[opt]; mu < stop[opt]; mu++) { |
| #define STAT_ALL_FMT "\t\t%s\tNode " DBGMACVAR "\trx_mpdu %u tx_frame %u\t" |
| #define STAT_ALL_CSV_FMT "%s," DBGMACVAR ",%u,%u," |
| printk(csv_format ? STAT_ALL_CSV_FMT : KERN_DEBUG STAT_ALL_FMT, |
| (mu == STATS_SU) ? "SU" : "MU", |
| DBGMACFMT(ni->ni_macaddr), |
| ni->ni_shared_stats->rx[mu].pkts, |
| ni->ni_shared_stats->tx[mu].pkts); |
| if (csv_format) { |
| display_log_node_info(ni, QDRV_MUC_STATS_SHOW_RSSI, mu, csv_format); |
| display_log_node_info(ni, QDRV_MUC_STATS_SHOW_EVM, mu, csv_format); |
| } else { |
| display_log_node_info(ni, display_choice, mu, csv_format); |
| } |
| #undef STAT_ALL_FMT |
| #undef STAT_ALL_CSV_FMT |
| } |
| } |
| IEEE80211_NODE_UNLOCK_IRQ(nt); |
| } |
| exit: |
| return; |
| } |
| |
| #define COMMON_CSV_HEADER(x) "Tstamp_"#x","#x",RxPkt_"#x",AMSDU_"#x",RxG_"#x",CRC_"#x",Noise_"#x",TxPkt_"#x",Defers_"#x",Touts_"#x",Retries_"#x",ShPmbl_"#x",LgPmbl_"#x",Scale_"#x"," |
| |
| #define RSSI_AND_EVM_CSV_HEADER(x) "PHY_RATE_TX_"#x"(M),PHY_RATE_RX_"#x"(M),"\ |
| "RSSI0_"#x"(dBm),RSSI1_"#x"(dBm),RSSI2_"#x"(dBm),RSSI3_"#x"(dBm),RSSV_AVR_"#x"(dBm),"\ |
| "MCS-TX_"#x",MCS-RX_"#x",RxSym_"#x","\ |
| "EVM0_"#x"(dB),EVM1_"#x"(dB),EVM2_"#x"(dB),EVM3_"#x"(dB),EVM_SUM_"#x"(dB)," |
| |
| #define FULL_CSV_HEADER(x) COMMON_CSV_HEADER(x)RSSI_AND_EVM_CSV_HEADER(x) |
| |
| static void display_log_hdr(struct ieee80211com *ic, enum muc_stats_opt opt, |
| int show_all_nodes, int csv_format) |
| { |
| if (!csv_format) { |
| printk(g_header_line); |
| } else { |
| /* New line */ |
| printk(KERN_DEBUG ""); |
| /* Comma to separate linux time stamp */ |
| printk(","STAT_CSV_TAG","); |
| |
| if (opt == MSO_BOTH || opt == MSO_SU) { |
| printk(FULL_CSV_HEADER(SU)); |
| } |
| if (opt == MSO_BOTH || opt == MSO_MU) { |
| printk(FULL_CSV_HEADER(MU)); |
| } |
| if (show_all_nodes) { |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| struct ieee80211_node *ni; |
| int i = 0; |
| |
| IEEE80211_NODE_LOCK_IRQ(nt); |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| if (opt == MSO_BOTH || opt == MSO_SU) { |
| printk("SU%u,MAC_SU%u,rxpkts_SU%u,txpkts_SU%u,", i, i, i, i); |
| printk("PHY_RATE_TX_SU%u(M),PHY_RATE_RX_SU%u(M),RSSI0_SU%u(dBm),RSSI1_SU%u(dBm),RSSI2_SU%u(dBm),RSSI3_SU%u(dBm)," |
| "RSSV_AVR_SU%u(dBm),MCS-TX_SU%u,MCS-RX_SU%u,RxSym_SU%u,EVM0_SU%u(dB),EVM1_SU%u(dB),EVM2_SU%u(dB)," |
| "EVM3_SU%u(dB),EVM_SUM_SU%u(dB),", i, i, i, i, i, i, i, i, i, i, i, i, i, i, i); |
| } |
| if (opt == MSO_BOTH || opt == MSO_MU) { |
| printk("MU%u,MAC_MU%u,rxpkts_MU%u,txpkts_MU%u,", i, i, i, i); |
| printk("PHY_RATE_TX_MU%u(M),PHY_RATE_RX_MU%u(M),RSSI0_MU%u(dBm),RSSI1_MU%u(dBm),RSSI2_MU%u(dBm),RSSI3_MU%u(dBm)," |
| "RSSV_AVR_MU%u(dBm),MCS-TX_MU%u,MCS-RX_MU%u,RxSym_MU%u,EVM0_MU%u(dB),EVM1_MU%u(dB),EVM2_MU%u(dB)," |
| "EVM3_MU%u(dB),EVM_SUM_MU%u(dB),", i, i, i, i, i, i, i, i, i, i, i, i, i, i, i); |
| } |
| i++; |
| } |
| IEEE80211_NODE_UNLOCK_IRQ(nt); |
| } |
| } |
| } |
| |
| /* |
| * Possible values for required_phy_stat_mode are defined in include/qtn/muc_phy_stats.h |
| * They define the possible phy stats mode in the ieee80211com. As the parameter |
| * required_phy_stat_mode the meaning of the values is: |
| * |
| * MUC_PHY_STATS_ALTERNATE - any block will do, so return the latest |
| * MUC_PHY_STATS_RSSI_RCPI_ONLY - block must have RSSIs and RCPI, not errored sums |
| * MUC_PHY_STATS_ERROR_SUM_ONLY - block must have errored sums, not RSSIs or RCPIs. |
| */ |
| static int qtn_muc_stats_does_stat_have_required(struct qtn_stats *curr_log_ptr, |
| int cur_phy_stats_mode, |
| int required_phy_stat_mode) |
| { |
| int retval = 0; |
| |
| if (required_phy_stat_mode == MUC_PHY_STATS_ALTERNATE) { |
| retval = 1; |
| } else { |
| int block_has_rssi = qtn_select_rssi_over_error_sums(curr_log_ptr->tstamp, |
| cur_phy_stats_mode); |
| |
| if (block_has_rssi != 0) { |
| retval = (required_phy_stat_mode == MUC_PHY_STATS_RSSI_RCPI_ONLY); |
| } else { |
| retval = (required_phy_stat_mode == MUC_PHY_STATS_ERROR_SUM_ONLY); |
| } |
| } |
| |
| return(retval); |
| } |
| |
| /* |
| * If required_phy_stat_mode is MUC_PHY_STATS_RSSI_RCPI_ONLY or MUC_PHY_STATS_ERROR_SUM_ONLY, |
| * none of the blocks may have what is required (based on the current phy stats mode in the |
| * ieee80211com). If so, return NULL. |
| */ |
| struct qtn_stats *qtn_muc_stats_get_addr_latest_stats(struct qdrv_mac *mac, |
| const struct ieee80211com *ic, |
| int required_phy_stat_mode) |
| { |
| struct qtn_stats_log *log = NULL; |
| struct qtn_stats *curr_log_ptr = NULL; |
| struct qtn_stats *retaddr = NULL; |
| int cr_indx; |
| unsigned int latest_tstamp = 0; |
| int cur_phy_stats_mode; |
| |
| if (mac == NULL || ic == NULL) { |
| return NULL; |
| } |
| |
| cur_phy_stats_mode = ic->ic_mode_get_phy_stats; |
| |
| log = (struct qtn_stats_log *)mac->mac_sys_stats; |
| if (log == NULL) { |
| return(NULL); |
| } else { |
| int required_phy_stats_available = 1; |
| |
| if (required_phy_stat_mode == MUC_PHY_STATS_ERROR_SUM_ONLY && |
| cur_phy_stats_mode == MUC_PHY_STATS_RSSI_RCPI_ONLY) { |
| required_phy_stats_available = 0; |
| } else if (required_phy_stat_mode == MUC_PHY_STATS_RSSI_RCPI_ONLY && |
| cur_phy_stats_mode == MUC_PHY_STATS_ERROR_SUM_ONLY) { |
| required_phy_stats_available = 0; |
| } |
| |
| if (required_phy_stats_available == 0) { |
| return(NULL); |
| } |
| } |
| |
| cr_indx = MOD(log->curr_buff + 2, NUM_LOG_BUFFS); |
| curr_log_ptr = &log->stat_buffs[cr_indx]; |
| |
| while (cr_indx != log->curr_buff) { |
| if (curr_log_ptr->tstamp > latest_tstamp && |
| qtn_muc_stats_does_stat_have_required(curr_log_ptr, |
| cur_phy_stats_mode, |
| required_phy_stat_mode)) { |
| latest_tstamp = curr_log_ptr->tstamp; |
| retaddr = curr_log_ptr; |
| } |
| |
| cr_indx = MOD(cr_indx + 1, NUM_LOG_BUFFS); |
| curr_log_ptr = &log->stat_buffs[cr_indx]; |
| } |
| |
| return(retaddr); |
| } |
| |
| /* |
| * Parse log is subtly different from get address latest stats. |
| * |
| * Parse Log reports ALL stats that have not been previously reported; |
| * thus it uses a file-scope variable "last_tstamp" to track which |
| * stats have not been reported. If called repeatedly between MUC |
| * updates it returns without reporting anything. |
| * |
| * Get Address Latest Stats returns the address of the stats with the |
| * latest time stamp. It can thus be repeatedly called between updates |
| * from the MUC, with each call in this situation returning the address |
| * of the same qtn_stats. |
| */ |
| |
| static void parse_log(struct qtn_stats_log *log, struct ieee80211com *ic, enum muc_stats_opt opt, |
| int show_all_nodes, int csv_format) |
| { |
| struct qtn_stats *curr_log_ptr; |
| int cr_indx; |
| |
| cr_indx = MOD(log->curr_buff + 2, NUM_LOG_BUFFS); |
| |
| while(cr_indx != log->curr_buff) { |
| curr_log_ptr = &log->stat_buffs[cr_indx]; |
| cr_indx = MOD(cr_indx+1, NUM_LOG_BUFFS); |
| |
| if(curr_log_ptr->tstamp <= last_tstamp) continue; |
| |
| if (csv_format) printk("\n,"STAT_CSV_TAG","); |
| |
| last_tstamp = curr_log_ptr->tstamp; |
| display_log_info(curr_log_ptr, ic, opt, show_all_nodes, csv_format); |
| } |
| } |
| |
| #if 0 |
| static void dump_log(struct qtn_stats_log *log) |
| { |
| |
| int cr_indx = 0; |
| |
| printk("Current Log indx %d\n", log->curr_buff); |
| |
| for(cr_indx=0;cr_indx < NUM_LOG_BUFFS; cr_indx++){ |
| printk("Indx %d, Tstamp %d\n",cr_indx,log->stat_buffs[cr_indx].tstamp); |
| } |
| } |
| #endif |
| |
| static void copy_log_to_local(u32 *muc_log_addr,struct qtn_stats_log *log) |
| { |
| |
| memcpy(log,muc_log_addr,sizeof(struct qtn_stats_log)); |
| } |
| |
| int qdrv_muc_stats_printlog(const struct qdrv_cb *data, |
| struct qdrv_mac *mac, |
| struct ieee80211com *ic, |
| int argc, |
| char **argv) |
| { |
| static int cnt = 0; |
| enum muc_stats_opt opt = MSO_BOTH; |
| int show_all_nodes = 0; |
| int csv_format = 0; |
| int title_only = 0; |
| int i; |
| |
| if (mac->mac_sys_stats == NULL) { |
| printk(KERN_DEBUG "No MuC stats available\n"); |
| return 0; |
| } |
| |
| for (i = 0; i < argc; i++) { |
| if (strcmp(argv[i], "both") == 0) { |
| opt = MSO_BOTH; |
| } else if (strcmp(argv[i], "su") == 0) { |
| opt = MSO_SU; |
| } else if (strcmp(argv[i], "mu") == 0) { |
| if (!ic->ic_mu_enable) { |
| printk("MU not enabled\n"); |
| return 0; |
| } |
| opt = MSO_MU; |
| } else if (strcmp(argv[i], "all") == 0) { |
| show_all_nodes = 1; |
| } else if (strcmp(argv[i], "csv") == 0) { |
| csv_format = 1; |
| } else if (strcmp(argv[i], "title") == 0) { |
| title_only = 1; |
| } |
| } |
| |
| if (opt == MSO_BOTH && !ic->ic_mu_enable) { |
| opt = MSO_SU; |
| } |
| |
| if((!csv_format && !(cnt++ & 0x3)) || (csv_format && title_only)) { |
| /* Display header periodically */ |
| display_log_hdr(ic, opt, show_all_nodes, csv_format); |
| } |
| |
| if (title_only) goto exit; |
| |
| copy_log_to_local((u32*)mac->mac_sys_stats, &host_log); |
| parse_log(&host_log, ic, opt, show_all_nodes, csv_format); |
| |
| exit: |
| return(0); |
| } |
| |
| /* |
| * These functions need to be prepared for qtn_muc_stats_get_addr_latest_stats returning NULL. |
| * |
| * Currently they all return -1 to signal Failed to Get Requested Value. |
| */ |
| int qdrv_muc_get_noise(struct qdrv_mac *mac, const struct ieee80211com *ic) |
| { |
| int retval = -1; |
| struct qtn_stats *address_current_log = qtn_muc_stats_get_addr_latest_stats( |
| mac, ic, MUC_PHY_STATS_ALTERNATE); |
| static uint32_t prev_hw_noise = 0; |
| |
| if (address_current_log != NULL) { |
| if (address_current_log->rx_phy_stats.hw_noise > 0) |
| prev_hw_noise = address_current_log->rx_phy_stats.hw_noise; |
| retval = prev_hw_noise; |
| } |
| |
| return(retval); |
| } |
| |
| int qdrv_muc_get_rssi_by_chain(struct qdrv_mac *mac, const struct ieee80211com *ic, unsigned int rf_chain) |
| { |
| int retval = -1; |
| struct qtn_stats *address_current_log = qtn_muc_stats_get_addr_latest_stats( |
| mac, ic, MUC_PHY_STATS_RSSI_RCPI_ONLY); |
| |
| if (rf_chain >= NUM_ANT) { |
| rf_chain = NUM_ANT - 1; |
| } |
| |
| if (address_current_log != NULL) { |
| retval = address_current_log->rx_phy_stats.last_rssi_evm[rf_chain]; |
| } |
| |
| return(retval); |
| } |
| |
| u_int32_t qdrv_muc_get_rx_gain_fields(struct qdrv_mac *mac, const struct ieee80211com *ic) |
| { |
| u_int32_t rx_gain_fields = (u_int32_t) -1; |
| struct qtn_stats *address_current_log = qtn_muc_stats_get_addr_latest_stats(mac, |
| ic, |
| MUC_PHY_STATS_RSSI_RCPI_ONLY); |
| |
| if (address_current_log != NULL) { |
| int8_t dagc_shift = 0; |
| |
| rx_gain_fields = address_current_log->rx_phy_stats.rx_gain_fields; |
| dagc_shift = (int8_t) qtn_muc_stats_get_dagc_shift_count(rx_gain_fields); |
| |
| rx_gain_fields = (rx_gain_fields & ~QDRV_MUC_STATS_DAGC_SHIFT_FIELD_M); |
| rx_gain_fields = (rx_gain_fields | |
| ((dagc_shift << QDRV_MUC_STATS_DAGC_SHIFT_FIELD_S) & QDRV_MUC_STATS_DAGC_SHIFT_FIELD_M)); |
| } |
| |
| return(rx_gain_fields); |
| } |
| |
| int qdrv_muc_get_phy_stat(struct qdrv_mac *mac, |
| const struct ieee80211com *ic, |
| const char *name_of_stat, |
| const unsigned int array_index, |
| int *stat_value) |
| { |
| const struct { |
| char *stat_param_name; |
| enum qtn_phy_stat_field stat_param_field; |
| int stat_phy_stat_mode; |
| } stat_param_table[] = { |
| { QTN_PHY_AVG_ERROR_SUM_NSYM_NAME, |
| QTN_PHY_AVG_ERROR_SUM_NSYM_FIELD, |
| MUC_PHY_STATS_ERROR_SUM_ONLY }, |
| }; |
| |
| int iter; |
| enum qtn_phy_stat_field param_field = QTN_PHY_NOSUCH_FIELD; |
| int required_phy_stat_mode = MUC_PHY_STATS_ALTERNATE; |
| struct qtn_stats *address_current_log = NULL; |
| int retval = 0; |
| |
| for (iter = 0; iter < ARRAY_SIZE(stat_param_table); iter++) { |
| if (strcmp(name_of_stat, stat_param_table[iter].stat_param_name) == 0) { |
| param_field = stat_param_table[iter].stat_param_field; |
| required_phy_stat_mode = stat_param_table[iter].stat_phy_stat_mode; |
| break; |
| } |
| } |
| |
| if (param_field == QTN_PHY_NOSUCH_FIELD) { |
| #if 0 |
| DPRINTF(LL_1, LF_ERROR, |
| (DBGEFMT "Unknown field %s in get phy_stat.\n", DBGARG, name_of_stat)); |
| #endif |
| return -1; |
| } |
| |
| address_current_log = qtn_muc_stats_get_addr_latest_stats(mac, |
| ic, |
| required_phy_stat_mode); |
| if (address_current_log == NULL) { |
| #if 0 |
| DPRINTF(LL_1, LF_ERROR, |
| (DBGEFMT "Incorrect phy stat mode in get phy_stat for %s.\n", DBGARG, name_of_stat)); |
| #endif |
| return -1; |
| } |
| |
| switch (param_field) { |
| case QTN_PHY_AVG_ERROR_SUM_NSYM_FIELD: |
| { |
| int sum_snr = 0; |
| int i; |
| |
| for (i = 0; i < NUM_ANT; i++) { |
| sum_snr += address_current_log->rx_phy_stats.last_rssi_evm[i]; |
| } |
| |
| if (sum_snr < 0) { |
| sum_snr = (sum_snr - 5) / 10; |
| } else { |
| sum_snr = (sum_snr + 5) / 10; |
| } |
| |
| *stat_value = (0 - sum_snr); |
| } |
| break; |
| |
| default: |
| #if 0 |
| DPRINTF(LL_1, LF_ERROR, |
| (DBGEFMT "No support for %s in get phy_stat.\n", DBGARG, name_of_stat)); |
| #endif |
| retval = -1; |
| break; |
| } |
| |
| return retval; |
| } |
| |
| int qdrv_muc_stats_rssi(const struct ieee80211_node *ni) |
| { |
| int mu = STATS_SU; |
| return ni->ni_shared_stats->rx[mu].last_rssi_dbm[NUM_ANT]; |
| } |
| |
| int qdrv_muc_stats_smoothed_rssi(const struct ieee80211_node *ni) |
| { |
| int mu = STATS_SU; |
| return ni->ni_shared_stats->rx[mu].rssi_dbm_smoothed[NUM_ANT]; |
| } |
| |
| int qdrv_muc_stats_hw_noise(const struct ieee80211_node *ni) |
| { |
| int mu = STATS_SU; |
| return ni->ni_shared_stats->rx[mu].last_hw_noise[NUM_ANT]; |
| } |
| |
| int qdrv_muc_stats_rxtx_phy_rate(const struct ieee80211_node *ni, const int is_rx, |
| uint8_t *nss, uint8_t *mcs, u_int32_t *phy_rate) |
| { |
| unsigned int last_mcs; |
| int mu = STATS_SU; |
| |
| last_mcs = (is_rx) ? ni->ni_shared_stats->rx[mu].last_mcs : |
| ni->ni_shared_stats->tx[mu].last_mcs; |
| |
| if (nss) |
| *nss = MS(last_mcs, QTN_PHY_STATS_MCS_NSS); |
| if (mcs) |
| *mcs = MS(last_mcs, QTN_STATS_MCS_RATE_MASK); |
| if (phy_rate) |
| *phy_rate = MS(last_mcs, QTN_PHY_STATS_MCS_PHYRATE); |
| |
| return 0; |
| } |
| |
| int qdrv_muc_stats_snr(const struct ieee80211_node *ni) |
| { |
| int sum_snr = 0; |
| int i; |
| const struct qtn_node_shared_stats_rx *rxstats; |
| int mu = STATS_SU; |
| |
| rxstats = &ni->ni_shared_stats->rx[mu]; |
| |
| for (i = 0; i < NUM_ANT; i++) { |
| sum_snr += rxstats->last_evm_dbm[i]; |
| } |
| |
| return sum_snr; |
| } |
| |
| int qdrv_muc_stats_max_queue(const struct ieee80211_node *ni) |
| { |
| int mu = STATS_SU; |
| return ni->ni_shared_stats->tx[mu].max_queue; |
| } |
| |
| ssize_t qdrv_muc_get_size_rssi_phy_stats(void) |
| { |
| return sizeof(struct qtn_stats); |
| } |
| |
| u_int32_t qdrv_muc_stats_tx_failed(const struct ieee80211_node *ni) |
| { |
| int mu = STATS_SU; |
| return ni->ni_shared_stats->tx[mu].txdone_failed_cum; |
| } |
| |
| static void qdrv_muc_stats_get_evm(struct ieee80211_phy_stats *ps, |
| const struct qtn_node_shared_stats_rx *node_rxstats) |
| { |
| int iter; |
| int v; |
| |
| for (iter = 0; iter <= NUM_ANT; iter++) { |
| v = node_rxstats->last_evm_dbm[iter]; |
| |
| if (iter < NUM_ANT) { |
| ps->last_evm_array[iter] = v; |
| } else { |
| ps->last_evm = v; |
| } |
| } |
| |
| #ifdef PHY_STATS_SUM_EVM |
| ps->last_evm = 0; |
| for (iter = 0; iter < NUM_ANT; iter++) { |
| ps->last_evm += node_rxstats->last_evm_dbm[iter]; |
| } |
| #endif |
| } |
| |
| static void qdrv_muc_stats_get_rssi(struct ieee80211_phy_stats *ps, |
| const struct qtn_node_shared_stats_rx *node_rxstats) |
| { |
| int iter; |
| int rssi_dbm; |
| |
| for (iter = 0; iter <= NUM_ANT; iter++) { |
| rssi_dbm = node_rxstats->last_rssi_dbm[iter]; |
| if (rssi_dbm) { |
| if (iter == NUM_ANT) { |
| ps->last_rssi = rssi_dbm; |
| } else { |
| ps->last_rssi_array[iter] = rssi_dbm; |
| } |
| } else { |
| if (iter == NUM_ANT) { |
| ps->last_rssi = 0; |
| } else { |
| ps->last_rssi_array[iter] = 0; |
| } |
| } |
| } |
| } |
| |
| void qdrv_muc_update_missing_stats(struct qdrv_mac *mac, |
| struct ieee80211com *ic, |
| struct ieee80211_phy_stats *ps, |
| enum qdrv_muc_stats_display_choice missing_type) |
| { |
| struct qtn_stats *prev_stats = NULL; |
| struct qtn_rx_stats *prev_rx_stats = NULL; |
| int iter; |
| |
| if (missing_type == QDRV_MUC_STATS_SHOW_RSSI) { |
| prev_stats = qtn_muc_stats_get_addr_latest_stats(mac, |
| ic, MUC_PHY_STATS_RSSI_RCPI_ONLY); |
| if (prev_stats == NULL) { |
| return; |
| } |
| prev_rx_stats = &prev_stats->rx_phy_stats; |
| |
| memcpy(ps->last_rssi_array, prev_rx_stats->last_rssi_evm, |
| sizeof(prev_rx_stats->last_rssi_evm)); |
| } else { |
| prev_stats = qtn_muc_stats_get_addr_latest_stats(mac, |
| ic, MUC_PHY_STATS_ERROR_SUM_ONLY); |
| if (prev_stats == NULL) { |
| return; |
| } |
| prev_rx_stats = &prev_stats->rx_phy_stats; |
| |
| memcpy(ps->last_evm_array, prev_rx_stats->last_rssi_evm, |
| sizeof(prev_rx_stats->last_rssi_evm)); |
| |
| #ifdef PHY_STATS_SUM_EVM |
| ps->last_evm = 0; |
| for (iter = 0; iter < NUM_ANT; iter++) { |
| ps->last_evm += ps->last_evm_array[iter]; |
| } |
| #endif |
| } |
| } |
| |
| int qdrv_muc_get_last_phy_stats(struct qdrv_mac *mac, |
| struct ieee80211com *ic, |
| struct ieee80211_phy_stats *ps, |
| uint8_t all_stats) |
| { |
| struct qtn_stats *stats; |
| struct qtn_rx_stats *rx_stats = NULL; |
| struct qtn_tx_stats *tx_stats = NULL; |
| enum qdrv_muc_stats_display_choice display_choice = QDRV_MUC_STATS_SHOW_RSSI; |
| enum qdrv_muc_stats_display_choice missing_type = QDRV_MUC_STATS_SHOW_EVM; |
| struct shared_params *sp = qtn_mproc_sync_shared_params_get(); |
| struct qtn_scs_info_set *scs_info_lh = sp->scs_info_lhost; |
| int txpower; |
| int rssi = SCS_RSSI_UNINITED; |
| int mu = STATS_SU; |
| uint32_t cca_try = scs_info_lh->scs_info[scs_info_lh->valid_index].cca_try; |
| uint32_t cca_tx = scs_info_lh->scs_info[scs_info_lh->valid_index].cca_tx; |
| uint32_t rx_usecs = scs_info_lh->scs_info[scs_info_lh->valid_index].rx_usecs; |
| uint32_t cca_interference = scs_info_lh->scs_info[scs_info_lh->valid_index].cca_interference; |
| uint32_t cca_idle = scs_info_lh->scs_info[scs_info_lh->valid_index].cca_idle; |
| |
| struct ieee80211_node *ni; |
| const struct qtn_node_shared_stats_rx *node_rxstats; |
| const struct qtn_node_shared_stats_tx *node_txstats; |
| |
| DBGPRINTF(DBG_LL_ERR, QDRV_LF_TRACE, "-->Enter\n"); |
| |
| if (ps == NULL) |
| return -1; |
| |
| memset(ps, 0, sizeof(struct ieee80211_phy_stats)); |
| |
| if (unlikely(mac == NULL || mac->data ==NULL || ic == NULL || scs_info_lh == NULL)) { |
| return -1; |
| } |
| |
| ni = qtn_muc_stats_get_node(ic, mu); |
| |
| if (unlikely(ni == NULL)) { |
| return -1; |
| } |
| |
| stats = qtn_muc_stats_get_addr_latest_stats(mac, ic, MUC_PHY_STATS_ALTERNATE); |
| if (unlikely(stats == NULL)) { |
| ieee80211_free_node(ni); |
| return -1; |
| } |
| |
| rx_stats = &stats->rx_phy_stats; |
| tx_stats = &stats->tx_phy_stats; |
| |
| display_choice = qdrv_muc_stats_get_display_choice(stats, ic); |
| |
| if (ic->ic_opmode & IEEE80211_M_STA) { |
| struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); |
| if (unlikely(vap == NULL)) { |
| ieee80211_free_node(ni); |
| return -1; |
| } |
| if (vap->iv_state & IEEE80211_S_RUN) |
| ps->assoc = 1; |
| else |
| ps->assoc = 0; |
| } else |
| ps->assoc = ic->ic_sta_assoc; |
| |
| node_rxstats = &ni->ni_shared_stats->rx[mu]; |
| node_txstats = &ni->ni_shared_stats->tx[mu]; |
| |
| ps->tstamp = stats->tstamp; |
| |
| if (cca_try) { |
| ps->cca_tx = cca_tx * 1000 / cca_try; |
| ps->cca_rx = rx_usecs / cca_try; |
| ps->cca_int = cca_interference * 1000 / cca_try; |
| ps->cca_idle = cca_idle * 1000 / cca_try; |
| ps->cca_total = ps->cca_tx + ps->cca_rx + ps->cca_int + ps->cca_idle; |
| } |
| |
| ps->rx_pkts = rx_stats->num_pkts; |
| ps->rx_gain = rx_stats->avg_rxgain; |
| ps->rx_cnt_crc = rx_stats->cnt_mac_crc; |
| ps->rx_noise = rx_stats->hw_noise; |
| ps->tx_pkts = tx_stats->num_pkts; |
| ps->tx_defers = tx_stats->num_defers; |
| ps->tx_touts = tx_stats->num_timeouts; |
| ps->tx_retries = tx_stats->num_retries; |
| ps->cnt_sp_fail = rx_stats->cnt_sp_fail; |
| ps->cnt_lp_fail = rx_stats->cnt_lp_fail; |
| |
| ps->last_rx_mcs = node_rxstats->last_mcs & QTN_STATS_MCS_RATE_MASK; |
| ps->last_tx_mcs = node_txstats->last_mcs & QTN_STATS_MCS_RATE_MASK; |
| ps->last_tx_scale = node_txstats->last_tx_scale; |
| |
| txpower = ic->ic_curchan->ic_maxpower; |
| |
| if (ic->ic_rssi) |
| rssi = ic->ic_rssi(ni); |
| |
| if (SCS_RSSI_VALID(rssi)) { |
| ps->atten = txpower - rssi / SCS_RSSI_PRECISION_RECIP; |
| } else |
| ps->atten = 0; |
| |
| switch (display_choice) { |
| case QDRV_MUC_STATS_SHOW_EVM: |
| qdrv_muc_stats_get_evm(ps, node_rxstats); |
| missing_type = QDRV_MUC_STATS_SHOW_RSSI; |
| break; |
| case QDRV_MUC_STATS_SHOW_RCPI: |
| ps->last_rcpi = node_rxstats->last_rcpi_dbm[NUM_ANT]; |
| break; |
| case QDRV_MUC_STATS_SHOW_RSSI: |
| default: |
| /* RSSIs in dBM (units are actually 0.1 dBM) ... */ |
| qdrv_muc_stats_get_rssi(ps, node_rxstats); |
| missing_type = QDRV_MUC_STATS_SHOW_EVM; |
| } |
| |
| if (all_stats) { |
| qdrv_muc_update_missing_stats(mac, ic, ps, missing_type); |
| } |
| |
| ieee80211_free_node(ni); |
| |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n"); |
| |
| return 0; |
| } |
| |
| u_int32_t qdrv_muc_stats_tx_airtime(const struct ieee80211_node *ni) |
| { |
| int mu = STATS_SU; |
| return ni->ni_shared_stats->tx[mu].tx_airtime; |
| } |
| |
| u_int32_t qdrv_muc_stats_tx_accum_airtime(const struct ieee80211_node *ni) |
| { |
| int mu = STATS_SU; |
| return ni->ni_shared_stats->tx[mu].tx_accum_airtime; |
| } |
| |
| |
| u_int32_t qdrv_muc_stats_rx_airtime(const struct ieee80211_node *ni) |
| { |
| int mu = STATS_SU; |
| return ni->ni_shared_stats->rx[mu].rx_airtime; |
| } |
| |
| u_int32_t qdrv_muc_stats_rx_accum_airtime(const struct ieee80211_node *ni) |
| { |
| int mu = STATS_SU; |
| return ni->ni_shared_stats->rx[mu].rx_accum_airtime; |
| } |
| |
| int qdrv_muc_get_last_cca_stats(struct qdrv_mac *mac, |
| struct ieee80211com *ic, |
| struct qtn_exp_cca_stats *cs) |
| { |
| DBGPRINTF(DBG_LL_ERR, QDRV_LF_TRACE, "-->Enter\n"); |
| |
| if((cs == NULL) || (mac == NULL)) |
| return -1; |
| |
| memset(cs, 0, sizeof(struct qtn_exp_cca_stats)); |
| if(unlikely(mac->cca_stats == NULL)) |
| return -1; |
| |
| cs->cca_fat = mac->cca_stats->cca_fat; |
| cs->cca_intf = mac->cca_stats->cca_intf; |
| cs->cca_trfc = mac->cca_stats->cca_trfc; |
| |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n"); |
| |
| return 0; |
| |
| } |