| /* |
| * 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 |
| -------------------------------------------------------------------------*/ |
| |
| /* Needs to be removed when completely root-caused */ |
| #define IPV6_MCAST_WAR 1 |
| |
| #include <wlan_hdd_tx_rx.h> |
| #include <wlan_hdd_softap_tx_rx.h> |
| #include <wlan_hdd_dp_utils.h> |
| #include <wlan_hdd_tsf.h> |
| |
| #include <wlan_qct_tl.h> |
| #include <linux/netdevice.h> |
| #include <linux/skbuff.h> |
| #include <linux/etherdevice.h> |
| #include <vos_sched.h> |
| #ifdef IPV6_MCAST_WAR |
| #include <linux/if_ether.h> |
| #endif |
| |
| #include <linux/inetdevice.h> |
| #include <wlan_hdd_p2p.h> |
| #include <linux/wireless.h> |
| #include <net/cfg80211.h> |
| #include <net/ieee80211_radiotap.h> |
| #include "sapApi.h" |
| |
| #ifdef FEATURE_WLAN_TDLS |
| #include "wlan_hdd_tdls.h" |
| #endif |
| |
| #ifdef IPA_OFFLOAD |
| #include <wlan_hdd_ipa.h> |
| #endif |
| #include "adf_trace.h" |
| |
| #include "wlan_hdd_nan_datapath.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, |
| }; |
| |
| /*--------------------------------------------------------------------------- |
| Function definitions and documentation |
| -------------------------------------------------------------------------*/ |
| |
| /**============================================================================ |
| @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; |
| |
| 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; |
| ++pAdapter->stats.tx_dropped; |
| kfree_skb(skb); |
| continue; |
| } |
| break; |
| } |
| SPIN_UNLOCK_BH(&pAdapter->wmm_tx_queue[i].lock); |
| /* Back pressure 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] Station 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; |
| v_SIZE_t size = 0; |
| v_U8_t skbStaIdx; |
| skb_list_node_t *pktNode = NULL; |
| hdd_list_node_t *tmp = NULL, *next = NULL; |
| struct netdev_queue *txq; |
| struct sk_buff *skb = NULL; |
| |
| if (NULL == pAdapter) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR, |
| FL("pAdapter is NULL %u"), STAId); |
| VOS_ASSERT(0); |
| return; |
| } |
| |
| for (i = 0; i < NUM_TX_QUEUES; i++) |
| { |
| SPIN_LOCK_BH(&pAdapter->wmm_tx_queue[i].lock); |
| |
| if ( list_empty( &pAdapter->wmm_tx_queue[i].anchor ) ) |
| { |
| SPIN_UNLOCK_BH(&pAdapter->wmm_tx_queue[i].lock); |
| continue; |
| } |
| |
| /* Iterate through the queue and identify the data for STAId */ |
| list_for_each_safe(tmp, next, &pAdapter->wmm_tx_queue[i].anchor) |
| { |
| pktNode = list_entry(tmp, skb_list_node_t, anchor); |
| skb = pktNode->skb; |
| |
| /* Get the STAId from data */ |
| skbStaIdx = *(v_U8_t *)(((v_U8_t *)(skb->data)) - 1); |
| if (skbStaIdx == STAId) { |
| /* Data for STAId is freed along with the queue node */ |
| |
| list_del(tmp); |
| kfree_skb(skb); |
| |
| pAdapter->wmm_tx_queue[i].count--; |
| } |
| } |
| |
| /* Restart the queue only-if suspend and the queue was flushed */ |
| hdd_list_size( &pAdapter->wmm_tx_queue[i], &size ); |
| txq = netdev_get_tx_queue(pAdapter->dev, i); |
| |
| if (VOS_TRUE == pAdapter->isTxSuspended[i] && |
| size <= HDD_TX_QUEUE_LOW_WATER_MARK && |
| netif_tx_queue_stopped(txq) ) |
| { |
| hddLog(LOG1, FL("Enabling queue for queue %d"), i); |
| netif_tx_start_queue(txq); |
| pAdapter->isTxSuspended[i] = VOS_FALSE; |
| } |
| |
| SPIN_UNLOCK_BH(&pAdapter->wmm_tx_queue[i].lock); |
| } |
| } |
| |
| #ifdef QCA_LL_TX_FLOW_CT |
| /**============================================================================ |
| @brief hdd_tx_resume_timer_expired_handler() - Resume OS TX Q timer expired |
| handler. |
| If Blocked OS Q is not resumed during timeout period, to prevent |
| permanent stall, resume OS Q forcefully. |
| |
| @param adapter_context : [in] pointer to vdev adapter |
| |
| @return : NONE |
| ===========================================================================*/ |
| void hdd_tx_resume_timer_expired_handler(void *adapter_context) |
| { |
| hdd_adapter_t *pAdapter = (hdd_adapter_t *)adapter_context; |
| |
| if (!pAdapter) |
| { |
| /* INVALID ARG */ |
| return; |
| } |
| |
| hddLog(LOG1, FL("Enabling queues")); |
| wlan_hdd_netif_queue_control(pAdapter, WLAN_WAKE_ALL_NETIF_QUEUE, |
| WLAN_DATA_FLOW_CONTROL); |
| pAdapter->hdd_stats.hddTxRxStats.txflow_unpause_cnt++; |
| pAdapter->hdd_stats.hddTxRxStats.is_txflow_paused = FALSE; |
| |
| return; |
| } |
| |
| /**============================================================================ |
| @brief hdd_tx_resume_cb() - Resume OS TX Q. |
| Q was stopped due to WLAN TX path low resource condition |
| |
| @param adapter_context : [in] pointer to vdev adapter |
| @param tx_resume : [in] TX Q resume trigger |
| |
| @return : NONE |
| ===========================================================================*/ |
| void hdd_tx_resume_cb(void *adapter_context, |
| v_BOOL_t tx_resume) |
| { |
| hdd_adapter_t *pAdapter = (hdd_adapter_t *)adapter_context; |
| hdd_station_ctx_t *hdd_sta_ctx = NULL; |
| |
| if (!pAdapter) |
| { |
| /* INVALID ARG */ |
| return; |
| } |
| |
| hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| |
| /* Resume TX */ |
| if (VOS_TRUE == tx_resume) |
| { |
| if (VOS_TIMER_STATE_STOPPED != |
| vos_timer_getCurrentState(&pAdapter->tx_flow_control_timer)) |
| { |
| vos_timer_stop(&pAdapter->tx_flow_control_timer); |
| } |
| hddLog(LOG1, FL("Enabling queues")); |
| wlan_hdd_netif_queue_control(pAdapter, |
| WLAN_WAKE_ALL_NETIF_QUEUE, |
| WLAN_DATA_FLOW_CONTROL); |
| pAdapter->hdd_stats.hddTxRxStats.txflow_unpause_cnt++; |
| pAdapter->hdd_stats.hddTxRxStats.is_txflow_paused = FALSE; |
| |
| } |
| #if defined(CONFIG_PER_VDEV_TX_DESC_POOL) |
| else if (VOS_FALSE == tx_resume) /* Pause TX */ |
| { |
| hddLog(LOG1, FL("Disabling queues")); |
| wlan_hdd_netif_queue_control(pAdapter, |
| WLAN_STOP_ALL_NETIF_QUEUE, |
| WLAN_DATA_FLOW_CONTROL); |
| if (VOS_TIMER_STATE_STOPPED == |
| vos_timer_getCurrentState(&pAdapter->tx_flow_control_timer)) |
| { |
| VOS_STATUS status; |
| status = vos_timer_start(&pAdapter->tx_flow_control_timer, |
| WLAN_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME); |
| if ( !VOS_IS_STATUS_SUCCESS(status) ) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to start tx_flow_control_timer", __func__); |
| } |
| else |
| { |
| pAdapter->hdd_stats.hddTxRxStats.txflow_timer_cnt++; |
| } |
| } |
| pAdapter->hdd_stats.hddTxRxStats.txflow_pause_cnt++; |
| pAdapter->hdd_stats.hddTxRxStats.is_txflow_paused = TRUE; |
| |
| } |
| #endif |
| |
| return; |
| } |
| #endif /* QCA_LL_TX_FLOW_CT */ |
| |
| /** |
| * hdd_drop_skb() - drop a packet |
| * @adapter: pointer to hdd adapter |
| * @skb: pointer to OS packet |
| * |
| * Return: Nothing to return |
| */ |
| void hdd_drop_skb(hdd_adapter_t *adapter, struct sk_buff *skb) |
| { |
| DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_DROP_PACKET_RECORD, |
| (uint8_t *)skb->data, skb->len, ADF_TX)); |
| if (skb->len > ADF_DP_TRACE_RECORD_SIZE) |
| DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_DROP_PACKET_RECORD, |
| (uint8_t *)&skb->data[ADF_DP_TRACE_RECORD_SIZE], |
| (skb->len - ADF_DP_TRACE_RECORD_SIZE), ADF_TX)); |
| |
| ++adapter->stats.tx_dropped; |
| ++adapter->hdd_stats.hddTxRxStats.txXmitDropped; |
| kfree_skb(skb); |
| } |
| |
| /** |
| * hdd_drop_skb_list() - drop packet list |
| * @adapter: pointer to hdd adapter |
| * @skb: pointer to OS packet list |
| * @is_update_ac_stats: macro to update ac stats |
| * |
| * Return: Nothing to return |
| */ |
| void hdd_drop_skb_list(hdd_adapter_t *adapter, struct sk_buff *skb, |
| bool is_update_ac_stats) |
| { |
| WLANTL_ACEnumType ac; |
| struct sk_buff *skb_next; |
| |
| while (skb) { |
| DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_DROP_PACKET_RECORD, |
| (uint8_t *)skb->data, skb->len, ADF_TX)); |
| if (skb->len > ADF_DP_TRACE_RECORD_SIZE) |
| DPTRACE(adf_dp_trace(skb, |
| ADF_DP_TRACE_DROP_PACKET_RECORD, |
| (uint8_t *)&skb->data[ADF_DP_TRACE_RECORD_SIZE], |
| (skb->len - ADF_DP_TRACE_RECORD_SIZE), ADF_TX)); |
| |
| ++adapter->stats.tx_dropped; |
| ++adapter->hdd_stats.hddTxRxStats.txXmitDropped; |
| if (is_update_ac_stats == TRUE) { |
| ac = hdd_QdiscAcToTlAC[skb->queue_mapping]; |
| ++adapter->hdd_stats.hddTxRxStats.txXmitDroppedAC[ac]; |
| } |
| skb_next = skb->next; |
| kfree_skb(skb); |
| skb = skb_next; |
| } |
| } |
| |
| /** |
| * wlan_hdd_classify_pkt() - classify skb packet type. |
| * @data: Pointer to skb |
| * |
| * This function classifies skb packet type. |
| * |
| * Return: none |
| */ |
| void wlan_hdd_classify_pkt(struct sk_buff *skb) |
| { |
| /* classify broadcast/multicast packet */ |
| if (adf_nbuf_is_bcast_pkt(skb)) |
| ADF_NBUF_SET_BCAST(skb); |
| else if (adf_nbuf_is_multicast_pkt(skb)) |
| ADF_NBUF_SET_MCAST(skb); |
| |
| /* classify eapol/arp/dhcp/wai packet */ |
| if (adf_nbuf_is_eapol_pkt(skb)) |
| ADF_NBUF_SET_EAPOL(skb); |
| else if (adf_nbuf_is_ipv4_arp_pkt(skb)) |
| ADF_NBUF_SET_ARP(skb); |
| else if (adf_nbuf_is_dhcp_pkt(skb)) |
| ADF_NBUF_SET_DHCP(skb); |
| else if (adf_nbuf_is_wai_pkt(skb)) |
| ADF_NBUF_SET_WAPI(skb); |
| } |
| |
| /** |
| * hdd_get_transmit_sta_id() - function to retrieve station id to be used for |
| * sending traffic towards a particular destination address. The destination |
| * address can be unicast, multicast or broadcast |
| * |
| * @adapter: Handle to adapter context |
| * @dst_addr: Destination address |
| * @station_id: station id |
| * |
| * Returns: None |
| */ |
| static void hdd_get_transmit_sta_id(hdd_adapter_t *adapter, |
| v_MACADDR_t *dst_addr, uint8_t *station_id) |
| { |
| bool mcbc_addr = false; |
| hdd_station_ctx_t *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| |
| hdd_get_peer_sta_id(sta_ctx, dst_addr, station_id); |
| if (*station_id == HDD_WLAN_INVALID_STA_ID) { |
| if (vos_is_macaddr_broadcast(dst_addr) || |
| vos_is_macaddr_group(dst_addr)) { |
| hddLog(LOG1, |
| "Received MC/BC packet for transmission"); |
| mcbc_addr = true; |
| } |
| } |
| |
| if (adapter->device_mode == WLAN_HDD_IBSS) { |
| /* |
| * This check is necessary to make sure station id is not |
| * overwritten for UC traffic in IBSS mode |
| */ |
| if (mcbc_addr) |
| *station_id = IBSS_BROADCAST_STAID; |
| } else if (adapter->device_mode == WLAN_HDD_OCB) { |
| *station_id = sta_ctx->conn_info.staId[0]; |
| } else if (adapter->device_mode == WLAN_HDD_NDI) { |
| /* |
| * This check is necessary to make sure station id is not |
| * overwritten for UC traffic in NAN data mode |
| */ |
| if (mcbc_addr) |
| *station_id = sta_ctx->broadcast_staid; |
| } else { |
| /* For the rest, traffic is directed to AP/P2P GO */ |
| if (eConnectionState_Associated == sta_ctx->conn_info.connState) |
| *station_id = sta_ctx->conn_info.staId[0]; |
| } |
| } |
| |
| /**============================================================================ |
| @brief hdd_hard_start_xmit() - Function registered with the Linux OS for |
| transmitting packets. This version of the function directly passes the packet |
| to Transport Layer. |
| |
| @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 successfully |
| ===========================================================================*/ |
| int __hdd_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) |
| { |
| VOS_STATUS status; |
| WLANTL_ACEnumType ac; |
| sme_QosWmmUpType up; |
| 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; |
| struct sk_buff *skb_next, *list_head = NULL, *list_tail = NULL; |
| void *vdev_handle = NULL, *vdev_temp; |
| bool is_update_ac_stats = FALSE; |
| v_MACADDR_t *pDestMacAddress = NULL; |
| hdd_context_t *hddCtxt = WLAN_HDD_GET_CTX(pAdapter); |
| #ifdef QCA_PKT_PROTO_TRACE |
| v_U8_t proto_type = 0; |
| #endif /* QCA_PKT_PROTO_TRACE */ |
| |
| #ifdef QCA_WIFI_FTM |
| if (hdd_get_conparam() == VOS_FTM_MODE) { |
| while (skb) { |
| skb_next = skb->next; |
| kfree_skb(skb); |
| skb = skb_next; |
| } |
| return NETDEV_TX_OK; |
| } |
| #endif |
| |
| ++pAdapter->hdd_stats.hddTxRxStats.txXmitCalled; |
| |
| if(vos_is_logp_in_progress(VOS_MODULE_ID_VOSS, NULL)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_WARN, |
| "LOPG in progress, dropping the packet\n"); |
| goto drop_list; |
| } |
| |
| while (skb) { |
| skb_next = skb->next; |
| /* memset skb control block */ |
| vos_mem_zero(skb->cb, sizeof(skb->cb)); |
| wlan_hdd_classify_pkt(skb); |
| |
| pDestMacAddress = (v_MACADDR_t*)skb->data; |
| STAId = HDD_WLAN_INVALID_STA_ID; |
| |
| hdd_tsf_record_sk_for_skb(hddCtxt, skb); |
| |
| /* |
| * The TCP TX throttling logic is changed a little after 3.19-rc1 kernel, |
| * the TCP sending limit will be smaller, which will throttle the TCP packets |
| * to the host driver. The TCP UP LINK throughput will drop heavily. |
| * In order to fix this issue, need to orphan the socket buffer asap, which will |
| * call skb's destructor to notify the TCP stack that the SKB buffer is |
| * unowned. And then the TCP stack will pump more packets to host driver. |
| * |
| * The TX packets might be dropped for UDP case in the iperf testing. |
| * So need to be protected by follow control. |
| */ |
| #ifdef QCA_LL_TX_FLOW_CT |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(3,19,0)) |
| //remove if condition for improving SCC TCP TX KPI |
| //if (pAdapter->tx_flow_low_watermark > 0) { |
| skb_orphan(skb); |
| // } |
| #endif |
| #else |
| /* |
| * For PTP feature enabled system, need to orphan the socket buffer asap |
| * otherwise the latency will become unacceptable |
| */ |
| if (hdd_cfg_is_ptp_opt_enable(hddCtxt)) |
| skb_orphan(skb); |
| #endif |
| |
| /* use self peer directly in monitor mode */ |
| if (VOS_MONITOR_MODE != vos_get_conparam()) { |
| hdd_get_transmit_sta_id(pAdapter, pDestMacAddress, &STAId); |
| if (STAId == HDD_WLAN_INVALID_STA_ID) { |
| hddLog(LOG1, "Invalid station id, transmit operation suspended"); |
| goto drop_pkt; |
| } |
| |
| vdev_temp = tlshim_peer_validity(hddCtxt->pvosContext, STAId); |
| } else { |
| vdev_temp = |
| tlshim_selfpeer_vdev(hddCtxt->pvosContext); |
| } |
| if (!vdev_temp) |
| goto drop_pkt; |
| |
| vdev_handle = vdev_temp; |
| |
| #ifdef QCA_LL_TX_FLOW_CT |
| if ((pAdapter->hdd_stats.hddTxRxStats.is_txflow_paused != TRUE) && |
| VOS_FALSE == |
| WLANTL_GetTxResource(hddCtxt->pvosContext, |
| pAdapter->sessionId, |
| pAdapter->tx_flow_low_watermark, |
| pAdapter->tx_flow_high_watermark_offset)) { |
| hddLog(LOG1, FL("Disabling queues")); |
| wlan_hdd_netif_queue_control(pAdapter, WLAN_STOP_ALL_NETIF_QUEUE, |
| WLAN_DATA_FLOW_CONTROL); |
| if ((pAdapter->tx_flow_timer_initialized == TRUE) && |
| (VOS_TIMER_STATE_STOPPED == |
| vos_timer_getCurrentState(&pAdapter->tx_flow_control_timer))) { |
| vos_timer_start(&pAdapter->tx_flow_control_timer, |
| WLAN_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME); |
| pAdapter->hdd_stats.hddTxRxStats.txflow_timer_cnt++; |
| pAdapter->hdd_stats.hddTxRxStats.txflow_pause_cnt++; |
| pAdapter->hdd_stats.hddTxRxStats.is_txflow_paused = TRUE; |
| } |
| } |
| #endif /* QCA_LL_TX_FLOW_CT */ |
| |
| /* Get TL AC corresponding to Qdisc queue index/AC */ |
| ac = hdd_QdiscAcToTlAC[skb->queue_mapping]; |
| |
| /* |
| * 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]; |
| #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 (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 already have access to this access category |
| * or it is EAPOL or WAPI frame during initial authentication which |
| * can have artifically boosted higher qos priority. |
| */ |
| |
| if (((pAdapter->psbChanged & (1 << ac)) && |
| likely( |
| pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcAccessAllowed)) || |
| ((pHddStaCtx->conn_info.uIsAuthenticated == VOS_FALSE) && |
| (ADF_NBUF_GET_IS_EAPOL(skb) || ADF_NBUF_GET_IS_WAPI(skb)))) |
| { |
| granted = VOS_TRUE; |
| } |
| else |
| { |
| status = hdd_wmm_acquire_access( pAdapter, ac, &granted ); |
| pAdapter->psbChanged |= (1 << ac); |
| } |
| |
| if (!granted) { |
| bool isDefaultAc = VOS_FALSE; |
| /* |
| * ADDTS request for this AC is sent, for now |
| * send this packet through next available lower |
| * Access category until ADDTS negotiation completes. |
| */ |
| while (!likely( |
| pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcAccessAllowed)) { |
| switch(ac) { |
| case WLANTL_AC_VO: |
| ac = WLANTL_AC_VI; |
| up = SME_QOS_WMM_UP_VI; |
| break; |
| case WLANTL_AC_VI: |
| ac = WLANTL_AC_BE; |
| up = SME_QOS_WMM_UP_BE; |
| break; |
| case WLANTL_AC_BE: |
| ac = WLANTL_AC_BK; |
| up = SME_QOS_WMM_UP_BK; |
| break; |
| default: |
| ac = WLANTL_AC_BK; |
| up = SME_QOS_WMM_UP_BK; |
| isDefaultAc = VOS_TRUE; |
| break; |
| } |
| if (isDefaultAc) |
| break; |
| } |
| skb->priority = up; |
| skb->queue_mapping = hddLinuxUpToAcMap[up]; |
| } |
| |
| #ifdef QCA_PKT_PROTO_TRACE |
| if ((hddCtxt->cfg_ini->gEnableDebugLog & VOS_PKT_TRAC_TYPE_EAPOL) || |
| (hddCtxt->cfg_ini->gEnableDebugLog & VOS_PKT_TRAC_TYPE_DHCP)) |
| { |
| proto_type = vos_pkt_get_proto_type(skb, |
| hddCtxt->cfg_ini->gEnableDebugLog, 0); |
| switch (proto_type) { |
| case VOS_PKT_TRAC_TYPE_EAPOL: |
| vos_pkt_trace_buf_update("ST:T:EPL"); |
| break; |
| case VOS_PKT_TRAC_TYPE_DHCP: |
| hdd_dhcp_pkt_trace_buf_update(skb, TX_PATH, STA); |
| break; |
| case VOS_PKT_TRAC_TYPE_ARP: |
| vos_pkt_trace_buf_update("ST:T:ARP"); |
| break; |
| case VOS_PKT_TRAC_TYPE_NS: |
| vos_pkt_trace_buf_update("ST:T:NS"); |
| break; |
| case VOS_PKT_TRAC_TYPE_NA: |
| vos_pkt_trace_buf_update("ST:T:NA"); |
| break; |
| default: |
| break; |
| } |
| } |
| #endif /* QCA_PKT_PROTO_TRACE */ |
| |
| pAdapter->stats.tx_bytes += skb->len; |
| ++pAdapter->stats.tx_packets; |
| |
| if (!list_head) { |
| list_head = skb; |
| list_tail = skb; |
| } else { |
| list_tail->next = skb; |
| list_tail = list_tail->next; |
| } |
| |
| adf_dp_trace_log_pkt(pAdapter->sessionId, skb, ADF_TX); |
| NBUF_SET_PACKET_TRACK(skb, NBUF_TX_PKT_DATA_TRACK); |
| NBUF_UPDATE_TX_PKT_COUNT(skb, NBUF_TX_PKT_HDD); |
| |
| adf_dp_trace_set_track(skb, ADF_TX); |
| DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_TX_PACKET_PTR_RECORD, |
| (uint8_t *)&skb->data, sizeof(skb->data), ADF_TX)); |
| DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_TX_PACKET_RECORD, |
| (uint8_t *)skb->data, adf_nbuf_len(skb), ADF_TX)); |
| |
| if (adf_nbuf_len(skb) > ADF_DP_TRACE_RECORD_SIZE) |
| DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_TX_PACKET_RECORD, |
| (uint8_t *)&skb->data[ADF_DP_TRACE_RECORD_SIZE], |
| (adf_nbuf_len(skb) - ADF_DP_TRACE_RECORD_SIZE), |
| ADF_TX)); |
| |
| skb = skb_next; |
| continue; |
| |
| drop_pkt: |
| hdd_drop_skb(pAdapter, skb); |
| skb = skb_next; |
| } /* end of while */ |
| |
| if (!vdev_handle) { |
| VOS_TRACE(VOS_MODULE_ID_HDD_SAP_DATA, VOS_TRACE_LEVEL_INFO, |
| "%s: All packets dropped in the list", __func__); |
| return NETDEV_TX_OK; |
| } |
| |
| list_tail->next = NULL; |
| |
| /* |
| * TODO: Should we stop net queues when txrx returns non-NULL?. |
| */ |
| skb = WLANTL_SendSTA_DataFrame(hddCtxt->pvosContext, vdev_handle, list_head |
| #ifdef QCA_PKT_PROTO_TRACE |
| , proto_type |
| #endif /* QCA_PKT_PROTO_TRACE */ |
| ); |
| if (skb != NULL) { |
| VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_WARN, |
| "%s: Failed to send packet to txrx", |
| __func__); |
| is_update_ac_stats = TRUE; |
| goto drop_list; |
| } |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)) |
| netif_trans_update(dev); |
| #else |
| dev->trans_start = jiffies; |
| #endif |
| return NETDEV_TX_OK; |
| |
| drop_list: |
| |
| hdd_drop_skb_list(pAdapter, skb, is_update_ac_stats); |
| 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; |
| } |
| |
| /** |
| * hdd_get_peer_sta_id() - Get the StationID using the Peer Mac address |
| * @sta_ctx: pointer to HDD Station Context |
| * @peer_mac_addr: pointer to Peer Mac address |
| * @sta_id: pointer to Station Index |
| * |
| * Returns: VOS_STATUS_SUCCESS on success, VOS_STATUS_E_FAILURE on error |
| */ |
| VOS_STATUS hdd_get_peer_sta_id(hdd_station_ctx_t *sta_ctx, |
| v_MACADDR_t *peer_mac_addr, |
| uint8_t *sta_id) |
| { |
| v_U8_t idx; |
| |
| for (idx = 0; idx < HDD_MAX_NUM_IBSS_STA; idx++) { |
| if (vos_mem_compare(&sta_ctx->conn_info.peerMacAddress[idx], |
| peer_mac_addr, sizeof(v_MACADDR_t))) { |
| *sta_id = sta_ctx->conn_info.staId[idx]; |
| return VOS_STATUS_SUCCESS; |
| } |
| } |
| |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| /** |
| * hdd_get_peer_idx() - Get the idx for given address in peer table |
| * @sta_ctx: pointer to HDD Station Context |
| * @addr: pointer to Peer Mac address |
| * |
| * Return: index when success else INVALID_PEER_IDX |
| */ |
| int hdd_get_peer_idx(hdd_station_ctx_t *sta_ctx, v_MACADDR_t *addr) |
| { |
| uint8_t idx; |
| |
| for (idx = 0; idx < HDD_MAX_NUM_IBSS_STA; idx++) { |
| if (sta_ctx->conn_info.staId[idx] == 0) |
| continue; |
| if (!vos_mem_compare(&sta_ctx->conn_info.peerMacAddress[idx], |
| addr, sizeof(v_MACADDR_t))) |
| continue; |
| return idx; |
| } |
| |
| return INVALID_PEER_IDX; |
| } |
| |
| /** |
| * wlan_display_tx_timeout_stats() - HDD tx timeout stats display handler |
| * @adapter: hdd adapter |
| * |
| * Function called by tx timeout handler to display the stats when timeout |
| * occurs during trabsmission. |
| * |
| * Return: none |
| */ |
| void wlan_display_tx_timeout_stats(hdd_adapter_t *adapter) |
| { |
| hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| uint8_t pause_bitmap = 0; |
| vos_time_t pause_timestamp = 0; |
| A_STATUS status; |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO, |
| "carrier state: %d", netif_carrier_ok(adapter->dev)); |
| |
| /* to display the neif queue pause/unpause history */ |
| wlan_hdd_display_netif_queue_history(hdd_ctx); |
| |
| /* to display the count of packets at diferent layers */ |
| adf_nbuf_tx_desc_count_display(); |
| |
| /* printing last 100 records from DPTRACE */ |
| adf_dp_trace_dump_all(100); |
| |
| /* to print the pause bitmap of local ll queues */ |
| status = tlshim_get_ll_queue_pause_bitmap(adapter->sessionId, |
| &pause_bitmap, &pause_timestamp); |
| if (status != A_OK) |
| hddLog(LOGE, FL("vdev is NULL for vdev id %d"), |
| adapter->sessionId); |
| else |
| hddLog(LOGE, |
| FL("LL vdev queues pause bitmap: %d, last pause timestamp %lu"), |
| pause_bitmap, pause_timestamp); |
| |
| /* |
| * To invoke the bug report to flush driver as well as fw logs |
| * when timeout happens. It happens when it has been more |
| * than 5 minutes and the timeout has happened at least 3 times |
| * since the last generation of bug report. |
| */ |
| adapter->bug_report_count++; |
| if (adapter->bug_report_count >= HDD_BUG_REPORT_MIN_COUNT && |
| (jiffies_to_msecs(jiffies - adapter->last_tx_jiffies) >= |
| HDD_BUG_REPORT_MIN_TIME)) { |
| adapter->bug_report_count = 0; |
| adapter->last_tx_jiffies = jiffies; |
| vos_flush_logs(WLAN_LOG_TYPE_FATAL, |
| WLAN_LOG_INDICATOR_HOST_DRIVER, |
| WLAN_LOG_REASON_HDD_TIME_OUT, |
| DUMP_VOS_TRACE); |
| } |
| } |
| |
| /** |
| * __hdd_tx_timeout() - HDD tx timeout handler |
| * @dev: pointer to net_device structure |
| * |
| * 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 |
| * |
| * Return: none |
| */ |
| static void __hdd_tx_timeout(struct net_device *dev) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct netdev_queue *txq; |
| int i = 0; |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)) |
| hddLog(LOGE, FL("Transmission timeout occurred jiffies %lu"),jiffies); |
| #else |
| hddLog(LOGE, FL("Transmission timeout occurred jiffies %lu trans_start %lu"), |
| jiffies, dev->trans_start); |
| #endif |
| DPTRACE(adf_dp_trace(NULL, ADF_DP_TRACE_HDD_TX_TIMEOUT, |
| NULL, 0, ADF_TX)); |
| /* |
| * 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_INFO, |
| "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_INFO, |
| "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 < NUM_TX_QUEUES; i++) { |
| txq = netdev_get_tx_queue(dev, i); |
| hddLog(LOGE, FL("Queue%d status: %d txq->trans_start %lu"), |
| i, netif_tx_queue_stopped(txq), txq->trans_start); |
| } |
| wlan_display_tx_timeout_stats(pAdapter); |
| } |
| |
| /** |
| * hdd_tx_timeout() - Wrapper function to protect __hdd_tx_timeout from SSR |
| * @dev: pointer to net_device structure |
| * |
| * 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 |
| * |
| * Return: none |
| */ |
| void hdd_tx_timeout(struct net_device *dev) |
| { |
| vos_ssr_protect(__func__); |
| __hdd_tx_timeout(dev); |
| vos_ssr_unprotect(__func__); |
| } |
| |
| /** |
| * __hdd_stats() - Function registered with the Linux OS for |
| * device TX/RX statistics |
| * @dev: pointer to net_device structure |
| * |
| * Return: pointer to net_device_stats structure |
| */ |
| static struct net_device_stats *__hdd_stats(struct net_device *dev) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| return &pAdapter->stats; |
| } |
| |
| /** |
| * hdd_stats() - SSR wrapper for __hdd_stats |
| * @dev: pointer to net_device structure |
| * |
| * Return: pointer to net_device_stats structure |
| */ |
| 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_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 && vos_be16_to_cpu( *(unsigned short*)pBuffer) == HDD_ETHERTYPE_802_1_X ) |
| { |
| fEAPOL = VOS_TRUE; |
| } |
| } |
| |
| return fEAPOL; |
| } |
| |
| |
| #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 && vos_be16_to_cpu( *((unsigned short*)pBuffer)) == HDD_ETHERTYPE_WAI) |
| { |
| fIsWAI = VOS_TRUE; |
| } |
| } |
| |
| return fIsWAI; |
| } |
| #endif /* FEATURE_WLAN_WAPI */ |
| |
| #ifdef IPV6_MCAST_WAR |
| /* |
| * Return TRUE if the packet is to be dropped |
| */ |
| static inline |
| bool drop_ip6_mcast(struct sk_buff *skb) |
| { |
| struct ethhdr *eth; |
| |
| eth = eth_hdr(skb); |
| if (unlikely(skb->pkt_type == PACKET_MULTICAST)) { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) |
| if (unlikely(ether_addr_equal(eth->h_source, skb->dev->dev_addr))) |
| #else |
| if (unlikely(!compare_ether_addr(eth->h_source, skb->dev->dev_addr))) |
| #endif |
| return true; |
| } |
| return false; |
| } |
| #else |
| #define drop_ip6_mcast(_a) 0 |
| #endif |
| |
| #ifdef CONFIG_HL_SUPPORT |
| /* |
| * hdd_move_radiotap_header_forward - move radiotap header to head of skb |
| * @skb: skb to be modified |
| * |
| * For HL monitor mode, radiotap is appended to tail when update radiotap |
| * info in htt layer. Need to copy it ahead of skb before indicating to OS. |
| */ |
| static VOS_STATUS hdd_move_radiotap_header_forward(struct sk_buff *skb) |
| { |
| adf_nbuf_t msdu = (adf_nbuf_t)skb; |
| struct ieee80211_radiotap_header *rthdr; |
| uint8_t rtap_len; |
| |
| if (!adf_nbuf_put_tail(msdu, |
| sizeof(struct ieee80211_radiotap_header))) |
| return VOS_STATUS_E_NOMEM; |
| else { |
| rthdr = (struct ieee80211_radiotap_header *) |
| (adf_nbuf_data(msdu) + adf_nbuf_len(msdu) - |
| sizeof(struct ieee80211_radiotap_header)); |
| rtap_len = rthdr->it_len; |
| if (!adf_nbuf_put_tail(msdu, |
| rtap_len - |
| sizeof(struct ieee80211_radiotap_header))) |
| return VOS_STATUS_E_NOMEM; |
| else { |
| adf_nbuf_push_head(msdu, rtap_len); |
| adf_os_mem_copy(adf_nbuf_data(msdu), rthdr, rtap_len); |
| adf_nbuf_trim_tail(msdu, rtap_len); |
| } |
| } |
| return VOS_STATUS_SUCCESS; |
| } |
| #else |
| static inline VOS_STATUS hdd_move_radiotap_header_forward(struct sk_buff *skb) |
| { |
| /* no-op */ |
| return VOS_STATUS_SUCCESS; |
| } |
| #endif |
| |
| /** |
| * hdd_mon_rx_packet_cbk() - Receive callback registered with TLSHIM. |
| * @vosContext: [in] pointer to VOS context |
| * @staId: [in] Station Id |
| * @rxBuf: [in] pointer to rx adf_nbuf |
| * |
| * TL will call this to notify the HDD when one or more packets were |
| * received for a registered STA. |
| * Return: VOS_STATUS_E_FAILURE if any errors encountered, VOS_STATUS_SUCCESS |
| * otherwise |
| */ |
| VOS_STATUS hdd_mon_rx_packet_cbk(v_VOID_t *vos_ctx, adf_nbuf_t rx_buf, |
| uint8_t sta_id) |
| { |
| hdd_adapter_t *adapter = NULL; |
| hdd_context_t *hdd_ctx = NULL; |
| int rxstat; |
| struct sk_buff *skb = NULL; |
| struct sk_buff *skb_next; |
| unsigned int cpu_index; |
| hdd_adapter_list_node_t *adapter_node = NULL; |
| |
| /* Sanity check on inputs */ |
| if ((NULL == vos_ctx) || (NULL == rx_buf)) { |
| VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR, |
| "%s: Null params being passed", __func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx); |
| if (NULL == hdd_ctx) { |
| VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR, |
| "%s: HDD adapter context is Null", __func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| hdd_get_front_adapter(hdd_ctx, &adapter_node); |
| adapter = adapter_node->pAdapter; |
| if ((NULL == adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { |
| VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR, |
| "invalid adapter %pK for sta Id %d", adapter, sta_id); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| cpu_index = wlan_hdd_get_cpu(); |
| |
| /* walk the chain until all are processed */ |
| skb = (struct sk_buff *) rx_buf; |
| while (NULL != skb) { |
| skb_next = skb->next; |
| skb->dev = adapter->dev; |
| |
| if(hdd_move_radiotap_header_forward(skb)) { |
| skb = skb_next; |
| continue; |
| } |
| |
| ++adapter->hdd_stats.hddTxRxStats.rxPackets[cpu_index]; |
| ++adapter->stats.rx_packets; |
| adapter->stats.rx_bytes += skb->len; |
| /* |
| * If this is not a last packet on the chain |
| * Just put packet into backlog queue, not scheduling RX sirq |
| */ |
| if (skb->next) { |
| rxstat = netif_rx(skb); |
| } else { |
| /* |
| * This is the last packet on the chain |
| * Scheduling rx sirq |
| */ |
| rxstat = netif_rx_ni(skb); |
| } |
| |
| if (NET_RX_SUCCESS == rxstat) |
| ++adapter-> |
| hdd_stats.hddTxRxStats.rxDelivered[cpu_index]; |
| else |
| ++adapter->hdd_stats.hddTxRxStats.rxRefused[cpu_index]; |
| |
| skb = skb_next; |
| } |
| |
| adapter->dev->last_rx = jiffies; |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| VOS_STATUS hdd_vir_mon_rx_cbk(v_VOID_t *vos_ctx, adf_nbuf_t rx_buf, |
| uint8_t sta_id) |
| { |
| hdd_adapter_t *adapter = NULL; |
| hdd_context_t *hdd_ctx = NULL; |
| int rxstat; |
| struct sk_buff *skb = NULL; |
| struct sk_buff *skb_next; |
| |
| /* Sanity check on inputs */ |
| if ((NULL == vos_ctx) || (NULL == rx_buf)) { |
| VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR, |
| "%s: Null params being passed", __func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx); |
| if (NULL == hdd_ctx) { |
| VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR, |
| "%s: HDD adapter context is Null", __func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_MONITOR); |
| if ((NULL == adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { |
| VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_ERROR, |
| "invalid adapter %p for sta Id %d", adapter, sta_id); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| /* walk the chain until all are processed */ |
| skb = (struct sk_buff *)rx_buf; |
| while (NULL != skb) { |
| skb_next = skb->next; |
| skb->dev = adapter->dev; |
| |
| /* |
| * If this is not a last packet on the chain |
| * Just put packet into backlog queue, not scheduling RX sirq |
| */ |
| if (skb->next) { |
| rxstat = netif_rx(skb); |
| } else { |
| /* |
| * This is the last packet on the chain |
| * Scheduling rx sirq |
| */ |
| rxstat = netif_rx_ni(skb); |
| } |
| |
| skb = skb_next; |
| } |
| |
| adapter->dev->last_rx = jiffies; |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| /** |
| * hdd_is_arp_local() - check if local or non local arp |
| * @skb: pointer to sk_buff |
| * |
| * Return: true if local arp or false otherwise. |
| */ |
| static bool hdd_is_arp_local(struct sk_buff *skb) |
| { |
| |
| struct arphdr *arp; |
| struct in_ifaddr **ifap = NULL; |
| struct in_ifaddr *ifa = NULL; |
| struct in_device *in_dev; |
| unsigned char *arp_ptr; |
| __be32 tip; |
| |
| arp = (struct arphdr *)skb->data; |
| if (arp->ar_op == htons(ARPOP_REQUEST)) { |
| if ((in_dev = __in_dev_get_rtnl(skb->dev)) != NULL) { |
| 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) { |
| arp_ptr = (unsigned char *)(arp + 1); |
| arp_ptr += (skb->dev->addr_len + 4 + skb->dev->addr_len); |
| memcpy(&tip, arp_ptr, 4); |
| hddLog(VOS_TRACE_LEVEL_INFO, "ARP packets: local IP: %x dest IP: %x\n", |
| ifa->ifa_local, tip); |
| if (ifa->ifa_local != tip) |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| #ifdef WLAN_FEATURE_TSF_PLUS |
| static inline void hdd_tsf_timestamp_rx(hdd_context_t *hdd_ctx, |
| adf_nbuf_t netbuf, |
| uint64_t target_time) |
| { |
| if (!HDD_TSF_IS_RX_SET(hdd_ctx)) |
| return; |
| |
| hdd_rx_timestamp(netbuf, target_time); |
| } |
| #else |
| static inline void hdd_tsf_timestamp_rx(hdd_context_t *hdd_ctx, |
| adf_nbuf_t netbuf, |
| uint64_t target_time) |
| { |
| } |
| #endif |
| |
| /**============================================================================ |
| @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 staId : [in] Station Id |
| @param rxBuf : [in] pointer to rx adf_nbuf |
| |
| @return : VOS_STATUS_E_FAILURE if any errors encountered, |
| : VOS_STATUS_SUCCESS otherwise |
| ===========================================================================*/ |
| VOS_STATUS hdd_rx_packet_cbk(v_VOID_t *vosContext, |
| adf_nbuf_t rxBuf, v_U8_t staId) |
| { |
| hdd_adapter_t *pAdapter = NULL; |
| hdd_context_t *pHddCtx = NULL; |
| int rxstat; |
| struct sk_buff *skb = NULL; |
| struct sk_buff *skb_next; |
| unsigned int cpu_index; |
| #ifdef QCA_PKT_PROTO_TRACE |
| v_U8_t proto_type = 0; |
| #endif /* QCA_PKT_PROTO_TRACE */ |
| hdd_station_ctx_t *pHddStaCtx = NULL; |
| bool wake_lock = false; |
| |
| //Sanity check on inputs |
| if ((NULL == vosContext) || (NULL == rxBuf)) |
| { |
| 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)) { |
| hddLog(LOGE, FL("invalid adapter %pK for sta Id %d"), pAdapter, staId); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| 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 VOS_STATUS_E_FAILURE; |
| } |
| |
| cpu_index = wlan_hdd_get_cpu(); |
| |
| // walk the chain until all are processed |
| skb = (struct sk_buff *) rxBuf; |
| pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| while (NULL != skb) { |
| skb_next = skb->next; |
| |
| if (((pHddStaCtx->conn_info.proxyARPService) && |
| cfg80211_is_gratuitous_arp_unsolicited_na(skb)) || |
| vos_is_load_unload_in_progress(VOS_MODULE_ID_VOSS, NULL)) { |
| ++pAdapter->hdd_stats.hddTxRxStats.rxDropped[cpu_index]; |
| VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO, |
| "%s: Dropping HS 2.0 Gratuitous ARP or Unsolicited NA" |
| " else dropping as Driver load/unload is in progress", |
| __func__); |
| adf_nbuf_free(skb); |
| skb = skb_next; |
| continue; |
| } |
| |
| DPTRACE(adf_dp_trace(skb, |
| ADF_DP_TRACE_RX_HDD_PACKET_PTR_RECORD, |
| adf_nbuf_data_addr(skb), |
| sizeof(adf_nbuf_data(skb)), ADF_RX)); |
| DPTRACE(adf_dp_trace(skb, |
| ADF_DP_TRACE_HDD_RX_PACKET_RECORD, |
| (uint8_t *)skb->data, adf_nbuf_len(skb), ADF_RX)); |
| |
| if (adf_nbuf_len(skb) > ADF_DP_TRACE_RECORD_SIZE) |
| DPTRACE(adf_dp_trace(skb, |
| ADF_DP_TRACE_HDD_RX_PACKET_RECORD, |
| (uint8_t *)&skb->data[ADF_DP_TRACE_RECORD_SIZE], |
| (adf_nbuf_len(skb) - ADF_DP_TRACE_RECORD_SIZE), |
| ADF_RX)); |
| |
| #ifdef QCA_PKT_PROTO_TRACE |
| if ((pHddCtx->cfg_ini->gEnableDebugLog & VOS_PKT_TRAC_TYPE_EAPOL) || |
| (pHddCtx->cfg_ini->gEnableDebugLog & VOS_PKT_TRAC_TYPE_DHCP)) { |
| proto_type = vos_pkt_get_proto_type(skb, |
| pHddCtx->cfg_ini->gEnableDebugLog, 0); |
| switch (proto_type) { |
| case VOS_PKT_TRAC_TYPE_EAPOL: |
| vos_pkt_trace_buf_update("ST:R:EPL"); |
| break; |
| case VOS_PKT_TRAC_TYPE_DHCP: |
| hdd_dhcp_pkt_trace_buf_update(skb, RX_PATH, STA); |
| break; |
| case VOS_PKT_TRAC_TYPE_ARP: |
| vos_pkt_trace_buf_update("ST:R:ARP"); |
| break; |
| case VOS_PKT_TRAC_TYPE_NS: |
| vos_pkt_trace_buf_update("ST:R:NS"); |
| break; |
| case VOS_PKT_TRAC_TYPE_NA: |
| vos_pkt_trace_buf_update("ST:R:NA"); |
| break; |
| default: |
| break; |
| } |
| } |
| #endif /* QCA_PKT_PROTO_TRACE */ |
| |
| skb->dev = pAdapter->dev; |
| skb->protocol = eth_type_trans(skb, skb->dev); |
| |
| /* Check & drop mcast packets (for IPV6) as required */ |
| if (drop_ip6_mcast(skb)) { |
| hddLog(VOS_TRACE_LEVEL_INFO, "MAC Header:"); |
| VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO, |
| skb_mac_header(skb), 16); |
| ++pAdapter->hdd_stats.hddTxRxStats.rxDropped[cpu_index]; |
| VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO, |
| "%s: Dropping multicast to self NA", __func__); |
| adf_nbuf_free(skb); |
| skb = skb_next; |
| continue; |
| } |
| |
| ++pAdapter->hdd_stats.hddTxRxStats.rxPackets[cpu_index]; |
| ++pAdapter->stats.rx_packets; |
| pAdapter->stats.rx_bytes += skb->len; |
| |
| /** |
| * Remove SKB from internal tracking table before submitting it |
| * to stack. |
| */ |
| adf_net_buf_debug_release_skb(skb); |
| |
| if (!wake_lock) { |
| if (skb->protocol == htons(ETH_P_ARP)) { |
| if (hdd_is_arp_local(skb)) |
| wake_lock = true; |
| } |
| else |
| wake_lock = true; |
| } |
| |
| hdd_tsf_timestamp_rx(pHddCtx, skb, ktime_to_us(skb->tstamp)); |
| |
| /* |
| * If this is not a last packet on the chain |
| * Just put packet into backlog queue, not scheduling RX sirq |
| */ |
| if (skb->next) { |
| rxstat = netif_rx(skb); |
| } else { |
| if ((pHddCtx->cfg_ini->rx_wakelock_timeout) && |
| (PACKET_BROADCAST != skb->pkt_type) && |
| (PACKET_MULTICAST != skb->pkt_type)) |
| wake_lock = true; |
| |
| if (wake_lock && pHddStaCtx->conn_info.uIsAuthenticated) |
| vos_wake_lock_timeout_acquire(&pHddCtx->rx_wake_lock, |
| pHddCtx->cfg_ini->rx_wakelock_timeout, |
| WIFI_POWER_EVENT_WAKELOCK_HOLD_RX); |
| /* |
| * This is the last packet on the chain |
| * Scheduling rx sirq |
| */ |
| rxstat = netif_rx_ni(skb); |
| } |
| |
| if (NET_RX_SUCCESS == rxstat) |
| ++pAdapter->hdd_stats.hddTxRxStats.rxDelivered[cpu_index]; |
| else |
| ++pAdapter->hdd_stats.hddTxRxStats.rxRefused[cpu_index]; |
| |
| skb = skb_next; |
| } |
| |
| pAdapter->dev->last_rx = jiffies; |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| /** |
| * hdd_reason_type_to_string() - return string conversion of reason type |
| * @reason: reason type |
| * |
| * This utility function helps log string conversion of reason type. |
| * |
| * Return: string conversion of device mode, if match found; |
| * "Unknown" otherwise. |
| */ |
| const char *hdd_reason_type_to_string(enum netif_reason_type reason) |
| { |
| switch (reason) { |
| CASE_RETURN_STRING(WLAN_CONTROL_PATH); |
| CASE_RETURN_STRING(WLAN_DATA_FLOW_CONTROL); |
| default: |
| return "Invalid"; |
| } |
| } |
| |
| /** |
| * hdd_action_type_to_string() - return string conversion of action type |
| * @action: action type |
| * |
| * This utility function helps log string conversion of action_type. |
| * |
| * Return: string conversion of device mode, if match found; |
| * "Unknown" otherwise. |
| */ |
| const char *hdd_action_type_to_string(enum netif_action_type action) |
| { |
| switch (action) { |
| CASE_RETURN_STRING(WLAN_STOP_ALL_NETIF_QUEUE); |
| CASE_RETURN_STRING(WLAN_START_ALL_NETIF_QUEUE); |
| CASE_RETURN_STRING(WLAN_WAKE_ALL_NETIF_QUEUE); |
| CASE_RETURN_STRING(WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER); |
| CASE_RETURN_STRING(WLAN_START_ALL_NETIF_QUEUE_N_CARRIER); |
| CASE_RETURN_STRING(WLAN_NETIF_TX_DISABLE); |
| CASE_RETURN_STRING(WLAN_NETIF_TX_DISABLE_N_CARRIER); |
| CASE_RETURN_STRING(WLAN_NETIF_CARRIER_ON); |
| CASE_RETURN_STRING(WLAN_NETIF_CARRIER_OFF); |
| default: |
| return "Invalid"; |
| } |
| } |
| |
| /** |
| * wlan_hdd_update_queue_oper_stats - update queue operation statistics |
| * @adapter: adapter handle |
| * @action: action type |
| * @reason: reason type |
| */ |
| static void wlan_hdd_update_queue_oper_stats(hdd_adapter_t *adapter, |
| enum netif_action_type action, enum netif_reason_type reason) |
| { |
| switch (action) { |
| case WLAN_STOP_ALL_NETIF_QUEUE: |
| case WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER: |
| case WLAN_NETIF_TX_DISABLE: |
| case WLAN_NETIF_TX_DISABLE_N_CARRIER: |
| adapter->queue_oper_stats[reason].pause_count++; |
| break; |
| case WLAN_START_ALL_NETIF_QUEUE: |
| case WLAN_WAKE_ALL_NETIF_QUEUE: |
| case WLAN_START_ALL_NETIF_QUEUE_N_CARRIER: |
| adapter->queue_oper_stats[reason].unpause_count++; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /** |
| * wlan_hdd_update_txq_timestamp() - update txq timestamp |
| * @dev: net device |
| * |
| * Return: none |
| */ |
| static void wlan_hdd_update_txq_timestamp(struct net_device *dev) |
| { |
| struct netdev_queue *txq; |
| int i; |
| bool unlock; |
| |
| for (i = 0; i < NUM_TX_QUEUES; i++) { |
| txq = netdev_get_tx_queue(dev, i); |
| unlock = __netif_tx_trylock(txq); |
| txq_trans_update(txq); |
| if (unlock == true) |
| __netif_tx_unlock(txq); |
| } |
| } |
| |
| /** |
| * wlan_hdd_update_unpause_time() - update unpause time |
| * @adapter: hdd adapter handle |
| * |
| * Return: none |
| */ |
| static void wlan_hdd_update_unpause_time(hdd_adapter_t *adapter) |
| { |
| adf_os_time_t curr_time = vos_system_ticks(); |
| |
| adapter->total_unpause_time += curr_time - adapter->last_time; |
| adapter->last_time = curr_time; |
| } |
| |
| /** |
| * wlan_hdd_update_pause_time() - update pause time |
| * @adapter: hdd adapter handle |
| * |
| * Return: none |
| */ |
| static void wlan_hdd_update_pause_time(hdd_adapter_t *adapter) |
| { |
| adf_os_time_t curr_time = vos_system_ticks(); |
| |
| adapter->total_pause_time += curr_time - adapter->last_time; |
| adapter->last_time = curr_time; |
| } |
| |
| /** |
| * wlan_hdd_netif_queue_control() - Use for netif_queue related actions |
| * @adapter: adapter handle |
| * @action: action type |
| * @reason: reason type |
| * |
| * This is single function which is used for netif_queue related |
| * actions like start/stop of network queues and on/off carrier |
| * option. |
| * |
| * Return: None |
| */ |
| void wlan_hdd_netif_queue_control(hdd_adapter_t *adapter, |
| enum netif_action_type action, enum netif_reason_type reason) |
| { |
| if ((!adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) || |
| (!adapter->dev)) { |
| hddLog(LOGE, FL("adapter is invalid")); |
| return; |
| } |
| hddLog(LOG1, FL("action is %d reason is %d"),action,reason); |
| |
| switch (action) { |
| case WLAN_NETIF_CARRIER_ON: |
| netif_carrier_on(adapter->dev); |
| break; |
| |
| case WLAN_NETIF_CARRIER_OFF: |
| netif_carrier_off(adapter->dev); |
| break; |
| |
| case WLAN_STOP_ALL_NETIF_QUEUE: |
| SPIN_LOCK_BH(&adapter->pause_map_lock); |
| if (!adapter->pause_map) { |
| netif_tx_stop_all_queues(adapter->dev); |
| wlan_hdd_update_txq_timestamp(adapter->dev); |
| wlan_hdd_update_unpause_time(adapter); |
| } |
| adapter->pause_map |= (1 << reason); |
| SPIN_UNLOCK_BH(&adapter->pause_map_lock); |
| break; |
| |
| case WLAN_START_ALL_NETIF_QUEUE: |
| SPIN_LOCK_BH(&adapter->pause_map_lock); |
| adapter->pause_map &= ~(1 << reason); |
| if (!adapter->pause_map) { |
| netif_tx_start_all_queues(adapter->dev); |
| wlan_hdd_update_pause_time(adapter); |
| } |
| SPIN_UNLOCK_BH(&adapter->pause_map_lock); |
| break; |
| |
| case WLAN_WAKE_ALL_NETIF_QUEUE: |
| SPIN_LOCK_BH(&adapter->pause_map_lock); |
| adapter->pause_map &= ~(1 << reason); |
| if (!adapter->pause_map) { |
| netif_tx_wake_all_queues(adapter->dev); |
| wlan_hdd_update_pause_time(adapter); |
| } |
| SPIN_UNLOCK_BH(&adapter->pause_map_lock); |
| break; |
| |
| case WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER: |
| SPIN_LOCK_BH(&adapter->pause_map_lock); |
| if (!adapter->pause_map) { |
| netif_tx_stop_all_queues(adapter->dev); |
| wlan_hdd_update_txq_timestamp(adapter->dev); |
| wlan_hdd_update_unpause_time(adapter); |
| } |
| adapter->pause_map |= (1 << reason); |
| netif_carrier_off(adapter->dev); |
| SPIN_UNLOCK_BH(&adapter->pause_map_lock); |
| break; |
| |
| case WLAN_START_ALL_NETIF_QUEUE_N_CARRIER: |
| SPIN_LOCK_BH(&adapter->pause_map_lock); |
| netif_carrier_on(adapter->dev); |
| adapter->pause_map &= ~(1 << reason); |
| if (!adapter->pause_map) { |
| netif_tx_start_all_queues(adapter->dev); |
| wlan_hdd_update_pause_time(adapter); |
| } |
| SPIN_UNLOCK_BH(&adapter->pause_map_lock); |
| break; |
| |
| case WLAN_NETIF_TX_DISABLE: |
| SPIN_LOCK_BH(&adapter->pause_map_lock); |
| if (!adapter->pause_map) { |
| netif_tx_disable(adapter->dev); |
| wlan_hdd_update_txq_timestamp(adapter->dev); |
| wlan_hdd_update_unpause_time(adapter); |
| } |
| adapter->pause_map |= (1 << reason); |
| SPIN_UNLOCK_BH(&adapter->pause_map_lock); |
| break; |
| |
| case WLAN_NETIF_TX_DISABLE_N_CARRIER: |
| SPIN_LOCK_BH(&adapter->pause_map_lock); |
| if (!adapter->pause_map) { |
| netif_tx_disable(adapter->dev); |
| wlan_hdd_update_txq_timestamp(adapter->dev); |
| wlan_hdd_update_unpause_time(adapter); |
| } |
| adapter->pause_map |= (1 << reason); |
| netif_carrier_off(adapter->dev); |
| SPIN_UNLOCK_BH(&adapter->pause_map_lock); |
| break; |
| |
| default: |
| hddLog(LOGE, FL("unsupported netif queue action %d"), action); |
| } |
| |
| wlan_hdd_update_queue_oper_stats(adapter, action, reason); |
| |
| adapter->queue_oper_history[adapter->history_index].time = |
| vos_system_ticks(); |
| adapter->queue_oper_history[adapter->history_index].netif_action = |
| action; |
| adapter->queue_oper_history[adapter->history_index].netif_reason = |
| reason; |
| adapter->queue_oper_history[adapter->history_index].pause_map = |
| adapter->pause_map; |
| if (++adapter->history_index == WLAN_HDD_MAX_HISTORY_ENTRY) |
| adapter->history_index = 0; |
| |
| } |
| |
| #ifdef QCA_PKT_PROTO_TRACE |
| /** |
| * hdd_dhcp_pkt_trace_buf_update() - Update protocol trace buffer with DHCP |
| * packet info. |
| * @skb: skb pointer |
| * @is_transmission: packet is in transmission or in rx |
| * @is_sta: tx/rx by STA mode |
| * |
| * Return: None |
| */ |
| void hdd_dhcp_pkt_trace_buf_update (struct sk_buff *skb, int is_transmission, |
| int is_sta) |
| { |
| char tbuf[20]; |
| 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: |
| snprintf(tbuf, sizeof(tbuf), |
| "%s:%s:DHCP DIS", |
| is_sta?"ST":"HA", |
| is_transmission?"T":"R"); |
| break; |
| case DHCPREQUEST: |
| snprintf(tbuf, sizeof(tbuf), |
| "%s:%s:DHCP REQ", |
| is_sta?"ST":"HA", |
| is_transmission?"T":"R"); |
| break; |
| case DHCPOFFER: |
| snprintf(tbuf, sizeof(tbuf), |
| "%s:%s:DHCP OFF", |
| is_sta?"ST":"HA", |
| is_transmission?"T":"R"); |
| break; |
| case DHCPACK: |
| snprintf(tbuf, sizeof(tbuf), |
| "%s:%s:DHCP ACK", |
| is_sta?"ST":"HA", |
| is_transmission?"T":"R"); |
| break; |
| case DHCPNAK: |
| snprintf(tbuf, sizeof(tbuf), |
| "%s:%s:DHCP NAK", |
| is_sta?"ST":"HA", |
| is_transmission?"T":"R"); |
| break; |
| case DHCPRELEASE: |
| snprintf(tbuf, sizeof(tbuf), |
| "%s:%s:DHCP REL", |
| is_sta?"ST":"HA", |
| is_transmission?"T":"R"); |
| break; |
| case DHCPINFORM: |
| snprintf(tbuf, sizeof(tbuf), |
| "%s:%s:DHCP INF", |
| is_sta?"ST":"HA", |
| is_transmission?"T":"R"); |
| break; |
| case DHCPDECLINE: |
| snprintf(tbuf, sizeof(tbuf), |
| "%s:%s:DHCP DELC", |
| is_sta?"ST":"HA", |
| is_transmission?"T":"R"); |
| break; |
| default: |
| snprintf(tbuf, sizeof(tbuf), |
| "%s:%s:DHCP INVL", |
| is_sta?"ST":"HA", |
| is_transmission?"T":"R"); |
| break; |
| } |
| vos_pkt_trace_buf_update(tbuf); |
| VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO, |
| FL("%s"), tbuf); |
| } |
| } |
| #endif |
| #ifdef FEATURE_BUS_BANDWIDTH |
| /** |
| * hdd_rst_tcp_delack() - Reset tcp delack value to original level |
| * @hdd_context_t : HDD context |
| * |
| * This is single function which is used for reseting TCP delack |
| * value to its original value. |
| * |
| * Return: None |
| */ |
| void hdd_rst_tcp_delack(hdd_context_t *hdd_ctx) |
| { |
| enum cnss_bus_width_type next_level = CNSS_BUS_WIDTH_LOW; |
| |
| hdd_ctx->rx_high_ind_cnt = 0; |
| wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, WLAN_SVC_WLAN_TP_IND, |
| &next_level, sizeof(next_level)); |
| } |
| #else |
| void hdd_rst_tcp_delack(hdd_context_t *hdd_ctx) |
| { |
| return; |
| } |
| #endif /* FEATURE_BUS_BANDWIDTH */ |