wlan: Add Vendor command to set/get NUD debug stats

Add vendor commands to set/reset and get NUD debug stats
from firmware.

Change-Id: Ib016a4580a97d7048c6c1c06ae51264ce0342f27
CRs-Fixed: 1115214
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index b2e69e6..214d9aa 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -7504,6 +7504,355 @@
 
     return ret;
 }
+
+/*
+ * define short names for the global vendor params
+ * used by wlan_hdd_cfg80211_setarp_stats_cmd()
+ */
+#define STATS_SET_INVALID \
+    QCA_ATTR_NUD_STATS_SET_INVALID
+#define STATS_SET_START \
+    QCA_ATTR_NUD_STATS_SET_START
+#define STATS_GW_IPV4 \
+    QCA_ATTR_NUD_STATS_GW_IPV4
+#define STATS_SET_MAX \
+    QCA_ATTR_NUD_STATS_SET_MAX
+
+const struct nla_policy
+qca_wlan_vendor_set_nud_stats[STATS_SET_MAX +1] =
+{
+    [STATS_SET_START] = {.type = NLA_FLAG },
+    [STATS_GW_IPV4] = {.type = NLA_U32 },
+};
+
+/**
+ * hdd_set_nud_stats_cb() - hdd callback api to get status
+ * @data: pointer to adapter
+ * @rsp: status
+ *
+ * Return: None
+ */
+static void hdd_set_nud_stats_cb(void *data, VOS_STATUS rsp)
+{
+
+   hdd_adapter_t *adapter = (hdd_adapter_t *)data;
+
+   if (NULL == adapter)
+      return;
+
+   if (VOS_STATUS_SUCCESS == rsp) {
+      VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                "%s success received STATS_SET_START", __func__);
+   } else {
+      VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                "%s STATS_SET_START Failed!!", __func__);
+   }
+   return;
+}
+
+/**
+ * __wlan_hdd_cfg80211_set_nud_stats() - set arp stats command to firmware
+ * @wiphy: pointer to wireless wiphy structure.
+ * @wdev: pointer to wireless_dev structure.
+ * @data: pointer to apfind configuration data.
+ * @data_len: the length in byte of apfind data.
+ *
+ * This is called when wlan driver needs to send arp stats to
+ * firmware.
+ *
+ * Return: An error code or 0 on success.
+ */
+static int __wlan_hdd_cfg80211_set_nud_stats(struct wiphy *wiphy,
+        struct wireless_dev *wdev,
+        const void *data, int data_len)
+{
+    struct nlattr *tb[STATS_SET_MAX + 1];
+    struct net_device   *dev = wdev->netdev;
+    hdd_adapter_t       *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
+    hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
+    setArpStatsParams arp_stats_params;
+    int err = 0;
+
+    ENTER();
+
+    err = wlan_hdd_validate_context(hdd_ctx);
+    if (0 != err)
+        return err;
+
+    if (!sme_IsFeatureSupportedByFW(NUD_DEBUG)) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "%s NUD_DEBUG feature not supported by firmware!!", __func__);
+        return -EINVAL;
+    }
+
+    err = nla_parse(tb, STATS_SET_MAX, data, data_len,
+                    qca_wlan_vendor_set_nud_stats);
+    if (err)
+    {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "%s STATS_SET_START ATTR", __func__);
+        return err;
+    }
+
+    if (tb[STATS_SET_START])
+    {
+        if (!tb[STATS_GW_IPV4]) {
+            VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                      "%s STATS_SET_START CMD", __func__);
+            return -EINVAL;
+        }
+        arp_stats_params.flag = true;
+        arp_stats_params.ip_addr = nla_get_u32(tb[STATS_GW_IPV4]);
+    } else {
+        arp_stats_params.flag = false;
+    }
+    if (arp_stats_params.flag) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                  "%s STATS_SET_START Cleared!!", __func__);
+        vos_mem_zero(&adapter->hdd_stats.hddArpStats, sizeof(adapter->hdd_stats.hddArpStats));
+    }
+
+    arp_stats_params.pkt_type = 1; // ARP packet type
+
+    arp_stats_params.rsp_cb_fn = hdd_set_nud_stats_cb;
+    arp_stats_params.data_ctx = adapter;
+
+    if (eHAL_STATUS_SUCCESS !=
+        sme_set_nud_debug_stats(hdd_ctx->hHal, &arp_stats_params)) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "%s STATS_SET_START CMD Failed!!", __func__);
+        return -EINVAL;
+    }
+
+    EXIT();
+
+    return err;
+}
+
+/**
+ * wlan_hdd_cfg80211_set_nud_stats() - set arp stats command to firmware
+ * @wiphy: pointer to wireless wiphy structure.
+ * @wdev: pointer to wireless_dev structure.
+ * @data: pointer to apfind configuration data.
+ * @data_len: the length in byte of apfind data.
+ *
+ * This is called when wlan driver needs to send arp stats to
+ * firmware.
+ *
+ * Return: An error code or 0 on success.
+ */
+static int wlan_hdd_cfg80211_set_nud_stats(struct wiphy *wiphy,
+        struct wireless_dev *wdev,
+        const void *data, int data_len)
+{
+    int ret;
+
+    vos_ssr_protect(__func__);
+    ret = __wlan_hdd_cfg80211_set_nud_stats(wiphy, wdev, data, data_len);
+    vos_ssr_unprotect(__func__);
+
+    return ret;
+}
+#undef STATS_SET_INVALID
+#undef STATS_SET_START
+#undef STATS_GW_IPV4
+#undef STATS_SET_MAX
+
+/*
+ * define short names for the global vendor params
+ * used by wlan_hdd_cfg80211_setarp_stats_cmd()
+ */
+#define STATS_GET_INVALID \
+    QCA_ATTR_NUD_STATS_SET_INVALID
+#define COUNT_FROM_NETDEV \
+    QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV
+#define COUNT_TO_LOWER_MAC \
+    QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC
+#define RX_COUNT_BY_LOWER_MAC \
+    QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC
+#define COUNT_TX_SUCCESS \
+    QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS
+#define RSP_RX_COUNT_BY_LOWER_MAC \
+    QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC
+#define RSP_RX_COUNT_BY_UPPER_MAC \
+    QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC
+#define RSP_COUNT_TO_NETDEV \
+    QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV
+#define RSP_COUNT_OUT_OF_ORDER_DROP \
+    QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP
+#define AP_LINK_ACTIVE \
+    QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE
+#define AP_LINK_DAD \
+    QCA_ATTR_NUD_STATS_AP_LINK_DAD
+#define STATS_GET_MAX \
+    QCA_ATTR_NUD_STATS_GET_MAX
+
+const struct nla_policy
+qca_wlan_vendor_get_nud_stats[STATS_GET_MAX +1] =
+{
+    [COUNT_FROM_NETDEV] = {.type = NLA_U16 },
+    [COUNT_TO_LOWER_MAC] = {.type = NLA_U16 },
+    [RX_COUNT_BY_LOWER_MAC] = {.type = NLA_U16 },
+    [COUNT_TX_SUCCESS] = {.type = NLA_U16 },
+    [RSP_RX_COUNT_BY_LOWER_MAC] = {.type = NLA_U16 },
+    [RSP_RX_COUNT_BY_UPPER_MAC] = {.type = NLA_U16 },
+    [RSP_COUNT_TO_NETDEV] = {.type = NLA_U16 },
+    [RSP_COUNT_OUT_OF_ORDER_DROP] = {.type = NLA_U16 },
+    [AP_LINK_ACTIVE] = {.type = NLA_FLAG },
+    [AP_LINK_DAD] = {.type = NLA_FLAG },
+};
+
+static void hdd_get_nud_stats_cb(void *data, rsp_stats *rsp)
+{
+
+    hdd_adapter_t *adapter = (hdd_adapter_t *)data;
+    hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+    struct hdd_nud_stats_context *context;
+    int status;
+
+    ENTER();
+
+    if (NULL == adapter)
+        return;
+
+    status = wlan_hdd_validate_context(hdd_ctx);
+    if (0 != status) {
+        return;
+    }
+
+    if (!rsp) {
+        hddLog(LOGE, FL("data is null"));
+        return;
+    }
+
+    adapter->hdd_stats.hddArpStats.tx_fw_cnt = rsp->tx_fw_cnt;
+    adapter->hdd_stats.hddArpStats.rx_fw_cnt = rsp->rx_fw_cnt;
+    adapter->hdd_stats.hddArpStats.tx_ack_cnt = rsp->tx_ack_cnt;
+    adapter->dad |= rsp->dad;
+
+    spin_lock(&hdd_context_lock);
+    context = &hdd_ctx->nud_stats_context;
+    complete(&context->response_event);
+    spin_unlock(&hdd_context_lock);
+
+    return;
+}
+static int __wlan_hdd_cfg80211_get_nud_stats(struct wiphy *wiphy,
+        struct wireless_dev *wdev,
+        const void *data, int data_len)
+{
+    int err = 0;
+    unsigned long rc;
+    struct hdd_nud_stats_context *context;
+    struct net_device   *dev = wdev->netdev;
+    hdd_adapter_t       *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
+    hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
+    getArpStatsParams arp_stats_params;
+    struct sk_buff *skb;
+
+    ENTER();
+
+    err = wlan_hdd_validate_context(hdd_ctx);
+    if (0 != err)
+        return err;
+
+    arp_stats_params.pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE;
+    arp_stats_params.get_rsp_cb_fn = hdd_get_nud_stats_cb;
+    arp_stats_params.data_ctx = adapter;
+
+    spin_lock(&hdd_context_lock);
+    context = &hdd_ctx->nud_stats_context;
+    INIT_COMPLETION(context->response_event);
+    spin_unlock(&hdd_context_lock);
+
+    if (!sme_IsFeatureSupportedByFW(NUD_DEBUG)) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "%s NUD_DEBUG feature not supported by firmware!!", __func__);
+        return -EINVAL;
+    }
+
+    if (eHAL_STATUS_SUCCESS !=
+        sme_get_nud_debug_stats(hdd_ctx->hHal, &arp_stats_params)) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "%s STATS_SET_START CMD Failed!!", __func__);
+        return -EINVAL;
+    }
+
+    rc = wait_for_completion_timeout(&context->response_event,
+            msecs_to_jiffies(WLAN_WAIT_TIME_NUD_STATS));
+    if (!rc)
+    {
+        hddLog(LOGE,
+            FL("Target response timed out request "));
+        return -ETIMEDOUT;
+    }
+
+    skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
+            WLAN_NUD_STATS_LEN);
+    if (!skb)
+    {
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+                "%s: cfg80211_vendor_cmd_alloc_reply_skb failed",
+                __func__);
+        return -ENOMEM;
+    }
+
+    if (nla_put_u16(skb, COUNT_FROM_NETDEV,
+            adapter->hdd_stats.hddArpStats.txCount) ||
+            nla_put_u16(skb, COUNT_TO_LOWER_MAC,
+            adapter->hdd_stats.hddArpStats.tx_host_fw_sent) ||
+            nla_put_u16(skb, RX_COUNT_BY_LOWER_MAC,
+            adapter->hdd_stats.hddArpStats.tx_fw_cnt) ||
+            nla_put_u16(skb, COUNT_TX_SUCCESS,
+            adapter->hdd_stats.hddArpStats.tx_ack_cnt) ||
+            nla_put_u16(skb, RSP_RX_COUNT_BY_LOWER_MAC,
+            adapter->hdd_stats.hddArpStats.rx_fw_cnt) ||
+            nla_put_u16(skb, RSP_RX_COUNT_BY_UPPER_MAC,
+            adapter->hdd_stats.hddArpStats.rxCount) ||
+            nla_put_u16(skb, RSP_COUNT_TO_NETDEV,
+            adapter->hdd_stats.hddArpStats.rxDelivered) ||
+            nla_put_u16(skb, RSP_COUNT_OUT_OF_ORDER_DROP,
+            adapter->hdd_stats.hddArpStats.rx_host_drop_reorder)) {
+            hddLog(LOGE, FL("nla put fail"));
+            kfree_skb(skb);
+            return -EINVAL;
+    }
+    if (adapter->con_status)
+        nla_put_flag(skb, AP_LINK_ACTIVE);
+    if (adapter->dad)
+        nla_put_flag(skb, AP_LINK_DAD);
+
+    cfg80211_vendor_cmd_reply(skb);
+    return err;
+}
+
+static int wlan_hdd_cfg80211_get_nud_stats(struct wiphy *wiphy,
+        struct wireless_dev *wdev,
+        const void *data, int data_len)
+{
+    int ret;
+
+    vos_ssr_protect(__func__);
+    ret = __wlan_hdd_cfg80211_get_nud_stats(wiphy, wdev, data, data_len);
+    vos_ssr_unprotect(__func__);
+
+    return ret;
+}
+
+#undef QCA_ATTR_NUD_STATS_SET_INVALID
+#undef QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV
+#undef QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC
+#undef QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC
+#undef QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS
+#undef QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC
+#undef QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC
+#undef QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV
+#undef QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP
+#undef QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE
+#undef QCA_ATTR_NUD_STATS_GET_MAX
+
+
+
 #ifdef WLAN_FEATURE_APFIND
 /**
  * __wlan_hdd_cfg80211_apfind_cmd() - set configuration to firmware
@@ -7827,6 +8176,22 @@
         .doit = wlan_hdd_cfg80211_apfind_cmd
     },
 #endif /* WLAN_FEATURE_APFIND */
+    {
+        .info.vendor_id = QCA_NL80211_VENDOR_ID,
+        .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET,
+        .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+            WIPHY_VENDOR_CMD_NEED_NETDEV |
+            WIPHY_VENDOR_CMD_NEED_RUNNING,
+        .doit = wlan_hdd_cfg80211_set_nud_stats
+    },
+    {
+        .info.vendor_id = QCA_NL80211_VENDOR_ID,
+        .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET,
+        .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+            WIPHY_VENDOR_CMD_NEED_NETDEV |
+            WIPHY_VENDOR_CMD_NEED_RUNNING,
+        .doit = wlan_hdd_cfg80211_get_nud_stats
+    },
 };
 
 /* vendor specific events */
@@ -7957,7 +8322,10 @@
         .vendor_id = QCA_NL80211_VENDOR_ID,
         .subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_LOST
     },
-
+    [QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET_INDEX] = {
+        .vendor_id = QCA_NL80211_VENDOR_ID,
+        .subcmd = QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET,
+    },
 };
 
 /*