blob: 3fadfa5979aa45f3f7994cbff14500031edbba5a [file] [log] [blame]
/*
* Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/**===========================================================================
\file wlan_hdd_tx_rx.c
\brief Linux HDD Tx/RX APIs
==========================================================================*/
/*---------------------------------------------------------------------------
Include files
-------------------------------------------------------------------------*/
#include <wlan_hdd_tx_rx.h>
#include <wlan_hdd_softap_tx_rx.h>
#include <wlan_hdd_dp_utils.h>
#include <wlan_qct_tl.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/ratelimit.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
#include <soc/qcom/subsystem_restart.h>
#else
#include <mach/subsystem_restart.h>
#endif
#include <wlan_hdd_p2p.h>
#include <linux/wireless.h>
#include <net/cfg80211.h>
#include <net/ieee80211_radiotap.h>
#include "sapApi.h"
#include <vos_sched.h>
#ifdef FEATURE_WLAN_TDLS
#include "wlan_hdd_tdls.h"
#endif
#include "vos_diag_core_event.h"
#include "vos_utils.h"
#include "sapInternal.h"
#include "wlan_hdd_trace.h"
#include "wlan_qct_wda.h"
/*---------------------------------------------------------------------------
Preprocessor definitions and constants
-------------------------------------------------------------------------*/
const v_U8_t hddWmmAcToHighestUp[] = {
SME_QOS_WMM_UP_RESV,
SME_QOS_WMM_UP_EE,
SME_QOS_WMM_UP_VI,
SME_QOS_WMM_UP_NC
};
//Mapping Linux AC interpretation to TL AC.
const v_U8_t hdd_QdiscAcToTlAC[] = {
WLANTL_AC_VO,
WLANTL_AC_VI,
WLANTL_AC_BE,
WLANTL_AC_BK,
WLANTL_AC_HIGH_PRIO,
};
#define HDD_TX_TIMEOUT_RATELIMIT_INTERVAL 20*HZ
#define HDD_TX_TIMEOUT_RATELIMIT_BURST 1
#define HDD_TX_STALL_SSR_THRESHOLD 5
#define HDD_TX_STALL_SSR_THRESHOLD_HIGH 13
#define HDD_TX_STALL_RECOVERY_THRESHOLD HDD_TX_STALL_SSR_THRESHOLD - 2
#define HDD_TX_STALL_KICKDXE_THRESHOLD HDD_TX_STALL_SSR_THRESHOLD - 4
#define HDD_TX_STALL_FATAL_EVENT_THRESHOLD 2
#define EAPOL_MASK 0x8013
#define EAPOL_M1_BIT_MASK 0x8000
#define EAPOL_M2_BIT_MASK 0x0001
#define EAPOL_M3_BIT_MASK 0x8013
#define EAPOL_M4_BIT_MASK 0x0003
int gRatefromIdx[] = {
10,20,55,100,
10,20,55,110,
60,90,120,180,240,360,480,540,
65,130,195,260,390,520,585,650,
72,144,217,289,434,578,650,722,
65,130,195,260,390,520,585,650,
135,270,405,540,810,1080,1215,1350,
150,300,450,600,900,1200,1350,1500,
135,270,405,540,810,1080,1215,1350,
1350,1350,65,130,195,260,390, 520,
585,650,780,1350,1350,1350,1350,1350,
1350,1350,1350,1350,655,722,866,1350,
1350,1350,135,270,405,540,810,1080,
1215,1350,1350,1620,1800,1350,1350,1350,
1350,1350,1350,1200,1350,1500,1350,1800,
2000,1350, 292,585,877,1170,1755,2340,
2632,2925,1350,3510,3900,1350,1350,1350,
1350,1350,1350,1350,2925,3250,1350,3900,
4333
};
#ifdef FEATURE_WLAN_DIAG_SUPPORT
#define HDD_EAPOL_PACKET_TYPE_OFFSET (15)
#define HDD_EAPOL_KEY_INFO_OFFSET (19)
#define HDD_EAPOL_DEST_MAC_OFFSET (0)
#define HDD_EAPOL_SRC_MAC_OFFSET (6)
#endif /* FEATURE_WLAN_DIAG_SUPPORT */
static DEFINE_RATELIMIT_STATE(hdd_tx_timeout_rs, \
HDD_TX_TIMEOUT_RATELIMIT_INTERVAL, \
HDD_TX_TIMEOUT_RATELIMIT_BURST);
static struct sk_buff* hdd_mon_tx_fetch_pkt(hdd_adapter_t* pAdapter);
/*---------------------------------------------------------------------------
Type declarations
-------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------
Function definitions and documenation
-------------------------------------------------------------------------*/
#ifdef DATA_PATH_UNIT_TEST
//Utility function to dump an sk_buff
static void dump_sk_buff(struct sk_buff * skb)
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,"%s: head = %pK", __func__, skb->head);
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,"%s: data = %pK", __func__, skb->data);
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,"%s: tail = %pK", __func__, skb->tail);
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,"%s: end = %pK", __func__, skb->end);
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,"%s: len = %d", __func__, skb->len);
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,"%s: data_len = %d", __func__, skb->data_len);
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,"%s: mac_len = %d", __func__, skb->mac_len);
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
skb->data[0], skb->data[1], skb->data[2], skb->data[3], skb->data[4],
skb->data[5], skb->data[6], skb->data[7]);
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
skb->data[8], skb->data[9], skb->data[10], skb->data[11], skb->data[12],
skb->data[13], skb->data[14], skb->data[15]);
}
//Function for Unit Test only
static void transport_thread(hdd_adapter_t *pAdapter)
{
v_U8_t staId;
WLANTL_ACEnumType ac = WLANTL_AC_BE;
vos_pkt_t *pVosPacket = NULL ;
vos_pkt_t dummyPacket;
WLANTL_MetaInfoType pktMetaInfo;
WLANTL_RxMetaInfoType pktRxMetaInfo;
VOS_STATUS status = VOS_STATUS_E_FAILURE;
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
FL("pAdapter is NULL"));
VOS_ASSERT(0);
return;
}
status = hdd_tx_fetch_packet_cbk( pAdapter->pvosContext,
&staId,
&ac,
&pVosPacket,
&pktMetaInfo );
if (status != VOS_STATUS_SUCCESS && status != VOS_STATUS_E_EMPTY)
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Test FAIL hdd_tx_fetch_packet_cbk", __func__);
else
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Test PASS hdd_tx_fetch_packet_cbk", __func__);
status = hdd_tx_complete_cbk(pAdapter->pvosContext, &dummyPacket, VOS_STATUS_SUCCESS);
if (status != VOS_STATUS_SUCCESS)
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Test FAIL hdd_tx_complete_cbk", __func__);
else
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Test PASS hdd_tx_complete_cbk", __func__);
status = hdd_tx_low_resource_cbk(pVosPacket, pAdapter);
if (status != VOS_STATUS_SUCCESS)
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Test FAIL hdd_tx_low_resource_cbk", __func__);
else
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Test PASS hdd_tx_low_resource_cbk", __func__);
status = hdd_rx_packet_cbk( pAdapter->pvosContext,
&dummyPacket,
staId,
&pktRxMetaInfo);
if (status != VOS_STATUS_SUCCESS)
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Test FAIL hdd_rx_packet_cbk", __func__);
else
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Test PASS hdd_rx_packet_cbk", __func__);
}
#endif
/**============================================================================
@brief hdd_flush_tx_queues() - Utility function to flush the TX queues
@param pAdapter : [in] pointer to adapter context
@return : VOS_STATUS_E_FAILURE if any errors encountered
: VOS_STATUS_SUCCESS otherwise
===========================================================================*/
static VOS_STATUS hdd_flush_tx_queues( hdd_adapter_t *pAdapter )
{
VOS_STATUS status = VOS_STATUS_SUCCESS;
v_SINT_t i = -1;
hdd_list_node_t *anchor = NULL;
skb_list_node_t *pktNode = NULL;
struct sk_buff *skb = NULL;
pAdapter->isVosLowResource = VOS_FALSE;
MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_FLUSH_TX_QUEUES,
pAdapter->sessionId, 0));
while (++i != NUM_TX_QUEUES)
{
//Free up any packets in the Tx queue
spin_lock_bh(&pAdapter->wmm_tx_queue[i].lock);
while (true)
{
status = hdd_list_remove_front( &pAdapter->wmm_tx_queue[i], &anchor );
if(VOS_STATUS_E_EMPTY != status)
{
pktNode = list_entry(anchor, skb_list_node_t, anchor);
skb = pktNode->skb;
//TODO
//++pAdapter->stats.tx_dropped;
++pAdapter->hdd_stats.hddTxRxStats.txFlushed;
++pAdapter->hdd_stats.hddTxRxStats.txFlushedAC[i];
kfree_skb(skb);
continue;
}
break;
}
spin_unlock_bh(&pAdapter->wmm_tx_queue[i].lock);
// backpressure is no longer in effect
pAdapter->isTxSuspended[i] = VOS_FALSE;
}
return status;
}
/**============================================================================
@brief hdd_flush_ibss_tx_queues() - Utility function to flush the TX queues
in IBSS mode
@param pAdapter : [in] pointer to adapter context
: [in] Staion Id
@return : VOS_STATUS_E_FAILURE if any errors encountered
: VOS_STATUS_SUCCESS otherwise
===========================================================================*/
void hdd_flush_ibss_tx_queues( hdd_adapter_t *pAdapter, v_U8_t STAId)
{
v_U8_t i;
hdd_list_node_t *anchor = NULL;
skb_list_node_t *pktNode = NULL;
struct sk_buff *skb = NULL;
hdd_station_ctx_t *pHddStaCtx = &pAdapter->sessionCtx.station;
hdd_ibss_peer_info_t *pPeerInfo = &pHddStaCtx->ibss_peer_info;
for (i = 0; i < NUM_TX_QUEUES; i ++)
{
spin_lock_bh(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[i].lock);
while (true)
{
if (VOS_STATUS_E_EMPTY !=
hdd_list_remove_front(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[i],
&anchor))
{
//If success then we got a valid packet from some AC
pktNode = list_entry(anchor, skb_list_node_t, anchor);
skb = pktNode->skb;
++pAdapter->stats.tx_dropped;
++pAdapter->hdd_stats.hddTxRxStats.txFlushed;
++pAdapter->hdd_stats.hddTxRxStats.txFlushedAC[i];
kfree_skb(skb);
continue;
}
break;
}
spin_unlock_bh(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[i].lock);
}
}
static struct sk_buff* hdd_mon_tx_fetch_pkt(hdd_adapter_t* pAdapter)
{
skb_list_node_t *pktNode = NULL;
struct sk_buff *skb = NULL;
v_SIZE_t size = 0;
WLANTL_ACEnumType ac = 0;
VOS_STATUS status = VOS_STATUS_E_FAILURE;
hdd_list_node_t *anchor = NULL;
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pAdapter is NULL"));
VOS_ASSERT(0);
return NULL;
}
// do we have any packets pending in this AC?
hdd_list_size( &pAdapter->wmm_tx_queue[ac], &size );
if( size == 0 )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: NO Packet Pending", __func__);
return NULL;
}
//Remove the packet from the queue
spin_lock_bh(&pAdapter->wmm_tx_queue[ac].lock);
status = hdd_list_remove_front( &pAdapter->wmm_tx_queue[ac], &anchor );
spin_unlock_bh(&pAdapter->wmm_tx_queue[ac].lock);
if(VOS_STATUS_SUCCESS == status)
{
//If success then we got a valid packet from some AC
pktNode = list_entry(anchor, skb_list_node_t, anchor);
skb = pktNode->skb;
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Not able to remove Packet from the list",
__func__);
return NULL;
}
// if we are in a backpressure situation see if we can turn the hose back on
if ( (pAdapter->isTxSuspended[ac]) &&
(size <= HDD_TX_QUEUE_LOW_WATER_MARK) )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_WARN,
"%s: TX queue[%d] re-enabled", __func__, ac);
pAdapter->isTxSuspended[ac] = VOS_FALSE;
/* Enable Queues which we have disabled earlier */
hddLog(VOS_TRACE_LEVEL_INFO, FL("Enabling queues"));
netif_tx_start_all_queues( pAdapter->dev );
}
return skb;
}
void hdd_mon_tx_mgmt_pkt(hdd_adapter_t* pAdapter)
{
hdd_cfg80211_state_t *cfgState;
struct sk_buff* skb;
hdd_adapter_t* pMonAdapter = NULL;
struct ieee80211_hdr *hdr;
hdd_context_t *pHddCtx;
int ret = 0;
ENTER();
if (pAdapter == NULL)
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
FL("pAdapter is NULL"));
VOS_ASSERT(0);
return;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return;
}
pMonAdapter = hdd_get_adapter( pAdapter->pHddCtx, WLAN_HDD_MONITOR );
if (pMonAdapter == NULL)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: pMonAdapter is NULL", __func__);
return;
}
cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter );
if( NULL != cfgState->buf )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Already one MGMT packet Tx going on", __func__);
return;
}
skb = hdd_mon_tx_fetch_pkt(pMonAdapter);
if (NULL == skb)
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: No Packet Pending", __func__);
return;
}
cfgState->buf = vos_mem_malloc( skb->len ); //buf;
if( cfgState->buf == NULL )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to Allocate memory", __func__);
goto fail;
}
cfgState->len = skb->len;
vos_mem_copy( cfgState->buf, skb->data, skb->len);
cfgState->skb = skb; //buf;
cfgState->action_cookie = (uintptr_t)cfgState->buf;
hdr = (struct ieee80211_hdr *)skb->data;
if( (hdr->frame_control & HDD_FRAME_TYPE_MASK)
== HDD_FRAME_TYPE_MGMT )
{
if( (hdr->frame_control & HDD_FRAME_SUBTYPE_MASK)
== HDD_FRAME_SUBTYPE_DEAUTH )
{
struct tagCsrDelStaParams delStaParams;
WLANSAP_PopulateDelStaParams(hdr->addr1,
eSIR_MAC_DEAUTH_LEAVING_BSS_REASON,
(SIR_MAC_MGMT_DEAUTH >> 4), &delStaParams);
hdd_softap_sta_deauth(pAdapter, &delStaParams);
goto mgmt_handled;
}
else if( (hdr->frame_control & HDD_FRAME_SUBTYPE_MASK)
== HDD_FRAME_SUBTYPE_DISASSOC )
{
hdd_softap_sta_disassoc( pAdapter, hdr->addr1 );
goto mgmt_handled;
}
}
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"%s: Sending action frame to SAP to TX, Len %d", __func__, skb->len);
if (VOS_STATUS_SUCCESS !=
WLANSAP_SendAction( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
skb->data, skb->len, 0) )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: WLANSAP_SendAction returned fail", __func__);
hdd_sendActionCnf( pAdapter, FALSE );
}
EXIT();
return;
mgmt_handled:
EXIT();
hdd_sendActionCnf( pAdapter, TRUE );
return;
fail:
kfree_skb(pAdapter->skb_to_tx);
pAdapter->skb_to_tx = NULL;
return;
}
void __hdd_mon_tx_work_queue(struct work_struct *work)
{
hdd_adapter_t* pAdapter = container_of(work, hdd_adapter_t, monTxWorkQueue);
hdd_mon_tx_mgmt_pkt(pAdapter);
}
void hdd_mon_tx_work_queue(struct work_struct *work)
{
vos_ssr_protect(__func__);
__hdd_mon_tx_work_queue(work);
vos_ssr_unprotect(__func__);
}
int hdd_mon_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Packet Rcvd at Monitor interface,"
" Dropping the packet",__func__);
kfree_skb(skb);
return NETDEV_TX_OK;
}
/**============================================================================
@brief hdd_dhcp_pkt_info() -
Function to log DHCP pkt info
@param skb : [in] pointer to OS packet (sk_buff)
@return : None
===========================================================================*/
void hdd_dhcp_pkt_info(struct sk_buff *skb)
{
/* port no 67 (0x43) or 68 (0x44) */
if (*((u8*)skb->data + BOOTP_MSG_OFFSET) == BOOTP_REQUEST_MSG)
hddLog(VOS_TRACE_LEVEL_INFO, FL("Request"));
else if (*((u8*)skb->data + BOOTP_MSG_OFFSET) == BOOTP_RESPONSE_MSG)
hddLog(VOS_TRACE_LEVEL_INFO, FL("Response"));
else
hddLog(VOS_TRACE_LEVEL_INFO, FL("DHCP invalid"));
hddLog(VOS_TRACE_LEVEL_INFO,
FL("DHCP Dest Addr: %pM Src Addr %pM "
" source port : %d, dest port : %d"),
skb->data, (skb->data + 6),
ntohs(*((u16*)((u8*)skb->data + UDP_SRC_PORT_OFFSET))),
ntohs(*((u16*)((u8*)skb->data + UDP_DEST_PORT_OFFSET))));
if ((skb->data[DHCP_OPTION53_OFFSET] == DHCP_OPTION53) &&
(skb->data[DHCP_OPTION53_LENGTH_OFFSET] == DHCP_OPTION53_LENGTH)) {
switch (skb->data[DHCP_OPTION53_STATUS_OFFSET]) {
case DHCPDISCOVER:
hddLog(VOS_TRACE_LEVEL_INFO,FL("DHCP DISCOVER"));
break;
case DHCPREQUEST:
hddLog(VOS_TRACE_LEVEL_INFO,FL("DHCP REQUEST"));
break;
case DHCPOFFER:
hddLog(VOS_TRACE_LEVEL_INFO,FL("DHCP OFFER"));
break;
case DHCPACK:
hddLog(VOS_TRACE_LEVEL_INFO,FL("DHCP ACK"));
break;
case DHCPNAK:
hddLog(VOS_TRACE_LEVEL_INFO,FL("DHCP NACK"));
break;
case DHCPRELEASE:
hddLog(VOS_TRACE_LEVEL_INFO,FL("DHCP RELEASE"));
break;
case DHCPINFORM:
hddLog(VOS_TRACE_LEVEL_INFO,FL("DHCP INFORM"));
break;
default:
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: DHCP Not Defined OPTION53 : %d", __func__,
skb->data[DHCP_OPTION53_STATUS_OFFSET]);
}
}
}
/**============================================================================
@brief hdd_dump_dhcp_pkt() -
Function to dump DHCP packets in TX and RX path.
@param skb : [in] pointer to OS packet (sk_buff)
@param path : [in] bool indicating TX/RX path
@return : None
===========================================================================*/
void hdd_dump_dhcp_pkt(struct sk_buff *skb, int path)
{
if ((ntohs(*((u16*)((u8*)skb->data + ETH_TYPE_OFFSET)))
== ETH_TYPE_IP_PKT) ||
(ntohs(*((u8*)skb->data + PROTOCOL_OFFSET)) == UDP_PROTOCOL)) {
/* IP protocol 12 bytes of mac addresses in 802.3 header */
if ( ntohs(*((u16*)((u8*)skb->data + UDP_DEST_PORT_OFFSET))) ==
BOOTP_SERVER_PORT ||
ntohs(*((u16*)((u8*)skb->data + UDP_DEST_PORT_OFFSET))) ==
BOOTP_CLIENT_PORT ||
ntohs(*((u16*)((u8*)skb->data + UDP_SRC_PORT_OFFSET))) ==
BOOTP_SERVER_PORT ||
ntohs(*((u16*)((u8*)skb->data + UDP_SRC_PORT_OFFSET))) ==
BOOTP_CLIENT_PORT ) {
if (path == TX_PATH) {
hddLog(VOS_TRACE_LEVEL_INFO, FL("DHCP TX PATH"));
} else {
hddLog(VOS_TRACE_LEVEL_INFO, FL("DHCP RX PATH"));
}
hdd_dhcp_pkt_info(skb);
}
}
}
/**============================================================================
@brief hdd_ibss_hard_start_xmit() - Function registered with the Linux OS for
transmitting packets in case of IBSS. There are 2 versions of this function.
One that uses locked queue and other that uses lockless queues. Both have been
retained to do some performance testing
@param skb : [in] pointer to OS packet (sk_buff)
@param dev : [in] pointer to network device
@return : NET_XMIT_DROP if packets are dropped
: NET_XMIT_SUCCESS if packet is enqueued succesfully
===========================================================================*/
int hdd_ibss_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
VOS_STATUS status;
WLANTL_ACEnumType ac;
sme_QosWmmUpType up;
skb_list_node_t *pktNode = NULL;
hdd_list_node_t *anchor = NULL;
v_SIZE_t pktListSize = 0;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_station_ctx_t *pHddStaCtx = &pAdapter->sessionCtx.station;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_ibss_peer_info_t * pPeerInfo;
v_U8_t STAId = WLAN_MAX_STA_COUNT;
v_BOOL_t txSuspended = VOS_FALSE;
struct sk_buff *skb1;
v_MACADDR_t *pDestMacAddress = (v_MACADDR_t*)skb->data;
if (NULL == pHddCtx || NULL == pHddStaCtx) {
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s HDD context is NULL", __func__);
return NETDEV_TX_BUSY;
}
pPeerInfo = &pHddStaCtx->ibss_peer_info;
++pAdapter->hdd_stats.hddTxRxStats.txXmitCalled;
//Get TL AC corresponding to Qdisc queue index/AC.
ac = hdd_QdiscAcToTlAC[skb->queue_mapping];
if (eConnectionState_IbssDisconnected == pHddStaCtx->conn_info.connState)
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"%s: Tx frame in disconnected state in IBSS mode", __func__);
++pAdapter->stats.tx_dropped;
++pAdapter->hdd_stats.hddTxRxStats.txXmitDropped;
++pAdapter->hdd_stats.hddTxRxStats.txXmitDroppedAC[ac];
kfree_skb(skb);
return NETDEV_TX_OK;
}
STAId = hdd_sta_id_find_from_mac_addr(pAdapter, pDestMacAddress);
if ((STAId == HDD_WLAN_INVALID_STA_ID) &&
(vos_is_macaddr_broadcast( pDestMacAddress ) ||
vos_is_macaddr_group(pDestMacAddress)))
{
STAId = IBSS_BROADCAST_STAID;
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO_LOW,
"%s: BC/MC packet", __func__);
}
else if (STAId >= HDD_MAX_NUM_IBSS_STA)
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"%s: Received Unicast frame with invalid staID", __func__);
++pAdapter->stats.tx_dropped;
++pAdapter->hdd_stats.hddTxRxStats.txXmitDropped;
++pAdapter->hdd_stats.hddTxRxStats.txXmitDroppedAC[ac];
kfree_skb(skb);
return NETDEV_TX_OK;
}
//user priority from IP header, which is already extracted and set from
//select_queue call back function
up = skb->priority;
++pAdapter->hdd_stats.hddTxRxStats.txXmitClassifiedAC[ac];
VOS_TRACE( VOS_MODULE_ID_HDD_SAP_DATA, VOS_TRACE_LEVEL_INFO,
"%s: Classified as ac %d up %d", __func__, ac, up);
if ( pHddCtx->cfg_ini->gEnableDebugLog & VOS_PKT_PROTO_TYPE_DHCP )
{
hdd_dump_dhcp_pkt(skb, TX_PATH);
}
spin_lock_bh(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac].lock);
hdd_list_size(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac], &pktListSize);
if(pktListSize >= pAdapter->aTxQueueLimit[ac])
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s: station %d ac %d queue over limit %d", __func__, STAId, ac, pktListSize);
pPeerInfo->ibssStaInfo[STAId].txSuspended[ac] = VOS_TRUE;
netif_stop_subqueue(dev, skb_get_queue_mapping(skb));
txSuspended = VOS_TRUE;
spin_unlock_bh(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac].lock);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s: TX queue full for AC=%d Disable OS TX queue",
__func__, ac );
return NETDEV_TX_BUSY;
}
/* If 3/4th of the max queue size is used then enable the flag.
* This flag indicates to place the DHCP packets in VOICE AC queue.*/
if (WLANTL_AC_BE == ac)
{
if (pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac].count >= HDD_TX_QUEUE_LOW_WATER_MARK)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s: TX queue for Best Effort AC is 3/4th full", __func__);
pAdapter->isVosLowResource = VOS_TRUE;
}
else
{
pAdapter->isVosLowResource = VOS_FALSE;
}
}
spin_unlock_bh(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac].lock);
//Use the skb->cb field to hold the list node information
pktNode = (skb_list_node_t *)&skb->cb;
//Stick the OS packet inside this node.
pktNode->skb = skb;
//Stick the User Priority inside this node
pktNode->userPriority = up;
INIT_LIST_HEAD(&pktNode->anchor);
spin_lock_bh(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac].lock);
status = hdd_list_insert_back_size( &pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac],
&pktNode->anchor, &pktListSize );
spin_unlock_bh(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac].lock);
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s:Insert Tx queue failed. Pkt dropped", __func__);
++pAdapter->hdd_stats.hddTxRxStats.txXmitDropped;
++pAdapter->hdd_stats.hddTxRxStats.txXmitDroppedAC[ac];
++pAdapter->stats.tx_dropped;
kfree_skb(skb);
return NETDEV_TX_OK;
}
++pAdapter->hdd_stats.hddTxRxStats.txXmitQueued;
++pAdapter->hdd_stats.hddTxRxStats.txXmitQueuedAC[ac];
++pAdapter->hdd_stats.hddTxRxStats.pkt_tx_count;
if (1 == pktListSize)
{
//Let TL know we have a packet to send for this AC
status = WLANTL_STAPktPending( pHddCtx->pvosContext, STAId, ac );
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s: Failed to signal TL for AC=%d STAId =%d",
__func__, ac, STAId );
/* Remove the packet from queue. It must be at the back of the queue, as TX thread
* cannot preempt us in the middle as we are in a soft irq context.
* Also it must be the same packet that we just allocated.
*/
spin_lock_bh(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac].lock);
status = hdd_list_remove_back( &pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac], &anchor);
spin_unlock_bh(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac].lock);
/* Free the skb only if we are able to remove it from the list.
* If we are not able to retrieve it from the list it means that
* the skb was pulled by TX Thread and is use so we should not free
* it here
*/
if (VOS_IS_STATUS_SUCCESS(status))
{
pktNode = list_entry(anchor, skb_list_node_t, anchor);
skb1 = pktNode->skb;
kfree_skb(skb1);
}
++pAdapter->stats.tx_dropped;
++pAdapter->hdd_stats.hddTxRxStats.txXmitDropped;
++pAdapter->hdd_stats.hddTxRxStats.txXmitDroppedAC[ac];
return NETDEV_TX_OK;
}
}
netif_trans_update(dev);
return NETDEV_TX_OK;
}
/**============================================================================
@brief __hdd_hard_start_xmit() - Function registered with the Linux OS for
transmitting packets. There are 2 versions of this function. One that uses
locked queue and other that uses lockless queues. Both have been retained to
do some performance testing
@param skb : [in] pointer to OS packet (sk_buff)
@param dev : [in] pointer to Libra network device
@return : NET_XMIT_DROP if packets are dropped
: NET_XMIT_SUCCESS if packet is enqueued succesfully
===========================================================================*/
int __hdd_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
VOS_STATUS status;
WLANTL_ACEnumType qid, ac;
sme_QosWmmUpType up;
skb_list_node_t *pktNode = NULL;
hdd_list_node_t *anchor = NULL;
v_SIZE_t pktListSize = 0;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
v_BOOL_t granted;
v_U8_t STAId = WLAN_MAX_STA_COUNT;
hdd_station_ctx_t *pHddStaCtx = &pAdapter->sessionCtx.station;
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,
"%s HDD context is NULL", __func__);
return NETDEV_TX_BUSY;
}
++pAdapter->hdd_stats.hddTxRxStats.txXmitCalled;
if (unlikely(netif_subqueue_stopped(dev, skb))) {
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s is called when netif TX %d is disabled",
__func__, skb->queue_mapping);
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;
if (qid == WLANTL_AC_HIGH_PRIO)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s It must be a Eapol/Wapi/DHCP packet device_mode:%d",
__func__, pAdapter->device_mode);
ac = hddWmmUpToAcMap[skb->priority];
}
if (eConnectionState_Associated != pHddStaCtx->conn_info.connState)
{
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];
kfree_skb(skb);
return NETDEV_TX_OK;
}
STAId = pHddStaCtx->conn_info.staId[0];
//user priority from IP header, which is already extracted and set from
//select_queue call back function
up = skb->priority;
++pAdapter->hdd_stats.hddTxRxStats.txXmitClassifiedAC[qid];
#ifdef HDD_WMM_DEBUG
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_FATAL,
"%s: Classified as ac %d up %d", __func__, ac, up);
#endif // HDD_WMM_DEBUG
if (pHddCtx->cfg_ini->gEnableRoamDelayStats)
{
vos_record_roam_event(e_HDD_FIRST_XMIT_TIME, (void *)skb, 0);
}
spin_lock(&pAdapter->wmm_tx_queue[qid].lock);
/*CR 463598,384996*/
/*For every increment of 10 pkts in the queue, we inform TL about pending pkts.
*We check for +1 in the logic,to take care of Zero count which
*occurs very frequently in low traffic cases */
if((pAdapter->wmm_tx_queue[qid].count + 1) % 10 == 0)
{
/* Use the following debug statement during Engineering Debugging.There are chance that this will lead to a Watchdog Bark
* if it is in the mainline code and if the log level is enabled by someone for debugging
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,"%s:Queue is Filling up.Inform TL again about pending packets", __func__);*/
status = WLANTL_STAPktPending( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
STAId, qid
);
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
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];
kfree_skb(skb);
spin_unlock(&pAdapter->wmm_tx_queue[qid].lock);
return NETDEV_TX_OK;
}
}
if (pHddCtx->bad_sta[STAId]) {
hdd_list_node_t *anchor = NULL;
skb_list_node_t *pktNode = NULL;
struct sk_buff *fskb = NULL;
if (pAdapter->wmm_tx_queue[qid].count >=
pAdapter->wmm_tx_queue[qid].max_size / 2) {
hdd_list_remove_front(&pAdapter->wmm_tx_queue[qid],
&anchor);
pktNode = list_entry(anchor, skb_list_node_t, anchor);
fskb = pktNode->skb;
kfree_skb(fskb);
++pAdapter->stats.tx_dropped;
++pAdapter->hdd_stats.hddTxRxStats.txXmitDropped;
}
}
//If we have already reached the max queue size, disable the TX queue
if ( pAdapter->wmm_tx_queue[qid].count == pAdapter->wmm_tx_queue[qid].max_size)
{
++pAdapter->hdd_stats.hddTxRxStats.txXmitBackPressured;
++pAdapter->hdd_stats.hddTxRxStats.txXmitBackPressuredAC[qid];
hddLog(VOS_TRACE_LEVEL_INFO, FL("Disabling queue for QId %d"), qid);
netif_tx_stop_queue(netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)));
pAdapter->isTxSuspended[qid] = VOS_TRUE;
txSuspended = VOS_TRUE;
MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_STOP_NETDEV,
pAdapter->sessionId, qid));
}
/* If 3/4th of the max queue size is used then enable the flag.
* This flag indicates to place the DHCP packets in VOICE AC queue.*/
if (WLANTL_AC_BE == qid)
{
if (pAdapter->wmm_tx_queue[qid].count >= HDD_TX_QUEUE_LOW_WATER_MARK)
{
pAdapter->isVosLowResource = VOS_TRUE;
}
else
{
pAdapter->isVosLowResource = VOS_FALSE;
}
}
spin_unlock(&pAdapter->wmm_tx_queue[qid].lock);
if (( NULL != pHddCtx ) &&
(pHddCtx->cfg_ini->gEnableDebugLog & VOS_PKT_PROTO_TYPE_DHCP))
{
hdd_dump_dhcp_pkt(skb, TX_PATH);
}
if (VOS_TRUE == txSuspended)
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"%s: TX queue full for QId=%d Disable OS TX queue",
__func__, qid );
return NETDEV_TX_BUSY;
}
//Use the skb->cb field to hold the list node information
pktNode = (skb_list_node_t *)&skb->cb;
//Stick the OS packet inside this node.
pktNode->skb = skb;
//Stick the User Priority inside this node
pktNode->userPriority = up;
INIT_LIST_HEAD(&pktNode->anchor);
//Insert the OS packet into the appropriate AC queue
spin_lock(&pAdapter->wmm_tx_queue[qid].lock);
status = hdd_list_insert_back_size( &pAdapter->wmm_tx_queue[qid], &pktNode->anchor, &pktListSize );
spin_unlock(&pAdapter->wmm_tx_queue[qid].lock);
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;
kfree_skb(skb);
return NETDEV_TX_OK;
}
++pAdapter->hdd_stats.hddTxRxStats.txXmitQueued;
++pAdapter->hdd_stats.hddTxRxStats.txXmitQueuedAC[qid];
++pAdapter->hdd_stats.hddTxRxStats.pkt_tx_count;
if (HDD_PSB_CHANGED == pAdapter->psbChanged)
{
/* Function which will determine acquire admittance for a
* WMM AC is required or not based on psb configuration done
* in the framework
*/
hdd_wmm_acquire_access_required(pAdapter, ac);
}
//Make sure we have access to this access category
if (((pAdapter->psbChanged & (1 << ac)) && likely(pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcAccessAllowed)) ||
(pHddStaCtx->conn_info.uIsAuthenticated == VOS_FALSE))
{
granted = VOS_TRUE;
}
else
{
status = hdd_wmm_acquire_access( pAdapter, ac, &granted );
pAdapter->psbChanged |= (1 << ac);
}
if ( (granted && ( pktListSize == 1 )) ||
(qid == WLANTL_AC_HIGH_PRIO))
{
//Let TL know we have a packet to send for this AC
//VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,"%s:Indicating Packet to TL", __func__);
status = WLANTL_STAPktPending(
(WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
STAId, qid );
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_WARN,
"%s: Failed to signal TL for QId=%d", __func__, qid );
//Remove the packet from queue. It must be at the back of the queue, as TX thread cannot preempt us in the middle
//as we are in a soft irq context. Also it must be the same packet that we just allocated.
spin_lock(&pAdapter->wmm_tx_queue[qid].lock);
status = hdd_list_remove_back( &pAdapter->wmm_tx_queue[qid], &anchor );
spin_unlock(&pAdapter->wmm_tx_queue[qid].lock);
/* Free the skb only if we are able to remove it from the list.
* If we are not able to retrieve it from the list it means that
* the skb was pulled by TX Thread and is use so we should not free
* it here
*/
if (VOS_IS_STATUS_SUCCESS(status))
{
pktNode = list_entry(anchor, skb_list_node_t, anchor);
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];
return NETDEV_TX_OK;
}
}
netif_trans_update(dev);
return NETDEV_TX_OK;
}
int hdd_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_hard_start_xmit(skb, dev);
vos_ssr_unprotect(__func__);
return ret;
}
/**============================================================================
@brief hdd_Ibss_GetStaId() - Get the StationID using the Peer Mac address
@param pHddStaCtx : [in] pointer to HDD Station Context
pMacAddress [in] pointer to Peer Mac address
staID [out] pointer to Station Index
@return : VOS_STATUS_SUCCESS/VOS_STATUS_E_FAILURE
===========================================================================*/
VOS_STATUS hdd_Ibss_GetStaId(hdd_station_ctx_t *pHddStaCtx, v_MACADDR_t *pMacAddress, v_U8_t *staId)
{
v_U8_t idx;
for (idx = 0; idx < HDD_MAX_NUM_IBSS_STA; idx++)
{
if (vos_mem_compare(&pHddStaCtx->conn_info.peerMacAddress[ idx ],
pMacAddress, sizeof(v_MACADDR_t)))
{
*staId = pHddStaCtx->conn_info.staId[idx];
return VOS_STATUS_SUCCESS;
}
}
return VOS_STATUS_E_FAILURE;
}
/**============================================================================
@brief __hdd_tx_timeout() - Function handles timeout during transmission.
@param dev : [in] pointer to network device
@return : None
===========================================================================*/
void __hdd_tx_timeout(struct net_device *dev)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
struct netdev_queue *txq;
hdd_remain_on_chan_ctx_t *pRemainChanCtx;
int i = 0;
int status = 0;
v_ULONG_t diff_in_jiffies = 0;
hdd_station_ctx_t *pHddStaCtx = NULL;
TX_TIMEOUT_TRACE(dev, VOS_MODULE_ID_HDD_DATA);
if ( NULL == pAdapter )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
FL("pAdapter is NULL"));
VOS_ASSERT(0);
return;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (status !=0 )
{
return;
}
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if ( NULL == pHddStaCtx )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
FL("pHddStaCtx is NULL"));
return;
}
++pAdapter->hdd_stats.hddTxRxStats.txTimeoutCount;
//Getting here implies we disabled the TX queues for too long. Queues are
//disabled either because of disassociation or low resource scenarios. In
//case of disassociation it is ok to ignore this. But if associated, we have
//do possible recovery here
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"num_bytes AC0: %d AC1: %d AC2: %d AC3: %d",
pAdapter->wmm_tx_queue[0].count,
pAdapter->wmm_tx_queue[1].count,
pAdapter->wmm_tx_queue[2].count,
pAdapter->wmm_tx_queue[3].count);
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"tx_suspend AC0: %d AC1: %d AC2: %d AC3: %d",
pAdapter->isTxSuspended[0],
pAdapter->isTxSuspended[1],
pAdapter->isTxSuspended[2],
pAdapter->isTxSuspended[3]);
for (i = 0; i < dev->num_tx_queues; i++)
{
txq = netdev_get_tx_queue(dev, i);
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"Queue%d status: %d txq->trans_start %lu",
i, netif_tx_queue_stopped(txq),txq->trans_start);
}
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"carrier state: %d", netif_carrier_ok(dev));
/* continuousTxTimeoutCount will be reset whenever TL fetches packet
* from HDD
*/
++pAdapter->hdd_stats.hddTxRxStats.continuousTxTimeoutCount;
diff_in_jiffies = jiffies - pAdapter->hdd_stats.hddTxRxStats.jiffiesLastTxTimeOut;
if((pAdapter->hdd_stats.hddTxRxStats.continuousTxTimeoutCount > 1)&&
((diff_in_jiffies) > (HDD_TX_TIMEOUT * 2 ))
)
{
/*
* In Open security case when there is no traffic is running, it may possible
* tx time-out may once happen and later we recovered then we need to
* reset the continuousTxTimeoutCount because it is only getting modified
* when traffic is running. So if over a period of time if this count reaches
* to HDD_TX_STALL_SSR_THRESHOLD then host is triggering false subsystem restart.
* so in genuine Tx Time out case kernel will call the tx time-out back to back at
* interval of HDD_TX_TIMEOUT.So now we are checking if previous TX TIME out was
* occurred more then twice of HDD_TX_TIMEOUT back then we may recovered here.
*/
pAdapter->hdd_stats.hddTxRxStats.continuousTxTimeoutCount = 0;
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
FL("This is false alarm so resetting the continuousTxTimeoutCount"));
}
//update last jiffies after the check
pAdapter->hdd_stats.hddTxRxStats.jiffiesLastTxTimeOut = jiffies;
if (!pHddStaCtx->conn_info.uIsAuthenticated) {
VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
FL("TL is not in authenticated state so skipping SSR"));
pAdapter->hdd_stats.hddTxRxStats.continuousTxTimeoutCount = 0;
goto print_log;
}
if (pAdapter->hdd_stats.hddTxRxStats.continuousTxTimeoutCount ==
HDD_TX_STALL_KICKDXE_THRESHOLD)
{
VOS_TRACE(VOS_MODULE_ID_HDD_SAP_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Request Kick DXE for recovery",__func__);
WLANTL_TLDebugMessage(WLANTL_DEBUG_KICKDXE);
}
if (pAdapter->hdd_stats.hddTxRxStats.continuousTxTimeoutCount ==
HDD_TX_STALL_RECOVERY_THRESHOLD)
{
VOS_TRACE(VOS_MODULE_ID_HDD_SAP_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Request firmware for recovery",__func__);
WLANTL_TLDebugMessage(WLANTL_DEBUG_FW_CLEANUP);
}
/*
* This function is getting called in softirq context, So don't hold
* any mutex.
* There is no harm here in not holding the mutex as long as we are
* not accessing the pRemainChanCtx contents.
*/
pRemainChanCtx = hdd_get_remain_on_channel_ctx(pHddCtx);
if (!pRemainChanCtx)
{
if (pAdapter->hdd_stats.hddTxRxStats.continuousTxTimeoutCount >
HDD_TX_STALL_SSR_THRESHOLD)
{
// Driver could not recover, issue SSR
VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Cannot recover from Data stall Issue SSR",
__func__);
WLANTL_FatalError();
// reset count after issuing the SSR
pAdapter->hdd_stats.hddTxRxStats.continuousTxTimeoutCount = 0;
return;
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"Remain on channel in progress");
/* The supplicant can retry "P2P Invitation Request" for 120 times
* and so there is a possbility that we can remain off channel for
* the entire duration of these retries(which can be max 60sec).
* If we encounter such a case, let us not trigger SSR after 30sec
* but wait for 60sec to let the device go on home channel and start
* tx. If tx does not start within 70sec we will issue SSR.
*/
if (pAdapter->hdd_stats.hddTxRxStats.continuousTxTimeoutCount >
HDD_TX_STALL_SSR_THRESHOLD_HIGH)
{
// Driver could not recover, issue SSR
VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Cannot recover from Data stall Issue SSR",
__func__);
WLANTL_FatalError();
return;
}
}
print_log:
/* If Tx stalled for a long time then *hdd_tx_timeout* is called
* every 5sec. The TL debug spits out a lot of information on the
* serial console, if it is called every time *hdd_tx_timeout* is
* called then we may get a watchdog bite on the Application
* processor, so ratelimit the TL debug logs.
*/
if (__ratelimit(&hdd_tx_timeout_rs))
{
hdd_wmm_tx_snapshot(pAdapter);
WLANTL_TLDebugMessage(WLANTL_DEBUG_TX_SNAPSHOT);
}
/* Call fatal event if data stall is for
* HDD_TX_STALL_FATAL_EVENT_THRESHOLD times
*/
if (HDD_TX_STALL_FATAL_EVENT_THRESHOLD ==
pAdapter->hdd_stats.hddTxRxStats.continuousTxTimeoutCount)
vos_fatal_event_logs_req(WLAN_LOG_TYPE_FATAL,
WLAN_LOG_INDICATOR_HOST_DRIVER,
WLAN_LOG_REASON_DATA_STALL,
FALSE, TRUE);
}
/**============================================================================
@brief hdd_tx_timeout() - Function called by OS if there is any
timeout during transmission. Since HDD simply enqueues packet
and returns control to OS right away, this would never be invoked
@param dev : [in] pointer to network device
@return : None
===========================================================================*/
void hdd_tx_timeout(struct net_device *dev)
{
vos_ssr_protect(__func__);
__hdd_tx_timeout(dev);
vos_ssr_unprotect(__func__);
}
/**============================================================================
@brief __hdd_stats() - Function registered with the Linux OS for
device TX/RX statistic
@param dev : [in] pointer to Libra network device
@return : pointer to net_device_stats structure
===========================================================================*/
struct net_device_stats* __hdd_stats(struct net_device *dev)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
if ( NULL == pAdapter )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
FL("pAdapter is NULL"));
VOS_ASSERT(0);
return NULL;
}
return &pAdapter->stats;
}
struct net_device_stats* hdd_stats(struct net_device *dev)
{
struct net_device_stats* dev_stats;
vos_ssr_protect(__func__);
dev_stats = __hdd_stats(dev);
vos_ssr_unprotect(__func__);
return dev_stats;
}
/**============================================================================
@brief hdd_ibss_init_tx_rx() - Init function to initialize Tx/RX
modules in HDD
@param pAdapter : [in] pointer to adapter context
@return : VOS_STATUS_E_FAILURE if any errors encountered
: VOS_STATUS_SUCCESS otherwise
===========================================================================*/
void hdd_ibss_init_tx_rx( hdd_adapter_t *pAdapter )
{
v_U8_t i;
v_U8_t STAId = 0;
hdd_station_ctx_t *pHddStaCtx = &pAdapter->sessionCtx.station;
hdd_ibss_peer_info_t *pPeerInfo = &pHddStaCtx->ibss_peer_info;
v_U8_t pACWeights[] = {
HDD_SOFTAP_BK_WEIGHT_DEFAULT,
HDD_SOFTAP_BE_WEIGHT_DEFAULT,
HDD_SOFTAP_VI_WEIGHT_DEFAULT,
HDD_SOFTAP_VO_WEIGHT_DEFAULT
};
pAdapter->isVosOutOfResource = VOS_FALSE;
pAdapter->isVosLowResource = VOS_FALSE;
// Since SAP model is used for IBSS also. Using same queue length as in SAP.
pAdapter->aTxQueueLimit[WLANTL_AC_BK] = HDD_SOFTAP_TX_BK_QUEUE_MAX_LEN;
pAdapter->aTxQueueLimit[WLANTL_AC_BE] = HDD_SOFTAP_TX_BE_QUEUE_MAX_LEN;
pAdapter->aTxQueueLimit[WLANTL_AC_VI] = HDD_SOFTAP_TX_VI_QUEUE_MAX_LEN;
pAdapter->aTxQueueLimit[WLANTL_AC_VO] = HDD_SOFTAP_TX_VO_QUEUE_MAX_LEN;
for (STAId = 0; STAId < HDD_MAX_NUM_IBSS_STA; STAId++)
{
vos_mem_zero(&pPeerInfo->ibssStaInfo[STAId], sizeof(hdd_ibss_station_info_t));
for (i = 0; i < NUM_TX_QUEUES; i ++)
{
hdd_list_init(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[i], HDD_TX_QUEUE_MAX_LEN);
}
}
/* Update the AC weights suitable for SoftAP mode of operation */
WLANTL_SetACWeights((WLAN_HDD_GET_CTX(pAdapter))->pvosContext, pACWeights);
}
/**============================================================================
@brief hdd_ibss_deinit_tx_rx() - Deinit function to clean up Tx/RX
modules in HDD
@param pAdapter : [in] pointer to adapter context..
@return : VOS_STATUS_E_FAILURE if any errors encountered.
: VOS_STATUS_SUCCESS otherwise
===========================================================================*/
VOS_STATUS hdd_ibss_deinit_tx_rx( hdd_adapter_t *pAdapter )
{
VOS_STATUS status = VOS_STATUS_SUCCESS;
v_U8_t STAId = 0;
hdd_station_ctx_t *pHddStaCtx = &pAdapter->sessionCtx.station;
hdd_ibss_peer_info_t * pPeerInfo = &pHddStaCtx->ibss_peer_info;
hdd_list_node_t *anchor = NULL;
skb_list_node_t *pktNode = NULL;
struct sk_buff *skb = NULL;
v_SINT_t i = -1;
for (STAId = 0; STAId < HDD_MAX_NUM_IBSS_STA; STAId++)
{
if (VOS_FALSE == pPeerInfo->ibssStaInfo[STAId].isUsed)
{
continue;
}
for (i = 0; i < NUM_TX_QUEUES; i ++)
{
spin_lock_bh(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[i].lock);
while (true)
{
status = hdd_list_remove_front ( &pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[i], &anchor);
if (VOS_STATUS_E_EMPTY != status)
{
//If success then we got a valid packet from some AC
pktNode = list_entry(anchor, skb_list_node_t, anchor);
skb = pktNode->skb;
++pAdapter->stats.tx_dropped;
++pAdapter->hdd_stats.hddTxRxStats.txFlushed;
++pAdapter->hdd_stats.hddTxRxStats.txFlushedAC[i];
kfree_skb(skb);
continue;
}
//current list is empty
break;
}
pPeerInfo->ibssStaInfo[STAId].txSuspended[i] = VOS_FALSE;
spin_unlock_bh(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[i].lock);
}
}
pAdapter->isVosLowResource = VOS_FALSE;
return status;
}
/**============================================================================
@brief hdd_init_tx_rx() - Init function to initialize Tx/RX
modules in HDD
@param pAdapter : [in] pointer to adapter context
@return : VOS_STATUS_E_FAILURE if any errors encountered
: VOS_STATUS_SUCCESS otherwise
===========================================================================*/
VOS_STATUS hdd_init_tx_rx( hdd_adapter_t *pAdapter )
{
VOS_STATUS status = VOS_STATUS_SUCCESS;
v_SINT_t i = -1;
if ( NULL == pAdapter )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
FL("pAdapter is NULL"));
VOS_ASSERT(0);
return VOS_STATUS_E_FAILURE;
}
pAdapter->isVosOutOfResource = VOS_FALSE;
pAdapter->isVosLowResource = VOS_FALSE;
//vos_mem_zero(&pAdapter->stats, sizeof(struct net_device_stats));
//Will be zeroed out during alloc
while (++i != NUM_TX_QUEUES)
{
pAdapter->isTxSuspended[i] = VOS_FALSE;
hdd_list_init( &pAdapter->wmm_tx_queue[i], HDD_TX_QUEUE_MAX_LEN);
}
return status;
}
/**============================================================================
@brief hdd_deinit_tx_rx() - Deinit function to clean up Tx/RX
modules in HDD
@param pAdapter : [in] pointer to adapter context
@return : VOS_STATUS_E_FAILURE if any errors encountered
: VOS_STATUS_SUCCESS otherwise
===========================================================================*/
VOS_STATUS hdd_deinit_tx_rx( hdd_adapter_t *pAdapter )
{
VOS_STATUS status = VOS_STATUS_SUCCESS;
v_SINT_t i = -1;
if ( NULL == pAdapter )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
FL("pAdapter is NULL"));
VOS_ASSERT(0);
return VOS_STATUS_E_FAILURE;
}
status = hdd_flush_tx_queues(pAdapter);
if (VOS_STATUS_SUCCESS != status)
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_WARN,
FL("failed to flush tx queues"));
while (++i != NUM_TX_QUEUES)
{
//Free up actual list elements in the Tx queue
hdd_list_destroy( &pAdapter->wmm_tx_queue[i] );
}
return status;
}
/**============================================================================
@brief hdd_disconnect_tx_rx() - Disconnect function to clean up Tx/RX
modules in HDD
@param pAdapter : [in] pointer to adapter context
@return : VOS_STATUS_E_FAILURE if any errors encountered
: VOS_STATUS_SUCCESS otherwise
===========================================================================*/
VOS_STATUS hdd_disconnect_tx_rx( hdd_adapter_t *pAdapter )
{
return hdd_flush_tx_queues(pAdapter);
}
/**============================================================================
@brief hdd_IsEAPOLPacket() - Checks the packet is EAPOL or not.
@param pVosPacket : [in] pointer to vos packet
@return : VOS_TRUE if the packet is EAPOL
: VOS_FALSE otherwise
===========================================================================*/
v_BOOL_t hdd_IsEAPOLPacket( vos_pkt_t *pVosPacket )
{
VOS_STATUS vosStatus = VOS_STATUS_SUCCESS;
v_BOOL_t fEAPOL = VOS_FALSE;
void *pBuffer = NULL;
vosStatus = vos_pkt_peek_data( pVosPacket, (v_SIZE_t)HDD_ETHERTYPE_802_1_X_FRAME_OFFSET,
&pBuffer, HDD_ETHERTYPE_802_1_X_SIZE );
if ( VOS_IS_STATUS_SUCCESS( vosStatus ) )
{
if ( pBuffer && *(unsigned short*)pBuffer ==
vos_cpu_to_be16(HDD_ETHERTYPE_802_1_X) )
{
fEAPOL = VOS_TRUE;
}
}
return fEAPOL;
}
/**============================================================================
@brief hdd_IsARP() - Checks the packet is ARP or not.
@param pVosPacket : [in] pointer to vos packet
@return : VOS_TRUE if the packet is ARP
: VOS_FALSE otherwise
===========================================================================*/
v_BOOL_t hdd_IsARP( vos_pkt_t *pVosPacket )
{
VOS_STATUS vosStatus = VOS_STATUS_SUCCESS;
v_BOOL_t fIsARP = VOS_FALSE;
void *pBuffer = NULL;
vosStatus = vos_pkt_peek_data( pVosPacket,
(v_SIZE_t)HDD_ETHERTYPE_802_1_X_FRAME_OFFSET,
&pBuffer, HDD_ETHERTYPE_802_1_X_SIZE );
if ( VOS_IS_STATUS_SUCCESS( vosStatus ) )
{
if ( pBuffer && *(unsigned short*)pBuffer ==
vos_cpu_to_be16(HDD_ETHERTYPE_ARP) )
{
fIsARP = VOS_TRUE;
}
}
return fIsARP;
}
#ifdef FEATURE_WLAN_WAPI // Need to update this function
/**============================================================================
@brief hdd_IsWAIPacket() - Checks the packet is WAI or not.
@param pVosPacket : [in] pointer to vos packet
@return : VOS_TRUE if the packet is WAI
: VOS_FALSE otherwise
===========================================================================*/
v_BOOL_t hdd_IsWAIPacket( vos_pkt_t *pVosPacket )
{
VOS_STATUS vosStatus = VOS_STATUS_SUCCESS;
v_BOOL_t fIsWAI = VOS_FALSE;
void *pBuffer = NULL;
// Need to update this function
vosStatus = vos_pkt_peek_data( pVosPacket, (v_SIZE_t)HDD_ETHERTYPE_802_1_X_FRAME_OFFSET,
&pBuffer, HDD_ETHERTYPE_802_1_X_SIZE );
if (VOS_IS_STATUS_SUCCESS( vosStatus ) )
{
if ( pBuffer && *(unsigned short*)pBuffer ==
vos_cpu_to_be16(HDD_ETHERTYPE_WAI) )
{
fIsWAI = VOS_TRUE;
}
}
return fIsWAI;
}
#endif /* FEATURE_WLAN_WAPI */
/**============================================================================
@brief hdd_tx_complete_cbk() - Callback function invoked by TL
to indicate that a packet has been transmitted across the SDIO bus
succesfully. OS packet resources can be released after this cbk.
@param vosContext : [in] pointer to VOS context
@param pVosPacket : [in] pointer to VOS packet (containing skb)
@param vosStatusIn : [in] status of the transmission
@return : VOS_STATUS_E_FAILURE if any errors encountered
: VOS_STATUS_SUCCESS otherwise
===========================================================================*/
VOS_STATUS hdd_tx_complete_cbk( v_VOID_t *vosContext,
vos_pkt_t *pVosPacket,
VOS_STATUS vosStatusIn )
{
VOS_STATUS status = VOS_STATUS_SUCCESS;
hdd_adapter_t *pAdapter = NULL;
hdd_context_t *pHddCtx = NULL;
void* pOsPkt = NULL;
if( ( NULL == vosContext ) || ( NULL == pVosPacket ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Null params being passed", __func__);
return VOS_STATUS_E_FAILURE;
}
//Return the skb to the OS
status = vos_pkt_get_os_packet( pVosPacket, &pOsPkt, VOS_TRUE );
if (!VOS_IS_STATUS_SUCCESS( status ))
{
//This is bad but still try to free the VOSS resources if we can
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Failure extracting skb from vos pkt", __func__);
vos_pkt_return_packet( pVosPacket );
return VOS_STATUS_E_FAILURE;
}
//Get the HDD context.
pHddCtx = (hdd_context_t *)vos_get_context( VOS_MODULE_ID_HDD, vosContext );
//Get the Adapter context.
pAdapter = hdd_get_adapter(pHddCtx,WLAN_HDD_INFRA_STATION);
if (pAdapter == NULL || WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)
{
VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"%s: Invalid adapter %pK", __func__, pAdapter);
}
else
{
++pAdapter->hdd_stats.hddTxRxStats.txCompleted;
}
kfree_skb((struct sk_buff *)pOsPkt);
//Return the VOS packet resources.
status = vos_pkt_return_packet( pVosPacket );
if (!VOS_IS_STATUS_SUCCESS( status ))
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Could not return VOS packet to the pool", __func__);
}
return status;
}
/**============================================================================
@brief hdd_ibss_tx_fetch_packet_cbk() - Callback function invoked by TL to
fetch a packet for transmission.
@param vosContext : [in] pointer to VOS context
@param staId : [in] Station for which TL is requesting a pkt
@param ac : [in] access category requested by TL
@param pVosPacket : [out] pointer to VOS packet packet pointer
@param pPktMetaInfo : [out] pointer to meta info for the pkt
@return : VOS_STATUS_E_EMPTY if no packets to transmit
: VOS_STATUS_E_FAILURE if any errors encountered
: VOS_STATUS_SUCCESS otherwise
===========================================================================*/
VOS_STATUS hdd_ibss_tx_fetch_packet_cbk( v_VOID_t *vosContext,
v_U8_t *pStaId,
WLANTL_ACEnumType ac,
vos_pkt_t **ppVosPacket,
WLANTL_MetaInfoType *pPktMetaInfo )
{
VOS_STATUS status = VOS_STATUS_E_FAILURE;
hdd_adapter_t *pAdapter = NULL;
hdd_list_node_t *anchor = NULL;
skb_list_node_t *pktNode = NULL;
struct sk_buff *skb = NULL;
vos_pkt_t *pVosPacket = NULL;
v_MACADDR_t* pDestMacAddress = NULL;
v_TIME_t timestamp;
v_SIZE_t size = 0;
v_U8_t STAId = WLAN_MAX_STA_COUNT;
hdd_context_t *pHddCtx = NULL;
hdd_station_ctx_t *pHddStaCtx = NULL;
hdd_ibss_peer_info_t *pPeerInfo = NULL;
v_U8_t proto_type = 0;
v_U16_t packet_size;
//Sanity check on inputs
if ( ( NULL == vosContext ) ||
( NULL == pStaId ) ||
( NULL == ppVosPacket ) ||
( NULL == pPktMetaInfo ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Null Params being passed", __func__);
return VOS_STATUS_E_FAILURE;
}
//Get the HDD context.
pHddCtx = (hdd_context_t *)vos_get_context( VOS_MODULE_ID_HDD, vosContext );
if ( NULL == pHddCtx )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: HDD adapter context is Null", __func__);
return VOS_STATUS_E_FAILURE;
}
STAId = *pStaId;
pAdapter = pHddCtx->sta_to_adapter[STAId];
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic))
{
VOS_ASSERT(0);
return VOS_STATUS_E_FAILURE;
}
pHddStaCtx = &pAdapter->sessionCtx.station;
pPeerInfo = &pHddStaCtx->ibss_peer_info;
if (FALSE == pPeerInfo->ibssStaInfo[STAId].isUsed )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Unregistered STAId %d passed by TL", __func__, STAId);
return VOS_STATUS_E_FAILURE;
}
++pAdapter->hdd_stats.hddTxRxStats.txFetched;
*ppVosPacket = NULL;
//Make sure the AC being asked for is sane
if( ac > WLANTL_MAX_AC || ac < 0)
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Invalid AC %d passed by TL", __func__, ac);
return VOS_STATUS_E_FAILURE;
}
++pAdapter->hdd_stats.hddTxRxStats.txFetchedAC[ac];
//Get the vos packet before so that we are prepare for VOS low reseurce condition
//This simplifies the locking and unlocking of Tx queue
status = vos_pkt_wrap_data_packet( &pVosPacket,
VOS_PKT_TYPE_TX_802_3_DATA,
NULL, //OS Pkt is not being passed
hdd_tx_low_resource_cbk,
pAdapter );
if ((status == VOS_STATUS_E_ALREADY) || (status == VOS_STATUS_E_RESOURCES))
{
//Remember VOS is in a low resource situation
pAdapter->isVosOutOfResource = VOS_TRUE;
++pAdapter->hdd_stats.hddTxRxStats.txFetchLowResources;
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_WARN,
"%s: VOSS in Low Resource scenario", __func__);
//TL needs to handle this case. VOS_STATUS_E_EMPTY is returned when the queue is empty.
return VOS_STATUS_E_FAILURE;
}
/* Only fetch this station and this AC. Return VOS_STATUS_E_EMPTY if nothing there.
Do not get next AC as the other branch does.
*/
spin_lock_bh(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac].lock);
hdd_list_size(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac], &size);
if (0 == size)
{
spin_unlock_bh(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac].lock);
vos_pkt_return_packet(pVosPacket);
return VOS_STATUS_E_EMPTY;
}
status = hdd_list_remove_front( &pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac], &anchor );
spin_unlock_bh(&pPeerInfo->ibssStaInfo[STAId].wmm_tx_queue[ac].lock);
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"%s: AC %d has packets pending", __func__, ac);
if(VOS_STATUS_SUCCESS == status)
{
//If success then we got a valid packet from some AC
pktNode = list_entry(anchor, skb_list_node_t, anchor);
skb = pktNode->skb;
}
else
{
++pAdapter->hdd_stats.hddTxRxStats.txFetchDequeueError;
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Error in de-queuing skb from Tx queue status = %d",
__func__, status );
vos_pkt_return_packet(pVosPacket);
return VOS_STATUS_E_FAILURE;
}
//Attach skb to VOS packet.
status = vos_pkt_set_os_packet( pVosPacket, skb );
if (status != VOS_STATUS_SUCCESS)
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Error attaching skb", __func__);
vos_pkt_return_packet(pVosPacket);
++pAdapter->stats.tx_dropped;
++pAdapter->hdd_stats.hddTxRxStats.txFetchDequeueError;
kfree_skb(skb);
return VOS_STATUS_E_FAILURE;
}
//Return VOS packet to TL;
*ppVosPacket = pVosPacket;
//Fill out the meta information needed by TL
vos_pkt_get_timestamp( pVosPacket, &timestamp );
pPktMetaInfo->usTimeStamp = (v_U16_t)timestamp;
if ( 1 < size )
{
pPktMetaInfo->bMorePackets = 1; //HDD has more packets to send
}
else
{
pPktMetaInfo->bMorePackets = 0;
}
if(pAdapter->sessionCtx.station.conn_info.uIsAuthenticated == VOS_TRUE)
pPktMetaInfo->ucIsEapol = 0;
else
pPktMetaInfo->ucIsEapol = hdd_IsEAPOLPacket( pVosPacket ) ? 1 : 0;
if ((NULL != pHddCtx) &&
(pHddCtx->cfg_ini->gEnableDebugLog))
{
proto_type = vos_pkt_get_proto_type(skb,
pHddCtx->cfg_ini->gEnableDebugLog);
if (VOS_PKT_PROTO_TYPE_EAPOL & proto_type)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"IBSS STA TX EAPOL");
}
else if (VOS_PKT_PROTO_TYPE_DHCP & proto_type)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"IBSS STA TX DHCP");
}
else if (VOS_PKT_PROTO_TYPE_ARP & proto_type)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"IBSS STA TX ARP");
}
}
vos_pkt_get_packet_length( pVosPacket,&packet_size );
if ( HDD_ETHERTYPE_ARP_SIZE == packet_size )
pPktMetaInfo->ucIsArp = hdd_IsARP( pVosPacket ) ? 1 : 0;
pPktMetaInfo->ucUP = pktNode->userPriority;
pPktMetaInfo->ucTID = pPktMetaInfo->ucUP;
pPktMetaInfo->ucType = 0;
//Extract the destination address from ethernet frame
pDestMacAddress = (v_MACADDR_t*)skb->data;
// we need 802.3 to 802.11 frame translation
// (note that Bcast/Mcast will be translated in SW, unicast in HW)
pPktMetaInfo->ucDisableFrmXtl = 0;
pPktMetaInfo->ucBcast = vos_is_macaddr_broadcast( pDestMacAddress ) ? 1 : 0;
pPktMetaInfo->ucMcast = vos_is_macaddr_group( pDestMacAddress ) ? 1 : 0;
if ( (pPeerInfo->ibssStaInfo[STAId].txSuspended[ac]) &&
(size <= ((pAdapter->aTxQueueLimit[ac]*3)/4) ))
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"%s: TX queue re-enabled", __func__);
pPeerInfo->ibssStaInfo[STAId].txSuspended[ac] = VOS_FALSE;
netif_wake_subqueue(pAdapter->dev, skb_get_queue_mapping(skb));
}
// We're giving the packet to TL so consider it transmitted from
// a statistics perspective. We account for it here instead of
// when the packet is returned for two reasons. First, TL will
// manipulate the skb to the point where the len field is not
// accurate, leading to inaccurate byte counts if we account for
// it later. Second, TL does not provide any feedback as to
// whether or not the packet was successfully sent over the air,
// so the packet counts will be the same regardless of where we
// account for them
pAdapter->stats.tx_bytes += skb->len;
++pAdapter->stats.tx_packets;
++pAdapter->hdd_stats.hddTxRxStats.txFetchDequeued;
++pAdapter->hdd_stats.hddTxRxStats.txFetchDequeuedAC[ac];
pAdapter->hdd_stats.hddTxRxStats.continuousTxTimeoutCount = 0;
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"%s: Valid VOS PKT returned to TL", __func__);
return status;
}
/**============================================================================
@brief hdd_tx_fetch_packet_cbk() - Callback function invoked by TL to
fetch a packet for transmission.
@param vosContext : [in] pointer to VOS context
@param staId : [in] Station for which TL is requesting a pkt
@param ac : [in] access category requested by TL
@param pVosPacket : [out] pointer to VOS packet packet pointer
@param pPktMetaInfo : [out] pointer to meta info for the pkt
@return : VOS_STATUS_E_EMPTY if no packets to transmit
: VOS_STATUS_E_FAILURE if any errors encountered
: VOS_STATUS_SUCCESS otherwise
===========================================================================*/
VOS_STATUS hdd_tx_fetch_packet_cbk( v_VOID_t *vosContext,
v_U8_t *pStaId,
WLANTL_ACEnumType ac,
vos_pkt_t **ppVosPacket,
WLANTL_MetaInfoType *pPktMetaInfo )
{
VOS_STATUS status = VOS_STATUS_E_FAILURE;
hdd_adapter_t *pAdapter = NULL;
hdd_context_t *pHddCtx = NULL;
hdd_list_node_t *anchor = NULL;
skb_list_node_t *pktNode = NULL;
struct sk_buff *skb = NULL;
vos_pkt_t *pVosPacket = NULL;
v_MACADDR_t* pDestMacAddress = NULL;
v_TIME_t timestamp;
WLANTL_ACEnumType newAc;
v_SIZE_t size = 0;
v_U16_t packet_size;
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 ) ||
( NULL == pStaId ) ||
( NULL == ppVosPacket ) ||
( NULL == pPktMetaInfo ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Null Params being passed", __func__);
return VOS_STATUS_E_FAILURE;
}
//Get the HDD context.
pHddCtx = (hdd_context_t *)vos_get_context( VOS_MODULE_ID_HDD, vosContext );
if(pHddCtx == NULL)
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: HDD adapter context is Null", __func__);
return VOS_STATUS_E_FAILURE;
}
pAdapter = pHddCtx->sta_to_adapter[*pStaId];
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic))
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
FL("invalid adapter:%pK for staId:%u"), pAdapter, *pStaId);
VOS_ASSERT(0);
return VOS_STATUS_E_FAILURE;
}
++pAdapter->hdd_stats.hddTxRxStats.txFetched;
*ppVosPacket = NULL;
//Make sure the AC being asked for is sane
if (ac > WLANTL_AC_HIGH_PRIO || ac < 0)
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Invalid QId %d passed by TL", __func__, ac);
return VOS_STATUS_E_FAILURE;
}
++pAdapter->hdd_stats.hddTxRxStats.txFetchedAC[ac];
#ifdef HDD_WMM_DEBUG
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_FATAL,
"%s: AC %d passed by TL", __func__, ac);
#endif // HDD_WMM_DEBUG
// do we have any packets pending in this AC?
hdd_list_size( &pAdapter->wmm_tx_queue[ac], &size );
if( size > 0 )
{
// yes, so process it
#ifdef HDD_WMM_DEBUG
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_FATAL,
"%s: AC %d has packets pending", __func__, ac);
#endif // HDD_WMM_DEBUG
}
else
{
++pAdapter->hdd_stats.hddTxRxStats.txFetchEmpty;
#ifdef HDD_WMM_DEBUG
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_FATAL,
"%s: no packets pending", __func__);
#endif // HDD_WMM_DEBUG
return VOS_STATUS_E_FAILURE;
}
// Note here that we are not checking "wmmAcAccessAllowed" for packets
// in new queue since there is no one AC associated to the new queue.
// Since there will be either eapol or dhcp pkts in new queue overlooking
// this should be okay from implicit QoS perspective.
if (ac != WLANTL_AC_HIGH_PRIO)
{
// We find an AC with packets
// or we determine we have no more packets to send
// HDD is not allowed to change AC.
// has this AC been admitted? or
// To allow EAPOL packets when not authenticated
if (unlikely((0==pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcAccessAllowed) &&
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.uIsAuthenticated))
{
++pAdapter->hdd_stats.hddTxRxStats.txFetchEmpty;
#ifdef HDD_WMM_DEBUG
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_FATAL,
"%s: no packets pending", __func__);
#endif // HDD_WMM_DEBUG
return VOS_STATUS_E_FAILURE;
}
}
//Get the vos packet. I don't want to dequeue and enqueue again if we are out of VOS resources
//This simplifies the locking and unlocking of Tx queue
status = vos_pkt_wrap_data_packet( &pVosPacket,
VOS_PKT_TYPE_TX_802_3_DATA,
NULL, //OS Pkt is not being passed
hdd_tx_low_resource_cbk,
pAdapter );
if (status == VOS_STATUS_E_ALREADY || status == VOS_STATUS_E_RESOURCES)
{
//Remember VOS is in a low resource situation
pAdapter->isVosOutOfResource = VOS_TRUE;
++pAdapter->hdd_stats.hddTxRxStats.txFetchLowResources;
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_WARN,"%s: VOSS in Low Resource scenario", __func__);
//TL will now think we have no more packets in this AC
return VOS_STATUS_E_FAILURE;
}
//Remove the packet from the queue
spin_lock_bh(&pAdapter->wmm_tx_queue[ac].lock);
status = hdd_list_remove_front( &pAdapter->wmm_tx_queue[ac], &anchor );
spin_unlock_bh(&pAdapter->wmm_tx_queue[ac].lock);
if(VOS_STATUS_SUCCESS == status)
{
//If success then we got a valid packet from some AC
pktNode = list_entry(anchor, skb_list_node_t, anchor);
skb = pktNode->skb;
actualAC = hddWmmUpToAcMap[pktNode->userPriority];
if (actualAC >= WLANTL_MAX_AC)
{
/* To fix klocwork */
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_WARN,
"%s: Invalid AC for packet:%d", __func__, actualAC);
actualAC = WLANTL_AC_BE;
}
}
else
{
++pAdapter->hdd_stats.hddTxRxStats.txFetchDequeueError;
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_WARN, "%s: Error in de-queuing "
"skb from Tx queue status = %d", __func__, status );
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;
++pAdapter->hdd_stats.hddTxRxStats.txFetchDequeueError;
kfree_skb(skb);
return VOS_STATUS_E_FAILURE;
}
//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;
kfree_skb(skb);
return VOS_STATUS_E_FAILURE;
}
#ifdef WLAN_FEATURE_LINK_LAYER_STATS
if (vos_is_macaddr_multicast((v_MACADDR_t*)skb->data))
{
pAdapter->hdd_stats.hddTxRxStats.txMcast[actualAC]++;
}
#endif
wlan_hdd_tdls_notify_packet(pAdapter, skb);
//Return VOS packet to TL;
*ppVosPacket = pVosPacket;
//Fill out the meta information needed by TL
//FIXME This timestamp is really the time stamp of wrap_data_packet
vos_pkt_get_timestamp( pVosPacket, &timestamp );
pPktMetaInfo->usTimeStamp = (v_U16_t)timestamp;
if(pAdapter->sessionCtx.station.conn_info.uIsAuthenticated == VOS_TRUE)
pPktMetaInfo->ucIsEapol = 0;
else
pPktMetaInfo->ucIsEapol = hdd_IsEAPOLPacket( pVosPacket ) ? 1 : 0;
if (pPktMetaInfo->ucIsEapol)
wlan_hdd_log_eapol(skb, WIFI_EVENT_DRIVER_EAPOL_FRAME_TRANSMIT_REQUESTED);
if ((NULL != pHddCtx) &&
(pHddCtx->cfg_ini->gEnableDebugLog))
{
proto_type = vos_pkt_get_proto_type(skb,
pHddCtx->cfg_ini->gEnableDebugLog);
if (VOS_PKT_PROTO_TYPE_EAPOL & proto_type)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"STA TX EAPOL");
}
else if (VOS_PKT_PROTO_TYPE_DHCP & proto_type)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"STA TX DHCP");
}
else if (VOS_PKT_PROTO_TYPE_ARP & proto_type)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"STA TX ARP");
}
}
vos_pkt_get_packet_length( pVosPacket,&packet_size );
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;
#endif /* FEATURE_WLAN_WAPI */
/* 1. Check if ACM is set for this AC
* 2. If set, check if this AC had already admitted
* 3. If not already admitted, downgrade the UP to next best UP
* 4. Allow only when medium time is non zero when Addts accepted
* else downgrade traffic. we opted downgrading over Delts when
* medium time is zero because while doing downgradig driver is not
* clearing the wmm context so consider in subsequent roaming
* if AP (new or same AP) accept the Addts with valid medium time
* no application support is required where if we have opted
* delts Applications have to again do Addts or STA will never
* go for Addts.
*/
pPktMetaInfo->ac = actualAC;
if(!pAdapter->hddWmmStatus.wmmAcStatus[actualAC].wmmAcAccessRequired ||
(pAdapter->hddWmmStatus.wmmAcStatus[actualAC].wmmAcTspecValid &&
pAdapter->hddWmmStatus.wmmAcStatus[actualAC].wmmAcTspecInfo.medium_time))
{
pPktMetaInfo->ucUP = pktNode->userPriority;
pPktMetaInfo->ucTID = pPktMetaInfo->ucUP;
}
else
{
//Downgrade the UP
acAdmitted = pAdapter->hddWmmStatus.wmmAcStatus[actualAC].wmmAcTspecValid;
newAc = WLANTL_AC_BK;
for (i=actualAC-1; i>0; i--)
{
if (pAdapter->hddWmmStatus.wmmAcStatus[i].wmmAcAccessRequired == 0)
{
newAc = i;
break;
}
}
pPktMetaInfo->ucUP = hddWmmAcToHighestUp[newAc];
pPktMetaInfo->ucTID = pPktMetaInfo->ucUP;
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO_LOW,
"Downgrading UP %d to UP %d ",
pktNode->userPriority, pPktMetaInfo->ucUP);
}
if (pHddCtx->cfg_ini->gEnableRoamDelayStats)
{
vos_record_roam_event(e_TL_FIRST_XMIT_TIME, NULL, 0);
}
pPktMetaInfo->ucType = 0; //FIXME Don't know what this is
pPktMetaInfo->ucDisableFrmXtl = 0; //802.3 frame so we need to xlate
if ( 1 < size )
{
pPktMetaInfo->bMorePackets = 1; //HDD has more packets to send
}
else
{
pPktMetaInfo->bMorePackets = 0;
}
//Extract the destination address from ethernet frame
pDestMacAddress = (v_MACADDR_t*)skb->data;
pPktMetaInfo->ucBcast = vos_is_macaddr_broadcast( pDestMacAddress ) ? 1 : 0;
pPktMetaInfo->ucMcast = vos_is_macaddr_group( pDestMacAddress ) ? 1 : 0;
// if we are in a backpressure situation see if we can turn the hose back on
if ( (pAdapter->isTxSuspended[ac]) &&
(size <= HDD_TX_QUEUE_LOW_WATER_MARK) )
{
++pAdapter->hdd_stats.hddTxRxStats.txFetchDePressured;
++pAdapter->hdd_stats.hddTxRxStats.txFetchDePressuredAC[ac];
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"%s: TX queue[%d] re-enabled", __func__, ac);
pAdapter->isTxSuspended[ac] = VOS_FALSE;
netif_tx_wake_queue(netdev_get_tx_queue(pAdapter->dev,
skb_get_queue_mapping(skb) ));
MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_WAKE_NETDEV,
pAdapter->sessionId, ac));
}
// We're giving the packet to TL so consider it transmitted from
// a statistics perspective. We account for it here instead of
// when the packet is returned for two reasons. First, TL will
// manipulate the skb to the point where the len field is not
// accurate, leading to inaccurate byte counts if we account for
// it later. Second, TL does not provide any feedback as to
// whether or not the packet was successfully sent over the air,
// so the packet counts will be the same regardless of where we
// account for them
pAdapter->stats.tx_bytes += skb->len;
++pAdapter->stats.tx_packets;
++pAdapter->hdd_stats.hddTxRxStats.txFetchDequeued;
++pAdapter->hdd_stats.hddTxRxStats.txFetchDequeuedAC[ac];
pAdapter->hdd_stats.hddTxRxStats.continuousTxTimeoutCount = 0;
if((pHddCtx->cfg_ini->thermalMitigationEnable) &&
(WLAN_HDD_INFRA_STATION == pAdapter->device_mode))
{
if(mutex_lock_interruptible(&pHddCtx->tmInfo.tmOperationLock))
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Tm Lock fail", __func__);
return VOS_STATUS_E_FAILURE;
}
if(WLAN_HDD_TM_LEVEL_1 < pHddCtx->tmInfo.currentTmLevel)
{
if(0 == pHddCtx->tmInfo.txFrameCount)
{
/* Just recovered from sleep timeout */
pHddCtx->tmInfo.lastOpenTs = timestamp;
}
if((VOS_FALSE == pHddCtx->tmInfo.qBlocked) &&
((timestamp - pHddCtx->tmInfo.lastOpenTs) > (pHddCtx->tmInfo.tmAction.txOperationDuration / 10)) &&
(pHddCtx->tmInfo.txFrameCount >= pHddCtx->tmInfo.tmAction.txBlockFrameCountThreshold))
{
/* During TX open duration, TX frame count is larger than threshold
* Block TX during Sleep time */
hddLog(VOS_TRACE_LEVEL_INFO, FL("Disabling queues"));
netif_tx_stop_all_queues(pAdapter->dev);
pHddCtx->tmInfo.qBlocked = VOS_TRUE;
pHddCtx->tmInfo.lastblockTs = timestamp;
if(VOS_TIMER_STATE_STOPPED == vos_timer_getCurrentState(&pHddCtx->tmInfo.txSleepTimer))
{
vos_timer_start(&pHddCtx->tmInfo.txSleepTimer, pHddCtx->tmInfo.tmAction.txSleepDuration);
}
}
else if(((timestamp - pHddCtx->tmInfo.lastOpenTs) > (pHddCtx->tmInfo.tmAction.txOperationDuration / 10)) &&
(pHddCtx->tmInfo.txFrameCount < pHddCtx->tmInfo.tmAction.txBlockFrameCountThreshold))
{
/* During TX open duration, TX frame count is less than threshold
* Reset count and timestamp to prepare next cycle */
pHddCtx->tmInfo.lastOpenTs = timestamp;
pHddCtx->tmInfo.txFrameCount = 0;
}
else
{
/* Do Nothing */
}
pHddCtx->tmInfo.txFrameCount++;
}
mutex_unlock(&pHddCtx->tmInfo.tmOperationLock);
}
#ifdef HDD_WMM_DEBUG
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_FATAL,"%s: Valid VOS PKT returned to TL", __func__);
#endif // HDD_WMM_DEBUG
return status;
}
/**============================================================================
@brief hdd_tx_low_resource_cbk() - Callback function invoked in the
case where VOS packets are not available at the time of the call to get
packets. This callback function is invoked by VOS when packets are
available.
@param pVosPacket : [in] pointer to VOS packet
@param userData : [in] opaque user data that was passed initially
@return : VOS_STATUS_E_FAILURE if any errors encountered,
: VOS_STATUS_SUCCESS otherwise
=============================================================================*/
VOS_STATUS hdd_tx_low_resource_cbk( vos_pkt_t *pVosPacket,
v_VOID_t *userData )
{
VOS_STATUS status;
v_SINT_t i = 0;
v_SIZE_t size = 0;
hdd_adapter_t* pAdapter = (hdd_adapter_t *)userData;
if (NULL == pAdapter || WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)
{
VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
FL("Invalid adapter %pK"), pAdapter);
return VOS_STATUS_E_FAILURE;
}
//Return the packet to VOS. We just needed to know that VOS is out of low resource
//situation. Here we will only signal TL that there is a pending data for a STA.
//VOS packet will be requested (if needed) when TL comes back to fetch data.
vos_pkt_return_packet( pVosPacket );
pAdapter->isVosOutOfResource = VOS_FALSE;
//Indicate to TL that there is pending data if a queue is non empty
for( i=NUM_TX_QUEUES-1; i>=0; --i )
{
size = 0;
hdd_list_size( &pAdapter->wmm_tx_queue[i], &size );
if ( size > 0 )
{
status = WLANTL_STAPktPending( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.staId [0],
(WLANTL_ACEnumType)i );
if( !VOS_IS_STATUS_SUCCESS( status ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Failure in indicating pkt to TL for ac=%d", __func__, i);
}
}
}
return VOS_STATUS_SUCCESS;
}
static void hdd_mon_add_rx_radiotap_hdr (struct sk_buff *skb,
int rtap_len, v_PVOID_t pRxPacket, hdd_mon_ctx_t *pMonCtx)
{
u8 rtap_temp[28] = {0};
struct ieee80211_radiotap_header *rthdr;
unsigned char *pos;
u32 mac_time;
u16 rx_flags = 0;
u16 rateIdx;
u16 channel_flags = 0;
u8 rfBand;
s8 currentRSSI, currentRSSI0, currentRSSI1;
s8 noise;
mac_time = WDA_GET_RX_TIMESTAMP(pRxPacket);
rateIdx = WDA_GET_RX_MAC_RATE_IDX(pRxPacket);
if( rateIdx >= 210 && rateIdx <= 217)
rateIdx-=202;
if( rateIdx >= 218 && rateIdx <= 225 )
rateIdx-=210;
if(rateIdx >= (sizeof(gRatefromIdx)/ sizeof(int))) {
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: invalid rateIdx %d make it 0", __func__, rateIdx);
rateIdx = 0;
}
rfBand = WDA_GET_RX_RFBAND(pRxPacket);
if (IS_5G_BAND(rfBand))
channel_flags |= IEEE80211_CHAN_5GHZ;
else
channel_flags |= IEEE80211_CHAN_2GHZ;
currentRSSI0 = WDA_GETRSSI0(pRxPacket) - 100;
currentRSSI1 = WDA_GETRSSI1(pRxPacket) - 100;
currentRSSI = (currentRSSI0 > currentRSSI1) ? currentRSSI0 : currentRSSI1;
noise = WDA_GET_RX_SNR(pRxPacket);
rthdr = (struct ieee80211_radiotap_header *)(&rtap_temp[0]);
/* radiotap header, set always present flags */
rthdr->it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_TSFT) |
(1 << IEEE80211_RADIOTAP_FLAGS) |
(1 << IEEE80211_RADIOTAP_CHANNEL) |
(1 << IEEE80211_RADIOTAP_RATE) |
(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
(1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
(1 << IEEE80211_RADIOTAP_RX_FLAGS));
rthdr->it_len = cpu_to_le16(rtap_len);
pos = (unsigned char *) (rthdr + 1);
/* IEEE80211_RADIOTAP_TSFT */
put_unaligned_le64(mac_time, pos);
pos += 8;
/* IEEE80211_RADIOTAP_FLAGS */
*pos = 0;
pos++;
/* IEEE80211_RADIOTAP_RATE */
*pos = gRatefromIdx[rateIdx]/5;
pos++;
/* IEEE80211_RADIOTAP_CHANNEL */
put_unaligned_le16(pMonCtx->ChannelNo, pos);
pos += 2;
put_unaligned_le16(channel_flags, pos);
pos += 2;
/* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
*pos = currentRSSI;
pos++;
/* IEEE80211_RADIOTAP_DBM_ANTNOISE */
*pos = noise;
pos++;
if ((pos - (u8 *)rthdr) & 1)
pos++;
put_unaligned_le16(rx_flags, pos);
pos += 2;
memcpy(skb_push(skb, rtap_len), &rtap_temp[0], rtap_len);
}
VOS_STATUS hdd_rx_packet_monitor_cbk(v_VOID_t *vosContext,vos_pkt_t *pVosPacket, int conversion)
{
struct sk_buff *skb = NULL;
VOS_STATUS status = VOS_STATUS_E_FAILURE;
hdd_adapter_t *pAdapter = NULL;
hdd_context_t *pHddCtx = NULL;
hdd_mon_ctx_t *pMonCtx = NULL;
v_PVOID_t pvBDHeader = NULL;
int rxstat;
int needed_headroom = 0;
//Sanity check on inputs
if ( ( NULL == vosContext ) ||
( NULL == pVosPacket ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Null params being passed", __func__);
return VOS_STATUS_E_FAILURE;
}
pHddCtx = (hdd_context_t *)vos_get_context( VOS_MODULE_ID_HDD, vosContext );
if (NULL == pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
FL("Failed to get pHddCtx from vosContext"));
vos_pkt_return_packet( pVosPacket );
return VOS_STATUS_E_FAILURE;
}
pAdapter = hdd_get_adapter(pHddCtx,WLAN_HDD_MONITOR);
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic) )
{
VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
FL("Invalid adapter %pK for MONITOR MODE"), pAdapter);
vos_pkt_return_packet( pVosPacket );
return VOS_STATUS_E_FAILURE;
}
status = WDA_DS_PeekRxPacketInfo( pVosPacket, (v_PVOID_t)&pvBDHeader, 1/*Swap BD*/ );
if ( NULL == pvBDHeader )
{
VOS_TRACE( VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_ERROR,
"Cannot extract BD header");
/* Drop packet */
vos_pkt_return_packet(pVosPacket);
return VOS_STATUS_E_FAILURE;
}
++pAdapter->hdd_stats.hddTxRxStats.rxChains;
status = vos_pkt_get_os_packet( pVosPacket, (v_VOID_t **)&skb, VOS_TRUE );
if(!VOS_IS_STATUS_SUCCESS( status ))
{
++pAdapter->hdd_stats.hddTxRxStats.rxDropped;
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Failure extracting skb from vos pkt", __func__);
vos_pkt_return_packet( pVosPacket );
return VOS_STATUS_E_FAILURE;
}
if(!conversion)
{
pMonCtx = WLAN_HDD_GET_MONITOR_CTX_PTR(pAdapter);
needed_headroom = sizeof(struct ieee80211_radiotap_header) + 18;
hdd_mon_add_rx_radiotap_hdr( skb, needed_headroom, pvBDHeader, pMonCtx );
}
skb_reset_mac_header( skb );
skb->dev = pAdapter->dev;
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = htons(ETH_P_802_2);
skb->ip_summed = CHECKSUM_NONE;
++pAdapter->hdd_stats.hddTxRxStats.rxPackets;
++pAdapter->stats.rx_packets;
pAdapter->stats.rx_bytes += skb->len;
rxstat = netif_rx_ni(skb);
if (NET_RX_SUCCESS == rxstat)
{
++pAdapter->hdd_stats.hddTxRxStats.rxDelivered;
++pAdapter->hdd_stats.hddTxRxStats.pkt_rx_count;
}
else
{
++pAdapter->hdd_stats.hddTxRxStats.rxRefused;
}
status = vos_pkt_return_packet( pVosPacket );
if(!VOS_IS_STATUS_SUCCESS( status ))
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,"%s: Failure returning vos pkt", __func__);
}
pAdapter->dev->last_rx = jiffies;
return status;
}
bool hdd_is_duplicate_ip_arp(struct sk_buff *skb)
{
struct in_ifaddr **ifap = NULL;
struct in_ifaddr *ifa = NULL;
struct in_device *in_dev;
uint32_t arp_ip,if_ip;
if (NULL == skb)
return false;
arp_ip = hdd_get_arp_src_ip(skb);
if(!skb->dev) return false;
in_dev = __in_dev_get_rtnl(skb->dev);
if (in_dev) {
for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
ifap = &ifa->ifa_next) {
if (!strcmp(skb->dev->name, ifa->ifa_label))
break;
}
}
if (ifa && ifa->ifa_local) {
if_ip = ntohl(ifa->ifa_local);
if (if_ip == arp_ip) {
return true;
}
}
return false;
}
/**============================================================================
@brief hdd_rx_packet_cbk() - Receive callback registered with TL.
TL will call this to notify the HDD when one or more packets were
received for a registered STA.
@param vosContext : [in] pointer to VOS context
@param pVosPacketChain : [in] pointer to VOS packet chain
@param staId : [in] Station Id
@param pRxMetaInfo : [in] pointer to meta info for the received pkt(s)
@return : VOS_STATUS_E_FAILURE if any errors encountered,
: VOS_STATUS_SUCCESS otherwise
===========================================================================*/
VOS_STATUS hdd_rx_packet_cbk( v_VOID_t *vosContext,
vos_pkt_t *pVosPacketChain,
v_U8_t staId,
WLANTL_RxMetaInfoType* pRxMetaInfo )
{
hdd_adapter_t *pAdapter = NULL;
hdd_context_t *pHddCtx = NULL;
VOS_STATUS status = VOS_STATUS_E_FAILURE;
int rxstat;
struct sk_buff *skb = NULL;
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 ) ||
( NULL == pVosPacketChain ) ||
( NULL == pRxMetaInfo ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Null params being passed", __func__);
return VOS_STATUS_E_FAILURE;
}
pHddCtx = (hdd_context_t *)vos_get_context( VOS_MODULE_ID_HDD, vosContext );
if ( NULL == pHddCtx )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: HDD adapter context is Null", __func__);
return VOS_STATUS_E_FAILURE;
}
pAdapter = pHddCtx->sta_to_adapter[staId];
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic) )
{
VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
FL("Invalid adapter %pK for staId %u"), pAdapter, staId);
return VOS_STATUS_E_FAILURE;
}
++pAdapter->hdd_stats.hddTxRxStats.rxChains;
// walk the chain until all are processed
pVosPacket = pVosPacketChain;
do
{
// get the pointer to the next packet in the chain
// (but don't unlink the packet since we free the entire chain later)
status = vos_pkt_walk_packet_chain( pVosPacket, &pNextVosPacket, VOS_FALSE);
// 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__);
return VOS_STATUS_E_FAILURE;
}
// Extract the OS packet (skb).
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__);
return VOS_STATUS_E_FAILURE;
}
if (TRUE == hdd_IsEAPOLPacket( pVosPacket ))
wlan_hdd_log_eapol(skb, WIFI_EVENT_DRIVER_EAPOL_FRAME_RECEIVED);
pVosPacket->pSkb = NULL;
if (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)
{
VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_FATAL,
"Magic cookie(%x) for adapter sanity verification is invalid", pAdapter->magic);
return eHAL_STATUS_FAILURE;
}
#ifdef FEATURE_WLAN_TDLS
if ((eTDLS_SUPPORT_ENABLED == pHddCtx->tdls_mode) &&
0 != pHddCtx->connected_peer_count)
{
hdd_station_ctx_t *pHddStaCtx = &pAdapter->sessionCtx.station;
u8 mac[6];
memcpy(mac, skb->data+6, 6);
if (vos_is_macaddr_group((v_MACADDR_t *)mac)) {
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO_MED,
"rx broadcast packet, not adding to peer list");
} else if ((memcmp(pHddStaCtx->conn_info.bssId,
mac, 6) != 0) && (pRxMetaInfo->isStaTdls)) {
wlan_hdd_tdls_update_rx_pkt_cnt_n_rssi(pAdapter, mac,
pRxMetaInfo->rssiAvg);
} else {
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO_MED,
"rx packet sa is bssid, not adding to peer list");
}
}
#endif
if (pHddCtx->cfg_ini->gEnableDebugLog)
{
proto_type = vos_pkt_get_proto_type(skb,
pHddCtx->cfg_ini->gEnableDebugLog);
if (VOS_PKT_PROTO_TYPE_EAPOL & proto_type)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"STA RX EAPOL");
}
else if (VOS_PKT_PROTO_TYPE_DHCP & proto_type)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"STA RX DHCP");
}
else if (VOS_PKT_PROTO_TYPE_ARP & proto_type)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"STA RX ARP");
}
}
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))
hdd_log_ip_addr(skb);
pHddCtx->rx_wow_dump = false;
}
if (pHddCtx->cfg_ini->gEnableRoamDelayStats)
{
vos_record_roam_event(e_HDD_RX_PKT_CBK_TIME, (void *)skb, 0);
}
if (( NULL != pHddCtx ) &&
(pHddCtx->cfg_ini->gEnableDebugLog & VOS_PKT_PROTO_TYPE_DHCP))
{
hdd_dump_dhcp_pkt(skb, RX_PATH);
}
skb->dev = pAdapter->dev;
skb->protocol = eth_type_trans(skb, skb->dev);
skb->ip_summed = CHECKSUM_NONE;
++pAdapter->hdd_stats.hddTxRxStats.rxPackets;
++pAdapter->stats.rx_packets;
pAdapter->stats.rx_bytes += skb->len;
if (arp_pkt)
{
pAdapter->dad = hdd_is_duplicate_ip_arp(skb);
if(pAdapter->dad)
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s :Duplicate IP detected",__func__);
}
#ifdef WLAN_FEATURE_HOLD_RX_WAKELOCK
vos_wake_lock_timeout_release(&pHddCtx->rx_wake_lock,
HDD_WAKE_LOCK_DURATION,
WIFI_POWER_EVENT_WAKELOCK_HOLD_RX);
#endif
if (pNextVosPacket)
{
rxstat = netif_rx(skb);
}
else
{
rxstat = netif_rx_ni(skb);
}
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
pVosPacket = pNextVosPacket;
} while (pVosPacket);
//Return the entire VOS packet chain to the resource pool
status = vos_pkt_return_packet( pVosPacketChain );
if(!VOS_IS_STATUS_SUCCESS( status ))
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,"%s: Failure returning vos pkt", __func__);
}
pAdapter->dev->last_rx = jiffies;
return status;
}
/**============================================================================
@brief hdd_tx_rx_pkt_cnt_stat_timer_handler() -
Enable/Disable split scan based on TX and RX traffic.
@param HddContext : [in] pointer to Hdd context
@return : None
===========================================================================*/
void hdd_tx_rx_pkt_cnt_stat_timer_handler( void *phddctx)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter = NULL;
hdd_station_ctx_t *pHddStaCtx = NULL;
hdd_context_t *pHddCtx = (hdd_context_t *)phddctx;
hdd_config_t *cfg_param = pHddCtx->cfg_ini;
VOS_STATUS status;
v_U8_t staId = 0;
v_U8_t fconnected = 0;
ENTER();
if (0 != (wlan_hdd_validate_context(pHddCtx)))
{
return;
}
if (!cfg_param->dynSplitscan)
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"%s: Error : Dynamic split scan is not Enabled : %d",
__func__, pHddCtx->cfg_ini->dynSplitscan);
return;
}
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
if ((NULL != pAdapter) && (WLAN_HDD_ADAPTER_MAGIC == pAdapter->magic))
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"%s: Adapter with device mode %d exists",
__func__, pAdapter->device_mode);
if ((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) ||
(WLAN_HDD_P2P_CLIENT == pAdapter->device_mode))
{
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if ((eConnectionState_Associated ==
pHddStaCtx->conn_info.connState) &&
(VOS_TRUE == pHddStaCtx->conn_info.uIsAuthenticated))
{
fconnected = TRUE;
}
}
else if ((WLAN_HDD_SOFTAP == pAdapter->device_mode) ||
(WLAN_HDD_P2P_GO == pAdapter->device_mode))
{
v_CONTEXT_t pVosContext = ( WLAN_HDD_GET_CTX(pAdapter))->pvosContext;
ptSapContext pSapCtx = VOS_GET_SAP_CB(pVosContext);
if(pSapCtx == NULL){
VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
FL("psapCtx is NULL"));
return;
}
for (staId = 0; staId < WLAN_MAX_STA_COUNT; staId++)
{
if ((pSapCtx->aStaInfo[staId].isUsed) &&
(WLANTL_STA_AUTHENTICATED ==
pSapCtx->aStaInfo[staId].tlSTAState))
{
fconnected = TRUE;
}
}
}
if ( fconnected )
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"%s: One of the interface is connected check for scan",
__func__);
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"%s: pkt_tx_count: %d, pkt_rx_count: %d "
"miracast = %d", __func__,
pAdapter->hdd_stats.hddTxRxStats.pkt_tx_count,
pAdapter->hdd_stats.hddTxRxStats.pkt_rx_count,
pHddCtx->drvr_miracast);
vos_timer_start(&pHddCtx->tx_rx_trafficTmr,
cfg_param->trafficMntrTmrForSplitScan);
//Check for the previous statistics count
if ((pAdapter->hdd_stats.hddTxRxStats.pkt_tx_count >
cfg_param->txRxThresholdForSplitScan) ||
(pAdapter->hdd_stats.hddTxRxStats.pkt_rx_count >
cfg_param->txRxThresholdForSplitScan) ||
pHddCtx->drvr_miracast || pHddCtx->is_vowifi_enabled ||
(WLAN_HDD_P2P_GO == pAdapter->device_mode))
{
pAdapter->hdd_stats.hddTxRxStats.pkt_tx_count = 0;
pAdapter->hdd_stats.hddTxRxStats.pkt_rx_count = 0;
if (!pHddCtx->issplitscan_enabled)
{
pHddCtx->issplitscan_enabled = TRUE;
sme_enable_disable_split_scan(
WLAN_HDD_GET_HAL_CTX(pAdapter),
cfg_param->nNumStaChanCombinedConc,
cfg_param->nNumP2PChanCombinedConc);
}
return;
}
else
{
pAdapter->hdd_stats.hddTxRxStats.pkt_tx_count = 0;
pAdapter->hdd_stats.hddTxRxStats.pkt_rx_count = 0;
}
fconnected = FALSE;
}
}
status = hdd_get_next_adapter( pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
/* If TDLSScanCoexistence is enabled, then the TDLS module shall take care
* of disabling the split scan and thus do not disable the same when the
* low TXRX condition is met.
*/
if ((pHddCtx->isTdlsScanCoexistence == FALSE) && (pHddCtx->issplitscan_enabled))
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"%s: Disable split scan", __func__);
pHddCtx->issplitscan_enabled = FALSE;
sme_enable_disable_split_scan(
pHddCtx->hHal,
SME_DISABLE_SPLIT_SCAN,
SME_DISABLE_SPLIT_SCAN);
}
EXIT();
return;
}
/**
* hdd_rx_fwd_eapol() - forward cached eapol frames
* @vosContext : pointer to vos global context
* @pVosPacket: pointer to vos packet
*
* Return: None
*
*/
void hdd_rx_fwd_eapol(v_VOID_t *vosContext, vos_pkt_t *pVosPacket)
{
hdd_context_t *pHddCtx = NULL;
hdd_adapter_t * pAdapter = NULL;
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
struct sk_buff *skb = NULL;
uint8_t proto_type;
uint8_t status;
if ((NULL == vosContext) || (NULL == pVosPacket))
{
VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: Null global context", __func__);
return;
}
pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, vosContext);
if (NULL == pHddCtx)
{
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: HDD adapter 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;
}
if (NULL == pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR,
"%s: No adapter", __func__);
return;
}
vos_pkt_get_os_packet(pVosPacket, (v_VOID_t **)&skb, VOS_FALSE);
proto_type = vos_pkt_get_proto_type(skb,
pHddCtx->cfg_ini->gEnableDebugLog);
if (VOS_PKT_PROTO_TYPE_EAPOL & proto_type)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"STA RX EAPOL");
}
skb->dev = pAdapter->dev;
skb->protocol = eth_type_trans(skb, skb->dev);
skb->ip_summed = CHECKSUM_NONE;
netif_rx_ni(skb);
}
#ifdef FEATURE_WLAN_DIAG_SUPPORT
/*
* wlan_hdd_get_eapol_params() - Function to extract EAPOL params
* @skb: skb data
* @eapol_params: Pointer to hold the parsed EAPOL params
* @event_type: Event type to indicate Tx/Rx
*
* This function parses the input skb data to get the EAPOL params,if the
* packet is EAPOL and store it in the pointer passed as input
*
*/
void wlan_hdd_get_eapol_params(struct sk_buff *skb,
struct vos_event_wlan_eapol *eapol_params,
uint8_t event_type)
{
uint8_t packet_type=0;
packet_type = (uint8_t)(*(uint8_t *)
(skb->data + HDD_EAPOL_PACKET_TYPE_OFFSET));
/* EAPOL msg type i.e. whether EAPOL-Start or
* EAPOL-Key etc. messages Present at 15 offset.
*/
eapol_params->eapol_packet_type = packet_type;
/* This tells if its a M1/M2/M3/M4 packet.
* Present at 19th offset in EAPOL frame.
*/
eapol_params->eapol_key_info = (uint16_t)(*(uint16_t *)
(skb->data + HDD_EAPOL_KEY_INFO_OFFSET));
/* This tells if EAPOL packet is in RX or TX
* direction.
*/
eapol_params->event_sub_type = event_type;
/* This tells the rate at which EAPOL packet
* is send or received.
*/
//TODO fill data rate for rx packet.
eapol_params->eapol_rate = 0;/* As of now, zero */
vos_mem_copy(eapol_params->dest_addr,
(skb->data + HDD_EAPOL_DEST_MAC_OFFSET),
sizeof(eapol_params->dest_addr));
vos_mem_copy(eapol_params->src_addr,
(skb->data + HDD_EAPOL_SRC_MAC_OFFSET),
sizeof(eapol_params->src_addr));
return;
}
/*
* wlan_hdd_event_eapol_log() - Function to log EAPOL events
* @eapol_params: Structure containing EAPOL params
*
* This function logs the parsed EAPOL params
*
* Return: None
*
*/
static void wlan_hdd_event_eapol_log(struct vos_event_wlan_eapol eapol_params)
{
WLAN_VOS_DIAG_EVENT_DEF(wlan_diag_event, struct vos_event_wlan_eapol);
wlan_diag_event.event_sub_type = eapol_params.event_sub_type;
wlan_diag_event.eapol_packet_type = eapol_params.eapol_packet_type;
wlan_diag_event.eapol_key_info = eapol_params.eapol_key_info;
wlan_diag_event.eapol_rate = eapol_params.eapol_rate;
vos_mem_copy(wlan_diag_event.dest_addr,
eapol_params.dest_addr,
sizeof (wlan_diag_event.dest_addr));
vos_mem_copy(wlan_diag_event.src_addr,
eapol_params.src_addr,
sizeof (wlan_diag_event.src_addr));
WLAN_VOS_DIAG_EVENT_REPORT(&wlan_diag_event, EVENT_WLAN_EAPOL);
}
/*
* wlan_hdd_log_eapol() - Function to check and extract EAPOL params
* @skb: skb data
* @event_type: One of enum wifi_connectivity_events to indicate Tx/Rx
*
* This function parses the input skb data to get the EAPOL params,if the
* packet is EAPOL and store it in the pointer passed as input
*
* Return: None
*
*/
void wlan_hdd_log_eapol(struct sk_buff *skb,
uint8_t event_type)
{
struct vos_event_wlan_eapol eapol_params;
wlan_hdd_get_eapol_params(skb, &eapol_params, event_type);
wlan_hdd_event_eapol_log(eapol_params);
if ((eapol_params.eapol_key_info & EAPOL_MASK )== EAPOL_M1_BIT_MASK)
{
hddLog(LOG1, FL("%s: M1 packet"),eapol_params.event_sub_type ==
WIFI_EVENT_DRIVER_EAPOL_FRAME_RECEIVED ? "RX" : "TX");
}
else if ((eapol_params.eapol_key_info & EAPOL_MASK )== EAPOL_M2_BIT_MASK)
{
hddLog(LOG1, FL("%s: M2 packet"),eapol_params.event_sub_type ==
WIFI_EVENT_DRIVER_EAPOL_FRAME_RECEIVED ? "RX" : "TX");
}
else if ((eapol_params.eapol_key_info & EAPOL_MASK )== EAPOL_M3_BIT_MASK)
{
hddLog(LOG1, FL("%s: M3 packet"),eapol_params.event_sub_type ==
WIFI_EVENT_DRIVER_EAPOL_FRAME_RECEIVED ? "RX" : "TX");
}
else if ((eapol_params.eapol_key_info & EAPOL_MASK )== EAPOL_M4_BIT_MASK)
{
hddLog(LOG1, FL("%s: M4 packet"),eapol_params.event_sub_type ==
WIFI_EVENT_DRIVER_EAPOL_FRAME_RECEIVED ? "RX" : "TX");
}
}
#endif /* FEATURE_WLAN_DIAG_SUPPORT */