| /* |
| * 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, ×tamp ); |
| 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, ×tamp ); |
| 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 */ |