wlan: HDD: Link Layer Statistics Implementation

This commit introduces the link layer statistics implementation
in HDD and SME layers.

Change-Id: I8240243aa87999fc14fbd7e3f23b9612b7aca4d6
CRs-Fixed: 665003
diff --git a/CORE/HDD/inc/wlan_hdd_cfg80211.h b/CORE/HDD/inc/wlan_hdd_cfg80211.h
index ed1a2e7..4180469 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg80211.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg80211.h
@@ -30,22 +30,18 @@
 
 
 /**===========================================================================
-  
+
   \file  wlan_hdd_cfg80211.h
-  
+
   \brief cfg80211 functions declarations
-    
-               Copyright 2008 (c) Qualcomm, Incorporated.
-               All Rights Reserved.
-               Qualcomm Confidential and Proprietary.
-  
+
   ==========================================================================*/
-  
+
 /* $HEADER$ */
 
 
 //value for initial part of frames and number of bytes to be compared
-#define GAS_INITIAL_REQ "\x04\x0a"  
+#define GAS_INITIAL_REQ "\x04\x0a"
 #define GAS_INITIAL_REQ_SIZE 2
 
 #define GAS_INITIAL_RSP "\x04\x0b"
@@ -57,7 +53,7 @@
 #define GAS_COMEBACK_RSP "\x04\x0d"
 #define GAS_COMEBACK_RSP_SIZE 2
 
-#define P2P_PUBLIC_ACTION_FRAME "\x04\x09\x50\x6f\x9a\x09" 
+#define P2P_PUBLIC_ACTION_FRAME "\x04\x09\x50\x6f\x9a\x09"
 #define P2P_PUBLIC_ACTION_FRAME_SIZE 6
 
 #define P2P_ACTION_FRAME "\x7f\x50\x6f\x9a\x09"
@@ -117,16 +113,335 @@
 }__attribute__((packed)) qcom_ie_age ;
 #endif
 
+enum qca_nl80211_vendor_subcmds {
+    QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
+    QCA_NL80211_VENDOR_SUBCMD_TEST = 1,
+    /* subcmds 2..9 not yet allocated */
+    QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10,
+    QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY =  11,
+    QCA_NL80211_VENDOR_SUBCMD_NAN =  12,
+    QCA_NL80211_VENDOR_SUBCMD_STATS_EXT = 13,
+
+    QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14,
+    QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15,
+    QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16,
+    QCA_NL80211_VENDOR_SUBCMD_LL_STATS_RADIO_RESULTS = 17,
+    QCA_NL80211_VENDOR_SUBCMD_LL_STATS_IFACE_RESULTS = 18,
+    QCA_NL80211_VENDOR_SUBCMD_LL_STATS_PEERS_RESULTS = 19,
+};
+
+enum qca_nl80211_vendor_subcmds_index {
+    QCA_NL80211_VENDOR_SUBCMD_NAN_INDEX = 0,
+    QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET_INDEX,
+    QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET_INDEX,
+    QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR_INDEX,
+    QCA_NL80211_VENDOR_SUBCMD_LL_RADIO_STATS_INDEX,
+    QCA_NL80211_VENDOR_SUBCMD_LL_IFACE_STATS_INDEX,
+    QCA_NL80211_VENDOR_SUBCMD_LL_PEER_INFO_STATS_INDEX,
+};
+
+enum qca_wlan_vendor_attr
+{
+    QCA_WLAN_VENDOR_ATTR_INVALID = 0,
+    /* used by QCOM_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */
+    QCA_WLAN_VENDOR_ATTR_DFS     = 1,
+    /* used by QCOM_NL80211_VENDOR_SUBCMD_NAN */
+    QCA_WLAN_VENDOR_ATTR_NAN     = 2,
+
+    /* keep last */
+    QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
+    QCA_WLAN_VENDOR_ATTR_MAX       = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
+};
+
+#ifdef WLAN_FEATURE_LINK_LAYER_STATS
+
+enum qca_wlan_vendor_attr_ll_stats_set
+{
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_INVALID = 0,
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD = 1,
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING,
+    /* keep last */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST,
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX =
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_get
+{
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_INVALID = 0,
+    /* Unsigned 32bit value provided by the caller issuing the GET stats
+     * command. When reporting the stats results, the driver uses the same
+     * value to indicate which GET request the results correspond to.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID,
+    /* Unsigned 34bit value - bit mask to identify what
+     * statistics are requested for retrieval.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK,
+    /* keep last */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST,
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX =
+                  QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_clr
+{
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_INVALID = 0,
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK,
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ,
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK,
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP,
+
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST,
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX       =
+                        QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_results
+{
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_INVALID = 0,
+    /* Unsigned 32bit value */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_REQ_ID = 1,
+    /* Unsigned 32bit value */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX,
+    /* Unsigned 32bit value */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX,
+    /* Unsigned 32bit value */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX,
+    /* Unsigned 32bit value */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX,
+    /* Unsigned 32bit value */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT,
+    /* Unsigned 32bit value */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA,
+    /* Unsigned 32bit value */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK,
+    /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_* are
+     * nested within the interface stats.
+     */
+
+    /* Interface mode, e.g., STA, SOFTAP, IBSS, etc.
+     * Type = enum wifi_interface_mode */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE,
+    /* Interface MAC address. An array of 6 Unsigned int8 */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR,
+    /* Type = enum wifi_connection_state,
+     * e.g., DISCONNECTED, AUTHENTICATING, etc.
+     * valid for STA, CLI only.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE,
+    /* Type = enum wifi_roam_state. Roaming state,
+     * e.g., IDLE or ACTIVE (is that valid for STA only?)
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING,
+    /* Unsigned 32bit value. WIFI_CAPABILITY_XXX */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES,
+    /* NULL terminated SSID. An array of 33 Unsigned 8bit values */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID,
+    /* BSSID. An array of 6 Unsigned 8bit values */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID,
+    /* Country string advertised by AP. An array of 3 Unsigned 8bit values */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR,
+    /* Country string for this association. An array of 3 Unsigned 8bit values*/
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR,
+
+    /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_* could
+     * be nested within the interface stats.
+     */
+
+    /* Type = enum wifi_traffic_ac, e.g., V0, VI, BE and BK */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC,
+    /* Unsigned int 32 value corresponding to respective AC */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU,
+    /* Unsigned int 32 value corresponding to respective AC */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU,
+    /* Unsigned int 32 value corresponding to respective AC */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST,
+    /* Unsigned int 32 value corresponding to respective AC */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST,
+    /* Unsigned int 32 value corresponding to respective AC */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU,
+    /* Unsigned int 32 value corresponding to respective AC */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU,
+    /* Unsigned int 32 value corresponding to respective AC */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST,
+    /* Unsigned int 32 value corresponding to respective AC */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES,
+    /* Unsigned int 32 value corresponding to respective AC  */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT,
+    /* Unsigned int 32 values corresponding to respective AC */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG,
+    /* Unsigned int 32 values corresponding to respective AC */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN,
+    /* Unsigned int 32 values corresponding to respective AC */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX,
+    /* Unsigned int 32 values corresponding to respective AC */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG,
+    /* Unsigned int 32 values corresponding to respective AC */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES,
+    /* Unsigned 32bit value. Number of peers */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS,
+
+    /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_* are
+     * nested within the interface stats.
+     */
+
+    /* Type = enum wifi_peer_type. Peer type, e.g., STA, AP, P2P GO etc. */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE,
+    /* MAC addr corresponding to respective peer.
+     *  An array of 6 Unsigned 8bit values.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS,
+    /* Unsigned int 32bit value representing capabilities
+     * corresponding to respective peer.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES,
+    /* Unsigned 32bit value. Number of rates */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES,
+
+    /* Attributes nested within the rate stats.*/
+    /* Unsigned 8bit value */
+    /* Unsigned int 8bit value; 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE,
+    /* Unsigned int 8bit value; 0:1x1, 1:2x2, 3:3x3, 4:4x4 */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS,
+    /* Unsigned int 8bit value; 0:20MHz, 1:40Mhz, 2:80Mhz, 3:160Mhz */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW,
+    /* Unsigned int 8bit value; OFDM/CCK rate code would be as per IEEE Std
+     * in the units of 0.5mbps HT/VHT it would be mcs index */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX,
+
+    /* Unsigned 32bit value. Bit rate in units of 100Kbps */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE,
+
+    /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_* could be
+     * nested within the peer info stats.
+     */
+
+    /* Unsigned int 32bit value. Number of successfully transmitted data pkts,
+     * i.e., with ACK received  *corresponding to the respective rate.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU,
+    /* Unsigned int 32bit value. Number of received data pkts
+     * corresponding to the respective rate. */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU,
+    /* Unsigned int 32bit value. Number of data pkts losses, i.e.,
+     * no ACK received corresponding to *the respective rate.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST,
+    /* Unsigned int 32bit value. Total number of data pkt retries for
+     *   the respective rate.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES,
+    /* Unsigned int 32bit value. Total number of short data pkt retries for
+      the respective rate. */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT,
+    /* Unsigned int 32bit value. Total number of long data pkt retries for
+     * the respective rate.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG,
+
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID,
+    /* Unsigned 32bit value. Total number of msecs the radio is awake
+     *  accruing over time.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME,
+    /* Unsigned 32bit value. Total number of msecs the radio is
+     * transmitting accruing over time.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME,
+    /* Unsigned 32bit value. Total number of msecs the radio is
+     * in active receive accruing over time.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME,
+    /* Unsigned 32bit value. Total number of msecs the radio is
+     * awake due to all scan accruing over time.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN,
+    /* Unsigned 32bit value. Total number of msecs the radio is
+     *   awake due to NAN accruing over time.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD,
+    /* Unsigned 32bit value. Total number of msecs the radio is
+     * awake due to GSCAN accruing over time.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN,
+    /* Unsigned 32bit value. Total number of msecs the radio is
+     * awake due to roam scan accruing over time.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN,
+    /* Unsigned 32bit value. Total number of msecs the radio is
+     * awake due to PNO scan accruing over time.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN,
+    /* Unsigned 32bit value. Total number of msecs the radio is
+     * awake due to HS2.0 scans and GAS exchange accruing over time.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20,
+    /* Unsigned 32bit value. Number of channels. */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS,
+
+    /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_
+     * could be nested within the channel stats.
+     */
+
+    /* Type = enum wifi_channel_width. Channel width, e.g., 20, 40, 80, etc.*/
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH,
+    /* Unsigned 32bit value. Primary 20MHz channel. */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ,
+    /* Unsigned 32bit value. Center frequency (MHz) first segment. */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0,
+    /* Unsigned 32bit value. Center frequency (MHz) second segment. */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1,
+
+    /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ could be
+     * nested within the radio stats.
+     */
+
+    /* Unsigned int 32bit value representing total number of msecs the radio
+     * s awake on that *channel accruing over time, corresponding to
+     * the respective channel.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME,
+    /* Unsigned int 32bit value representing total number of msecs the
+     * CCA register is busy accruing  *over time corresponding to the
+     * respective channel.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME,
+
+
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS,
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO,
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO,
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO,
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO,
+
+    /* Unsigned 8bit value. Used by the driver; if set to 1, it indicates that
+     * more stats, e.g., peers or radio, are to follow in the next
+     * QCA_NL80211_VENDOR_SUBCMD_LL_STATS_*_RESULTS event.
+     * Otherwise, it is set to 0.
+     */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA,
+
+    /* keep last */
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST,
+    QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX   =
+                        QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST -1,
+};
+
+
+#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
+
+
 /* Vendor id to be used in vendor specific command and events
  * to user space. Use QCA OUI 00:13:74 to match with define in
  * supplicant code.
  */
-#define QCOM_NL80211_VENDOR_ID                0x001374
+#define QCA_NL80211_VENDOR_ID                0x001374
 
 /* Vendor speicific sub-command id and their index */
 #ifdef FEATURE_WLAN_CH_AVOID
-#define QCOM_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY         10
-#define QCOM_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX   0
+#define QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX   0
 #endif /* FEATURE_WLAN_CH_AVOID */
 
 #ifdef FEATURE_WLAN_CH_AVOID
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index 72b546b..12646ab 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -1051,6 +1051,9 @@
    v_U8_t configuredPsb;
    v_BOOL_t is_roc_inprogress;
    v_U32_t maxRateFlags;
+#ifdef WLAN_FEATURE_LINK_LAYER_STATS
+   v_BOOL_t isLinkLayerStatsSet;
+#endif
 };
 
 #define WLAN_HDD_GET_STATION_CTX_PTR(pAdapter) (&(pAdapter)->sessionCtx.station)
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 652330a..791c20d 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -143,6 +143,17 @@
 
 #define HDD_CHANNEL_14 14
 
+#ifdef WLAN_FEATURE_LINK_LAYER_STATS
+/*
+ * Used to allocate the size of 4096 for the link layer stats.
+ * The size of 4096 is considered assuming that all data per
+ * respective event fit with in the limit.Please take a call
+ * on the limit based on the data requirements on link layer
+ * statistics.
+ */
+#define LL_STATS_EVENT_BUF_SIZE 4096
+#endif
+
 static const u32 hdd_cipher_suites[] =
 {
     WLAN_CIPHER_SUITE_WEP40,
@@ -573,7 +584,7 @@
 
     vendor_event = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
                        sizeof(tHddAvoidFreqList),
-                       QCOM_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX,
+                       QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX,
                        GFP_KERNEL);
     if (!vendor_event)
     {
@@ -592,15 +603,1320 @@
 }
 #endif /* FEATURE_WLAN_CH_AVOID */
 
+#ifdef WLAN_FEATURE_LINK_LAYER_STATS
+
+static v_BOOL_t put_wifi_rate_stat( tpSirWifiRateStat stats,
+                                struct sk_buff *vendor_event)
+{
+    if (nla_put_u8(vendor_event,
+                QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE,
+                stats->rate.preamble)  ||
+        nla_put_u8(vendor_event,
+            QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS,
+            stats->rate.nss)       ||
+        nla_put_u8(vendor_event,
+            QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW,
+            stats->rate.bw)        ||
+        nla_put_u8(vendor_event,
+            QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX,
+            stats->rate.rateMcsIdx) ||
+        nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE,
+            stats->rate.bitrate )   ||
+        nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU,
+            stats->txMpdu )    ||
+        nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU,
+                stats->rxMpdu )     ||
+        nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST,
+                stats->mpduLost )  ||
+        nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES,
+                stats->retries)     ||
+        nla_put_u32(vendor_event,
+                QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT,
+                stats->retriesShort )   ||
+        nla_put_u32(vendor_event,
+                QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG,
+                stats->retriesLong))
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+                FL("QCA_WLAN_VENDOR_ATTR put fail"));
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static v_BOOL_t put_wifi_peer_info( tpSirWifiPeerInfo stats,
+                               struct sk_buff *vendor_event)
+{
+    u32 i = 0;
+    struct nlattr *rateInfo;
+    if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE,
+                                     stats->type) ||
+        nla_put(vendor_event,
+                QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS,
+                VOS_MAC_ADDR_SIZE, &stats->peerMacAddress[0]) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES,
+                    stats->capabilities) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES,
+                    stats->numRate))
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+                FL("QCA_WLAN_VENDOR_ATTR put fail"));
+        goto error;
+    }
+
+    rateInfo = nla_nest_start(vendor_event,
+                            QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO);
+    for (i = 0; i < stats->numRate; i++)
+    {
+        struct nlattr *rates;
+        tpSirWifiRateStat pRateStats = (tpSirWifiRateStat )((uint8 *)
+                                            stats->rateStats +
+                                       (i * sizeof(tSirWifiRateStat)));
+        rates = nla_nest_start(vendor_event, i);
+
+        if (FALSE == put_wifi_rate_stat(pRateStats, vendor_event))
+        {
+            hddLog(VOS_TRACE_LEVEL_ERROR,
+                FL("QCA_WLAN_VENDOR_ATTR put fail"));
+            return FALSE;
+        }
+        nla_nest_end(vendor_event, rates);
+    }
+    nla_nest_end(vendor_event, rateInfo);
+
+    return TRUE;
+error:
+    return FALSE;
+}
+
+static v_BOOL_t put_wifi_wmm_ac_stat( tpSirWifiWmmAcStat stats,
+                                  struct sk_buff *vendor_event)
+{
+    if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC,
+                    stats->ac ) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU,
+                    stats->txMpdu ) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU,
+                    stats->rxMpdu ) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST,
+                    stats->txMcast ) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST,
+                    stats->rxMcast ) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU,
+                    stats->rxAmpdu ) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU,
+                    stats->txAmpdu ) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST,
+                    stats->mpduLost )||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES,
+                    stats->retries ) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT,
+                    stats->retriesShort ) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG,
+                    stats->retriesLong ) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN,
+                    stats->contentionTimeMin ) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX,
+                    stats->contentionTimeMax ) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG,
+                    stats->contentionTimeAvg ) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES,
+                    stats->contentionNumSamples ))
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+                FL("QCA_WLAN_VENDOR_ATTR put fail") );
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static v_BOOL_t put_wifi_interface_info(tpSirWifiInterfaceInfo stats,
+                                    struct sk_buff *vendor_event)
+{
+    if (nla_put_u32(vendor_event,
+                QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE, stats->mode ) ||
+            nla_put(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR,
+                    VOS_MAC_ADDR_SIZE, stats->macAddr) ||
+            nla_put_u32(vendor_event,
+                        QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE,
+                        stats->state ) ||
+            nla_put_u32(vendor_event,
+                        QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING,
+                        stats->roaming ) ||
+            nla_put_u32(vendor_event,
+                        QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES,
+                        stats->capabilities ) ||
+            nla_put(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID,
+                    strlen(stats->ssid), stats->ssid) ||
+            nla_put(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID,
+                    WNI_CFG_BSSID_LEN, stats->bssid) ||
+            nla_put(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR,
+                    WNI_CFG_COUNTRY_CODE_LEN, stats->apCountryStr) ||
+            nla_put(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR,
+                    WNI_CFG_COUNTRY_CODE_LEN, stats->countryStr)
+      )
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+                FL("QCA_WLAN_VENDOR_ATTR put fail") );
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static v_BOOL_t put_wifi_iface_stats(tpSirWifiIfaceStat pWifiIfaceStat,
+                                 struct sk_buff *vendor_event)
+{
+    int i = 0;
+    struct nlattr *wmmInfo;
+    if (FALSE == put_wifi_interface_info(
+                                &pWifiIfaceStat->info,
+                                vendor_event))
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+                FL("QCA_WLAN_VENDOR_ATTR put fail") );
+        return FALSE;
+
+    }
+
+    if (nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX,
+                    pWifiIfaceStat->beaconRx) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX,
+                    pWifiIfaceStat->mgmtRx) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX,
+                    pWifiIfaceStat->mgmtActionRx) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX,
+                    pWifiIfaceStat->mgmtActionTx) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT,
+                    pWifiIfaceStat->rssiMgmt) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA,
+                    pWifiIfaceStat->rssiData) ||
+        nla_put_u32(vendor_event,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK,
+                    pWifiIfaceStat->rssiAck))
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+                FL("QCA_WLAN_VENDOR_ATTR put fail"));
+        return FALSE;
+    }
+
+    wmmInfo = nla_nest_start(vendor_event,
+                            QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO);
+    for (i = 0; i < WIFI_AC_MAX; i++)
+    {
+        struct nlattr *wmmStats;
+        wmmStats = nla_nest_start(vendor_event, i);
+        if (FALSE == put_wifi_wmm_ac_stat(
+                                &pWifiIfaceStat->AccessclassStats[i],
+                                vendor_event))
+        {
+            hddLog(VOS_TRACE_LEVEL_ERROR,
+                    FL("QCA_WLAN_VENDOR_ATTR put Fail"));
+            return FALSE;
+        }
+
+        nla_nest_end(vendor_event, wmmStats);
+    }
+    nla_nest_end(vendor_event, wmmInfo);
+    return TRUE;
+}
+
+static tSirWifiInterfaceMode
+    hdd_map_device_to_ll_iface_mode ( int deviceMode )
+{
+    switch (deviceMode)
+    {
+    case  WLAN_HDD_INFRA_STATION:
+        return WIFI_INTERFACE_STA;
+    case  WLAN_HDD_SOFTAP:
+        return WIFI_INTERFACE_SOFTAP;
+    case  WLAN_HDD_P2P_CLIENT:
+        return WIFI_INTERFACE_P2P_CLIENT;
+    case  WLAN_HDD_P2P_GO:
+        return WIFI_INTERFACE_P2P_GO;
+    case  WLAN_HDD_IBSS:
+        return WIFI_INTERFACE_IBSS;
+    default:
+        /* Return Interface Mode as STA for all the unsupported modes */
+        return WIFI_INTERFACE_STA;
+    }
+}
+
+static v_BOOL_t hdd_get_interface_info(hdd_adapter_t *pAdapter,
+                           tpSirWifiInterfaceInfo pInfo)
+{
+    v_U8_t *staMac = NULL;
+    hdd_station_ctx_t *pHddStaCtx;
+    tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
+    tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
+
+    pInfo->mode = hdd_map_device_to_ll_iface_mode(pAdapter->device_mode);
+
+    vos_mem_copy(pInfo->macAddr,
+        pAdapter->macAddressCurrent.bytes, sizeof(v_MACADDR_t));
+
+    if (((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) ||
+            (WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) ||
+            (WLAN_HDD_P2P_DEVICE == pAdapter->device_mode)))
+    {
+        pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
+        if (eConnectionState_NotConnected == pHddStaCtx->conn_info.connState)
+        {
+            pInfo->state = WIFI_DISCONNECTED;
+        }
+        if (eConnectionState_Connecting == pHddStaCtx->conn_info.connState)
+        {
+            hddLog(VOS_TRACE_LEVEL_ERROR,
+                    "%s: Session ID %d, Connection is in progress", __func__,
+                    pAdapter->sessionId);
+            pInfo->state = WIFI_ASSOCIATING;
+        }
+        if ((eConnectionState_Associated == pHddStaCtx->conn_info.connState) &&
+            (VOS_FALSE == pHddStaCtx->conn_info.uIsAuthenticated))
+        {
+            staMac = (v_U8_t *) &(pAdapter->macAddressCurrent.bytes[0]);
+            hddLog(VOS_TRACE_LEVEL_ERROR,
+                "%s: client " MAC_ADDRESS_STR
+                " is in the middle of WPS/EAPOL exchange.", __func__,
+                MAC_ADDR_ARRAY(staMac));
+            pInfo->state = WIFI_AUTHENTICATING;
+        }
+        if (eConnectionState_Associated == pHddStaCtx->conn_info.connState)
+        {
+            pInfo->state = WIFI_ASSOCIATED;
+            vos_mem_copy(pInfo->bssid,
+                    &pHddStaCtx->conn_info.bssId, WNI_CFG_BSSID_LEN);
+            vos_mem_copy(pInfo->ssid,
+                    pHddStaCtx->conn_info.SSID.SSID.ssId,
+                    pHddStaCtx->conn_info.SSID.SSID.length);
+            //NULL Terminate the string.
+            pInfo->ssid[pHddStaCtx->conn_info.SSID.SSID.length] = 0;
+        }
+    }
+    vos_mem_copy(pInfo->countryStr,
+        pMac->scan.countryCodeCurrent, WNI_CFG_COUNTRY_CODE_LEN);
+
+    vos_mem_copy(pInfo->apCountryStr,
+        pMac->scan.countryCodeCurrent, WNI_CFG_COUNTRY_CODE_LEN);
+
+    return TRUE;
+}
+
+/*
+ * hdd_link_layer_process_peer_stats () - This function is called after
+ * receiving Link Layer Peer statistics from FW.This function converts
+ * the firmware data to the NL data and sends the same to the kernel/upper
+ * layers.
+ */
+static v_VOID_t hdd_link_layer_process_peer_stats(hdd_adapter_t *pAdapter,
+                                                   v_VOID_t *pData)
+{
+    hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
+    tpSirWifiRateStat   pWifiRateStat;
+    tpSirWifiPeerStat   pWifiPeerStat;
+    tpSirWifiPeerInfo   pWifiPeerInfo;
+    struct nlattr *peerInfo;
+    struct sk_buff *vendor_event;
+    int status, i;
+
+    status = wlan_hdd_validate_context(pHddCtx);
+    if (0 != status)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("HDD context is not valid") );
+        return;
+    }
+
+    pWifiPeerStat = (tpSirWifiPeerStat) pData;
+
+    hddLog(VOS_TRACE_LEVEL_INFO,
+            "LL_STATS_PEER_ALL : numPeers %u",
+            pWifiPeerStat->numPeers);
+    {
+        for (i = 0; i < pWifiPeerStat->numPeers; i++)
+        {
+            pWifiPeerInfo = (tpSirWifiPeerInfo)
+                ((uint8 *)pWifiPeerStat->peerInfo +
+                ( i * sizeof(tSirWifiPeerInfo)));
+
+            hddLog(VOS_TRACE_LEVEL_INFO,
+                    " %d) LL_STATS Channel Stats "
+                    " Peer Type %u "
+                    " peerMacAddress  %pM "
+                    " capabilities 0x%x "
+                    " numRate %u ",
+                    i,
+                    pWifiPeerInfo->type,
+                    pWifiPeerInfo->peerMacAddress,
+                    pWifiPeerInfo->capabilities,
+                    pWifiPeerInfo->numRate);
+            {
+                int j;
+                for (j = 0; j < pWifiPeerInfo->numRate; j++)
+                {
+                    pWifiRateStat = (tpSirWifiRateStat)
+                        ((tANI_U8 *) pWifiPeerInfo->rateStats +
+                         ( j * sizeof(tSirWifiRateStat)));
+
+                    hddLog(VOS_TRACE_LEVEL_INFO,
+                            "   peer Rate Stats "
+                            "   preamble  %u "
+                            "   nss %u "
+                            "   bw %u "
+                            "   rateMcsIdx  %u "
+                            "   reserved %u "
+                            "   bitrate %u "
+                            "   txMpdu %u "
+                            "   rxMpdu %u "
+                            "   mpduLost %u "
+                            "   retries %u "
+                            "   retriesShort %u "
+                            "   retriesLong %u",
+                            pWifiRateStat->rate.preamble,
+                            pWifiRateStat->rate.nss,
+                            pWifiRateStat->rate.bw,
+                            pWifiRateStat->rate.rateMcsIdx,
+                            pWifiRateStat->rate.reserved,
+                            pWifiRateStat->rate.bitrate,
+                            pWifiRateStat->txMpdu,
+                            pWifiRateStat->rxMpdu,
+                            pWifiRateStat->mpduLost,
+                            pWifiRateStat->retries,
+                            pWifiRateStat->retriesShort,
+                            pWifiRateStat->retriesLong);
+                }
+            }
+        }
+    }
+
+    /*
+     * Allocate a size of 4096 for the peer stats comprising
+     * each of size = sizeof (tSirWifiPeerInfo) + numRate *
+     * sizeof (tSirWifiRateStat).Each field is put with an
+     * NL attribute.The size of 4096 is considered assuming
+     * that number of rates shall not exceed beyond 50 with
+     * the sizeof (tSirWifiRateStat) being 32.
+     */
+    vendor_event = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
+            LL_STATS_EVENT_BUF_SIZE + NLMSG_HDRLEN,
+            QCA_NL80211_VENDOR_SUBCMD_LL_PEER_INFO_STATS_INDEX,
+            GFP_KERNEL);
+    if (!vendor_event)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+                "%s: cfg80211_vendor_event_alloc failed",
+                __func__);
+        return;
+    }
+    if (nla_put_u32(vendor_event,
+            QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS,
+            pWifiPeerStat->numPeers))
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+                "%s: QCA_WLAN_VENDOR_ATTR put fail", __func__);
+        kfree_skb(vendor_event);
+        return;
+    }
+
+    peerInfo = nla_nest_start(vendor_event,
+            QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO);
+
+    pWifiPeerInfo = (tpSirWifiPeerInfo)  ((uint8 *)
+                pWifiPeerStat->peerInfo);
+
+    for (i = 1; i <= pWifiPeerStat->numPeers; i++)
+    {
+        struct nlattr *peers = nla_nest_start(vendor_event, i);
+        int numRate = pWifiPeerInfo->numRate;
+
+        if (FALSE == put_wifi_peer_info(
+                                     pWifiPeerInfo, vendor_event))
+        {
+            hddLog(VOS_TRACE_LEVEL_ERROR,
+                "%s: put_wifi_peer_info put fail", __func__);
+            kfree_skb(vendor_event);
+            return;
+        }
+
+        pWifiPeerInfo = (tpSirWifiPeerInfo)  ((uint8 *)
+                pWifiPeerStat->peerInfo +
+                (i * sizeof(tSirWifiPeerInfo)) +
+                (numRate * sizeof (tSirWifiRateStat)));
+        nla_nest_end(vendor_event, peers);
+    }
+    nla_nest_end(vendor_event, peerInfo);
+    cfg80211_vendor_event(vendor_event, GFP_KERNEL);
+}
+
+/*
+ * hdd_link_layer_process_iface_stats () - This function is called after
+ * receiving Link Layer Interface statistics from FW.This function converts
+ * the firmware data to the NL data and sends the same to the kernel/upper
+ * layers.
+ */
+static v_VOID_t hdd_link_layer_process_iface_stats(hdd_adapter_t *pAdapter,
+                                                   v_VOID_t *pData)
+{
+    tpSirWifiIfaceStat  pWifiIfaceStat;
+    struct sk_buff *vendor_event;
+    hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
+    int status;
+
+    status = wlan_hdd_validate_context(pHddCtx);
+    if (0 != status)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("HDD context is not valid") );
+        return;
+    }
+    /*
+     * Allocate a size of 4096 for the interface stats comprising
+     * sizeof (tpSirWifiIfaceStat).The size of 4096 is considered
+     * assuming that all these fit with in the limit.Please take
+     * a call on the limit based on the data requirements on
+     * interface statistics.
+     */
+    vendor_event = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
+           LL_STATS_EVENT_BUF_SIZE + NLMSG_HDRLEN,
+           QCA_NL80211_VENDOR_SUBCMD_LL_IFACE_STATS_INDEX,
+           GFP_KERNEL);
+    if (!vendor_event)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("cfg80211_vendor_event_alloc failed") );
+        return;
+    }
+
+    pWifiIfaceStat = (tpSirWifiIfaceStat) pData;
+
+    hddLog(VOS_TRACE_LEVEL_INFO,
+           "WMI_LINK_STATS_IFACE Data");
+
+    hddLog(VOS_TRACE_LEVEL_INFO,
+           "LL_STATS_IFACE: "
+           " Mode %u "
+           " MAC %pM "
+           " State %u "
+           " Roaming %u "
+           " capabilities 0x%x "
+           " SSID %s "
+           " BSSID %pM",
+           pWifiIfaceStat->info.mode,
+           pWifiIfaceStat->info.macAddr,
+           pWifiIfaceStat->info.state,
+           pWifiIfaceStat->info.roaming,
+           pWifiIfaceStat->info.capabilities,
+           pWifiIfaceStat->info.ssid,
+           pWifiIfaceStat->info.bssid);
+
+    hddLog(VOS_TRACE_LEVEL_INFO,
+           " AP country str: %c%c%c",
+           pWifiIfaceStat->info.apCountryStr[0],
+           pWifiIfaceStat->info.apCountryStr[1],
+           pWifiIfaceStat->info.apCountryStr[2]);
+
+
+    hddLog(VOS_TRACE_LEVEL_INFO,
+           " Country Str Association: %c%c%c",
+           pWifiIfaceStat->info.countryStr[0],
+           pWifiIfaceStat->info.countryStr[1],
+           pWifiIfaceStat->info.countryStr[2]);
+
+    hddLog(VOS_TRACE_LEVEL_INFO,
+           " beaconRx %u "
+           " mgmtRx %u "
+           " mgmtActionRx  %u "
+           " mgmtActionTx %u "
+           " rssiMgmt %u "
+           " rssiData %u "
+           " rssiAck  %u",
+           pWifiIfaceStat->beaconRx,
+           pWifiIfaceStat->mgmtRx,
+           pWifiIfaceStat->mgmtActionRx,
+           pWifiIfaceStat->mgmtActionTx,
+           pWifiIfaceStat->rssiMgmt,
+           pWifiIfaceStat->rssiData,
+           pWifiIfaceStat->rssiAck );
+
+
+    {
+        int i;
+        for (i = 0 ; i < WIFI_AC_MAX; i ++)
+        {
+            hddLog(VOS_TRACE_LEVEL_INFO,
+
+                   " %d) LL_STATS IFACE: "
+                   " ac:  %u  txMpdu: %u "
+                   " rxMpdu: %u txMcast: %u "
+                   " rxMcast: %u  rxAmpdu: %u "
+                   " txAmpdu:  %u  mpduLost: %u "
+                   " retries: %u  retriesShort: %u "
+                   " retriesLong: %u  contentionTimeMin: %u "
+                   " contentionTimeMax: %u  contentionTimeAvg: %u "
+                   " contentionNumSamples: %u",
+                   i,
+                   pWifiIfaceStat->AccessclassStats[i].ac,
+                   pWifiIfaceStat->AccessclassStats[i].txMpdu,
+                   pWifiIfaceStat->AccessclassStats[i].rxMpdu,
+                   pWifiIfaceStat->AccessclassStats[i].txMcast,
+                   pWifiIfaceStat->AccessclassStats[i].rxMcast,
+                   pWifiIfaceStat->AccessclassStats[i].rxAmpdu,
+                   pWifiIfaceStat->AccessclassStats[i].txAmpdu,
+                   pWifiIfaceStat->AccessclassStats[i].mpduLost,
+                   pWifiIfaceStat->AccessclassStats[i].retries,
+                   pWifiIfaceStat->
+                       AccessclassStats[i].retriesShort,
+                   pWifiIfaceStat->AccessclassStats[i].retriesLong,
+                   pWifiIfaceStat->
+                       AccessclassStats[i].contentionTimeMin,
+                   pWifiIfaceStat->
+                       AccessclassStats[i].contentionTimeMax,
+                   pWifiIfaceStat->
+                       AccessclassStats[i].contentionTimeAvg,
+                   pWifiIfaceStat->
+                       AccessclassStats[i].contentionNumSamples);
+
+        }
+    }
+
+    if (FALSE == hdd_get_interface_info( pAdapter,
+                                        &pWifiIfaceStat->info))
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("hdd_get_interface_info get fail") );
+        kfree_skb(vendor_event);
+        return;
+    }
+
+    if (FALSE == put_wifi_iface_stats( pWifiIfaceStat,
+                                       vendor_event))
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("put_wifi_iface_stats fail") );
+        kfree_skb(vendor_event);
+        return;
+    }
+    cfg80211_vendor_event(vendor_event, GFP_KERNEL);
+}
+
+/*
+ * hdd_link_layer_process_radio_stats () - This function is called after
+ * receiving Link Layer Radio statistics from FW.This function converts
+ * the firmware data to the NL data and sends the same to the kernel/upper
+ * layers.
+ */
+static v_VOID_t hdd_link_layer_process_radio_stats(hdd_adapter_t *pAdapter,
+                                                   v_VOID_t *pData)
+{
+    int status, i;
+    tpSirWifiRadioStat  pWifiRadioStat;
+    tpSirWifiChannelStats pWifiChannelStats;
+    struct sk_buff *vendor_event;
+    hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
+    struct nlattr *chList;
+
+    status = wlan_hdd_validate_context(pHddCtx);
+    if (0 != status)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("HDD context is not valid") );
+        return;
+    }
+    pWifiRadioStat = (tpSirWifiRadioStat) pData;
+
+    hddLog(VOS_TRACE_LEVEL_INFO,
+           "LL_STATS_RADIO"
+           " radio is %d onTime is %u "
+           " txTime is %u  rxTime is %u "
+           " onTimeScan is %u  onTimeNbd is %u "
+           " onTimeGscan is %u onTimeRoamScan is %u "
+           " onTimePnoScan is %u  onTimeHs20 is %u "
+           " numChannels is %u",
+           pWifiRadioStat->radio, pWifiRadioStat->onTime,
+           pWifiRadioStat->txTime, pWifiRadioStat->rxTime,
+           pWifiRadioStat->onTimeScan, pWifiRadioStat->onTimeNbd,
+           pWifiRadioStat->onTimeGscan,
+           pWifiRadioStat->onTimeRoamScan,
+           pWifiRadioStat->onTimePnoScan,
+           pWifiRadioStat->onTimeHs20,
+           pWifiRadioStat->numChannels);
+    /*
+     * Allocate a size of 4096 for the Radio stats comprising
+     * sizeof (tSirWifiRadioStat) + numChannels * sizeof
+     * (tSirWifiChannelStats).Each channel data is put with an
+     * NL attribute.The size of 4096 is considered assuming that
+     * number of channels shall not exceed beyond  60 with the
+     * sizeof (tSirWifiChannelStats) being 24 bytes.
+     */
+
+    vendor_event = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
+           LL_STATS_EVENT_BUF_SIZE + NLMSG_HDRLEN ,
+           QCA_NL80211_VENDOR_SUBCMD_LL_RADIO_STATS_INDEX,
+           GFP_KERNEL);
+
+    if (!vendor_event)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("cfg80211_vendor_event_alloc failed") );
+        return;
+    }
+
+    if (nla_put_u32(vendor_event,
+             QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID,
+             pWifiRadioStat->radio)      ||
+        nla_put_u32(vendor_event,
+             QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME,
+             pWifiRadioStat->onTime)     ||
+        nla_put_u32(vendor_event,
+             QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME,
+             pWifiRadioStat->txTime)     ||
+        nla_put_u32(vendor_event,
+             QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME,
+             pWifiRadioStat->rxTime)     ||
+        nla_put_u32(vendor_event,
+             QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN,
+             pWifiRadioStat->onTimeScan) ||
+        nla_put_u32(vendor_event,
+             QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD,
+             pWifiRadioStat->onTimeNbd)  ||
+        nla_put_u32(vendor_event,
+             QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN,
+             pWifiRadioStat->onTimeGscan)||
+        nla_put_u32(vendor_event,
+             QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN,
+             pWifiRadioStat->onTimeRoamScan) ||
+        nla_put_u32(vendor_event,
+             QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN,
+             pWifiRadioStat->onTimePnoScan) ||
+        nla_put_u32(vendor_event,
+             QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20,
+             pWifiRadioStat->onTimeHs20)    ||
+        nla_put_u32(vendor_event,
+             QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS,
+             pWifiRadioStat->numChannels))
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+                FL("QCA_WLAN_VENDOR_ATTR put fail"));
+        kfree_skb(vendor_event);
+        return ;
+    }
+
+    chList = nla_nest_start(vendor_event,
+             QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO);
+    for (i = 0; i < pWifiRadioStat->numChannels; i++)
+    {
+        struct nlattr *chInfo;
+
+        pWifiChannelStats = (tpSirWifiChannelStats) ((uint8*)
+                pWifiRadioStat->channels +
+                (i * sizeof(tSirWifiChannelStats)));
+
+        hddLog(VOS_TRACE_LEVEL_INFO,
+               " %d) Channel Info"
+               "  width is %u "
+               "  CenterFreq %u "
+               "  CenterFreq0 %u "
+               "  CenterFreq1 %u "
+               "  onTime %u "
+               "  ccaBusyTime %u",
+               i,
+               pWifiChannelStats->channel.width,
+               pWifiChannelStats->channel.centerFreq,
+               pWifiChannelStats->channel.centerFreq0,
+               pWifiChannelStats->channel.centerFreq1,
+               pWifiChannelStats->onTime,
+               pWifiChannelStats->ccaBusyTime);
+
+
+        chInfo = nla_nest_start(vendor_event, i);
+
+        if (nla_put_u32(vendor_event,
+                QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH,
+                pWifiChannelStats->channel.width) ||
+            nla_put_u32(vendor_event,
+                QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ,
+                pWifiChannelStats->channel.centerFreq) ||
+            nla_put_u32(vendor_event,
+                QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0,
+                pWifiChannelStats->channel.centerFreq0)  ||
+            nla_put_u32(vendor_event,
+                QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1,
+                pWifiChannelStats->channel.centerFreq1)    ||
+            nla_put_u32(vendor_event,
+                QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME,
+                pWifiChannelStats->onTime)  ||
+            nla_put_u32(vendor_event,
+                QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME,
+                pWifiChannelStats->ccaBusyTime))
+        {
+            hddLog(VOS_TRACE_LEVEL_ERROR,
+                   FL("cfg80211_vendor_event_alloc failed") );
+            kfree_skb(vendor_event);
+            return ;
+        }
+        nla_nest_end(vendor_event, chInfo);
+    }
+    nla_nest_end(vendor_event, chList);
+
+    cfg80211_vendor_event(vendor_event, GFP_KERNEL);
+    return;
+}
+
+/*
+ * hdd_link_layer_stats_ind_callback () - This function is called after
+ * receiving Link Layer indications from FW.This callback converts the firmware
+ * data to the NL data and send the same to the kernel/upper layers.
+ */
+static void hdd_link_layer_stats_ind_callback ( void *pCtx,
+                                                int indType,
+                                                void *pRsp )
+{
+    hdd_adapter_t *pAdapter = (hdd_adapter_t *)pCtx;
+    hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
+    int status;
+
+    status = wlan_hdd_validate_context(pHddCtx);
+
+    if (0 != status)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("HDD context is not valid"));
+        return;
+    }
+
+    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+            "%s: Link Layer Indication indType: %d", __func__, indType);
+    switch (indType)
+    {
+    case SIR_HAL_LL_STATS_RESULTS_RSP:
+        {
+            tpSirLLStatsResults linkLayerStatsResults =
+                (tpSirLLStatsResults)pRsp;
+
+
+            hddLog(VOS_TRACE_LEVEL_INFO,
+                    FL("RESPONSE SIR_HAL_LL_STATS_RESULTS_RSP") );
+            hddLog(VOS_TRACE_LEVEL_INFO,
+                    "LL_STATS RESULTS RESPONSE paramID = 0x%x",
+                    linkLayerStatsResults->paramId);
+            hddLog(VOS_TRACE_LEVEL_INFO,
+                    "LL_STATS RESULTS RESPONSE ifaceId = %u",
+                    linkLayerStatsResults->ifaceId);
+            hddLog(VOS_TRACE_LEVEL_INFO,
+                    "LL_STATS RESULTS RESPONSE respId = %u",
+                    linkLayerStatsResults->respId);
+            hddLog(VOS_TRACE_LEVEL_INFO,
+                    "LL_STATS RESULTS RESPONSE moreResultToFollow = %u",
+                    linkLayerStatsResults->moreResultToFollow);
+            hddLog(VOS_TRACE_LEVEL_INFO,
+                    "LL_STATS RESULTS RESPONSE result = %p",
+                    linkLayerStatsResults->result);
+            if ( linkLayerStatsResults->paramId & WMI_LINK_STATS_RADIO )
+            {
+                hdd_link_layer_process_radio_stats(pAdapter,
+                                (v_VOID_t *)linkLayerStatsResults->result);
+            }
+            else if ( linkLayerStatsResults->paramId & WMI_LINK_STATS_IFACE )
+            {
+                hdd_link_layer_process_iface_stats(pAdapter,
+                                (v_VOID_t *)linkLayerStatsResults->result);
+            }
+            else if ( linkLayerStatsResults->paramId &
+                    WMI_LINK_STATS_ALL_PEER )
+            {
+                hdd_link_layer_process_peer_stats(pAdapter,
+                                (v_VOID_t *)linkLayerStatsResults->result);
+            } /* WMI_LINK_STATS_ALL_PEER */
+            else
+            {
+                hddLog(VOS_TRACE_LEVEL_ERROR,
+                        FL("INVALID LL_STATS_NOTIFY RESPONSE ***********"));
+            }
+
+            break;
+        }
+        default:
+            hddLog(VOS_TRACE_LEVEL_ERROR, "invalid event type %d", indType);
+            break;
+    }
+    return;
+}
+
+const struct
+nla_policy
+qca_wlan_vendor_ll_set_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX +1] =
+{
+    [QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD] =
+    { .type = NLA_U32 },
+    [QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING] =
+    { .type = NLA_U32 },
+};
+
+static int wlan_hdd_cfg80211_ll_stats_set(struct wiphy *wiphy,
+        struct wireless_dev *wdev,
+        void *data,
+        int data_len)
+{
+    int status;
+    struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX + 1];
+    tpSirLLStatsSetReq pLinkLayerStatsSetReq;
+    struct net_device *dev = wdev->netdev;
+    hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
+    hdd_context_t *pHddCtx = wiphy_priv(wiphy);
+
+    status = wlan_hdd_validate_context(pHddCtx);
+    if (0 != status)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("HDD context is not valid"));
+        return -EINVAL;
+    }
+
+    if (NULL == pAdapter)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("HDD adapter is Null"));
+        return -ENODEV;
+    }
+
+    if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX,
+           (struct nlattr *)data,
+           data_len, qca_wlan_vendor_ll_set_policy))
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL() );
+        return -EINVAL;
+    }
+    if (!tb_vendor
+            [QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD])
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL("MPDU size Not present"));
+        return -EINVAL;
+    }
+    if (!tb_vendor[
+         QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING])
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR, FL(" Stats Gathering Not Present"));
+        return -EINVAL;
+    }
+    pLinkLayerStatsSetReq = vos_mem_malloc(sizeof(tSirLLStatsSetReq));
+    if (NULL == pLinkLayerStatsSetReq)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL(" Unable to allocate memory to pLinkLayerStatsSetReq") );
+        return -ENOMEM;
+    }
+    // Shall take the request Id if the Upper layers pass. 1 For now.
+    pLinkLayerStatsSetReq->reqId = 1;
+
+    pLinkLayerStatsSetReq->mpduSizeThreshold =
+        nla_get_u32(
+            tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD]);
+
+    pLinkLayerStatsSetReq->aggressiveStatisticsGathering =
+        nla_get_u32(
+            tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING]);
+
+    /* staId 0 in Firmware is reserved for Broadcast/Multicast data.
+     * Hence the interface staId start from 1. Hence the staId matching the
+     * interface in the firmware is sessionId + 1.
+     */
+    pLinkLayerStatsSetReq->staId = pAdapter->sessionId + 1;
+
+
+    hddLog(VOS_TRACE_LEVEL_INFO,
+           "LL_STATS_SET reqId = %d",
+           pLinkLayerStatsSetReq->reqId);
+    hddLog(VOS_TRACE_LEVEL_INFO,
+            "LL_STATS_SET staId = %d", pLinkLayerStatsSetReq->staId);
+    hddLog(VOS_TRACE_LEVEL_INFO,
+            "LL_STATS_SET mpduSizeThreshold = %d",
+            pLinkLayerStatsSetReq->mpduSizeThreshold);
+    hddLog(VOS_TRACE_LEVEL_INFO,
+            "LL_STATS_SET aggressive Statistics Gathering  = %d",
+            pLinkLayerStatsSetReq->aggressiveStatisticsGathering);
+
+    if (eHAL_STATUS_SUCCESS != sme_SetLinkLayerStatsIndCB(
+                               pHddCtx->hHal,
+                               pAdapter->sessionId,
+                               hdd_link_layer_stats_ind_callback,
+                               pAdapter))
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR, "%s:"
+           "sme_SetLinkLayerStatsIndCB Failed", __func__);
+        vos_mem_free(pLinkLayerStatsSetReq);
+        return -EINVAL;
+
+    }
+    if (eHAL_STATUS_SUCCESS != sme_LLStatsSetReq( pHddCtx->hHal,
+                                            pLinkLayerStatsSetReq))
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR, "%s:"
+           "sme_LLStatsSetReq Failed", __func__);
+        vos_mem_free(pLinkLayerStatsSetReq);
+        return -EINVAL;
+    }
+
+    pAdapter->isLinkLayerStatsSet = 1;
+
+    return 0;
+}
+
+const struct
+nla_policy
+qca_wlan_vendor_ll_get_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX +1] =
+{
+    /* Unsigned 32bit value provided by the caller issuing the GET stats
+     * command. When reporting
+     * the stats results, the driver uses the same value to indicate
+     * which GET request the results
+     * correspond to.
+     */
+    [QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID] = { .type = NLA_U32 },
+
+    /* Unsigned 32bit value . bit mask to identify what statistics are
+         requested for retrieval */
+    [QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK] = { .type = NLA_U32 },
+};
+
+static int wlan_hdd_cfg80211_ll_stats_get(struct wiphy *wiphy,
+                                          struct wireless_dev *wdev,
+                                          void *data,
+                                          int data_len)
+{
+    hdd_context_t *pHddCtx = wiphy_priv(wiphy);
+    struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX + 1];
+    tpSirLLStatsGetReq pLinkLayerStatsGetReq;
+    struct net_device *dev = wdev->netdev;
+    hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
+    int status;
+
+    status = wlan_hdd_validate_context(pHddCtx);
+    if (0 != status)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("HDD context is not valid"));
+        return -EINVAL ;
+    }
+
+    if (NULL == pAdapter)
+    {
+        hddLog(VOS_TRACE_LEVEL_FATAL,
+               "%s: HDD adapter is Null", __func__);
+        return -ENODEV;
+    }
+
+    if (!pAdapter->isLinkLayerStatsSet)
+    {
+        hddLog(VOS_TRACE_LEVEL_FATAL,
+               "%s: isLinkLayerStatsSet : %d",
+               __func__, pAdapter->isLinkLayerStatsSet);
+        return -EINVAL;
+    }
+
+    if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX,
+            (struct nlattr *)data,
+            data_len, qca_wlan_vendor_ll_get_policy))
+    {
+       hddLog(VOS_TRACE_LEVEL_ERROR, FL() );
+       return -EINVAL;
+    }
+
+    if (!tb_vendor
+            [QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID])
+    {
+       hddLog(VOS_TRACE_LEVEL_ERROR, FL("Request Id Not present"));
+       return -EINVAL;
+    }
+
+    if (!tb_vendor
+            [QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK])
+    {
+       hddLog(VOS_TRACE_LEVEL_ERROR, FL("Req Mask Not present"));
+       return -EINVAL;
+    }
+
+    pLinkLayerStatsGetReq = vos_mem_malloc(sizeof(tSirLLStatsGetReq));
+
+    if (NULL == pLinkLayerStatsGetReq)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("Unable to allocate memory to pLinkLayerStatsGetReq"));
+        return -ENOMEM;
+    }
+
+    pLinkLayerStatsGetReq->reqId =
+        nla_get_u32( tb_vendor[
+            QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID]);
+    pLinkLayerStatsGetReq->paramIdMask =
+        nla_get_u32( tb_vendor[
+            QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK]);
+
+    /* staId 0 in Firmware is reserved for Broadcast/Multicast data.
+     * Hence the interface staId start from 1. Hence the staId matching the
+     * interface in the firmware is sessionId + 1.
+     */
+    pLinkLayerStatsGetReq->staId = pAdapter->sessionId + 1;
+
+    hddLog(VOS_TRACE_LEVEL_INFO,
+           "LL_STATS_GET reqId = %d", pLinkLayerStatsGetReq->reqId);
+    hddLog(VOS_TRACE_LEVEL_INFO,
+           "LL_STATS_GET staId = %d", pLinkLayerStatsGetReq->staId);
+    hddLog(VOS_TRACE_LEVEL_INFO,
+           "LL_STATS_GET paramIdMask = %d",
+           pLinkLayerStatsGetReq->paramIdMask);
+
+    if (eHAL_STATUS_SUCCESS  != sme_LLStatsGetReq( pHddCtx->hHal,
+                                                pLinkLayerStatsGetReq))
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR, "%s:"
+               "sme_LLStatsGetReq Failed", __func__);
+        vos_mem_free(pLinkLayerStatsGetReq);
+        return -EINVAL;
+    }
+    return 0;
+}
+
+const struct
+nla_policy
+qca_wlan_vendor_ll_clr_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX +1] =
+{
+    [QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK] = {.type = NLA_U32 },
+    [QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ] = {.type = NLA_U8 },
+    [QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK] = {.type = NLA_U32 },
+    [QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP] = {.type = NLA_U8 },
+};
+
+static int wlan_hdd_cfg80211_ll_stats_clear(struct wiphy *wiphy,
+                                            struct wireless_dev *wdev,
+                                            void *data,
+                                            int data_len)
+{
+    hdd_context_t *pHddCtx = wiphy_priv(wiphy);
+    struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX + 1];
+    tpSirLLStatsClearReq pLinkLayerStatsClearReq;
+    struct net_device *dev = wdev->netdev;
+    hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
+    u32 statsClearReqMask;
+    u8 stopReq;
+    int status;
+
+    status = wlan_hdd_validate_context(pHddCtx);
+    if (0 != status)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("HDD context is not valid"));
+        return -EINVAL;
+    }
+
+    if (NULL == pAdapter)
+    {
+        hddLog(VOS_TRACE_LEVEL_FATAL,
+                   "%s: HDD adapter is Null", __func__);
+        return -ENODEV;
+    }
+
+    if (!pAdapter->isLinkLayerStatsSet)
+    {
+        hddLog(VOS_TRACE_LEVEL_FATAL,
+                   "%s: isLinkLayerStatsSet : %d",
+                   __func__, pAdapter->isLinkLayerStatsSet);
+        return -EINVAL;
+    }
+
+    if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX,
+            (struct nlattr *)data,
+            data_len, qca_wlan_vendor_ll_clr_policy))
+    {
+       hddLog(VOS_TRACE_LEVEL_ERROR, FL() );
+       return -EINVAL;
+    }
+
+    if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK] ||
+
+        !tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ])
+    {
+       hddLog(VOS_TRACE_LEVEL_ERROR, FL("Error in LL_STATS CLR CONFIG PARA") );
+       return -EINVAL;
+
+    }
+
+    pLinkLayerStatsClearReq = vos_mem_malloc(sizeof(tSirLLStatsClearReq));
+    if (NULL == pLinkLayerStatsClearReq)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+               FL("Unable to allocate memory to pLinkLayerStatsClearReq"));
+        return -ENOMEM;
+    }
+
+    statsClearReqMask = pLinkLayerStatsClearReq->statsClearReqMask =
+        nla_get_u32(
+            tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK]);
+
+    stopReq = pLinkLayerStatsClearReq->stopReq =
+        nla_get_u8(
+            tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ]);
+
+    // Shall take the request Id if the Upper layers pass. 1 For now.
+    pLinkLayerStatsClearReq->reqId = 1;
+
+    /* staId 0 in Firmware is reserved for Broadcast/Multicast data.
+     * Hence the interface staId start from 1. Hence the staId matching the
+     * interface in the firmware is sessionId + 1.
+     */
+    pLinkLayerStatsClearReq->staId = pAdapter->sessionId + 1;
+
+    hddLog(VOS_TRACE_LEVEL_INFO,
+            "LL_STATS_CLEAR reqId = %d", pLinkLayerStatsClearReq->reqId);
+    hddLog(VOS_TRACE_LEVEL_INFO,
+            "LL_STATS_CLEAR staId = %d", pLinkLayerStatsClearReq->staId);
+    hddLog(VOS_TRACE_LEVEL_INFO,
+            "LL_STATS_CLEAR statsClearReqMask = 0x%X",
+            pLinkLayerStatsClearReq->statsClearReqMask);
+    hddLog(VOS_TRACE_LEVEL_INFO,
+            "LL_STATS_CLEAR stopReq  = %d",
+            pLinkLayerStatsClearReq->stopReq);
+
+    if (eHAL_STATUS_SUCCESS == sme_LLStatsClearReq(pHddCtx->hHal,
+                                                     pLinkLayerStatsClearReq))
+    {
+        struct sk_buff *temp_skbuff;
+        temp_skbuff = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
+                                2 * sizeof(u32) +
+                            NLMSG_HDRLEN);
+
+        if (temp_skbuff != NULL)
+        {
+
+            if (nla_put_u32(temp_skbuff,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK,
+                    statsClearReqMask) ||
+                 nla_put_u32(temp_skbuff,
+                    QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP,
+                    stopReq))
+            {
+                 hddLog(VOS_TRACE_LEVEL_ERROR, FL("LL_STATS_CLR put fail"));
+                 kfree_skb(temp_skbuff);
+                 return -EINVAL;
+            }
+            /* If the ask is to stop the stats collection as part of clear
+             * (stopReq = 1) , ensure that no further requests of get
+             * go to the firmware by having isLinkLayerStatsSet set to 0.
+             * However it the stopReq as part of the clear request is 0 ,
+             * the request to get the statistics are ehonoured as in this
+             * case the firmware is just asked to clear the statistics.
+             */
+            if (pLinkLayerStatsClearReq->stopReq == 1)
+                pAdapter->isLinkLayerStatsSet = 0;
+            return cfg80211_vendor_cmd_reply(temp_skbuff);
+        }
+        return -ENOMEM;
+    }
+    vos_mem_free(pLinkLayerStatsClearReq);
+    return -EINVAL;
+}
+#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
+
+const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] =
+{
+#ifdef WLAN_FEATURE_LINK_LAYER_STATS
+    {
+        .info.vendor_id = QCA_NL80211_VENDOR_ID,
+        .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR,
+        .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+            WIPHY_VENDOR_CMD_NEED_NETDEV |
+            WIPHY_VENDOR_CMD_NEED_RUNNING,
+        .doit = wlan_hdd_cfg80211_ll_stats_clear
+    },
+
+    {
+        .info.vendor_id = QCA_NL80211_VENDOR_ID,
+        .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET,
+        .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+            WIPHY_VENDOR_CMD_NEED_NETDEV |
+            WIPHY_VENDOR_CMD_NEED_RUNNING,
+        .doit = wlan_hdd_cfg80211_ll_stats_set
+    },
+
+    {
+        .info.vendor_id = QCA_NL80211_VENDOR_ID,
+        .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET,
+        .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+            WIPHY_VENDOR_CMD_NEED_NETDEV |
+            WIPHY_VENDOR_CMD_NEED_RUNNING,
+        .doit = wlan_hdd_cfg80211_ll_stats_get
+    }
+#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
+};
+
 /* vendor specific events */
-static const struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] =
+static const
+struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] =
 {
 #ifdef FEATURE_WLAN_CH_AVOID
     {
-        .vendor_id = QCOM_NL80211_VENDOR_ID,
-        .subcmd = QCOM_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY
+        .vendor_id = QCA_NL80211_VENDOR_ID,
+        .subcmd = QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY
     },
-#endif /* FEATURE_WLAN_CH_AVOID */
+#endif /* FEATURE_WLAN_CH_AVOID Index = 0*/
+#ifdef WLAN_FEATURE_LINK_LAYER_STATS
+    {
+        /* Index = 1*/
+        .vendor_id = QCA_NL80211_VENDOR_ID,
+        .subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET
+    },
+    {
+        /* Index = 2*/
+        .vendor_id = QCA_NL80211_VENDOR_ID,
+        .subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET
+    },
+    {
+        /* Index = 3*/
+        .vendor_id = QCA_NL80211_VENDOR_ID,
+        .subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR
+    },
+    {
+        /* Index = 4*/
+        .vendor_id = QCA_NL80211_VENDOR_ID,
+        .subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_RADIO_RESULTS
+    },
+    {
+        /* Index = 5*/
+        .vendor_id = QCA_NL80211_VENDOR_ID,
+        .subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_IFACE_RESULTS
+    },
+    {
+        /* Index = 6*/
+        .vendor_id = QCA_NL80211_VENDOR_ID,
+        .subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_PEERS_RESULTS
+    },
+#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
+
 };
 
 /*
@@ -625,6 +1941,7 @@
         return NULL;
     }
 
+
     return wiphy;
 }
 
@@ -868,7 +2185,8 @@
     wiphy->max_remain_on_channel_duration = 1000;
 #endif
 
-    wiphy->n_vendor_commands = 0;
+    wiphy->n_vendor_commands = ARRAY_SIZE(hdd_wiphy_vendor_commands);
+    wiphy->vendor_commands = hdd_wiphy_vendor_commands;
     wiphy->vendor_events = wlan_hdd_cfg80211_vendor_events;
     wiphy->n_vendor_events = ARRAY_SIZE(wlan_hdd_cfg80211_vendor_events);
 
diff --git a/CORE/MAC/inc/sirApi.h b/CORE/MAC/inc/sirApi.h
index 8b8bd0c..c58213f 100644
--- a/CORE/MAC/inc/sirApi.h
+++ b/CORE/MAC/inc/sirApi.h
@@ -4752,4 +4752,370 @@
     void             *data;
 }tSirBcnMissRateInfo;
 
+#ifdef WLAN_FEATURE_LINK_LAYER_STATS
+
+typedef struct
+{
+  u32 reqId;
+  u8  staId;
+  u32 mpduSizeThreshold;
+  u32 aggressiveStatisticsGathering;
+}tSirLLStatsSetReq, *tpSirLLStatsSetReq;
+
+typedef struct
+{
+  u32 reqId;
+  u8  staId;
+  u32 paramIdMask;
+}tSirLLStatsGetReq, *tpSirLLStatsGetReq;
+
+typedef struct
+{
+  u32  reqId;
+  u8   staId;
+  u32  statsClearReqMask;
+  u8   stopReq;
+}tSirLLStatsClearReq, *tpSirLLStatsClearReq;
+
+/*---------------------------------------------------------------------------
+  WLAN_HAL_LL_NOTIFY_STATS
+---------------------------------------------------------------------------*/
+
+
+/******************************LINK LAYER Statistics**********************/
+
+typedef int tSirWifiRadio;
+typedef int tSirWifiChannel;
+typedef int tSirwifiTxRate;
+
+/* channel operating width */
+typedef PACKED_PRE enum PACKED_POST
+{
+    WIFI_CHAN_WIDTH_20    = 0,
+    WIFI_CHAN_WIDTH_40    = 1,
+    WIFI_CHAN_WIDTH_80    = 2,
+    WIFI_CHAN_WIDTH_160   = 3,
+    WIFI_CHAN_WIDTH_80P80 = 4,
+    WIFI_CHAN_WIDTH_5     = 5,
+    WIFI_CHAN_WIDTH_10    = 6,
+} tSirWifiChannelWidth;
+
+typedef PACKED_PRE enum PACKED_POST
+{
+    WIFI_DISCONNECTED = 0,
+    WIFI_AUTHENTICATING = 1,
+    WIFI_ASSOCIATING = 2,
+    WIFI_ASSOCIATED = 3,
+    WIFI_EAPOL_STARTED = 4,   // if done by firmware/driver
+    WIFI_EAPOL_COMPLETED = 5, // if done by firmware/driver
+} tSirWifiConnectionState;
+
+typedef PACKED_PRE enum PACKED_POST
+{
+    WIFI_ROAMING_IDLE = 0,
+    WIFI_ROAMING_ACTIVE = 1,
+} tSirWifiRoamState;
+
+typedef PACKED_PRE enum PACKED_POST
+{
+    WIFI_INTERFACE_STA = 0,
+    WIFI_INTERFACE_SOFTAP = 1,
+    WIFI_INTERFACE_IBSS = 2,
+    WIFI_INTERFACE_P2P_CLIENT = 3,
+    WIFI_INTERFACE_P2P_GO = 4,
+    WIFI_INTERFACE_NAN = 5,
+    WIFI_INTERFACE_MESH = 6,
+ } tSirWifiInterfaceMode;
+
+// set for QOS association
+#define WIFI_CAPABILITY_QOS          0x00000001
+// set for protected association (802.11 beacon frame control protected bit set)
+#define WIFI_CAPABILITY_PROTECTED    0x00000002
+// set if 802.11 Extended Capabilities element interworking bit is set
+#define WIFI_CAPABILITY_INTERWORKING 0x00000004
+// set for HS20 association
+#define WIFI_CAPABILITY_HS20         0x00000008
+// set is 802.11 Extended Capabilities element UTF-8 SSID bit is set
+#define WIFI_CAPABILITY_SSID_UTF8    0x00000010
+// set is 802.11 Country Element is present
+#define WIFI_CAPABILITY_COUNTRY      0x00000020
+
+typedef PACKED_PRE struct PACKED_POST
+{
+    /*tSirWifiInterfaceMode*/
+    // interface mode
+    tANI_U8                  mode;
+    // interface mac address (self)
+    tSirMacAddr              macAddr;
+    /*tSirWifiConnectionState*/
+    // connection state (valid for STA, CLI only)
+    tANI_U8                  state;
+    /*tSirWifiRoamState*/
+    // roaming state
+    tANI_U8                  roaming;
+    // WIFI_CAPABILITY_XXX (self)
+    tANI_U32                 capabilities;
+    // null terminated SSID
+    tANI_U8                  ssid[33];
+    // bssid
+    tSirMacAddr              bssid;
+    // country string advertised by AP
+    tANI_U8                  apCountryStr[WNI_CFG_COUNTRY_CODE_LEN];
+    // country string for this association
+    tANI_U8                  countryStr[WNI_CFG_COUNTRY_CODE_LEN];
+} tSirWifiInterfaceInfo, *tpSirWifiInterfaceInfo;
+
+/* channel information */
+typedef PACKED_PRE struct PACKED_POST
+{
+    // channel width (20, 40, 80, 80+80, 160)
+    tSirWifiChannelWidth      width;
+    // primary 20 MHz channel
+    tSirWifiChannel           centerFreq;
+    // center frequency (MHz) first segment
+    tSirWifiChannel           centerFreq0;
+    // center frequency (MHz) second segment
+    tSirWifiChannel           centerFreq1;
+} tSirWifiChannelInfo, *tpSirWifiChannelInfo;
+
+/* wifi rate info */
+typedef PACKED_PRE struct PACKED_POST
+{
+    // 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved
+    tANI_U32 preamble   :3;
+    // 0:1x1, 1:2x2, 3:3x3, 4:4x4
+    tANI_U32 nss        :2;
+    // 0:20MHz, 1:40Mhz, 2:80Mhz, 3:160Mhz
+    tANI_U32 bw         :3;
+    // OFDM/CCK rate code would be as per ieee std in the units of 0.5mbps
+    // HT/VHT it would be mcs index
+    tANI_U32 rateMcsIdx :8;
+    // reserved
+    tANI_U32 reserved  :16;
+    // units of 100 Kbps
+    tANI_U32 bitrate;
+} tSirWifiRate, *tpSirWifiRate;
+
+/* channel statistics */
+typedef PACKED_PRE struct PACKED_POST
+{
+    // channel
+    tSirWifiChannelInfo channel;
+    // msecs the radio is awake (32 bits number accruing over time)
+    tANI_U32          onTime;
+    // msecs the CCA register is busy (32 bits number accruing over time)
+    tANI_U32          ccaBusyTime;
+} tSirWifiChannelStats, *tpSirWifiChannelStats;
+
+/* radio statistics */
+typedef PACKED_PRE struct PACKED_POST
+{
+    // wifi radio (if multiple radio supported)
+    tSirWifiRadio   radio;
+    // msecs the radio is awake (32 bits number accruing over time)
+    tANI_U32        onTime;
+    /* msecs the radio is transmitting
+     * (32 bits number accruing over time)
+     */
+    tANI_U32        txTime;
+    /* msecs the radio is in active receive
+     *(32 bits number accruing over time)
+     */
+    tANI_U32        rxTime;
+    /* msecs the radio is awake due to all scan
+     * (32 bits number accruing over time)
+     */
+    tANI_U32        onTimeScan;
+    /* msecs the radio is awake due to NAN
+     * (32 bits number accruing over time)
+     */
+    tANI_U32        onTimeNbd;
+    /* msecs the radio is awake due to Gscan
+     * (32 bits number accruing over time)
+     */
+    tANI_U32        onTimeGscan;
+    /* msecs the radio is awake due to roam?scan
+     * (32 bits number accruing over time)
+     */
+    tANI_U32        onTimeRoamScan;
+    /* msecs the radio is awake due to PNO scan
+     * (32 bits number accruing over time)
+     */
+    tANI_U32        onTimePnoScan;
+    /* msecs the radio is awake due to HS2.0 scans and GAS exchange
+     * (32 bits number accruing over time)
+     */
+    tANI_U32        onTimeHs20;
+    // number of channels
+    tANI_U32        numChannels;
+    // channel statistics tSirWifiChannelStats
+    tSirWifiChannelStats channels[1];
+} tSirWifiRadioStat, *tpSirWifiRadioStat;
+
+/* per rate statistics */
+typedef PACKED_PRE struct PACKED_POST
+{
+    // rate information
+    tSirWifiRate rate;
+    // number of successfully transmitted data pkts (ACK rcvd)
+    tANI_U32 txMpdu;
+    // number of received data pkts
+    tANI_U32 rxMpdu;
+    // number of data packet losses (no ACK)
+    tANI_U32 mpduLost;
+    // total number of data pkt retries *
+    tANI_U32 retries;
+    // number of short data pkt retries
+    tANI_U32 retriesShort;
+    // number of long data pkt retries
+    tANI_U32 retriesLong;
+} tSirWifiRateStat, *tpSirWifiRateStat;
+
+/* access categories */
+typedef PACKED_PRE enum PACKED_POST
+{
+    WIFI_AC_VO  = 0,
+    WIFI_AC_VI  = 1,
+    WIFI_AC_BE  = 2,
+    WIFI_AC_BK  = 3,
+    WIFI_AC_MAX = 4,
+} tSirWifiTrafficAc;
+
+/* wifi peer type */
+typedef PACKED_PRE enum  PACKED_POST
+{
+    WIFI_PEER_STA,
+    WIFI_PEER_AP,
+    WIFI_PEER_P2P_GO,
+    WIFI_PEER_P2P_CLIENT,
+    WIFI_PEER_NAN,
+    WIFI_PEER_TDLS,
+    WIFI_PEER_INVALID,
+} tSirWifiPeerType;
+
+/* per peer statistics */
+typedef PACKED_PRE struct PACKED_POST
+{
+    // peer type (AP, TDLS, GO etc.)
+    tSirWifiPeerType type;
+    // mac address
+    tSirMacAddr    peerMacAddress;
+    // peer WIFI_CAPABILITY_XXX
+    tANI_U32       capabilities;
+    // number of rates
+    tANI_U32       numRate;
+    // per rate statistics, number of entries  = num_rate
+    tSirWifiRateStat rateStats[1];
+} tSirWifiPeerInfo, *tpSirWifiPeerInfo;
+
+/* per access category statistics */
+typedef PACKED_PRE struct PACKED_POST
+{
+    /*tSirWifiTrafficAc*/
+    // access category (VI, VO, BE, BK)
+    tANI_U8 ac;
+    // number of successfully transmitted unicast data pkts (ACK rcvd)
+    tANI_U32 txMpdu;
+    // number of received unicast mpdus
+    tANI_U32 rxMpdu;
+    // number of succesfully transmitted multicast data packets
+    // STA case: implies ACK received from AP for the unicast
+    // packet in which mcast pkt was sent
+    tANI_U32 txMcast;
+    // number of received multicast data packets
+    tANI_U32 rxMcast;
+    // number of received unicast a-mpdus
+    tANI_U32 rxAmpdu;
+    // number of transmitted unicast a-mpdus
+    tANI_U32 txAmpdu;
+    // number of data pkt losses (no ACK)
+    tANI_U32 mpduLost;
+    // total number of data pkt retries
+    tANI_U32 retries;
+    // number of short data pkt retries
+    tANI_U32 retriesShort;
+    // number of long data pkt retries
+    tANI_U32 retriesLong;
+    // data pkt min contention time (usecs)
+    tANI_U32 contentionTimeMin;
+    // data pkt max contention time (usecs)
+    tANI_U32 contentionTimeMax;
+    // data pkt avg contention time (usecs)
+    tANI_U32 contentionTimeAvg;
+    // num of data pkts used for contention statistics
+    tANI_U32 contentionNumSamples;
+} tSirWifiWmmAcStat, *tpSirWifiWmmAcStat;
+
+/* Interface statistics - corresponding to 2nd most
+ * LSB in wifi statistics bitmap  for getting statistics
+ */
+typedef PACKED_PRE struct PACKED_POST
+{
+    // current state of the interface
+    tSirWifiInterfaceInfo info;
+    // access point beacon received count from connected AP
+    tANI_U32            beaconRx;
+    // access point mgmt frames received count from
+    // connected AP (including Beacon)
+    tANI_U32            mgmtRx;
+    // action frames received count
+    tANI_U32            mgmtActionRx;
+    // action frames transmit count
+    tANI_U32            mgmtActionTx;
+    // access Point Beacon and Management frames RSSI (averaged)
+    tANI_U32            rssiMgmt;
+    // access Point Data Frames RSSI (averaged) from connected AP
+    tANI_U32            rssiData;
+    // access Point ACK RSSI (averaged) from connected AP
+    tANI_U32            rssiAck;
+    // per ac data packet statistics
+    tSirWifiWmmAcStat    AccessclassStats[WIFI_AC_MAX];
+} tSirWifiIfaceStat, *tpSirWifiIfaceStat;
+
+/* Peer statistics - corresponding to 3rd most LSB in
+ * wifi statistics bitmap  for getting statistics
+ */
+typedef PACKED_PRE struct PACKED_POST
+{
+    // number of peers
+    tANI_U32       numPeers;
+    // per peer statistics
+    tSirWifiPeerInfo peerInfo[1];
+} tSirWifiPeerStat, *tpSirWifiPeerStat;
+
+/* wifi statistics bitmap  for getting statistics */
+#define WMI_LINK_STATS_RADIO          0x00000001
+#define WMI_LINK_STATS_IFACE          0x00000002
+#define WMI_LINK_STATS_ALL_PEER       0x00000004
+#define WMI_LINK_STATS_PER_PEER       0x00000008
+
+/* wifi statistics bitmap  for clearing statistics */
+// all radio statistics
+#define WIFI_STATS_RADIO              0x00000001
+// cca_busy_time (within radio statistics)
+#define WIFI_STATS_RADIO_CCA          0x00000002
+// all channel statistics (within radio statistics)
+#define WIFI_STATS_RADIO_CHANNELS     0x00000004
+// all scan statistics (within radio statistics)
+#define WIFI_STATS_RADIO_SCAN         0x00000008
+// all interface statistics
+#define WIFI_STATS_IFACE              0x00000010
+// all tx rate statistics (within interface statistics)
+#define WIFI_STATS_IFACE_TXRATE       0x00000020
+// all ac statistics (within interface statistics)
+#define WIFI_STATS_IFACE_AC           0x00000040
+// all contention (min, max, avg) statistics (within ac statistics)
+#define WIFI_STATS_IFACE_CONTENTION   0x00000080
+
+typedef PACKED_PRE struct PACKED_POST
+{
+   tANI_U32 paramId;
+   tANI_U8  ifaceId;
+   tANI_U32 respId;
+   tANI_U32 moreResultToFollow;
+   tANI_U8  result[1];
+}  tSirLLStatsResults, *tpSirLLStatsResults;
+
+#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
+
 #endif /* __SIR_API_H */
diff --git a/CORE/SME/inc/sme_Api.h b/CORE/SME/inc/sme_Api.h
index 649f6d0..7cdb8a5 100644
--- a/CORE/SME/inc/sme_Api.h
+++ b/CORE/SME/inc/sme_Api.h
@@ -112,6 +112,68 @@
     tANI_U32      fDeferIMPSTime;
 } tSmeConfigParams, *tpSmeConfigParams;
 
+#ifdef WLAN_FEATURE_LINK_LAYER_STATS
+
+/* ---------------------------------------------------------------------------
+    \fn sme_LLStatsSetReq
+    \brief  API to set link layer stats request to FW
+    \param  hHal - The handle returned by macOpen.
+
+    \Param  pStatsReq - a pointer to a caller allocated object of
+     typedef struct tSirLLStatsSetReq, signifying the parameters to link layer
+     stats set.
+
+    \return eHalStatus
+  ---------------------------------------------------------------------------*/
+eHalStatus sme_LLStatsSetReq(tHalHandle hHal, tSirLLStatsSetReq *pStatsReq);
+
+/* ---------------------------------------------------------------------------
+    \fn sme_LLStatsGetReq
+    \brief  API to get link layer stats request to FW
+    \param  hHal - The handle returned by macOpen.
+
+    \Param  pStatsReq - a pointer to a caller allocated object of
+     typedef struct tSirLLStatsGetReq, signifying the parameters to link layer
+     stats get.
+
+    \return eHalStatus
+  ---------------------------------------------------------------------------*/
+eHalStatus sme_LLStatsGetReq(tHalHandle hHal, tSirLLStatsGetReq *pStatsReq);
+
+/* ---------------------------------------------------------------------------
+    \fn sme_LLStatsClearReq
+    \brief  API to clear link layer stats request to FW
+    \param  hHal - The handle returned by macOpen.
+
+    \Param  pStatsReq - a pointer to a caller allocated object of
+     typedef struct tSirLLStatsClearReq, signifying the parameters to link layer
+     stats clear.
+
+    \return eHalStatus
+  ---------------------------------------------------------------------------*/
+eHalStatus sme_LLStatsClearReq(tHalHandle hHal, tSirLLStatsClearReq *pStatsReq);
+
+/* ---------------------------------------------------------------------------
+    \fn sme_SetLinkLayerStatsIndCB
+    \brief  API to trigger Link Layer stats result indications from from FW
+    \param  hHal - The handle returned by macOpen.
+    \param  sessionId - session ID
+    \param  callbackRoutine - HDD callback which needs to be invoked after
+            getting get Link Layer Statistics results from FW
+    \param  callbackContext - pAdapter context
+    \return eHalStatus
+  ---------------------------------------------------------------------------*/
+eHalStatus
+sme_SetLinkLayerStatsIndCB
+(
+    tHalHandle hHal, tANI_U8 sessionId,
+    void (*callbackRoutine) (void *callbackCtx, int indType, void *pRsp),
+    void *callbackContext
+);
+
+
+#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
+
 typedef enum
 {
     eSME_ROAM_TRIGGER_NONE = 0,
diff --git a/CORE/SME/src/sme_common/sme_Api.c b/CORE/SME/src/sme_common/sme_Api.c
index 127f635..645fb5e 100644
--- a/CORE/SME/src/sme_common/sme_Api.c
+++ b/CORE/SME/src/sme_common/sme_Api.c
@@ -10546,7 +10546,6 @@
 }
 #endif /* FEATURE_WLAN_CH_AVOID */
 
-
 void activeListCmdTimeoutHandle(void *userData)
 {
     if (NULL == userData)
@@ -10557,6 +10556,182 @@
     smeGetCommandQStatus((tHalHandle) userData);
 }
 
+#ifdef WLAN_FEATURE_LINK_LAYER_STATS
+
+/* ---------------------------------------------------------------------------
+    \fn sme_LLStatsSetReq
+    \brief  API to set link layer stats request to FW
+    \param  hHal - The handle returned by macOpen.
+
+    \Param  pStatsReq - a pointer to a caller allocated object of
+     typedef struct tSirLLStatsSetReq, signifying the parameters to link layer
+     stats set.
+
+    \return eHalStatus
+  ---------------------------------------------------------------------------*/
+eHalStatus sme_LLStatsSetReq(tHalHandle hHal,
+                                tSirLLStatsSetReq *pLinkLayerStatsSetReq)
+{
+    tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
+    vos_msg_t msg;
+    eHalStatus status = eHAL_STATUS_FAILURE;
+
+    VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
+      "%s:  pLinkLayerStatsSetReq.mpdu_size = %u", __func__,
+        pLinkLayerStatsSetReq->mpduSizeThreshold);
+
+    VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
+      " pLinkLayerStatsSetReq->mpdu_size = %u",
+      pLinkLayerStatsSetReq->mpduSizeThreshold);
+
+    if ( eHAL_STATUS_SUCCESS == ( status = sme_AcquireGlobalLock( &pMac->sme )))
+    {
+        msg.type = WDA_LINK_LAYER_STATS_SET_REQ;
+        msg.reserved = 0;
+        msg.bodyptr = pLinkLayerStatsSetReq;
+
+        if(VOS_STATUS_SUCCESS != vos_mq_post_message(VOS_MODULE_ID_WDA, &msg))
+        {
+            VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
+                    "Not able to post SIR_HAL_LL_STATS_SET message to HAL", __func__);
+            status = eHAL_STATUS_FAILURE;
+        }
+        sme_ReleaseGlobalLock( &pMac->sme );
+    }
+    else
+    {
+        VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
+                "sme_AcquireGlobalLock error", __func__);
+    }
+    return status;
+}
+
+/* ---------------------------------------------------------------------------
+    \fn sme_LLStatsGetReq
+    \brief  API to get link layer stats request to FW
+    \param  hHal - The handle returned by macOpen.
+
+    \Param  pStatsReq - a pointer to a caller allocated object of
+     typedef struct tSirLLStatsGetReq, signifying the parameters to link layer
+     stats get.
+
+    \return eHalStatus
+  ---------------------------------------------------------------------------*/
+eHalStatus sme_LLStatsGetReq(tHalHandle hHal,
+                                tSirLLStatsGetReq *pLinkLayerStatsGetReq)
+{
+    tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
+    vos_msg_t msg;
+    eHalStatus status = eHAL_STATUS_FAILURE;
+
+
+    if ( eHAL_STATUS_SUCCESS == ( status = sme_AcquireGlobalLock( &pMac->sme )))
+    {
+        msg.type = WDA_LINK_LAYER_STATS_GET_REQ;
+        msg.reserved = 0;
+        msg.bodyptr = pLinkLayerStatsGetReq;
+        if(VOS_STATUS_SUCCESS != vos_mq_post_message(VOS_MODULE_ID_WDA, &msg))
+        {
+            VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
+                    "Not able to post SIR_HAL_LL_STATS_GET message to HAL", __func__);
+            status = eHAL_STATUS_FAILURE;
+        }
+        sme_ReleaseGlobalLock( &pMac->sme );
+    }
+    else
+    {
+        VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
+                "sme_AcquireGlobalLock error", __func__);
+    }
+    return status;
+}
+
+/* ---------------------------------------------------------------------------
+    \fn sme_LLStatsClearReq
+    \brief  API to clear link layer stats request to FW
+    \param  hHal - The handle returned by macOpen.
+
+    \Param  pStatsReq - a pointer to a caller allocated object of
+     typedef struct tSirLLStatsClearReq, signifying the parameters to link layer
+     stats clear.
+
+    \return eHalStatus
+  ---------------------------------------------------------------------------*/
+eHalStatus sme_LLStatsClearReq(tHalHandle hHal,
+                                tSirLLStatsClearReq *pLinkLayerStatsClear)
+{
+    tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
+    vos_msg_t msg;
+    eHalStatus status = eHAL_STATUS_FAILURE;
+
+
+    VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
+                  "reqId = %u", pLinkLayerStatsClear->reqId);
+    VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
+              "staId = %u", pLinkLayerStatsClear->staId);
+    VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
+              "statsClearReqMask = 0x%X",
+              pLinkLayerStatsClear->statsClearReqMask);
+    VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
+              "stopReq = %u", pLinkLayerStatsClear->stopReq);
+
+    if ( eHAL_STATUS_SUCCESS == ( status = sme_AcquireGlobalLock( &pMac->sme )))
+    {
+        msg.type = WDA_LINK_LAYER_STATS_CLEAR_REQ;
+        msg.reserved = 0;
+        msg.bodyptr = pLinkLayerStatsClear;
+
+        if(VOS_STATUS_SUCCESS != vos_mq_post_message(VOS_MODULE_ID_WDA, &msg))
+        {
+            VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
+                    "Not able to post SIR_HAL_LL_STATS_CLEAR message to HAL", __func__);
+            status = eHAL_STATUS_FAILURE;
+        }
+        sme_ReleaseGlobalLock( &pMac->sme );
+    }
+    else
+    {
+        VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
+                "sme_AcquireGlobalLock error", __func__);
+    }
+
+    return status;
+}
+
+/* ---------------------------------------------------------------------------
+    \fn sme_SetLinkLayerStatsIndCB
+    \brief  API to trigger Link Layer Statistic indications from FW
+    \param  hHal - The handle returned by macOpen.
+    \param  sessionId - session ID
+    \param  callbackRoutine - HDD callback which needs to be invoked after
+            getting Link Layer Statistics from FW
+    \param  callbackContext - pAdapter context
+    \return eHalStatus
+  ---------------------------------------------------------------------------*/
+eHalStatus sme_SetLinkLayerStatsIndCB
+(
+    tHalHandle hHal, tANI_U8 sessionId,
+    void (*callbackRoutine) (void *callbackCtx, int indType, void *pRsp),
+    void *callbackContext
+)
+{
+    tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
+    eHalStatus status;
+
+    if ( eHAL_STATUS_SUCCESS == ( status = sme_AcquireGlobalLock( &pMac->sme )))
+    {
+        if (NULL != callbackRoutine)
+        {
+           pMac->sme.pLinkLayerStatsIndCallback = callbackRoutine;
+           pMac->sme.pLinkLayerStatsCallbackContext = callbackContext;
+        }
+        sme_ReleaseGlobalLock( &pMac->sme );
+    }
+
+    return status;
+}
+#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
+
 eHalStatus sme_UpdateConnectDebug(tHalHandle hHal, tANI_U32 set_value)
 {
     eHalStatus status = eHAL_STATUS_SUCCESS;
diff --git a/Kbuild b/Kbuild
index ff5e229..b78f612 100644
--- a/Kbuild
+++ b/Kbuild
@@ -552,7 +552,8 @@
                 -DFEATURE_WLAN_CH144 \
                 -DWLAN_BUG_ON_SKB_ERROR \
                 -DWLAN_DXE_LOW_RESOURCE_TIMER \
-                -DWLAN_LOGGING_SOCK_SVC_ENABLE
+                -DWLAN_LOGGING_SOCK_SVC_ENABLE \
+                -DWLAN_FEATURE_LINK_LAYER_STATS
 
 ifneq ($(CONFIG_PRONTO_WLAN),)
 CDEFINES += -DWCN_PRONTO