| /* |
| * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. |
| * |
| * 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. |
| */ |
| |
| /* denote that this file does not allow legacy hddLog */ |
| #define HDD_DISALLOW_LEGACY_HDDLOG 1 |
| |
| /* Include files */ |
| #include <linux/semaphore.h> |
| #include "osif_sync.h" |
| #include <wlan_hdd_tx_rx.h> |
| #include <wlan_hdd_softap_tx_rx.h> |
| #include <linux/netdevice.h> |
| #include <linux/skbuff.h> |
| #include <linux/etherdevice.h> |
| #include <qdf_types.h> |
| #include <ani_global.h> |
| #include <qdf_types.h> |
| #include <net/ieee80211_radiotap.h> |
| #include <cds_sched.h> |
| #include <wlan_hdd_napi.h> |
| #include <cdp_txrx_cmn.h> |
| #include <cdp_txrx_peer_ops.h> |
| #include <cds_utils.h> |
| #include <cdp_txrx_flow_ctrl_v2.h> |
| #include <cdp_txrx_misc.h> |
| #include <wlan_hdd_object_manager.h> |
| #include "wlan_p2p_ucfg_api.h" |
| #include <wlan_hdd_regulatory.h> |
| #include "wlan_ipa_ucfg_api.h" |
| #include "wlan_policy_mgr_ucfg.h" |
| #include <wma_types.h> |
| #include "wlan_hdd_sta_info.h" |
| #include "ol_defines.h" |
| #include <wlan_hdd_sar_limits.h> |
| |
| /* Preprocessor definitions and constants */ |
| #undef QCA_HDD_SAP_DUMP_SK_BUFF |
| |
| /* Type declarations */ |
| |
| /* Function definitions and documenation */ |
| #ifdef QCA_HDD_SAP_DUMP_SK_BUFF |
| /** |
| * hdd_softap_dump_sk_buff() - Dump an skb |
| * @skb: skb to dump |
| * |
| * Return: None |
| */ |
| static void hdd_softap_dump_sk_buff(struct sk_buff *skb) |
| { |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, |
| "%s: head = %pK ", __func__, skb->head); |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_INFO, |
| "%s: tail = %pK ", __func__, skb->tail); |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, |
| "%s: end = %pK ", __func__, skb->end); |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, |
| "%s: len = %d ", __func__, skb->len); |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, |
| "%s: data_len = %d ", __func__, skb->data_len); |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, |
| "%s: mac_len = %d", __func__, skb->mac_len); |
| |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_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]); |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_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]); |
| } |
| #else |
| static void hdd_softap_dump_sk_buff(struct sk_buff *skb) |
| { |
| } |
| #endif |
| |
| #ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL |
| void hdd_softap_tx_resume_timer_expired_handler(void *adapter_context) |
| { |
| struct hdd_adapter *adapter = (struct hdd_adapter *) adapter_context; |
| |
| if (!adapter) { |
| hdd_err("NULL adapter"); |
| return; |
| } |
| |
| hdd_debug("Enabling queues"); |
| wlan_hdd_netif_queue_control(adapter, WLAN_WAKE_ALL_NETIF_QUEUE, |
| WLAN_CONTROL_PATH); |
| } |
| |
| /** |
| * hdd_softap_tx_resume_false() - Resume OS TX Q false leads to queue disabling |
| * @adapter: pointer to hdd adapter |
| * @tx_resume: TX Q resume trigger |
| * |
| * |
| * Return: None |
| */ |
| static void |
| hdd_softap_tx_resume_false(struct hdd_adapter *adapter, bool tx_resume) |
| { |
| if (true == tx_resume) |
| return; |
| |
| hdd_debug("Disabling queues"); |
| wlan_hdd_netif_queue_control(adapter, WLAN_STOP_ALL_NETIF_QUEUE, |
| WLAN_DATA_FLOW_CONTROL); |
| |
| if (QDF_TIMER_STATE_STOPPED == |
| qdf_mc_timer_get_current_state(&adapter-> |
| tx_flow_control_timer)) { |
| QDF_STATUS status; |
| |
| status = qdf_mc_timer_start(&adapter->tx_flow_control_timer, |
| WLAN_SAP_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME); |
| |
| if (!QDF_IS_STATUS_SUCCESS(status)) |
| hdd_err("Failed to start tx_flow_control_timer"); |
| else |
| adapter->hdd_stats.tx_rx_stats.txflow_timer_cnt++; |
| } |
| } |
| |
| void hdd_softap_tx_resume_cb(void *adapter_context, bool tx_resume) |
| { |
| struct hdd_adapter *adapter = (struct hdd_adapter *) adapter_context; |
| |
| if (!adapter) { |
| hdd_err("NULL adapter"); |
| return; |
| } |
| |
| /* Resume TX */ |
| if (true == tx_resume) { |
| if (QDF_TIMER_STATE_STOPPED != |
| qdf_mc_timer_get_current_state(&adapter-> |
| tx_flow_control_timer)) { |
| qdf_mc_timer_stop(&adapter->tx_flow_control_timer); |
| } |
| |
| hdd_debug("Enabling queues"); |
| wlan_hdd_netif_queue_control(adapter, |
| WLAN_WAKE_ALL_NETIF_QUEUE, |
| WLAN_DATA_FLOW_CONTROL); |
| } |
| hdd_softap_tx_resume_false(adapter, tx_resume); |
| } |
| |
| static inline struct sk_buff *hdd_skb_orphan(struct hdd_adapter *adapter, |
| struct sk_buff *skb) |
| { |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| int need_orphan = 0; |
| |
| if (adapter->tx_flow_low_watermark > 0) { |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 19, 0)) |
| /* |
| * 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. |
| */ |
| need_orphan = 1; |
| #else |
| if (hdd_ctx->config->tx_orphan_enable) |
| need_orphan = 1; |
| #endif |
| } else if (hdd_ctx->config->tx_orphan_enable) { |
| if (qdf_nbuf_is_ipv4_tcp_pkt(skb) || |
| qdf_nbuf_is_ipv6_tcp_pkt(skb)) |
| need_orphan = 1; |
| } |
| |
| if (need_orphan) { |
| skb_orphan(skb); |
| ++adapter->hdd_stats.tx_rx_stats.tx_orphaned; |
| } else |
| skb = skb_unshare(skb, GFP_ATOMIC); |
| |
| return skb; |
| } |
| |
| #else |
| /** |
| * hdd_skb_orphan() - skb_unshare a cloned packed else skb_orphan |
| * @adapter: pointer to HDD adapter |
| * @skb: pointer to skb data packet |
| * |
| * Return: pointer to skb structure |
| */ |
| static inline struct sk_buff *hdd_skb_orphan(struct hdd_adapter *adapter, |
| struct sk_buff *skb) { |
| |
| struct sk_buff *nskb; |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 19, 0)) |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| #endif |
| |
| hdd_skb_fill_gso_size(adapter->dev, skb); |
| |
| nskb = skb_unshare(skb, GFP_ATOMIC); |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 19, 0)) |
| if (unlikely(hdd_ctx->config->tx_orphan_enable) && (nskb == skb)) { |
| /* |
| * For UDP packets we want to orphan the packet to allow the app |
| * to send more packets. The flow would ultimately be controlled |
| * by the limited number of tx descriptors for the vdev. |
| */ |
| ++adapter->hdd_stats.tx_rx_stats.tx_orphaned; |
| skb_orphan(skb); |
| } |
| #endif |
| return nskb; |
| } |
| #endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */ |
| |
| #define IEEE8021X_AUTH_TYPE_EAP 0 |
| #define EAP_CODE_OFFSET 18 |
| #define EAP_CODE_FAILURE 4 |
| |
| /* Wait EAP Failure frame timeout in (MS) */ |
| #define EAP_FRM_TIME_OUT 80 |
| |
| /** |
| * hdd_softap_inspect_tx_eap_pkt() - Inspect eap pkt tx/tx-completion |
| * @adapter: pointer to hdd adapter |
| * @skb: sk_buff |
| * @tx_comp: tx sending or tx completion |
| * |
| * Inspect the EAP-Failure pkt tx sending and tx completion. |
| * |
| * Return: void |
| */ |
| static void hdd_softap_inspect_tx_eap_pkt(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| bool tx_comp) |
| { |
| struct qdf_mac_addr *mac_addr; |
| uint8_t *data; |
| uint8_t auth_type, eap_code; |
| struct hdd_station_info *sta_info; |
| struct hdd_hostapd_state *hapd_state; |
| |
| if (qdf_likely(QDF_NBUF_CB_GET_PACKET_TYPE(skb) != |
| QDF_NBUF_CB_PACKET_TYPE_EAPOL) || skb->len < (EAP_CODE_OFFSET + 1)) |
| return; |
| |
| if (cds_is_driver_recovering() || cds_is_driver_in_bad_state() || |
| cds_is_load_or_unload_in_progress()) { |
| hdd_debug("Recovery/(Un)load in Progress. Ignore!!!"); |
| return; |
| } |
| if (adapter->device_mode != QDF_P2P_GO_MODE) |
| return; |
| hapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); |
| if (!hapd_state || hapd_state->bss_state != BSS_START) { |
| hdd_debug("Hostapd State is not START"); |
| return; |
| } |
| data = skb->data; |
| auth_type = *(uint8_t *)(data + EAPOL_PACKET_TYPE_OFFSET); |
| if (auth_type != IEEE8021X_AUTH_TYPE_EAP) |
| return; |
| eap_code = *(uint8_t *)(data + EAP_CODE_OFFSET); |
| if (eap_code != EAP_CODE_FAILURE) |
| return; |
| mac_addr = (struct qdf_mac_addr *)skb->data; |
| sta_info = hdd_get_sta_info_by_mac(&adapter->sta_info_list, |
| mac_addr->bytes, |
| STA_INFO_SOFTAP_INSPECT_TX_EAP_PKT); |
| if (!sta_info) |
| return; |
| if (tx_comp) { |
| hdd_debug("eap_failure frm tx done "QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(mac_addr->bytes)); |
| qdf_atomic_clear_bit(PENDING_TYPE_EAP_FAILURE, |
| &sta_info->pending_eap_frm_type); |
| qdf_event_set(&hapd_state->qdf_sta_eap_frm_done_event); |
| } else { |
| hdd_debug("eap_failure frm tx pending "QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(mac_addr->bytes)); |
| qdf_event_reset(&hapd_state->qdf_sta_eap_frm_done_event); |
| qdf_atomic_set_bit(PENDING_TYPE_EAP_FAILURE, |
| &sta_info->pending_eap_frm_type); |
| QDF_NBUF_CB_TX_EXTRA_FRAG_FLAGS_NOTIFY_COMP(skb) = 1; |
| } |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, |
| STA_INFO_SOFTAP_INSPECT_TX_EAP_PKT); |
| } |
| |
| void hdd_softap_check_wait_for_tx_eap_pkt(struct hdd_adapter *adapter, |
| struct qdf_mac_addr *mac_addr) |
| { |
| struct hdd_station_info *sta_info; |
| QDF_STATUS qdf_status; |
| struct hdd_hostapd_state *hapd_state; |
| |
| if (adapter->device_mode != QDF_P2P_GO_MODE) |
| return; |
| hapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter); |
| if (!hapd_state || hapd_state->bss_state != BSS_START) { |
| hdd_err("Hostapd State is not START"); |
| return; |
| } |
| sta_info = hdd_get_sta_info_by_mac( |
| &adapter->sta_info_list, |
| mac_addr->bytes, |
| STA_INFO_SOFTAP_CHECK_WAIT_FOR_TX_EAP_PKT); |
| if (!sta_info) |
| return; |
| if (qdf_atomic_test_bit(PENDING_TYPE_EAP_FAILURE, |
| &sta_info->pending_eap_frm_type)) { |
| hdd_debug("eap_failure frm pending "QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(mac_addr->bytes)); |
| qdf_status = qdf_wait_for_event_completion( |
| &hapd_state->qdf_sta_eap_frm_done_event, |
| EAP_FRM_TIME_OUT); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) |
| hdd_debug("eap_failure tx timeout"); |
| } |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, |
| STA_INFO_SOFTAP_CHECK_WAIT_FOR_TX_EAP_PKT); |
| } |
| |
| #ifdef SAP_DHCP_FW_IND |
| /** |
| * hdd_post_dhcp_ind() - Send DHCP START/STOP indication to FW |
| * @adapter: pointer to hdd adapter |
| * @sta_id: peer station ID |
| * @type: WMA message type |
| * |
| * Return: error number |
| */ |
| int hdd_post_dhcp_ind(struct hdd_adapter *adapter, uint8_t *mac_addr, |
| uint16_t type) |
| { |
| tAniDHCPInd pmsg; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| |
| hdd_debug("Post DHCP indication,sta_mac=" QDF_MAC_ADDR_FMT |
| " , type=%d", QDF_MAC_ADDR_REF(mac_addr), type); |
| |
| if (!adapter) { |
| hdd_err("NULL adapter"); |
| return -EINVAL; |
| } |
| |
| pmsg.msgType = type; |
| pmsg.msgLen = (uint16_t) sizeof(tAniDHCPInd); |
| pmsg.device_mode = adapter->device_mode; |
| qdf_mem_copy(pmsg.adapterMacAddr.bytes, |
| adapter->mac_addr.bytes, |
| QDF_MAC_ADDR_SIZE); |
| qdf_mem_copy(pmsg.peerMacAddr.bytes, |
| mac_addr, |
| QDF_MAC_ADDR_SIZE); |
| |
| status = wma_process_dhcp_ind(cds_get_context(QDF_MODULE_ID_WMA), |
| &pmsg); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, |
| "%s: Post DHCP Ind MSG fail", __func__); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| #define DHCP_CLIENT_MAC_ADDR_OFFSET 0x46 |
| |
| /** |
| * hdd_softap_notify_dhcp_ind() - Notify SAP for DHCP indication for tx desc |
| * @context: pointer to HDD context |
| * @netbuf: pointer to OS packet (sk_buff) |
| * |
| * Return: None |
| */ |
| static void hdd_softap_notify_dhcp_ind(void *context, struct sk_buff *netbuf) |
| { |
| struct hdd_ap_ctx *hdd_ap_ctx; |
| uint8_t *dest_mac_addr; |
| struct hdd_adapter *adapter = context; |
| |
| if (hdd_validate_adapter(adapter)) |
| return; |
| |
| hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| if (!hdd_ap_ctx) { |
| hdd_err("HDD sap context is NULL"); |
| return; |
| } |
| |
| dest_mac_addr = netbuf->data + DHCP_CLIENT_MAC_ADDR_OFFSET; |
| |
| hdd_post_dhcp_ind(adapter, dest_mac_addr, WMA_DHCP_STOP_IND); |
| } |
| |
| int hdd_softap_inspect_dhcp_packet(struct hdd_adapter *adapter, |
| struct sk_buff *skb, |
| enum qdf_proto_dir dir) |
| { |
| enum qdf_proto_subtype subtype = QDF_PROTO_INVALID; |
| struct hdd_station_info *hdd_sta_info; |
| int errno = 0; |
| struct qdf_mac_addr *src_mac; |
| |
| if (((adapter->device_mode == QDF_SAP_MODE) || |
| (adapter->device_mode == QDF_P2P_GO_MODE)) && |
| ((dir == QDF_TX && QDF_NBUF_CB_PACKET_TYPE_DHCP == |
| QDF_NBUF_CB_GET_PACKET_TYPE(skb)) || |
| (dir == QDF_RX && qdf_nbuf_is_ipv4_dhcp_pkt(skb) == true))) { |
| |
| src_mac = (struct qdf_mac_addr *)(skb->data + |
| DHCP_CLIENT_MAC_ADDR_OFFSET); |
| |
| subtype = qdf_nbuf_get_dhcp_subtype(skb); |
| hdd_sta_info = hdd_get_sta_info_by_mac( |
| &adapter->sta_info_list, |
| src_mac->bytes, |
| STA_INFO_SOFTAP_INSPECT_DHCP_PACKET); |
| if (!hdd_sta_info) { |
| hdd_debug("Station not found"); |
| return -EINVAL; |
| } |
| |
| hdd_debug("ENTER: type=%d, phase=%d, nego_status=%d", |
| subtype, |
| hdd_sta_info->dhcp_phase, |
| hdd_sta_info->dhcp_nego_status); |
| |
| switch (subtype) { |
| case QDF_PROTO_DHCP_DISCOVER: |
| if (dir != QDF_RX) |
| break; |
| if (hdd_sta_info->dhcp_nego_status == DHCP_NEGO_STOP) |
| errno = hdd_post_dhcp_ind( |
| adapter, |
| hdd_sta_info->sta_mac.bytes, |
| WMA_DHCP_START_IND); |
| hdd_sta_info->dhcp_phase = DHCP_PHASE_DISCOVER; |
| hdd_sta_info->dhcp_nego_status = DHCP_NEGO_IN_PROGRESS; |
| break; |
| case QDF_PROTO_DHCP_OFFER: |
| hdd_sta_info->dhcp_phase = DHCP_PHASE_OFFER; |
| break; |
| case QDF_PROTO_DHCP_REQUEST: |
| if (dir != QDF_RX) |
| break; |
| if (hdd_sta_info->dhcp_nego_status == DHCP_NEGO_STOP) |
| errno = hdd_post_dhcp_ind( |
| adapter, |
| hdd_sta_info->sta_mac.bytes, |
| WMA_DHCP_START_IND); |
| hdd_sta_info->dhcp_nego_status = DHCP_NEGO_IN_PROGRESS; |
| case QDF_PROTO_DHCP_DECLINE: |
| if (dir == QDF_RX) |
| hdd_sta_info->dhcp_phase = DHCP_PHASE_REQUEST; |
| break; |
| case QDF_PROTO_DHCP_ACK: |
| case QDF_PROTO_DHCP_NACK: |
| hdd_sta_info->dhcp_phase = DHCP_PHASE_ACK; |
| if (hdd_sta_info->dhcp_nego_status == |
| DHCP_NEGO_IN_PROGRESS) { |
| hdd_debug("Setting NOTIFY_COMP Flag"); |
| QDF_NBUF_CB_TX_EXTRA_FRAG_FLAGS_NOTIFY_COMP(skb) |
| = 1; |
| } |
| hdd_sta_info->dhcp_nego_status = DHCP_NEGO_STOP; |
| break; |
| default: |
| break; |
| } |
| |
| hdd_debug("EXIT: phase=%d, nego_status=%d", |
| hdd_sta_info->dhcp_phase, |
| hdd_sta_info->dhcp_nego_status); |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &hdd_sta_info, |
| true, STA_INFO_SOFTAP_INSPECT_DHCP_PACKET); |
| } |
| |
| return errno; |
| } |
| #else |
| static void hdd_softap_notify_dhcp_ind(void *context, struct sk_buff *netbuf) |
| { |
| } |
| #endif |
| |
| /** |
| * __hdd_softap_hard_start_xmit() - Transmit a frame |
| * @skb: pointer to OS packet (sk_buff) |
| * @dev: pointer to network device |
| * |
| * Function registered with the Linux OS for transmitting |
| * packets. This version of the function directly passes |
| * the packet to Transport Layer. |
| * In case of any packet drop or error, log the error with |
| * INFO HIGH/LOW/MEDIUM to avoid excessive logging in kmsg. |
| * |
| * Return: None |
| */ |
| static void __hdd_softap_hard_start_xmit(struct sk_buff *skb, |
| struct net_device *dev) |
| { |
| sme_ac_enum_type ac = SME_AC_BE; |
| struct hdd_adapter *adapter = (struct hdd_adapter *) netdev_priv(dev); |
| struct hdd_ap_ctx *ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| struct hdd_context *hdd_ctx = adapter->hdd_ctx; |
| struct qdf_mac_addr *dest_mac_addr, *mac_addr; |
| static struct qdf_mac_addr bcast_mac_addr = QDF_MAC_ADDR_BCAST_INIT; |
| void *soc = cds_get_context(QDF_MODULE_ID_SOC); |
| uint32_t num_seg; |
| struct hdd_station_info *sta_info = NULL; |
| |
| ++adapter->hdd_stats.tx_rx_stats.tx_called; |
| adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt = 0; |
| |
| /* Prevent this function from being called during SSR since TL |
| * context may not be reinitialized at this time which may |
| * lead to a crash. |
| */ |
| if (cds_is_driver_recovering() || cds_is_driver_in_bad_state() || |
| cds_is_load_or_unload_in_progress()) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_INFO_HIGH, |
| "%s: Recovery/(Un)load in Progress. Ignore!!!", |
| __func__); |
| goto drop_pkt; |
| } |
| |
| if (hdd_ctx->hdd_wlan_suspended) { |
| hdd_err_rl("Device is system suspended, drop pkt"); |
| goto drop_pkt; |
| } |
| |
| /* |
| * If the device is operating on a DFS Channel |
| * then check if SAP is in CAC WAIT state and |
| * drop the packets. In CAC WAIT state device |
| * is expected not to transmit any frames. |
| * SAP starts Tx only after the BSS START is |
| * done. |
| */ |
| if (ap_ctx->dfs_cac_block_tx) |
| goto drop_pkt; |
| |
| if (ap_ctx->hostapd_state.bss_state != BSS_START) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_INFO_HIGH, |
| "%s: SAP is not in START state (%d). Ignore!!!", |
| __func__, |
| ap_ctx->hostapd_state.bss_state); |
| goto drop_pkt; |
| } |
| |
| /* |
| * If a transmit function is not registered, drop packet |
| */ |
| if (!adapter->tx_fn) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_INFO_HIGH, |
| "%s: TX function not registered by the data path", |
| __func__); |
| goto drop_pkt; |
| } |
| |
| wlan_hdd_classify_pkt(skb); |
| |
| dest_mac_addr = (struct qdf_mac_addr *)skb->data; |
| |
| /* In case of mcast, fetch the bcast sta_info. Else use the pkt addr */ |
| if (QDF_NBUF_CB_GET_IS_MCAST(skb)) |
| mac_addr = &bcast_mac_addr; |
| else |
| mac_addr = dest_mac_addr; |
| |
| sta_info = hdd_get_sta_info_by_mac(&adapter->sta_info_list, |
| mac_addr->bytes, |
| STA_INFO_SOFTAP_HARD_START_XMIT); |
| |
| if (!QDF_NBUF_CB_GET_IS_BCAST(skb) && !QDF_NBUF_CB_GET_IS_MCAST(skb)) { |
| if (!sta_info) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, |
| QDF_TRACE_LEVEL_INFO_HIGH, |
| "%s: Failed to find right station", __func__); |
| goto drop_pkt; |
| } |
| |
| if (sta_info->is_deauth_in_progress) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, |
| QDF_TRACE_LEVEL_INFO_HIGH, |
| "%s: STA " QDF_MAC_ADDR_FMT |
| "deauth in progress", __func__, |
| QDF_MAC_ADDR_REF(sta_info->sta_mac.bytes)); |
| goto drop_pkt; |
| } |
| |
| if (sta_info->peer_state != OL_TXRX_PEER_STATE_CONN && |
| sta_info->peer_state != OL_TXRX_PEER_STATE_AUTH) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, |
| QDF_TRACE_LEVEL_INFO_HIGH, |
| "%s: Station not connected yet", __func__); |
| goto drop_pkt; |
| } |
| |
| if (sta_info->peer_state == OL_TXRX_PEER_STATE_CONN) { |
| if (ntohs(skb->protocol) != HDD_ETHERTYPE_802_1_X) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, |
| QDF_TRACE_LEVEL_INFO_HIGH, |
| "%s: NON-EAPOL packet in non-Authenticated state", |
| __func__); |
| goto drop_pkt; |
| } |
| } |
| } |
| |
| if (QDF_NBUF_CB_GET_IS_BCAST(skb) || QDF_NBUF_CB_GET_IS_MCAST(skb)) |
| hdd_get_tx_resource( |
| adapter, &adapter->mac_addr, |
| WLAN_SAP_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME); |
| else |
| hdd_get_tx_resource( |
| adapter, dest_mac_addr, |
| WLAN_SAP_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME); |
| |
| /* Get TL AC corresponding to Qdisc queue index/AC. */ |
| ac = hdd_qdisc_ac_to_tl_ac[skb->queue_mapping]; |
| ++adapter->hdd_stats.tx_rx_stats.tx_classified_ac[ac]; |
| |
| #if defined(IPA_OFFLOAD) |
| if (!qdf_nbuf_ipa_owned_get(skb)) { |
| #endif |
| |
| skb = hdd_skb_orphan(adapter, skb); |
| if (!skb) |
| goto drop_pkt_accounting; |
| |
| #if defined(IPA_OFFLOAD) |
| } else { |
| /* |
| * Clear the IPA ownership after check it to avoid ipa_free_skb |
| * is called when Tx completed for intra-BSS Tx packets |
| */ |
| qdf_nbuf_ipa_owned_clear(skb); |
| } |
| #endif |
| |
| /* |
| * Add SKB to internal tracking table before further processing |
| * in WLAN driver. |
| */ |
| qdf_net_buf_debug_acquire_skb(skb, __FILE__, __LINE__); |
| |
| adapter->stats.tx_bytes += skb->len; |
| |
| if (sta_info) { |
| sta_info->tx_bytes += skb->len; |
| |
| if (qdf_nbuf_is_tso(skb)) { |
| num_seg = qdf_nbuf_get_tso_num_seg(skb); |
| adapter->stats.tx_packets += num_seg; |
| sta_info->tx_packets += num_seg; |
| } else { |
| ++adapter->stats.tx_packets; |
| sta_info->tx_packets++; |
| hdd_ctx->no_tx_offload_pkt_cnt++; |
| } |
| sta_info->last_tx_rx_ts = qdf_system_ticks(); |
| } |
| |
| QDF_NBUF_CB_TX_EXTRA_FRAG_FLAGS_NOTIFY_COMP(skb) = 0; |
| |
| hdd_softap_inspect_dhcp_packet(adapter, skb, QDF_TX); |
| hdd_softap_inspect_tx_eap_pkt(adapter, skb, false); |
| |
| hdd_event_eapol_log(skb, QDF_TX); |
| QDF_NBUF_CB_TX_PACKET_TRACK(skb) = QDF_NBUF_TX_PKT_DATA_TRACK; |
| QDF_NBUF_UPDATE_TX_PKT_COUNT(skb, QDF_NBUF_TX_PKT_HDD); |
| qdf_dp_trace_set_track(skb, QDF_TX); |
| DPTRACE(qdf_dp_trace(skb, QDF_DP_TRACE_HDD_TX_PACKET_PTR_RECORD, |
| QDF_TRACE_DEFAULT_PDEV_ID, qdf_nbuf_data_addr(skb), |
| sizeof(qdf_nbuf_data(skb)), |
| QDF_TX)); |
| |
| /* check whether need to linearize skb, like non-linear udp data */ |
| if (hdd_skb_nontso_linearize(skb) != QDF_STATUS_SUCCESS) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_DATA, |
| QDF_TRACE_LEVEL_INFO_HIGH, |
| "%s: skb %pK linearize failed. drop the pkt", |
| __func__, skb); |
| ++adapter->hdd_stats.tx_rx_stats.tx_dropped_ac[ac]; |
| goto drop_pkt_and_release_skb; |
| } |
| |
| if (adapter->tx_fn(soc, adapter->vdev_id, (qdf_nbuf_t)skb)) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_INFO_HIGH, |
| "%s: Failed to send packet to txrx for sta: " |
| QDF_MAC_ADDR_FMT, __func__, |
| QDF_MAC_ADDR_REF(dest_mac_addr->bytes)); |
| ++adapter->hdd_stats.tx_rx_stats.tx_dropped_ac[ac]; |
| goto drop_pkt_and_release_skb; |
| } |
| netif_trans_update(dev); |
| |
| wlan_hdd_sar_unsolicited_timer_start(hdd_ctx); |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, |
| STA_INFO_SOFTAP_HARD_START_XMIT); |
| |
| return; |
| |
| drop_pkt_and_release_skb: |
| qdf_net_buf_debug_release_skb(skb); |
| drop_pkt: |
| qdf_dp_trace_data_pkt(skb, QDF_TRACE_DEFAULT_PDEV_ID, |
| QDF_DP_TRACE_DROP_PACKET_RECORD, 0, |
| QDF_TX); |
| kfree_skb(skb); |
| |
| drop_pkt_accounting: |
| if (sta_info) |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, |
| STA_INFO_SOFTAP_HARD_START_XMIT); |
| ++adapter->stats.tx_dropped; |
| ++adapter->hdd_stats.tx_rx_stats.tx_dropped; |
| } |
| |
| netdev_tx_t hdd_softap_hard_start_xmit(struct sk_buff *skb, |
| struct net_device *net_dev) |
| { |
| struct osif_vdev_sync *vdev_sync; |
| |
| if (osif_vdev_sync_op_start(net_dev, &vdev_sync)) { |
| hdd_debug_rl("Operation on net_dev is not permitted"); |
| kfree_skb(skb); |
| return NETDEV_TX_OK; |
| } |
| |
| __hdd_softap_hard_start_xmit(skb, net_dev); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| |
| return NETDEV_TX_OK; |
| } |
| |
| QDF_STATUS hdd_softap_ipa_start_xmit(qdf_nbuf_t nbuf, qdf_netdev_t dev) |
| { |
| if (NETDEV_TX_OK == hdd_softap_hard_start_xmit( |
| (struct sk_buff *)nbuf, |
| (struct net_device *)dev)) |
| return QDF_STATUS_SUCCESS; |
| else |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| static void __hdd_softap_tx_timeout(struct net_device *dev) |
| { |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx; |
| struct netdev_queue *txq; |
| void *soc = cds_get_context(QDF_MODULE_ID_SOC); |
| int i; |
| |
| DPTRACE(qdf_dp_trace(NULL, QDF_DP_TRACE_HDD_SOFTAP_TX_TIMEOUT, |
| QDF_TRACE_DEFAULT_PDEV_ID, |
| NULL, 0, QDF_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 |
| */ |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, |
| "%s: Recovery in Progress. Ignore!!!", __func__); |
| return; |
| } |
| |
| if (hdd_ctx->hdd_wlan_suspended) { |
| hdd_debug("wlan is suspended, ignore timeout"); |
| return; |
| } |
| |
| TX_TIMEOUT_TRACE(dev, QDF_MODULE_ID_HDD_SAP_DATA); |
| |
| for (i = 0; i < NUM_TX_QUEUES; i++) { |
| txq = netdev_get_tx_queue(dev, i); |
| QDF_TRACE(QDF_MODULE_ID_HDD_DATA, |
| QDF_TRACE_LEVEL_DEBUG, |
| "Queue: %d status: %d txq->trans_start: %lu", |
| i, netif_tx_queue_stopped(txq), txq->trans_start); |
| } |
| |
| wlan_hdd_display_adapter_netif_queue_history(adapter); |
| |
| cdp_dump_flow_pool_info(cds_get_context(QDF_MODULE_ID_SOC)); |
| QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG, |
| "carrier state: %d", netif_carrier_ok(dev)); |
| |
| ++adapter->hdd_stats.tx_rx_stats.tx_timeout_cnt; |
| ++adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt; |
| |
| if (adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt > |
| HDD_TX_STALL_THRESHOLD) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, |
| "Detected data stall due to continuous TX timeouts"); |
| adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt = 0; |
| |
| if (cdp_cfg_get(soc, cfg_dp_enable_data_stall)) |
| cdp_post_data_stall_event(soc, |
| DATA_STALL_LOG_INDICATOR_HOST_DRIVER, |
| DATA_STALL_LOG_HOST_SOFTAP_TX_TIMEOUT, |
| OL_TXRX_PDEV_ID, 0xFF, |
| DATA_STALL_LOG_RECOVERY_TRIGGER_PDR); |
| } |
| } |
| |
| void hdd_softap_tx_timeout(struct net_device *net_dev) |
| { |
| struct osif_vdev_sync *vdev_sync; |
| |
| if (osif_vdev_sync_op_start(net_dev, &vdev_sync)) |
| return; |
| |
| __hdd_softap_tx_timeout(net_dev); |
| |
| osif_vdev_sync_op_stop(vdev_sync); |
| } |
| |
| void hdd_softap_init_tx_rx(struct hdd_adapter *adapter) |
| { |
| qdf_mem_zero(&adapter->stats, sizeof(struct net_device_stats)); |
| } |
| |
| QDF_STATUS hdd_softap_deinit_tx_rx(struct hdd_adapter *adapter) |
| { |
| QDF_BUG(adapter); |
| if (!adapter) |
| return QDF_STATUS_E_FAILURE; |
| |
| adapter->tx_fn = NULL; |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static void |
| hdd_reset_sta_info_during_reattach(struct hdd_station_info *sta_info) |
| { |
| sta_info->in_use = 0; |
| sta_info->sta_id = 0; |
| sta_info->sta_type = 0; |
| qdf_mem_zero(&sta_info->sta_mac, QDF_MAC_ADDR_SIZE); |
| sta_info->peer_state = 0; |
| sta_info->is_qos_enabled = 0; |
| sta_info->is_deauth_in_progress = 0; |
| sta_info->nss = 0; |
| sta_info->rate_flags = 0; |
| sta_info->ecsa_capable = 0; |
| sta_info->max_phy_rate = 0; |
| sta_info->tx_packets = 0; |
| sta_info->tx_bytes = 0; |
| sta_info->rx_packets = 0; |
| sta_info->rx_bytes = 0; |
| sta_info->last_tx_rx_ts = 0; |
| sta_info->assoc_ts = 0; |
| sta_info->disassoc_ts = 0; |
| sta_info->tx_rate = 0; |
| sta_info->rx_rate = 0; |
| sta_info->ampdu = 0; |
| sta_info->sgi_enable = 0; |
| sta_info->tx_stbc = 0; |
| sta_info->rx_stbc = 0; |
| sta_info->ch_width = 0; |
| sta_info->mode = 0; |
| sta_info->max_supp_idx = 0; |
| sta_info->max_ext_idx = 0; |
| sta_info->max_mcs_idx = 0; |
| sta_info->rx_mcs_map = 0; |
| sta_info->tx_mcs_map = 0; |
| sta_info->freq = 0; |
| sta_info->dot11_mode = 0; |
| sta_info->ht_present = 0; |
| sta_info->vht_present = 0; |
| qdf_mem_zero(&sta_info->ht_caps, sizeof(sta_info->ht_caps)); |
| qdf_mem_zero(&sta_info->vht_caps, sizeof(sta_info->vht_caps)); |
| sta_info->reason_code = 0; |
| sta_info->rssi = 0; |
| sta_info->dhcp_phase = 0; |
| sta_info->dhcp_nego_status = 0; |
| sta_info->capability = 0; |
| sta_info->support_mode = 0; |
| sta_info->rx_retry_cnt = 0; |
| sta_info->rx_mc_bc_cnt = 0; |
| |
| if (sta_info->assoc_req_ies.len) { |
| qdf_mem_free(sta_info->assoc_req_ies.data); |
| sta_info->assoc_req_ies.data = NULL; |
| sta_info->assoc_req_ies.len = 0; |
| } |
| |
| sta_info->pending_eap_frm_type = 0; |
| } |
| |
| /** |
| * hdd_sta_info_re_attach() - Re-Attach the station info structure into the list |
| * @sta_info_container: The station info container obj that stores and maintains |
| * the sta_info obj. |
| * @sta_info: The station info structure that is to be attached to the |
| * container object. |
| * |
| * This function re-attaches the station if it gets re-connect after |
| * disconnecting and before its all references are released. |
| * |
| * Return: QDF STATUS SUCCESS on successful attach, error code otherwise |
| */ |
| |
| static QDF_STATUS hdd_sta_info_re_attach( |
| struct hdd_sta_info_obj *sta_info_container, |
| struct hdd_station_info *sta_info, |
| struct qdf_mac_addr *sta_mac) |
| { |
| if (!sta_info_container || !sta_info) { |
| hdd_err("Parameter(s) null"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| qdf_spin_lock_bh(&sta_info_container->sta_obj_lock); |
| |
| if (sta_info->is_attached) { |
| qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); |
| hdd_err("sta info is alredy attached"); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| hdd_reset_sta_info_during_reattach(sta_info); |
| /* Add one extra ref for reattach */ |
| hdd_take_sta_info_ref(sta_info_container, sta_info, false, |
| STA_INFO_ATTACH_DETACH); |
| qdf_mem_copy(&sta_info->sta_mac, sta_mac, sizeof(struct qdf_mac_addr)); |
| sta_info->is_attached = true; |
| qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS hdd_softap_init_tx_rx_sta(struct hdd_adapter *adapter, |
| struct qdf_mac_addr *sta_mac) |
| { |
| struct hdd_station_info *sta_info; |
| QDF_STATUS status; |
| |
| sta_info = hdd_get_sta_info_by_mac(&adapter->sta_info_list, |
| sta_mac->bytes, |
| STA_INFO_SOFTAP_INIT_TX_RX_STA); |
| |
| if (sta_info) { |
| hdd_err("Reinit of in use station " QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(sta_mac->bytes)); |
| status = hdd_sta_info_re_attach(&adapter->sta_info_list, |
| sta_info, sta_mac); |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, |
| STA_INFO_SOFTAP_INIT_TX_RX_STA); |
| return status; |
| } |
| |
| sta_info = qdf_mem_malloc(sizeof(struct hdd_station_info)); |
| if (!sta_info) |
| return QDF_STATUS_E_NOMEM; |
| |
| sta_info->is_deauth_in_progress = false; |
| qdf_mem_copy(&sta_info->sta_mac, sta_mac, sizeof(struct qdf_mac_addr)); |
| |
| status = hdd_sta_info_attach(&adapter->sta_info_list, sta_info); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| hdd_err("Failed to attach station: " QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(sta_mac->bytes)); |
| qdf_mem_free(sta_info); |
| } |
| |
| return status; |
| } |
| |
| /** |
| * hdd_softap_tsf_timestamp_rx() - time stamp Rx netbuf |
| * @context: pointer to HDD context |
| * @netbuf: pointer to a Rx netbuf |
| * |
| * Return: None |
| */ |
| #ifdef WLAN_FEATURE_TSF_PLUS |
| static inline void hdd_softap_tsf_timestamp_rx(struct hdd_context *hdd_ctx, |
| qdf_nbuf_t netbuf) |
| { |
| uint64_t target_time; |
| |
| if (!hdd_tsf_is_rx_set(hdd_ctx)) |
| return; |
| |
| target_time = ktime_to_us(netbuf->tstamp); |
| hdd_rx_timestamp(netbuf, target_time); |
| } |
| #else |
| static inline void hdd_softap_tsf_timestamp_rx(struct hdd_context *hdd_ctx, |
| qdf_nbuf_t netbuf) |
| { |
| } |
| #endif |
| |
| /** |
| * hdd_softap_notify_tx_compl_cbk() - callback to notify tx completion |
| * @skb: pointer to skb data |
| * @adapter: pointer to vdev apdapter |
| * |
| * Return: None |
| */ |
| static void hdd_softap_notify_tx_compl_cbk(struct sk_buff *skb, |
| void *context) |
| { |
| int errno; |
| struct hdd_adapter *adapter = context; |
| |
| errno = hdd_validate_adapter(adapter); |
| if (errno) |
| return; |
| |
| if (QDF_NBUF_CB_PACKET_TYPE_DHCP == QDF_NBUF_CB_GET_PACKET_TYPE(skb)) { |
| hdd_debug("sending DHCP indication"); |
| hdd_softap_notify_dhcp_ind(context, skb); |
| } else if (QDF_NBUF_CB_GET_PACKET_TYPE(skb) == |
| QDF_NBUF_CB_PACKET_TYPE_EAPOL) { |
| hdd_softap_inspect_tx_eap_pkt(adapter, skb, true); |
| } |
| } |
| |
| QDF_STATUS hdd_softap_rx_packet_cbk(void *adapter_context, qdf_nbuf_t rx_buf) |
| { |
| struct hdd_adapter *adapter = NULL; |
| QDF_STATUS qdf_status; |
| unsigned int cpu_index; |
| struct sk_buff *skb = NULL; |
| struct sk_buff *next = NULL; |
| struct hdd_context *hdd_ctx = NULL; |
| struct qdf_mac_addr *src_mac; |
| struct hdd_station_info *sta_info; |
| |
| /* Sanity check on inputs */ |
| if (unlikely((!adapter_context) || (!rx_buf))) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, |
| "%s: Null params being passed", __func__); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| adapter = (struct hdd_adapter *)adapter_context; |
| if (unlikely(WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR, |
| "Magic cookie(%x) for adapter sanity verification is invalid", |
| adapter->magic); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (unlikely(!hdd_ctx)) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, QDF_TRACE_LEVEL_ERROR, |
| "%s: HDD context is Null", __func__); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| /* walk the chain until all are processed */ |
| next = (struct sk_buff *)rx_buf; |
| |
| while (next) { |
| skb = next; |
| next = skb->next; |
| skb->next = NULL; |
| |
| /* Debug code, remove later */ |
| #if defined(QCA_WIFI_QCA6290) || defined(QCA_WIFI_QCA6390) || \ |
| defined(QCA_WIFI_QCA6490) || defined(QCA_WIFI_QCA6750) |
| QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_DEBUG, |
| "%s: skb %pK skb->len %d\n", __func__, skb, skb->len); |
| #endif |
| |
| hdd_softap_dump_sk_buff(skb); |
| |
| skb->dev = adapter->dev; |
| |
| if (unlikely(!skb->dev)) { |
| QDF_TRACE(QDF_MODULE_ID_HDD_SAP_DATA, |
| QDF_TRACE_LEVEL_ERROR, |
| "%s: ERROR!!Invalid netdevice", __func__); |
| qdf_nbuf_free(skb); |
| continue; |
| } |
| cpu_index = wlan_hdd_get_cpu(); |
| ++adapter->hdd_stats.tx_rx_stats.rx_packets[cpu_index]; |
| ++adapter->stats.rx_packets; |
| adapter->stats.rx_bytes += skb->len; |
| |
| /* Send DHCP Indication to FW */ |
| src_mac = (struct qdf_mac_addr *)(skb->data + |
| QDF_NBUF_SRC_MAC_OFFSET); |
| sta_info = hdd_get_sta_info_by_mac( |
| &adapter->sta_info_list, |
| (uint8_t *)src_mac, |
| STA_INFO_SOFTAP_RX_PACKET_CBK); |
| |
| if (sta_info) { |
| sta_info->rx_packets++; |
| sta_info->rx_bytes += skb->len; |
| sta_info->last_tx_rx_ts = qdf_system_ticks(); |
| hdd_softap_inspect_dhcp_packet(adapter, skb, QDF_RX); |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, |
| true, |
| STA_INFO_SOFTAP_RX_PACKET_CBK); |
| } |
| |
| hdd_event_eapol_log(skb, QDF_RX); |
| qdf_dp_trace_log_pkt(adapter->vdev_id, |
| skb, QDF_RX, QDF_TRACE_DEFAULT_PDEV_ID); |
| DPTRACE(qdf_dp_trace(skb, |
| QDF_DP_TRACE_RX_HDD_PACKET_PTR_RECORD, |
| QDF_TRACE_DEFAULT_PDEV_ID, |
| qdf_nbuf_data_addr(skb), |
| sizeof(qdf_nbuf_data(skb)), QDF_RX)); |
| DPTRACE(qdf_dp_trace_data_pkt(skb, QDF_TRACE_DEFAULT_PDEV_ID, |
| QDF_DP_TRACE_RX_PACKET_RECORD, 0, QDF_RX)); |
| |
| skb->protocol = eth_type_trans(skb, skb->dev); |
| |
| /* hold configurable wakelock for unicast traffic */ |
| if (!hdd_is_current_high_throughput(hdd_ctx) && |
| hdd_ctx->config->rx_wakelock_timeout && |
| skb->pkt_type != PACKET_BROADCAST && |
| skb->pkt_type != PACKET_MULTICAST) { |
| cds_host_diag_log_work(&hdd_ctx->rx_wake_lock, |
| hdd_ctx->config->rx_wakelock_timeout, |
| WIFI_POWER_EVENT_WAKELOCK_HOLD_RX); |
| qdf_wake_lock_timeout_acquire(&hdd_ctx->rx_wake_lock, |
| hdd_ctx->config-> |
| rx_wakelock_timeout); |
| } |
| |
| /* Remove SKB from internal tracking table before submitting |
| * it to stack |
| */ |
| qdf_net_buf_debug_release_skb(skb); |
| |
| hdd_softap_tsf_timestamp_rx(hdd_ctx, skb); |
| |
| qdf_status = hdd_rx_deliver_to_stack(adapter, skb); |
| |
| if (QDF_IS_STATUS_SUCCESS(qdf_status)) |
| ++adapter->hdd_stats.tx_rx_stats.rx_delivered[cpu_index]; |
| else |
| ++adapter->hdd_stats.tx_rx_stats.rx_refused[cpu_index]; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS hdd_softap_deregister_sta(struct hdd_adapter *adapter, |
| struct hdd_station_info **sta_info) |
| { |
| struct hdd_context *hdd_ctx; |
| struct qdf_mac_addr *mac_addr; |
| struct hdd_station_info *sta = *sta_info; |
| |
| if (!adapter) { |
| hdd_err("NULL adapter"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { |
| hdd_err("Invalid adapter magic"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (!sta) { |
| hdd_err("Invalid station"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| |
| if (!hdd_ctx) { |
| hdd_err("HDD context is null"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| /* |
| * If the address is a broadcast address then the CDP layers expects |
| * the self mac address of the adapter. |
| */ |
| if (QDF_IS_ADDR_BROADCAST(sta->sta_mac.bytes)) |
| mac_addr = &adapter->mac_addr; |
| else |
| mac_addr = &sta->sta_mac; |
| |
| if (ucfg_ipa_is_enabled()) { |
| if (ucfg_ipa_wlan_evt(hdd_ctx->pdev, adapter->dev, |
| adapter->device_mode, |
| adapter->vdev_id, |
| WLAN_IPA_CLIENT_DISCONNECT, |
| mac_addr->bytes) != QDF_STATUS_SUCCESS) |
| hdd_debug("WLAN_CLIENT_DISCONNECT event failed"); |
| } |
| hdd_sta_info_detach(&adapter->sta_info_list, &sta); |
| |
| ucfg_mlme_update_oce_flags(hdd_ctx->pdev); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS hdd_softap_register_sta(struct hdd_adapter *adapter, |
| bool auth_required, |
| bool privacy_required, |
| struct qdf_mac_addr *sta_mac, |
| bool wmm_enabled) |
| { |
| QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; |
| struct ol_txrx_desc_type txrx_desc = {0}; |
| struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| struct ol_txrx_ops txrx_ops; |
| void *soc = cds_get_context(QDF_MODULE_ID_SOC); |
| struct hdd_ap_ctx *ap_ctx; |
| struct hdd_station_info *sta_info; |
| |
| ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| |
| /* |
| * If the address is a broadcast address, then provide the self mac addr |
| * to the data path. Else provide the mac address of the connected peer. |
| */ |
| if (qdf_is_macaddr_broadcast(sta_mac) && ap_ctx) |
| qdf_mem_copy(&txrx_desc.peer_addr, &adapter->mac_addr, |
| QDF_MAC_ADDR_SIZE); |
| else |
| qdf_mem_copy(&txrx_desc.peer_addr, sta_mac, |
| QDF_MAC_ADDR_SIZE); |
| |
| qdf_status = hdd_softap_init_tx_rx_sta(adapter, sta_mac); |
| sta_info = hdd_get_sta_info_by_mac(&adapter->sta_info_list, |
| sta_mac->bytes, |
| STA_INFO_SOFTAP_REGISTER_STA); |
| |
| if (!sta_info) { |
| hdd_debug("STA not found"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| txrx_desc.is_qos_enabled = wmm_enabled; |
| |
| /* Register the vdev transmit and receive functions */ |
| qdf_mem_zero(&txrx_ops, sizeof(txrx_ops)); |
| |
| txrx_ops.tx.tx_comp = hdd_softap_notify_tx_compl_cbk; |
| |
| if (adapter->hdd_ctx->enable_dp_rx_threads) { |
| txrx_ops.rx.rx = hdd_rx_pkt_thread_enqueue_cbk; |
| txrx_ops.rx.rx_stack = hdd_softap_rx_packet_cbk; |
| txrx_ops.rx.rx_flush = hdd_rx_flush_packet_cbk; |
| txrx_ops.rx.rx_gro_flush = hdd_rx_thread_gro_flush_ind_cbk; |
| } else { |
| txrx_ops.rx.rx = hdd_softap_rx_packet_cbk; |
| txrx_ops.rx.rx_stack = NULL; |
| txrx_ops.rx.rx_flush = NULL; |
| } |
| |
| cdp_vdev_register(soc, |
| adapter->vdev_id, |
| (ol_osif_vdev_handle)adapter, |
| &txrx_ops); |
| adapter->tx_fn = txrx_ops.tx.tx; |
| |
| qdf_status = cdp_peer_register(soc, OL_TXRX_PDEV_ID, &txrx_desc); |
| if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { |
| hdd_debug("cdp_peer_register() failed to register. Status = %d [0x%08X]", |
| qdf_status, qdf_status); |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, |
| STA_INFO_SOFTAP_REGISTER_STA); |
| return qdf_status; |
| } |
| |
| /* if ( WPA ), tell TL to go to 'connected' and after keys come to the |
| * driver then go to 'authenticated'. For all other authentication |
| * types (those that do not require upper layer authentication) we can |
| * put TL directly into 'authenticated' state |
| */ |
| |
| sta_info->is_qos_enabled = wmm_enabled; |
| |
| if (!auth_required) { |
| hdd_debug("open/shared auth STA MAC= " QDF_MAC_ADDR_FMT |
| ". Changing TL state to AUTHENTICATED at Join time", |
| QDF_MAC_ADDR_REF(sta_info->sta_mac.bytes)); |
| |
| /* Connections that do not need Upper layer auth, |
| * transition TL directly to 'Authenticated' state. |
| */ |
| qdf_status = hdd_change_peer_state(adapter, |
| txrx_desc.peer_addr.bytes, |
| OL_TXRX_PEER_STATE_AUTH, |
| false); |
| |
| sta_info->peer_state = OL_TXRX_PEER_STATE_AUTH; |
| if (!qdf_is_macaddr_broadcast(sta_mac)) |
| qdf_status = wlan_hdd_send_sta_authorized_event( |
| adapter, hdd_ctx, |
| sta_mac); |
| } else { |
| |
| hdd_debug("ULA auth STA MAC = " QDF_MAC_ADDR_FMT |
| ". Changing TL state to CONNECTED at Join time", |
| QDF_MAC_ADDR_REF(sta_info->sta_mac.bytes)); |
| |
| qdf_status = hdd_change_peer_state(adapter, |
| txrx_desc.peer_addr.bytes, |
| OL_TXRX_PEER_STATE_CONN, |
| false); |
| |
| sta_info->peer_state = OL_TXRX_PEER_STATE_CONN; |
| } |
| |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, |
| STA_INFO_SOFTAP_REGISTER_STA); |
| hdd_debug("Enabling queues"); |
| wlan_hdd_netif_queue_control(adapter, |
| WLAN_START_ALL_NETIF_QUEUE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| ucfg_mlme_update_oce_flags(hdd_ctx->pdev); |
| return qdf_status; |
| } |
| |
| /** |
| * hdd_softap_register_bc_sta() - Register the SoftAP broadcast STA |
| * @adapter: pointer to adapter context |
| * @privacy_required: should 802.11 privacy bit be set? |
| * |
| * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error |
| */ |
| QDF_STATUS hdd_softap_register_bc_sta(struct hdd_adapter *adapter, |
| bool privacy_required) |
| { |
| QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; |
| struct qdf_mac_addr broadcast_macaddr = QDF_MAC_ADDR_BCAST_INIT; |
| struct hdd_ap_ctx *ap_ctx; |
| uint8_t sta_id; |
| ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| sta_id = ap_ctx->broadcast_sta_id; |
| |
| if (sta_id >= WLAN_MAX_STA_COUNT) { |
| hdd_err("Error: Invalid sta_id: %u", sta_id); |
| return qdf_status; |
| } |
| |
| qdf_status = hdd_softap_register_sta(adapter, false, |
| privacy_required, |
| &broadcast_macaddr, 0); |
| |
| return qdf_status; |
| } |
| |
| QDF_STATUS hdd_softap_stop_bss(struct hdd_adapter *adapter) |
| { |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| uint8_t indoor_chnl_marking = 0; |
| struct hdd_context *hdd_ctx; |
| struct hdd_ap_ctx *ap_ctx; |
| struct hdd_station_info *sta_info, *tmp = NULL; |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| |
| status = ucfg_policy_mgr_get_indoor_chnl_marking(hdd_ctx->psoc, |
| &indoor_chnl_marking); |
| if (QDF_STATUS_SUCCESS != status) |
| hdd_err("can't get indoor channel marking, using default"); |
| /* This is stop bss callback running in scheduler thread so do not |
| * driver unload in progress check otherwise it can lead to peer |
| * object leak |
| */ |
| |
| hdd_for_each_sta_ref_safe(adapter->sta_info_list, sta_info, tmp, |
| STA_INFO_SOFTAP_STOP_BSS) { |
| status = hdd_softap_deregister_sta(adapter, &sta_info); |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, |
| STA_INFO_SOFTAP_STOP_BSS); |
| } |
| |
| if (adapter->device_mode == QDF_SAP_MODE && |
| !hdd_ctx->config->disable_channel) |
| wlan_hdd_restore_channels(hdd_ctx); |
| |
| /* Mark the indoor channel (passive) to enable */ |
| if (indoor_chnl_marking && adapter->device_mode == QDF_SAP_MODE) { |
| hdd_update_indoor_channel(hdd_ctx, false); |
| sme_update_channel_list(hdd_ctx->mac_handle); |
| } |
| |
| if (ucfg_ipa_is_enabled()) { |
| if (ucfg_ipa_wlan_evt(hdd_ctx->pdev, |
| adapter->dev, |
| adapter->device_mode, |
| adapter->vdev_id, |
| WLAN_IPA_AP_DISCONNECT, |
| adapter->dev->dev_addr) != |
| QDF_STATUS_SUCCESS) |
| hdd_err("WLAN_AP_DISCONNECT event failed"); |
| } |
| |
| return status; |
| } |
| |
| QDF_STATUS hdd_softap_change_sta_state(struct hdd_adapter *adapter, |
| struct qdf_mac_addr *sta_mac, |
| enum ol_txrx_peer_state state) |
| { |
| QDF_STATUS qdf_status; |
| struct hdd_station_info *sta_info; |
| struct qdf_mac_addr mac_addr; |
| |
| hdd_enter_dev(adapter->dev); |
| |
| sta_info = hdd_get_sta_info_by_mac(&adapter->sta_info_list, |
| sta_mac->bytes, |
| STA_INFO_SOFTAP_CHANGE_STA_STATE); |
| |
| if (!sta_info) { |
| hdd_debug("Failed to find right station MAC: " QDF_MAC_ADDR_FMT, |
| QDF_MAC_ADDR_REF(sta_mac->bytes)); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (qdf_is_macaddr_broadcast(&sta_info->sta_mac)) |
| qdf_mem_copy(&mac_addr, &adapter->mac_addr, QDF_MAC_ADDR_SIZE); |
| else |
| qdf_mem_copy(&mac_addr, sta_mac, QDF_MAC_ADDR_SIZE); |
| |
| qdf_status = |
| hdd_change_peer_state(adapter, mac_addr.bytes, |
| state, false); |
| hdd_debug("Station " QDF_MAC_ADDR_FMT " changed to state %d", |
| QDF_MAC_ADDR_REF(mac_addr.bytes), state); |
| |
| if (QDF_STATUS_SUCCESS == qdf_status) { |
| sta_info->peer_state = OL_TXRX_PEER_STATE_AUTH; |
| p2p_peer_authorized(adapter->vdev, sta_mac->bytes); |
| } |
| |
| hdd_put_sta_info_ref(&adapter->sta_info_list, &sta_info, true, |
| STA_INFO_SOFTAP_CHANGE_STA_STATE); |
| hdd_exit(); |
| return qdf_status; |
| } |
| |