mwifiex: add rx histogram statistics support
authorXinming Hu <huxm@marvell.com>
Tue, 23 Dec 2014 13:44:07 +0000 (19:14 +0530)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 6 Jan 2015 18:54:11 +0000 (20:54 +0200)
This patch add a new debugfs item histogram used for reporting
rx data packet statitics(rx rate, snr, noise floor, signal strenth)
to userspace.

Signed-off-by: Xinming Hu <huxm@marvell.com>
Signed-off-by: Cathy Luo <cluo@marvell.com>
Signed-off-by: Avinash Patil <patila@marvell.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/cfp.c
drivers/net/wireless/mwifiex/debugfs.c
drivers/net/wireless/mwifiex/decl.h
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/sta_event.c
drivers/net/wireless/mwifiex/sta_rx.c
drivers/net/wireless/mwifiex/uap_event.c
drivers/net/wireless/mwifiex/util.c

index 4a66a655536647ed0a72d5a1e41bcccc15fae15c..f8b1ce0a0fd6bfdd3e0fe93f009fafb6c9fb42db 100644 (file)
@@ -2386,6 +2386,10 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
 
        priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
 
+       if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
+           GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
+               kfree(priv->hist_data);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf);
index b8242eb2be6fd272906c605532f47b986ae61c08..f494fc7eeb627ae846c2693c0cea3b5e8eb69ce8 100644 (file)
@@ -509,3 +509,21 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
 
        return k;
 }
+
+u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv,
+                           u8 rx_rate, u8 rate_info)
+{
+       u8 rate_index = 0;
+
+       /* HT40 */
+       if ((rate_info & BIT(0)) && (rate_info & BIT(1)))
+               rate_index = MWIFIEX_RATE_INDEX_MCS0 +
+                            MWIFIEX_BW20_MCS_NUM + rx_rate;
+       else if (rate_info & BIT(0)) /* HT20 */
+               rate_index = MWIFIEX_RATE_INDEX_MCS0 + rx_rate;
+       else
+               rate_index = (rx_rate > MWIFIEX_RATE_INDEX_OFDM0) ?
+                             rx_rate - 1 : rx_rate;
+
+       return rate_index;
+}
index 3d2fb9af20697ff46d711dedb222d01c0bc7a858..fe1eb74d6703218a2f6fe79b661edfd2e298d584 100644 (file)
@@ -366,6 +366,103 @@ free_and_exit:
        return ret;
 }
 
+/* Sysfs histogram file read handler.
+ *
+ * This function is called when the 'histogram' file is opened for reading
+ * It prints the following histogram information -
+ *      - Number of histogram samples
+ *      - Receive packet number of each rx_rate
+ *      - Receive packet number of each snr
+ *      - Receive packet number of each nosie_flr
+ *      - Receive packet number of each signal streath
+ */
+static ssize_t
+mwifiex_histogram_read(struct file *file, char __user *ubuf,
+                      size_t count, loff_t *ppos)
+{
+       struct mwifiex_private *priv =
+               (struct mwifiex_private *)file->private_data;
+       ssize_t ret;
+       struct mwifiex_histogram_data *phist_data;
+       int i, value;
+       unsigned long page = get_zeroed_page(GFP_KERNEL);
+       char *p = (char *)page;
+
+       if (!p)
+               return -ENOMEM;
+
+       if (!priv || !priv->hist_data)
+               return -EFAULT;
+       phist_data = priv->hist_data;
+
+       p += sprintf(p, "\n"
+                    "total samples = %d\n",
+                    atomic_read(&phist_data->num_samples));
+
+       p += sprintf(p, "rx rates (in Mbps): 0=1M   1=2M");
+       p += sprintf(p, "2=5.5M  3=11M   4=6M   5=9M  6=12M\n");
+       p += sprintf(p, "7=18M  8=24M  9=36M  10=48M  11=54M");
+       p += sprintf(p, "12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n");
+
+       if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {
+               p += sprintf(p, "44-53=MCS0-9(VHT:BW20)");
+               p += sprintf(p, "54-63=MCS0-9(VHT:BW40)");
+               p += sprintf(p, "64-73=MCS0-9(VHT:BW80)\n\n");
+       } else {
+               p += sprintf(p, "\n");
+       }
+
+       for (i = 0; i < MWIFIEX_MAX_RX_RATES; i++) {
+               value = atomic_read(&phist_data->rx_rate[i]);
+               if (value)
+                       p += sprintf(p, "rx_rate[%02d] = %d\n", i, value);
+       }
+
+       if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {
+               for (i = MWIFIEX_MAX_RX_RATES; i < MWIFIEX_MAX_AC_RX_RATES;
+                    i++) {
+                       value = atomic_read(&phist_data->rx_rate[i]);
+                       if (value)
+                               p += sprintf(p, "rx_rate[%02d] = %d\n",
+                                          i, value);
+               }
+       }
+
+       for (i = 0; i < MWIFIEX_MAX_SNR; i++) {
+               value =  atomic_read(&phist_data->snr[i]);
+               if (value)
+                       p += sprintf(p, "snr[%02ddB] = %d\n", i, value);
+       }
+       for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) {
+               value = atomic_read(&phist_data->noise_flr[i]);
+               if (value)
+                       p += sprintf(p, "noise_flr[-%02ddBm] = %d\n",
+                               (int)(i-128), value);
+       }
+       for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) {
+               value = atomic_read(&phist_data->sig_str[i]);
+               if (value)
+                       p += sprintf(p, "sig_strength[-%02ddBm] = %d\n",
+                               i, value);
+       }
+
+       ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
+                                     (unsigned long)p - page);
+
+       return ret;
+}
+
+static ssize_t
+mwifiex_histogram_write(struct file *file, const char __user *ubuf,
+                       size_t count, loff_t *ppos)
+{
+       struct mwifiex_private *priv = (void *)file->private_data;
+
+       if (priv && priv->hist_data)
+               mwifiex_hist_data_reset(priv);
+       return 0;
+}
+
 static struct mwifiex_debug_info info;
 
 /*
@@ -832,6 +929,7 @@ MWIFIEX_DFS_FILE_READ_OPS(fw_dump);
 MWIFIEX_DFS_FILE_OPS(regrdwr);
 MWIFIEX_DFS_FILE_OPS(rdeeprom);
 MWIFIEX_DFS_FILE_OPS(hscfg);
+MWIFIEX_DFS_FILE_OPS(histogram);
 
 /*
  * This function creates the debug FS directory structure and the files.
@@ -855,6 +953,7 @@ mwifiex_dev_debugfs_init(struct mwifiex_private *priv)
        MWIFIEX_DFS_ADD_FILE(rdeeprom);
        MWIFIEX_DFS_ADD_FILE(fw_dump);
        MWIFIEX_DFS_ADD_FILE(hscfg);
+       MWIFIEX_DFS_ADD_FILE(histogram);
 }
 
 /*
index 21b5130c264ed5af6e0332919bafc12b4fd33e61..cdca8ed7b3b146fa32f69d2a5a8a0848ee544a38 100644 (file)
 #define MWIFIEX_TDLS_MAX_FAIL_COUNT      4
 #define MWIFIEX_AUTO_TDLS_IDLE_TIME     10
 
+/* 54M rates, index from 0 to 11 */
+#define MWIFIEX_RATE_INDEX_MCS0 12
+/* 12-27=MCS0-15(BW20) */
+#define MWIFIEX_BW20_MCS_NUM 15
+
+/* Rate index for OFDM 0 */
+#define MWIFIEX_RATE_INDEX_OFDM0   4
+
 enum mwifiex_bss_type {
        MWIFIEX_BSS_TYPE_STA = 0,
        MWIFIEX_BSS_TYPE_UAP = 1,
@@ -205,4 +213,20 @@ struct mwifiex_chan_stats {
        u16 cca_scan_dur;
        u16 cca_busy_dur;
 } __packed;
+
+#define MWIFIEX_HIST_MAX_SAMPLES       1048576
+#define MWIFIEX_MAX_RX_RATES                44
+#define MWIFIEX_MAX_AC_RX_RATES                     74
+#define MWIFIEX_MAX_SNR                            256
+#define MWIFIEX_MAX_NOISE_FLR              256
+#define MWIFIEX_MAX_SIG_STRENGTH           256
+
+struct mwifiex_histogram_data {
+       atomic_t rx_rate[MWIFIEX_MAX_AC_RX_RATES];
+       atomic_t snr[MWIFIEX_MAX_SNR];
+       atomic_t noise_flr[MWIFIEX_MAX_NOISE_FLR];
+       atomic_t sig_str[MWIFIEX_MAX_SIG_STRENGTH];
+       atomic_t num_samples;
+};
+
 #endif /* !_MWIFIEX_DECL_H_ */
index d4d2223d1f317585c7248e368501613a3574d9f1..0c44c5ee0a70d2977fa74a0bf03518b594d599e4 100644 (file)
@@ -847,6 +847,7 @@ static const struct net_device_ops mwifiex_netdev_ops = {
  *      - Nick name             : Set to null
  *      - Number of Tx timeout  : Set to 0
  *      - Device address        : Set to current address
+ *      - Rx histogram statistc : Set to 0
  *
  * In addition, the CFG80211 work queue is also created.
  */
@@ -867,6 +868,13 @@ void mwifiex_init_priv_params(struct mwifiex_private *priv,
        priv->rsn_idx = MWIFIEX_AUTO_IDX_MASK;
        priv->num_tx_timeout = 0;
        memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN);
+
+       if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
+           GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+               priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL);
+               if (priv->hist_data)
+                       mwifiex_hist_data_reset(priv);
+       }
 }
 
 /*
index b3e23f625e1afa1f451e8086476d9d5ce16304a4..6f7e0601f804b4e0ef74b25981f15a0d93f4576c 100644 (file)
@@ -582,6 +582,8 @@ struct mwifiex_private {
        struct idr ack_status_frames;
        /* spin lock for ack status */
        spinlock_t ack_status_lock;
+       /** rx histogram data */
+       struct mwifiex_histogram_data *hist_data;
 };
 
 enum mwifiex_ba_status {
@@ -1350,6 +1352,14 @@ struct sk_buff *
 mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
                                struct sk_buff *skb, u8 flag, u64 *cookie);
 
+void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr,
+                          s8 nflr);
+void mwifiex_hist_data_reset(struct mwifiex_private *priv);
+void mwifiex_hist_data_add(struct mwifiex_private *priv,
+                          u8 rx_rate, s8 snr, s8 nflr);
+u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv,
+                           u8 rx_rate, u8 ht_info);
+
 #ifdef CONFIG_DEBUG_FS
 void mwifiex_debugfs_init(void);
 void mwifiex_debugfs_remove(void);
index b8c171df6223d827da184ec4617c6ac14791752e..fbec95bbc10f199d6c170b866d0843f26fae7973 100644 (file)
@@ -90,6 +90,10 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code)
        priv->is_data_rate_auto = true;
        priv->data_rate = 0;
 
+       if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
+            GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && priv->hist_data)
+               mwifiex_hist_data_reset(priv);
+
        if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
                priv->adhoc_state = ADHOC_IDLE;
                priv->adhoc_is_link_sensed = false;
index c2ad3b63ae7000cd616c42b44f315b053b64606c..b8729c9394e92553b12301317c9b281209e248c6 100644 (file)
@@ -90,6 +90,7 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv,
        struct ethhdr *eth;
        u16 rx_pkt_off, rx_pkt_len;
        u8 *offset;
+       u8 adj_rx_rate = 0;
 
        local_rx_pd = (struct rxpd *) (skb->data);
 
@@ -155,6 +156,14 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv,
 
        priv->rxpd_htinfo = local_rx_pd->ht_info;
 
+       if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
+           GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
+               adj_rx_rate = mwifiex_adjust_data_rate(priv, priv->rxpd_rate,
+                                                      priv->rxpd_htinfo);
+               mwifiex_hist_data_add(priv, adj_rx_rate, local_rx_pd->snr,
+                                     local_rx_pd->nf);
+       }
+
        ret = mwifiex_recv_packet(priv, skb);
        if (ret == -1)
                dev_err(priv->adapter->dev, "recv packet failed\n");
index c54a537e31fbd2daa33da36e4d08a665778cf8be..f31086cdf937a12af4d66b9a9e1a6e1c730bcd0b 100644 (file)
@@ -132,6 +132,8 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
                dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause);
                memcpy(priv->netdev->dev_addr, adapter->event_body + 2,
                       ETH_ALEN);
+               if (priv->hist_data)
+                       mwifiex_hist_data_reset(priv);
                break;
        case EVENT_UAP_MIC_COUNTERMEASURES:
                /* For future development */
index 3bd917975e5bcc13c7ebb80f466e0dc30c2529f9..6a83c04cf3073c9693c8852a950a5e9fee5c3bde 100644 (file)
@@ -406,3 +406,44 @@ void mwifiex_del_all_sta_list(struct mwifiex_private *priv)
        spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
        return;
 }
+
+/* This function adds histogram data to histogram array*/
+void mwifiex_hist_data_add(struct mwifiex_private *priv,
+                          u8 rx_rate, s8 snr, s8 nflr)
+{
+       struct mwifiex_histogram_data *phist_data = priv->hist_data;
+
+       if (atomic_read(&phist_data->num_samples) > MWIFIEX_HIST_MAX_SAMPLES)
+               mwifiex_hist_data_reset(priv);
+       mwifiex_hist_data_set(priv, rx_rate, snr, nflr);
+}
+
+/* function to add histogram record */
+void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr,
+                          s8 nflr)
+{
+       struct mwifiex_histogram_data *phist_data = priv->hist_data;
+
+       atomic_inc(&phist_data->num_samples);
+       atomic_inc(&phist_data->rx_rate[rx_rate]);
+       atomic_inc(&phist_data->snr[snr]);
+       atomic_inc(&phist_data->noise_flr[128 + nflr]);
+       atomic_inc(&phist_data->sig_str[nflr - snr]);
+}
+
+/* function to reset histogram data during init/reset */
+void mwifiex_hist_data_reset(struct mwifiex_private *priv)
+{
+       int ix;
+       struct mwifiex_histogram_data *phist_data = priv->hist_data;
+
+       atomic_set(&phist_data->num_samples, 0);
+       for (ix = 0; ix < MWIFIEX_MAX_AC_RX_RATES; ix++)
+               atomic_set(&phist_data->rx_rate[ix], 0);
+       for (ix = 0; ix < MWIFIEX_MAX_SNR; ix++)
+               atomic_set(&phist_data->snr[ix], 0);
+       for (ix = 0; ix < MWIFIEX_MAX_NOISE_FLR; ix++)
+               atomic_set(&phist_data->noise_flr[ix], 0);
+       for (ix = 0; ix < MWIFIEX_MAX_SIG_STRENGTH; ix++)
+               atomic_set(&phist_data->sig_str[ix], 0);
+}