wlan: Add changes to collect Arp packet stats

Add changes to collect arp packet stats along
with existing hdd stats to debug the arp packet
related issues.

Change-Id: I3574d512744bcdd0021f8a57d2f9f70e1b154458
CRs-Fixed: 1115366
diff --git a/CORE/DXE/src/wlan_qct_dxe.c b/CORE/DXE/src/wlan_qct_dxe.c
index c7f7235..de64b93 100644
--- a/CORE/DXE/src/wlan_qct_dxe.c
+++ b/CORE/DXE/src/wlan_qct_dxe.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -3445,6 +3445,12 @@
          //HDXE_ASSERT(0);
       }
 
+      if(wpalIsArpPkt(palPacket))
+      {
+         HDXE_MSG(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
+                  "%s :ARP packet", __func__);
+      }
+
       /* Everything is ready
        * Trigger to start DMA */
       status = wpalWriteRegister(channelEntry->channelRegister.chDXECtrlRegAddr,
@@ -3695,6 +3701,14 @@
             }
             return status;
          }
+
+         if(wpalIsArpPkt(currentCtrlBlk->xfrFrame))
+         {
+             HDXE_MSG(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_ERROR,
+                      "%s :ARP packet DMA-ed ", __func__);
+             wpalUpdateTXArpFWdeliveredStats();
+         }
+
          hostCtxt->txCompCB(hostCtxt->clientCtxt,
                             currentCtrlBlk->xfrFrame,
                             eWLAN_PAL_STATUS_SUCCESS);
diff --git a/CORE/HDD/src/wlan_hdd_tx_rx.c b/CORE/HDD/src/wlan_hdd_tx_rx.c
index 9a42301..fad7736 100644
--- a/CORE/HDD/src/wlan_hdd_tx_rx.c
+++ b/CORE/HDD/src/wlan_hdd_tx_rx.c
@@ -819,6 +819,7 @@
    hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
    v_BOOL_t txSuspended = VOS_FALSE;
    struct sk_buff *skb1;
+   v_BOOL_t arp_pkt;
 
    if (NULL == pHddCtx) {
        VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
@@ -835,6 +836,15 @@
        return NETDEV_TX_BUSY;
    }
 
+   arp_pkt = vos_is_arp_pkt(skb, false);
+
+   if (arp_pkt)
+   {
+       ++pAdapter->hdd_stats.hddArpStats.txCount;
+       VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                 "%s :ARP packet received form net_dev", __func__);
+   }
+
    //Get TL Q index corresponding to Qdisc queue index/AC.
    qid = hdd_QdiscAcToTlAC[skb->queue_mapping];
    ac  = qid;
@@ -852,6 +862,15 @@
          VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
                 FL("Tx frame in not associated state in %d context"),
                     pAdapter->device_mode);
+
+         if (arp_pkt)
+         {
+            ++pAdapter->hdd_stats.hddArpStats.txDropped;
+            pAdapter->hdd_stats.hddArpStats.reason = HDD_TX_FRAME_IN_NOT_ASSOCIATED_STATE;
+            VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+            "%s :Tx frame in not associated state, ARP packet Dropped ",
+            __func__);
+         }
          ++pAdapter->stats.tx_dropped;
          ++pAdapter->hdd_stats.hddTxRxStats.txXmitDropped;
          ++pAdapter->hdd_stats.hddTxRxStats.txXmitDroppedAC[qid];
@@ -895,6 +914,15 @@
          VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
                     "%s: WLANTL_STAPktPending() returned error code %d",
                     __func__, status);
+
+         if (arp_pkt)
+         {
+            ++pAdapter->hdd_stats.hddArpStats.txDropped;
+            pAdapter->hdd_stats.hddArpStats.reason = HDD_WLANTL_STAPKTPENDING_RETURNED_ERROR_CODE;
+            VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+            "%s:ARP Packet Dropped WLANTL_STAPktPending returned error %d",
+            __func__, status);
+         }
          ++pAdapter->stats.tx_dropped;
          ++pAdapter->hdd_stats.hddTxRxStats.txXmitDropped;
          ++pAdapter->hdd_stats.hddTxRxStats.txXmitDroppedAC[qid];
@@ -984,6 +1012,14 @@
    if ( !VOS_IS_STATUS_SUCCESS( status ) )
    {
       VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_WARN,"%s:Insert Tx queue failed. Pkt dropped", __func__);
+
+      if (arp_pkt)
+      {
+          ++pAdapter->hdd_stats.hddArpStats.txDropped;
+          pAdapter->hdd_stats.hddArpStats.reason = HDD_INSERT_TX_QUEUE_FAILED;
+          VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                    "%s:Insert Tx queue failed. ARP packet dropped", __func__);
+      }
       ++pAdapter->hdd_stats.hddTxRxStats.txXmitDropped;
       ++pAdapter->hdd_stats.hddTxRxStats.txXmitDroppedAC[qid];
       ++pAdapter->stats.tx_dropped;
@@ -1045,6 +1081,15 @@
             skb1 = pktNode->skb;
             kfree_skb(skb1);
          }
+
+         if (arp_pkt)
+         {
+           ++pAdapter->hdd_stats.hddArpStats.txDropped;
+           pAdapter->hdd_stats.hddArpStats.reason = HDD_FAILED_TO_SIGNAL_TL;
+           VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+           "%s: ARP packet Dropped : Failed to signal TL for QId=%d",
+           __func__, qid );
+         }
          ++pAdapter->stats.tx_dropped;
          ++pAdapter->hdd_stats.hddTxRxStats.txXmitDropped;
          ++pAdapter->hdd_stats.hddTxRxStats.txXmitDroppedAC[qid];
@@ -1956,6 +2001,7 @@
    tANI_U8   acAdmitted, i;
    v_U8_t proto_type = 0;
    WLANTL_ACEnumType actualAC;
+   v_BOOL_t arp_pkt;
 
    //Sanity check on inputs
    if ( ( NULL == vosContext ) || 
@@ -2093,10 +2139,20 @@
       vos_pkt_return_packet(pVosPacket);
       return VOS_STATUS_E_FAILURE;
    }
+   arp_pkt = vos_is_arp_pkt(skb, false);
    //Attach skb to VOS packet.
    status = vos_pkt_set_os_packet( pVosPacket, skb );
    if (status != VOS_STATUS_SUCCESS)
    {
+
+      if (arp_pkt)
+      {
+          ++pAdapter->hdd_stats.hddArpStats.txDropped;
+          pAdapter->hdd_stats.hddArpStats.reason = HDD_ERROR_ATTACHING_SKB;
+          VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                 "%s :Error attaching skb,ARP packet droped", __func__);
+      }
+
       VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_WARN,"%s: Error attaching skb", __func__);
       vos_pkt_return_packet(pVosPacket);
       ++pAdapter->stats.tx_dropped;
@@ -2108,6 +2164,16 @@
    //Just being paranoid. To be removed later
    if(pVosPacket == NULL)
    {
+
+      if (arp_pkt)
+      {
+          ++pAdapter->hdd_stats.hddArpStats.txDropped;
+          pAdapter->hdd_stats.hddArpStats.reason = HDD_VOS_PACKET_RETURNED_BY_VOSS_IS_NULL;
+          VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+          "%s :VOS packet returned by VOSS is NULL,ARP packet droped",
+          __func__);
+      }
+
       VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_WARN,"%s: VOS packet returned by VOSS is NULL", __func__);
       ++pAdapter->stats.tx_dropped;
       ++pAdapter->hdd_stats.hddTxRxStats.txFetchDequeueError;
@@ -2166,6 +2232,12 @@
    if( HDD_ETHERTYPE_ARP_SIZE == packet_size )
       pPktMetaInfo->ucIsArp = hdd_IsARP( pVosPacket ) ? 1 : 0;
 
+   if(pPktMetaInfo->ucIsArp)
+   {
+      VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                 "%s :STA TX ARP Received in TL ",__func__);
+   }
+
 #ifdef FEATURE_WLAN_WAPI
    // Override usIsEapol value when its zero for WAPI case
    pPktMetaInfo->ucIsWai = hdd_IsWAIPacket( pVosPacket ) ? 1 : 0;
@@ -2587,6 +2659,7 @@
    vos_pkt_t* pVosPacket;
    vos_pkt_t* pNextVosPacket;
    v_U8_t proto_type = 0;
+   v_BOOL_t arp_pkt;
 
    //Sanity check on inputs
    if ( ( NULL == vosContext ) || 
@@ -2627,6 +2700,14 @@
       // both "success" and "empty" are acceptable results
       if (!((status == VOS_STATUS_SUCCESS) || (status == VOS_STATUS_E_EMPTY)))
       {
+
+        if(hdd_IsARP(pVosPacket))
+        {
+            ++pAdapter->hdd_stats.hddArpStats.rxDropped;
+            pAdapter->hdd_stats.hddArpStats.reason = HDD_FAILURE_WALKING_PACKET_CHAIN;
+            VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+            "%s: ARP packet Drop: Failure walking packet chain", __func__);
+        }
          ++pAdapter->hdd_stats.hddTxRxStats.rxDropped;
          VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
                          "%s: Failure walking packet chain", __func__);
@@ -2637,6 +2718,15 @@
       status = vos_pkt_get_os_packet( pVosPacket, (v_VOID_t **)&skb, VOS_FALSE );
       if(!VOS_IS_STATUS_SUCCESS( status ))
       {
+
+        if(hdd_IsARP(pVosPacket))
+        {
+            ++pAdapter->hdd_stats.hddArpStats.rxDropped;
+            pAdapter->hdd_stats.hddArpStats.reason = HDD_FAILURE_EXTRACTING_SKB_FROM_VOS_PKT;
+            VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+             "%s: ARP packet Dropped: Failure extracting skb from vos pkt",
+             __func__);
+        }
          ++pAdapter->hdd_stats.hddTxRxStats.rxDropped;
          VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
                                 "%s: Failure extracting skb from vos pkt", __func__);
@@ -2698,6 +2788,15 @@
          }
       }
 
+      arp_pkt = vos_is_arp_pkt(skb, false);
+
+      if (arp_pkt)
+      {
+         ++pAdapter->hdd_stats.hddArpStats.rxCount;
+         VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                   "%s :STA RX ARP received",__func__);
+      }
+
       if (pHddCtx->rx_wow_dump) {
           if (!(VOS_PKT_PROTO_TYPE_ARP & proto_type) &&
               !(VOS_PKT_PROTO_TYPE_EAPOL & proto_type))
@@ -2737,11 +2836,28 @@
 
       if (NET_RX_SUCCESS == rxstat)
       {
+
+        if (arp_pkt)
+        {
+           ++pAdapter->hdd_stats.hddArpStats.rxDelivered;
+           VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                     "STA RX ARP packet Delivered to net stack");
+        }
+
          ++pAdapter->hdd_stats.hddTxRxStats.rxDelivered;
          ++pAdapter->hdd_stats.hddTxRxStats.pkt_rx_count;
       }
       else
       {
+
+        if (arp_pkt)
+        {
+           ++pAdapter->hdd_stats.hddArpStats.rxRefused;
+           pAdapter->hdd_stats.hddArpStats.reason = HDD_STA_RX_ARP_PACKET_REFUSED_IN_NET_STACK;
+           VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                     "%s :STA RX ARP packet Refused in net stack", __func__);
+        }
+
          ++pAdapter->hdd_stats.hddTxRxStats.rxRefused;
       }
       // now process the next packet in the chain
diff --git a/CORE/TL/src/wlan_qct_tl_ba.c b/CORE/TL/src/wlan_qct_tl_ba.c
index f940585..b97af6c 100644
--- a/CORE/TL/src/wlan_qct_tl_ba.c
+++ b/CORE/TL/src/wlan_qct_tl_ba.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1165,6 +1165,9 @@
                  TLLOG1(VOS_TRACE(VOS_MODULE_ID_TL, VOS_TRACE_LEVEL_INFO,
                           "(QCUR_FWDBUF) dropping old frame, SN=%d LastSN=%d",
                           CSN, currentReorderInfo->LastSN));
+                 if (vos_is_arp_pkt((*vosDataBuff)->pSkb, true))
+                    vos_update_arp_rx_drop_reorder();
+
                  status = vos_pkt_return_packet(*vosDataBuff);
                  if (!VOS_IS_STATUS_SUCCESS(status))
                  {
diff --git a/CORE/VOSS/inc/vos_api.h b/CORE/VOSS/inc/vos_api.h
index abcef0a..5c7fd8c 100644
--- a/CORE/VOSS/inc/vos_api.h
+++ b/CORE/VOSS/inc/vos_api.h
@@ -532,4 +532,6 @@
 void vos_dump_wdi_events(void);
 
 bool vos_check_arp_target_ip(vos_pkt_t *pPacket);
+void vos_update_arp_fw_tx_delivered(void);
+void vos_update_arp_rx_drop_reorder(void);
 #endif // if !defined __VOS_NVITEM_H
diff --git a/CORE/VOSS/inc/vos_packet.h b/CORE/VOSS/inc/vos_packet.h
index 3aa0a33..ad838d7 100644
--- a/CORE/VOSS/inc/vos_packet.h
+++ b/CORE/VOSS/inc/vos_packet.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1170,4 +1170,16 @@
 */
 v_VOID_t vos_recover_tail(vos_pkt_t *pPacket);
 
+/**
+  @breaf vos_is_arp_pkt() - Check the packet is ARP or not.
+
+  @param
+        pskb - pointer to skb
+        is_translated - header translation check
+  @return
+         TRUE - if packet is ARP
+         FALSE -if packet is not ARP
+*/
+bool vos_is_arp_pkt(void *pskb, bool is_translated);
+
 #endif  // !defined( __VOS_PKT_H )
diff --git a/CORE/VOSS/src/vos_api.c b/CORE/VOSS/src/vos_api.c
index 68e93f8..42e623e 100644
--- a/CORE/VOSS/src/vos_api.c
+++ b/CORE/VOSS/src/vos_api.c
@@ -3809,3 +3809,85 @@
 
    return false;
 }
+
+/**
+ * vos_update_arp_fw_tx_delivered() - update the ARP stats host to FW deliver
+ *                                    count
+ *
+ * Return: None
+ */
+void vos_update_arp_fw_tx_delivered(void)
+{
+   v_CONTEXT_t pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
+   hdd_context_t *pHddCtx = NULL;
+   hdd_adapter_t * pAdapter;
+   hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
+   uint8_t status;
+
+   if(!pVosContext) {
+      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Global VOS context is Null", __func__);
+      return;
+   }
+
+   pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext );
+   if(!pHddCtx) {
+      VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
+               "%s: HDD context is Null", __func__);
+      return;
+   }
+
+   status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
+
+   while (NULL != pAdapterNode && 0 == status)
+   {
+      pAdapter = pAdapterNode->pAdapter;
+      if (pAdapter->device_mode == WLAN_HDD_INFRA_STATION)
+         break;
+
+      status = hdd_get_next_adapter (pHddCtx, pAdapterNode, &pNext);
+      pAdapterNode = pNext;
+   }
+
+   pAdapter->hdd_stats.hddArpStats.tx_host_fw_sent++;
+}
+
+/**
+ * vos_update_arp_rx_drop_reorder() - update the RX ARP stats drop due
+ *                                    reorder logic at host
+ *
+ * Return: None
+ */
+void vos_update_arp_rx_drop_reorder(void)
+{
+   v_CONTEXT_t pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
+   hdd_context_t *pHddCtx = NULL;
+   hdd_adapter_t * pAdapter;
+   hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
+   uint8_t status;
+
+   if(!pVosContext) {
+      hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Global VOS context is Null", __func__);
+      return;
+   }
+
+   pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext );
+   if(!pHddCtx) {
+      VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
+               "%s: HDD context is Null", __func__);
+      return;
+   }
+
+   status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
+
+   while (NULL != pAdapterNode && 0 == status)
+   {
+      pAdapter = pAdapterNode->pAdapter;
+      if (pAdapter->device_mode == WLAN_HDD_INFRA_STATION)
+         break;
+
+      status = hdd_get_next_adapter (pHddCtx, pAdapterNode, &pNext);
+      pAdapterNode = pNext;
+   }
+
+   pAdapter->hdd_stats.hddArpStats.rx_host_drop_reorder++;
+}
diff --git a/CORE/VOSS/src/vos_packet.c b/CORE/VOSS/src/vos_packet.c
index 43daf9f..7332176 100644
--- a/CORE/VOSS/src/vos_packet.c
+++ b/CORE/VOSS/src/vos_packet.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -3100,6 +3100,35 @@
    return pkt_proto_type;
 }
 
+bool vos_is_arp_pkt(void *pskb, bool is_translated)
+{
+   v_U16_t    ether_type;
+   struct sk_buff *skb = NULL;
+#define HEADER_OFFSET_802_11 20
+
+   if (NULL == pskb)
+   {
+      return FALSE;
+   }
+
+   skb = (struct sk_buff *)pskb;
+
+   if (is_translated)
+      ether_type = (v_U16_t)(*(v_U16_t *)(skb->data + VOS_PKT_PROT_ETH_TYPE_OFFSET + HEADER_OFFSET_802_11));
+   else
+      ether_type = (v_U16_t)(*(v_U16_t *)(skb->data + VOS_PKT_PROT_ETH_TYPE_OFFSET));
+
+   if (VOS_PKT_PROT_ARP_ETH_TYPE == VOS_SWAP_U16(ether_type))
+   {
+      return TRUE;
+   }
+   else
+   {
+      return FALSE;
+   }
+#undef HEADER_OFFSET_802_11
+}
+
 v_PVOID_t vos_get_pkt_head(vos_pkt_t *pPacket)
 {
    struct sk_buff *skb;
diff --git a/CORE/WDI/DP/src/wlan_qct_wdi_ds.c b/CORE/WDI/DP/src/wlan_qct_wdi_ds.c
index e0f2c53..ac4512a 100644
--- a/CORE/WDI/DP/src/wlan_qct_wdi_ds.c
+++ b/CORE/WDI/DP/src/wlan_qct_wdi_ds.c
@@ -213,6 +213,13 @@
     WPAL_TRACE( eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO,
               "Packet Length is %d\n", pTxMetadata->fPktlen);
   }
+
+  if (pTxMetadata->isArp)
+  {
+    WPAL_TRACE( eWLAN_MODULE_DAL_CTRL, eWLAN_PAL_TRACE_LEVEL_INFO,
+                "%s :Transmitting ARP packet",__func__);
+  }
+
   wdiStatus = WDI_FillTxBd(pContext, ucTypeSubtype, pSTAMACAddress, pAddr2MACAddress,
     &ucUP, 1, pvBDHeader, ucTxFlag /* No ACK */, ucProtMgmtFrame, 0, isEapol, isArp,
     &staId, pTxMetadata->txBdToken);
diff --git a/CORE/WDI/TRP/DTS/src/wlan_qct_wdi_dts.c b/CORE/WDI/TRP/DTS/src/wlan_qct_wdi_dts.c
index ae1afe5..15a44bc 100644
--- a/CORE/WDI/TRP/DTS/src/wlan_qct_wdi_dts.c
+++ b/CORE/WDI/TRP/DTS/src/wlan_qct_wdi_dts.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1296,6 +1296,13 @@
 #else
       ((pTxMetadata->isEapol) ? WDTS_CHANNEL_TX_HIGH_PRI : WDTS_CHANNEL_TX_LOW_PRI) : WDTS_CHANNEL_TX_HIGH_PRI;
 #endif
+
+  if (pTxMetadata->isArp)
+  {
+    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+              "%s :Transmitting ARP packet", __func__);
+  }
+
   // Send packet to  Transport Driver. 
   status =  gTransportDriver.xmit(pDTDriverContext, pFrame, channel);
   if ((WLAN_LOG_LEVEL_ACTIVE ==
diff --git a/CORE/WDI/WPAL/inc/wlan_qct_pal_api.h b/CORE/WDI/WPAL/inc/wlan_qct_pal_api.h
index c1a8d18..aeacf6f 100644
--- a/CORE/WDI/WPAL/inc/wlan_qct_pal_api.h
+++ b/CORE/WDI/WPAL/inc/wlan_qct_pal_api.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2015,2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -389,4 +389,6 @@
 wpt_uint8 wpalIsFwLoggingEnabled(void);
 wpt_uint8 wpalIsFwLoggingSupported(void);
 wpt_uint8 wpalIsFwEvLoggingEnabled(void);
+bool wpalIsArpPkt(void *pPacket);
+void  wpalUpdateTXArpFWdeliveredStats(void);
 #endif // __WLAN_QCT_PAL_API_H
diff --git a/CORE/WDI/WPAL/src/wlan_qct_pal_api.c b/CORE/WDI/WPAL/src/wlan_qct_pal_api.c
index 9e0f047..a6019cb 100644
--- a/CORE/WDI/WPAL/src/wlan_qct_pal_api.c
+++ b/CORE/WDI/WPAL/src/wlan_qct_pal_api.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012,2014-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012,2014-2015,2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -542,3 +542,14 @@
    return vos_get_dxeSSREnable();
 }
 
+bool wpalIsArpPkt(void *pPacket)
+{
+   vos_pkt_t *pkt = (vos_pkt_t*)pPacket;
+
+   return vos_is_arp_pkt(pkt->pSkb, true);
+}
+
+void wpalUpdateTXArpFWdeliveredStats(void)
+{
+   vos_update_arp_fw_tx_delivered();
+}