wlan: Add support to start/stop specific logging features

Add support to start and stop the logging of specific
logging features such as logging of wakelocks, per packet
statistics and connectivity events.

Change-Id: I94155e04c01757620be8686eec2c689166f22f3a
CRs-Fixed: 915570
diff --git a/CORE/HDD/inc/wlan_hdd_cfg.h b/CORE/HDD/inc/wlan_hdd_cfg.h
index 3bd6efa..d98bf55 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg.h
@@ -2274,6 +2274,12 @@
 #define CFG_WLAN_LOGGING_NUM_BUF_DEFAULT  ( 32 )
 #endif //WLAN_LOGGING_SOCK_SVC_ENABLE
 
+//Enable PerPKT stats Logging
+#define CFG_WLAN_PKT_STATS_LOGGING_NAME            "wlanPerPktStatsLogEnable"
+#define CFG_WLAN_PKT_STATS_LOGGING_ENABLE          ( 1 )
+#define CFG_WLAN_PKT_STATS_LOGGING_DISABLE         ( 0 )
+#define CFG_WLAN_PKT_STATS_LOGGING_DEFAULT         ( 1 )
+
 #define CFG_IGNORE_PEER_ERP_INFO_NAME      "gIgnorePeerErpInfo"
 #define CFG_IGNORE_PEER_ERP_INFO_MIN       ( 0 )
 #define CFG_IGNORE_PEER_ERP_INFO_MAX       ( 1 )
@@ -2997,6 +3003,7 @@
    v_U32_t                     wlanLoggingEnable;
    v_U32_t                     wlanLoggingFEToConsole;
    v_U32_t                     wlanLoggingNumBuf;
+   v_U32_t                     wlanPerPktStatsLogEnable;
 #endif
    v_BOOL_t                    ignorePeerErpInfo;
    v_BOOL_t                    initialScanSkipDFSCh;
diff --git a/CORE/HDD/inc/wlan_hdd_cfg80211.h b/CORE/HDD/inc/wlan_hdd_cfg80211.h
index 68aed06..79c54f9 100644
--- a/CORE/HDD/inc/wlan_hdd_cfg80211.h
+++ b/CORE/HDD/inc/wlan_hdd_cfg80211.h
@@ -164,6 +164,8 @@
 
     /* Get Concurrency Matrix */
     QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX = 42,
+    /* Start Wifi Logger */
+    QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START = 62,
 
     /* Get Wifi Specific Info */
     QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO = 61,
@@ -1026,6 +1028,26 @@
         QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_AFTER_LAST - 1,
 };
 
+/**
+ * enum qca_wlan_vendor_attr_wifi_logger_start - Enum for wifi logger starting
+ * @QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_INVALID: Invalid attribute
+ * @QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID: Ring ID
+ * @QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL: Verbose level
+ * @QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS: Flag
+ * @QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST: Last value
+ * @QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX: Max value
+ */
+enum qca_wlan_vendor_attr_wifi_logger_start {
+    QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_INVALID = 0,
+    QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID = 1,
+    QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL = 2,
+    QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS = 3,
+    /* keep last */
+    QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST,
+    QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX =
+       QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST - 1,
+};
+
 /* Feature defines */
 #define WIFI_FEATURE_INFRA              0x0001   /* Basic infrastructure mode */
 #define WIFI_FEATURE_INFRA_5G           0x0002   /* Support for 5 GHz Band */
diff --git a/CORE/HDD/src/wlan_hdd_cfg.c b/CORE/HDD/src/wlan_hdd_cfg.c
index 5163f55..834ff92 100644
--- a/CORE/HDD/src/wlan_hdd_cfg.c
+++ b/CORE/HDD/src/wlan_hdd_cfg.c
@@ -3052,6 +3052,13 @@
                  CFG_WLAN_LOGGING_NUM_BUF_DEFAULT,
                  CFG_WLAN_LOGGING_NUM_BUF_MIN,
                  CFG_WLAN_LOGGING_NUM_BUF_MAX ),
+   REG_VARIABLE( CFG_WLAN_PKT_STATS_LOGGING_NAME, WLAN_PARAM_Integer,
+                 hdd_config_t, wlanPerPktStatsLogEnable,
+                 VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+                 CFG_WLAN_PKT_STATS_LOGGING_DEFAULT,
+                 CFG_WLAN_PKT_STATS_LOGGING_DISABLE,
+                 CFG_WLAN_PKT_STATS_LOGGING_ENABLE ),
+
 #endif //WLAN_LOGGING_SOCK_SVC_ENABLE
 
    REG_VARIABLE( CFG_IGNORE_PEER_ERP_INFO_NAME, WLAN_PARAM_Integer,
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index 4cb9e0d..d9b3646 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -5738,17 +5738,127 @@
 wlan_hdd_cfg80211_get_fw_mem_dump(struct wiphy *wiphy,
                                               struct wireless_dev *wdev,
                                          const void *data, int data_len)
+{
+    int ret = 0;
+    vos_ssr_protect(__func__);
+    ret = __wlan_hdd_cfg80211_get_fw_mem_dump(wiphy, wdev, data,
+                                        data_len);
+    vos_ssr_unprotect(__func__);
+    return ret;
+}
 
+static const struct
+nla_policy
+qca_wlan_vendor_wifi_logger_start_policy
+[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX + 1] = {
+   [QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]
+     = {.type = NLA_U32 },
+     [QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]
+        = {.type = NLA_U32 },
+     [QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]
+          = {.type = NLA_U32 },
+};
+
+/**
+ * __wlan_hdd_cfg80211_wifi_logger_start() - This function is used to enable
+ * or disable the collection of packet statistics from the firmware
+ * @wiphy:    WIPHY structure pointer
+ * @wdev:     Wireless device structure pointer
+ * @data:     Pointer to the data received
+ * @data_len: Length of the data received
+ *
+ * This function is used to enable or disable the collection of packet
+ * statistics from the firmware
+ *
+ * Return: 0 on success and errno on failure
+ */
+static int __wlan_hdd_cfg80211_wifi_logger_start(struct wiphy *wiphy,
+                       struct wireless_dev *wdev,
+                        const void *data,
+                                int data_len)
+{
+    eHalStatus status;
+    hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
+    struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX + 1];
+    tAniWifiStartLog start_log;
+
+    status = wlan_hdd_validate_context(hdd_ctx);
+    if (0 != status) {
+        return -EINVAL;
+    }
+
+     if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX,
+             data, data_len,
+            qca_wlan_vendor_wifi_logger_start_policy)) {
+        hddLog(LOGE, FL("Invalid attribute"));
+        return -EINVAL;
+    }
+
+    /* Parse and fetch ring id */
+    if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]) {
+        hddLog(LOGE, FL("attr ATTR failed"));
+        return -EINVAL;
+    }
+    start_log.ringId = nla_get_u32(
+           tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]);
+    hddLog(LOG1, FL("Ring ID=%d"), start_log.ringId);
+
+    /* Parse and fetch verbose level */
+    if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]) {
+        hddLog(LOGE, FL("attr verbose_level failed"));
+        return -EINVAL;
+    }
+    start_log.verboseLevel = nla_get_u32(
+         tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]);
+    hddLog(LOG1, FL("verbose_level=%d"), start_log.verboseLevel);
+
+    /* Parse and fetch flag */
+    if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]) {
+        hddLog(LOGE, FL("attr flag failed"));
+        return -EINVAL;
+    }
+    start_log.flag = nla_get_u32(
+        tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]);
+    hddLog(LOG1, FL("flag=%d"), start_log.flag);
+
+    if ((RING_ID_PER_PACKET_STATS == start_log.ringId) &&
+                 !hdd_ctx->cfg_ini->wlanPerPktStatsLogEnable)
+    {
+       hddLog(LOGE, FL("per pkt stats not enabled"));
+       return -EINVAL;
+    }
+    vos_set_ring_log_level(start_log.ringId, start_log.verboseLevel);
+
+    return 0;
+}
+
+/**
+ * wlan_hdd_cfg80211_wifi_logger_start() - Wrapper function used to enable
+ * or disable the collection of packet statistics from the firmware
+ * @wiphy:    WIPHY structure pointer
+ * @wdev:     Wireless device structure pointer
+ * @data:     Pointer to the data received
+ * @data_len: Length of the data received
+ *
+ * This function is used to enable or disable the collection of packet
+ * statistics from the firmware
+ *
+ * Return: 0 on success and errno on failure
+ */
+static int wlan_hdd_cfg80211_wifi_logger_start(struct wiphy *wiphy,
+                        struct wireless_dev *wdev,
+                        const void *data,
+                        int data_len)
 {
     int ret = 0;
 
     vos_ssr_protect(__func__);
-    ret = __wlan_hdd_cfg80211_get_fw_mem_dump(wiphy, wdev, data,
-                                              data_len);
+
+    ret = __wlan_hdd_cfg80211_wifi_logger_start(wiphy,
+          wdev, data, data_len);
     vos_ssr_unprotect(__func__);
 
     return ret;
-
 }
 
 
@@ -6155,7 +6265,14 @@
             WIPHY_VENDOR_CMD_NEED_NETDEV |
             WIPHY_VENDOR_CMD_NEED_RUNNING,
         .doit = wlan_hdd_cfg80211_setband
-    }
+    },
+    {
+         .info.vendor_id = QCA_NL80211_VENDOR_ID,
+         .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START,
+         .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+                  WIPHY_VENDOR_CMD_NEED_NETDEV,
+         .doit = wlan_hdd_cfg80211_wifi_logger_start
+    },
 };
 
 /* vendor specific events */
diff --git a/CORE/MAC/inc/sirApi.h b/CORE/MAC/inc/sirApi.h
index 5477b24..5cd541f 100644
--- a/CORE/MAC/inc/sirApi.h
+++ b/CORE/MAC/inc/sirApi.h
@@ -5648,6 +5648,20 @@
     tANI_U8     SetTdls2040BSSCoex; //enabled or disabled
 } tAniSetTdls2040BSSCoex, *tpAniSetTdls2040BSSCoex;
 
+/**
+ * struct sir_wifi_start_log - Structure to store the params sent to start/
+ * stop logging
+ * @ringId:        Attribute which indicates the type of logging like per packet
+ *                 statistics, connectivity etc.
+ * @verboseLevel: Verbose level which can be 0,1,2,3
+ * @flag:          Flag field for future use
+ */
+typedef struct sir_wifi_start_log {
+    tANI_U32 ringId;
+    tANI_U32 verboseLevel;
+    tANI_U32 flag;
+}tAniWifiStartLog, *tpAniWifiStartLog;
+
 typedef struct
 {
     tANI_U16   mesgType;
diff --git a/CORE/VOSS/inc/vos_api.h b/CORE/VOSS/inc/vos_api.h
index 7e5d0da..2cd0e03 100644
--- a/CORE/VOSS/inc/vos_api.h
+++ b/CORE/VOSS/inc/vos_api.h
@@ -75,6 +75,55 @@
 #include <vos_timer.h>
 #include <vos_pack_align.h>
 
+/**
+ * enum userspace_log_level - Log level at userspace
+ * @LOG_LEVEL_NO_COLLECTION: verbose_level 0 corresponds to no collection
+ * @LOG_LEVEL_NORMAL_COLLECT: verbose_level 1 correspond to normal log level,
+ * with minimal user impact. this is the default value
+ * @LOG_LEVEL_ISSUE_REPRO: verbose_level 2 are enabled when user is lazily
+ * trying to reproduce a problem, wifi performances and power can be impacted
+ * but device should not otherwise be significantly impacted
+ * @LOG_LEVEL_ACTIVE: verbose_level 3+ are used when trying to
+ * actively debug a problem
+ *
+ * Various log levels defined in the userspace for logging applications
+ */
+enum userspace_log_level {
+    LOG_LEVEL_NO_COLLECTION,
+    LOG_LEVEL_NORMAL_COLLECT,
+    LOG_LEVEL_ISSUE_REPRO,
+    LOG_LEVEL_ACTIVE,
+};
+
+/**
+ * enum wifi_driver_log_level - Log level defined in the driver for logging
+ * @WLAN_LOG_LEVEL_OFF: No logging
+ * @WLAN_LOG_LEVEL_NORMAL: Default logging
+ * @WLAN_LOG_LEVEL_REPRO: Normal debug level
+ * @WLAN_LOG_LEVEL_ACTIVE: Active debug level
+ *
+ * Log levels defined for logging by the wifi driver
+ */
+enum wifi_driver_log_level {
+    WLAN_LOG_LEVEL_OFF,
+    WLAN_LOG_LEVEL_NORMAL,
+    WLAN_LOG_LEVEL_REPRO,
+    WLAN_LOG_LEVEL_ACTIVE,
+};
+
+/**
+ * enum wifi_logging_ring_id - Ring id of logging entities
+ * @RING_ID_WAKELOCK:         Power events ring id
+ * @RING_ID_CONNECTIVITY:     Connectivity event ring id
+ * @RING_ID_PER_PACKET_STATS: Per packet statistic ring id
+ *
+ * This enum has the ring id values of logging rings
+ */
+enum wifi_logging_ring_id {
+    RING_ID_WAKELOCK,
+    RING_ID_CONNECTIVITY,
+    RING_ID_PER_PACKET_STATS,
+};
 
 /**
  * enum log_event_type - Type of event initiating bug report
@@ -432,7 +481,8 @@
 v_U8_t vos_is_fw_logging_supported(void);
 void vos_set_multicast_logging(uint8_t value);
 v_U8_t vos_is_multicast_logging(void);
-bool vos_is_wakelock_enabled(void);
+void vos_set_ring_log_level(v_U32_t ring_id, v_U32_t log_level);
+v_U8_t vos_get_ring_log_level(v_U32_t ring_id);
 v_BOOL_t vos_isUnloadInProgress(void);
 v_BOOL_t vos_isLoadUnloadInProgress(void);
 
diff --git a/CORE/VOSS/src/vos_api.c b/CORE/VOSS/src/vos_api.c
index 721e624..2bd28d9 100644
--- a/CORE/VOSS/src/vos_api.c
+++ b/CORE/VOSS/src/vos_api.c
@@ -2936,3 +2936,79 @@
     }
 }
 
+/**
+ * vos_set_ring_log_level() - Convert HLOS values to driver log levels
+ * @ring_id: ring_id
+ * @log_levelvalue: Log level specificed
+ *
+ * This function sets the log level of a particular ring
+ *
+ * Return: None
+ */
+ void vos_set_ring_log_level(v_U32_t ring_id, v_U32_t log_level)
+{
+    VosContextType *vos_context;
+    v_U32_t log_val;
+
+    vos_context = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
+    if (!vos_context) {
+      VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+              "%s: vos context is Invald", __func__);
+      return;
+    }
+
+    switch (log_level) {
+    case LOG_LEVEL_NO_COLLECTION:
+        log_val = WLAN_LOG_LEVEL_OFF;
+        break;
+    case LOG_LEVEL_NORMAL_COLLECT:
+        log_val = WLAN_LOG_LEVEL_NORMAL;
+        break;
+    case LOG_LEVEL_ISSUE_REPRO:
+        log_val = WLAN_LOG_LEVEL_REPRO;
+        break;
+    case LOG_LEVEL_ACTIVE:
+    default:
+        log_val = WLAN_LOG_LEVEL_ACTIVE;
+        break;
+    }
+
+    if (ring_id == RING_ID_WAKELOCK) {
+        vos_context->wakelock_log_level = log_val;
+        return;
+    } else if (ring_id == RING_ID_CONNECTIVITY) {
+        vos_context->connectivity_log_level = log_val;
+        return;
+    } else if (ring_id == RING_ID_PER_PACKET_STATS) {
+        vos_context->packet_stats_log_level = log_val;
+        return;
+    }
+}
+/**
+ * vos_get_ring_log_level() - Get the a ring id's log level
+ * @ring_id: Ring id
+ *
+ * Fetch and return the log level corresponding to a ring id
+ *
+ * Return: Log level corresponding to the ring ID
+ */
+v_U8_t vos_get_ring_log_level(v_U32_t ring_id)
+{
+    VosContextType *vos_context;
+
+     vos_context = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
+     if (!vos_context) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+            "%s: vos context is Invald", __func__);
+        return WLAN_LOG_LEVEL_OFF;
+    }
+
+    if (ring_id == RING_ID_WAKELOCK)
+        return vos_context->wakelock_log_level;
+    else if (ring_id == RING_ID_CONNECTIVITY)
+        return vos_context->connectivity_log_level;
+    else if (ring_id == RING_ID_PER_PACKET_STATS)
+        return vos_context->packet_stats_log_level;
+
+    return WLAN_LOG_LEVEL_OFF;
+}
diff --git a/CORE/VOSS/src/vos_diag.c b/CORE/VOSS/src/vos_diag.c
index 285581d..aab6c6e 100644
--- a/CORE/VOSS/src/vos_diag.c
+++ b/CORE/VOSS/src/vos_diag.c
@@ -211,10 +211,18 @@
 void vos_log_wlock_diag(uint32_t reason, const char *wake_lock_name,
                               uint32_t timeout, uint32_t status)
 {
+     VosContextType *vos_context;
      WLAN_VOS_DIAG_EVENT_DEF(wlan_diag_event,
      struct vos_event_wlan_wake_lock);
 
-     if (nl_srv_is_initialized() != 0)
+    vos_context = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
+    if (!vos_context) {
+      VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+            "vos context is Invald");
+       return;
+    }
+     if (nl_srv_is_initialized() != 0 ||
+         vos_context->wakelock_log_level == WLAN_LOG_LEVEL_OFF)
           return;
 
      wlan_diag_event.status = status;
diff --git a/CORE/VOSS/src/vos_sched.h b/CORE/VOSS/src/vos_sched.h
index 7dcd7a5..89cbf58 100644
--- a/CORE/VOSS/src/vos_sched.h
+++ b/CORE/VOSS/src/vos_sched.h
@@ -339,6 +339,9 @@
 
    /*Fw log complete Event*/
    vos_event_t fwLogsComplete;
+   v_U32_t wakelock_log_level;
+   v_U32_t connectivity_log_level;
+   v_U32_t packet_stats_log_level;
 } VosContextType, *pVosContextType;