| /* |
| * 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_hostapd.c |
| \brief WLAN Host Device Driver implementation |
| |
| ========================================================================*/ |
| /**========================================================================= |
| EDIT HISTORY FOR FILE |
| |
| |
| This section contains comments describing changes made to the module. |
| Notice that changes are listed in reverse chronological order. |
| |
| $Header:$ $DateTime: $ $Author: $ |
| |
| |
| when who what, where, why |
| -------- --- -------------------------------------------------------- |
| 04/5/09 Shailender Created module. |
| 06/03/10 js - Added support to hostapd driven deauth/disassoc/mic failure |
| ==========================================================================*/ |
| /*-------------------------------------------------------------------------- |
| Include Files |
| ------------------------------------------------------------------------*/ |
| |
| #include <linux/version.h> |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/wireless.h> |
| #include <linux/semaphore.h> |
| #include <linux/compat.h> |
| #include <vos_api.h> |
| #include <vos_sched.h> |
| #include <linux/etherdevice.h> |
| #include <wlan_hdd_includes.h> |
| #include <qc_sap_ioctl.h> |
| #include <wlan_hdd_hostapd.h> |
| #include <sapApi.h> |
| #include <sapInternal.h> |
| #include <wlan_qct_tl.h> |
| #include <wlan_hdd_softap_tx_rx.h> |
| #include <wlan_hdd_main.h> |
| #include <linux/netdevice.h> |
| #include <linux/mmc/sdio_func.h> |
| #include "wlan_nlink_common.h" |
| #include "wlan_hdd_p2p.h" |
| #ifdef IPA_OFFLOAD |
| #include <wlan_hdd_ipa.h> |
| #endif |
| #include "cfgApi.h" |
| #include "wni_cfg.h" |
| #include "wlan_hdd_misc.h" |
| #include <vos_utils.h> |
| #include "vos_cnss.h" |
| #include "tl_shim.h" |
| |
| #include "wma.h" |
| #ifdef WLAN_DEBUG |
| #include "wma_api.h" |
| #endif |
| extern int process_wma_set_command(int sessid, int paramid, |
| int sval, int vpdev); |
| #include "wlan_hdd_trace.h" |
| #include "vos_types.h" |
| #include "vos_trace.h" |
| #include "adf_trace.h" |
| #include "wlan_hdd_cfg.h" |
| #include <wlan_hdd_wowl.h> |
| #include "wlan_hdd_tsf.h" |
| #include "wlan_hdd_oemdata.h" |
| |
| #ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| #include <vos_utils.h> |
| #endif//#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| |
| #define IS_UP(_dev) \ |
| (((_dev)->flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP)) |
| #define IS_UP_AUTO(_ic) \ |
| (IS_UP((_ic)->ic_dev) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO) |
| #define WE_WLAN_VERSION 1 |
| #define WE_GET_STA_INFO_SIZE 30 |
| /* WEXT limitation: MAX allowed buf len for any * |
| * IW_PRIV_TYPE_CHAR is 2Kbytes * |
| */ |
| #define WE_SAP_MAX_STA_INFO 0x7FF |
| |
| #define RC_2_RATE_IDX(_rc) ((_rc) & 0x7) |
| #define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) |
| #define RC_2_RATE_IDX_11AC(_rc) ((_rc) & 0xf) |
| #define HT_RC_2_STREAMS_11AC(_rc) ((((_rc) & 0x30) >> 4) + 1) |
| |
| #define SAP_24GHZ_CH_COUNT (14) |
| #define ACS_SCAN_EXPIRY_TIMEOUT_S 4 |
| |
| /* EID byte + length byte + four byte WiFi OUI */ |
| #define DOT11F_EID_HEADER_LEN (6) |
| |
| /* Data rate 100KBPS based on IE Index */ |
| struct index_data_rate_type { |
| uint8_t beacon_rate_index; |
| uint16_t supported_rate[4]; |
| }; |
| |
| /* |
| * 11B, 11G Rate table include Basic rate and Extended rate |
| * The IDX field is the rate index |
| * The HI field is the rate when RSSI is strong or being ignored |
| * (in this case we report actual rate) |
| * The MID field is the rate when RSSI is moderate |
| * (in this case we cap 11b rates at 5.5 and 11g rates at 24) |
| * The LO field is the rate when RSSI is low |
| * (in this case we don't report rates, actual current rate used) |
| */ |
| static const struct index_data_rate_type supported_data_rate[] = { |
| /* IDX HI HM LM LO (RSSI-based index */ |
| {2, { 10, 10, 10, 0} }, |
| {4, { 20, 20, 10, 0} }, |
| {11, { 55, 20, 10, 0} }, |
| {12, { 60, 55, 20, 0} }, |
| {18, { 90, 55, 20, 0} }, |
| {22, {110, 55, 20, 0} }, |
| {24, {120, 90, 60, 0} }, |
| {36, {180, 120, 60, 0} }, |
| {44, {220, 180, 60, 0} }, |
| {48, {240, 180, 90, 0} }, |
| {66, {330, 180, 90, 0} }, |
| {72, {360, 240, 90, 0} }, |
| {96, {480, 240, 120, 0} }, |
| {108, {540, 240, 120, 0} } |
| }; |
| |
| /* MCS Based rate table */ |
| /* HT MCS parameters with Nss = 1 */ |
| static struct index_data_rate_type supported_mcs_rate_nss1[] = { |
| /* MCS L20 L40 S20 S40 */ |
| {0, { 65, 135, 72, 150} }, |
| {1, { 130, 270, 144, 300} }, |
| {2, { 195, 405, 217, 450} }, |
| {3, { 260, 540, 289, 600} }, |
| {4, { 390, 810, 433, 900} }, |
| {5, { 520, 1080, 578, 1200} }, |
| {6, { 585, 1215, 650, 1350} }, |
| {7, { 650, 1350, 722, 1500} } |
| }; |
| |
| /* HT MCS parameters with Nss = 2 */ |
| static struct index_data_rate_type supported_mcs_rate_nss2[] = { |
| /* MCS L20 L40 S20 S40 */ |
| {0, {130, 270, 144, 300} }, |
| {1, {260, 540, 289, 600} }, |
| {2, {390, 810, 433, 900} }, |
| {3, {520, 1080, 578, 1200} }, |
| {4, {780, 1620, 867, 1800} }, |
| {5, {1040, 2160, 1156, 2400} }, |
| {6, {1170, 2430, 1300, 2700} }, |
| {7, {1300, 2700, 1444, 3000} } |
| }; |
| |
| #ifdef WLAN_FEATURE_11AC |
| #define DATA_RATE_11AC_MCS_MASK 0x03 |
| |
| enum data_rate_11ac_max_mcs { |
| DATA_RATE_11AC_MAX_MCS_7, |
| DATA_RATE_11AC_MAX_MCS_8, |
| DATA_RATE_11AC_MAX_MCS_9, |
| DATA_RATE_11AC_MAX_MCS_NA |
| }; |
| |
| struct index_vht_data_rate_type { |
| uint8_t beacon_rate_index; |
| uint16_t supported_VHT80_rate[2]; |
| uint16_t supported_VHT40_rate[2]; |
| uint16_t supported_VHT20_rate[2]; |
| }; |
| |
| /* MCS Based VHT rate table */ |
| /* MCS parameters with Nss = 1*/ |
| static struct index_vht_data_rate_type supported_vht_mcs_rate_nss1[] = { |
| /* MCS L80 S80 L40 S40 L20 S40*/ |
| {0, {293, 325}, {135, 150}, {65, 72} }, |
| {1, {585, 650}, {270, 300}, {130, 144} }, |
| {2, {878, 975}, {405, 450}, {195, 217} }, |
| {3, {1170, 1300}, {540, 600}, {260, 289} }, |
| {4, {1755, 1950}, {810, 900}, {390, 433} }, |
| {5, {2340, 2600}, {1080, 1200}, {520, 578} }, |
| {6, {2633, 2925}, {1215, 1350}, {585, 650} }, |
| {7, {2925, 3250}, {1350, 1500}, {650, 722} }, |
| {8, {3510, 3900}, {1620, 1800}, {780, 867} }, |
| {9, {3900, 4333}, {1800, 2000}, {780, 867} } |
| }; |
| |
| /*MCS parameters with Nss = 2*/ |
| static struct index_vht_data_rate_type supported_vht_mcs_rate_nss2[] = { |
| /* MCS L80 S80 L40 S40 L20 S40*/ |
| {0, {585, 650}, {270, 300}, {130, 144} }, |
| {1, {1170, 1300}, {540, 600}, {260, 289} }, |
| {2, {1755, 1950}, {810, 900}, {390, 433} }, |
| {3, {2340, 2600}, {1080, 1200}, {520, 578} }, |
| {4, {3510, 3900}, {1620, 1800}, {780, 867} }, |
| {5, {4680, 5200}, {2160, 2400}, {1040, 1156} }, |
| {6, {5265, 5850}, {2430, 2700}, {1170, 1300} }, |
| {7, {5850, 6500}, {2700, 3000}, {1300, 1444} }, |
| {8, {7020, 7800}, {3240, 3600}, {1560, 1733} }, |
| {9, {7800, 8667}, {3600, 4000}, {1560, 1733} } |
| }; |
| #endif /* WLAN_FEATURE_11AC */ |
| |
| /*--------------------------------------------------------------------------- |
| * Function definitions |
| *-------------------------------------------------------------------------*/ |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_hostapd_channel_wakelock_init |
| |
| \param - Pointer to HDD context |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| void hdd_hostapd_channel_wakelock_init(hdd_context_t *pHddCtx) |
| { |
| /* Initialize the wakelock */ |
| vos_wake_lock_init(&pHddCtx->sap_dfs_wakelock, "sap_dfs_wakelock"); |
| atomic_set(&pHddCtx->sap_dfs_ref_cnt, 0); |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_hostapd_channel_allow_suspend - Allow suspend in a channel. |
| |
| Called when, |
| 1. BSS stopped |
| 2. Channel switch |
| |
| \param - pAdapter, channel |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| void hdd_hostapd_channel_allow_suspend(hdd_adapter_t *pAdapter, |
| u_int8_t channel) |
| { |
| |
| hdd_context_t *pHddCtx = (hdd_context_t*)(pAdapter->pHddCtx); |
| hdd_hostapd_state_t *pHostapdState = |
| WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter); |
| |
| hddLog(LOG1, FL("bssState: %d, channel: %d, dfs_ref_cnt: %d"), |
| pHostapdState->bssState, channel, |
| atomic_read(&pHddCtx->sap_dfs_ref_cnt)); |
| |
| /* Return if BSS is already stopped */ |
| if (pHostapdState->bssState == BSS_STOP) |
| return; |
| |
| /* Release wakelock when no more DFS channels are used */ |
| if (NV_CHANNEL_DFS == vos_nv_getChannelEnabledState(channel)) { |
| if (atomic_dec_and_test(&pHddCtx->sap_dfs_ref_cnt)) { |
| hddLog(LOGE, FL("DFS: allowing suspend (chan %d)"), channel); |
| vos_wake_lock_release(&pHddCtx->sap_dfs_wakelock, |
| WIFI_POWER_EVENT_WAKELOCK_DFS); |
| vos_runtime_pm_allow_suspend(pHddCtx->runtime_context.dfs); |
| } |
| } |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_hostapd_channel_prevent_suspend - Prevent suspend in a channel. |
| |
| Called when, |
| 1. BSS started |
| 2. Channel switch |
| |
| \param - pAdapter, channel |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| void hdd_hostapd_channel_prevent_suspend(hdd_adapter_t *pAdapter, |
| u_int8_t channel) |
| { |
| hdd_context_t *pHddCtx = (hdd_context_t*)(pAdapter->pHddCtx); |
| hdd_hostapd_state_t *pHostapdState = |
| WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter); |
| |
| hddLog(LOG1, FL("bssState: %d, channel: %d, dfs_ref_cnt: %d"), |
| pHostapdState->bssState, channel, |
| atomic_read(&pHddCtx->sap_dfs_ref_cnt)); |
| |
| /* Return if BSS is already started && wakelock is acquired */ |
| if ((pHostapdState->bssState == BSS_START) && |
| (atomic_read(&pHddCtx->sap_dfs_ref_cnt) >= 1)) |
| return; |
| |
| /* Acquire wakelock if we have at least one DFS channel in use */ |
| if (NV_CHANNEL_DFS == vos_nv_getChannelEnabledState(channel)) { |
| if (atomic_inc_return(&pHddCtx->sap_dfs_ref_cnt) == 1) { |
| hddLog(LOGE, FL("DFS: preventing suspend (chan %d)"), channel); |
| vos_runtime_pm_prevent_suspend(pHddCtx->runtime_context.dfs); |
| vos_wake_lock_acquire(&pHddCtx->sap_dfs_wakelock, |
| WIFI_POWER_EVENT_WAKELOCK_DFS); |
| } |
| } |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_hostapd_channel_wakelock_deinit |
| |
| \param - Pointer to HDD context |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| void hdd_hostapd_channel_wakelock_deinit(hdd_context_t *pHddCtx) |
| { |
| if (atomic_read(&pHddCtx->sap_dfs_ref_cnt)) { |
| /* Release wakelock */ |
| vos_wake_lock_release(&pHddCtx->sap_dfs_wakelock, |
| WIFI_POWER_EVENT_WAKELOCK_DRIVER_EXIT); |
| /* Reset the reference count */ |
| atomic_set(&pHddCtx->sap_dfs_ref_cnt, 0); |
| hddLog(LOGE, FL("DFS: allowing suspend")); |
| } |
| |
| /* Destroy lock */ |
| vos_wake_lock_destroy(&pHddCtx->sap_dfs_wakelock); |
| } |
| |
| #ifdef FEATURE_WLAN_SUB_20_MHZ |
| /** |
| * hdd_hostapd_sub20_channelwidth_can_switch() - check |
| * channel width switch to 5/10M condition |
| * @adapter: pointer to HDD context |
| * @sub20_channel_width: 5MHz/10MHz channel width |
| * |
| * Return: true or false |
| */ |
| bool hdd_hostapd_sub20_channelwidth_can_switch( |
| hdd_adapter_t *adapter, uint32_t *sub20_channel_width) |
| { |
| int i; |
| int sta_count = 0; |
| uint8_t sap_s20_caps; |
| uint8_t sap_s20_config; |
| uint8_t sta_s20_caps = SUB20_MODE_NONE; |
| tHalHandle hal_ptr = WLAN_HDD_GET_HAL_CTX(adapter); |
| tSmeConfigParams *sme_config; |
| hdd_station_info_t *sta; |
| hdd_ap_ctx_t *ap = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| |
| sme_config = vos_mem_malloc(sizeof(*sme_config)); |
| if (!sme_config) { |
| hddLog(LOGE, FL("mem alloc failed for sme_config")); |
| return false; |
| } |
| vos_mem_zero(sme_config, sizeof(*sme_config)); |
| |
| sme_GetConfigParam(hal_ptr, sme_config); |
| sap_s20_caps = sme_config->sub20_dynamic_channelwidth; |
| sap_s20_config = sme_config->sub20_config_info; |
| vos_mem_free(sme_config); |
| if (sap_s20_caps == SUB20_MODE_NONE || |
| sap_s20_config == CFG_SUB_20_CHANNEL_WIDTH_MANUAL) { |
| hddLog(LOGE, FL("sub20 not switch")); |
| return false; |
| } |
| |
| spin_lock_bh(&adapter->staInfo_lock); |
| for (i = 0; i < WLAN_MAX_STA_COUNT; i++) { |
| sta = &adapter->aStaInfo[i]; |
| if (sta->isUsed && (ap->uBCStaId != i)) { |
| sta_count++; |
| sta_s20_caps |= |
| sta->sub20_dynamic_channelwidth; |
| } |
| } |
| spin_unlock_bh(&adapter->staInfo_lock); |
| |
| if (sta_count != 1) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%d STAs connected with sub20 Channelwidth %d", |
| sta_count, sta_s20_caps); |
| return false; |
| } |
| |
| *sub20_channel_width = sta_s20_caps & sap_s20_caps; |
| |
| if (*sub20_channel_width == (SUB20_MODE_5MHZ | SUB20_MODE_10MHZ)) |
| *sub20_channel_width = SUB20_MODE_10MHZ; |
| |
| if (*sub20_channel_width != 0) |
| return true; |
| else |
| return false; |
| } |
| |
| /** |
| * hdd_hostapd_sub20_channelwidth_can_restore() - check |
| * channel width switch to normal condition |
| * @adapter: pointer to HDD context |
| * |
| * Return: true or false |
| */ |
| bool hdd_hostapd_sub20_channelwidth_can_restore( |
| hdd_adapter_t *adapter) |
| { |
| int i; |
| int sta_count = 0; |
| uint8_t sap_s20_caps; |
| uint8_t sta_s20_caps = SUB20_MODE_NONE; |
| tHalHandle hal_ptr = WLAN_HDD_GET_HAL_CTX(adapter); |
| tSmeConfigParams *sme_config; |
| hdd_station_info_t *sta; |
| hdd_ap_ctx_t *ap = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| |
| sme_config = vos_mem_malloc(sizeof(*sme_config)); |
| if (!sme_config) { |
| hddLog(LOGE, FL("mem alloc failed for sme_config")); |
| return false; |
| } |
| vos_mem_zero(sme_config, sizeof(*sme_config)); |
| sme_GetConfigParam(hal_ptr, sme_config); |
| |
| sap_s20_caps = sme_config->sub20_dynamic_channelwidth; |
| vos_mem_free(sme_config); |
| if (sap_s20_caps == SUB20_MODE_NONE) { |
| hddLog(LOGE, FL("sub20 none")); |
| return false; |
| } |
| spin_lock_bh(&adapter->staInfo_lock); |
| for (i = 0; i < WLAN_MAX_STA_COUNT; i++) { |
| sta = &adapter->aStaInfo[i]; |
| if (sta->isUsed && (ap->uBCStaId != i)) { |
| sta_count++; |
| sta_s20_caps |= |
| sta->sub20_dynamic_channelwidth; |
| } |
| } |
| spin_unlock_bh(&adapter->staInfo_lock); |
| |
| if (sta_count != 0) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%d STAs connected with sub20 Channelwidth %d", |
| sta_count, sta_s20_caps); |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| /** |
| * hdd_sub20_channelwidth_can_set() - check |
| * channel width manual switch to 5/10M condition |
| * @adapter: pointer to HDD context |
| * @sub20_channel_width: new channel width |
| * |
| * Return: true or false |
| */ |
| bool hdd_sub20_channelwidth_can_set( |
| hdd_adapter_t *adapter, uint32_t sub20_channel_width) |
| { |
| int i; |
| uint32_t sta_count = 0; |
| uint8_t sap_s20_config; |
| uint8_t sta_s20_caps = SUB20_MODE_10MHZ|SUB20_MODE_5MHZ; |
| tHalHandle hal_ptr; |
| tSmeConfigParams *sme_config; |
| hdd_station_info_t *sta; |
| hdd_ap_ctx_t *ap; |
| bool channel_support_sub20 = true; |
| enum phy_ch_width phy_sub20_channel_width = CH_WIDTH_INVALID; |
| hdd_station_ctx_t *hddstactx; |
| |
| if (adapter == NULL) { |
| hddLog(LOGE, FL("adapter NULL")); |
| return false; |
| } |
| hal_ptr = WLAN_HDD_GET_HAL_CTX(adapter); |
| ap = WLAN_HDD_GET_AP_CTX_PTR(adapter); |
| |
| sme_config = vos_mem_malloc(sizeof(*sme_config)); |
| if (!sme_config) { |
| hddLog(LOGE, FL("mem alloc failed for sme_config")); |
| return false; |
| } |
| vos_mem_zero(sme_config, sizeof(*sme_config)); |
| |
| sme_GetConfigParam(hal_ptr, sme_config); |
| sap_s20_config = sme_config->sub20_config_info; |
| vos_mem_free(sme_config); |
| sme_config = NULL; |
| if (sap_s20_config != CFG_SUB_20_CHANNEL_WIDTH_MANUAL) { |
| hddLog(LOGE, FL("ini unsupport manual set sub20")); |
| return false; |
| } |
| |
| switch (sub20_channel_width) { |
| case SUB20_MODE_5MHZ: |
| phy_sub20_channel_width = CH_WIDTH_5MHZ; |
| break; |
| case SUB20_MODE_10MHZ: |
| phy_sub20_channel_width = CH_WIDTH_10MHZ; |
| break; |
| case SUB20_MODE_NONE: |
| if (WLAN_HDD_SOFTAP == adapter->device_mode) |
| return true; |
| break; |
| default: |
| return false; |
| } |
| |
| if (WLAN_HDD_INFRA_STATION == adapter->device_mode) { |
| hddstactx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| if (hddstactx == NULL) { |
| hddLog(LOGE, FL("hdd hddstactx is null")); |
| return false; |
| } |
| if (hdd_connIsConnected(hddstactx)) { |
| hddLog(LOGE, FL("sta in Connected state!")); |
| return false; |
| } |
| hddLog(LOGE, FL("sta can set sub20")); |
| return true; |
| } |
| |
| channel_support_sub20 = |
| vos_is_channel_support_sub20(ap->operatingChannel, |
| phy_sub20_channel_width, |
| 0); |
| if (!channel_support_sub20) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("ch%d width%d unsupport by reg domain"), |
| ap->operatingChannel, phy_sub20_channel_width); |
| return false; |
| } |
| |
| spin_lock_bh(&adapter->staInfo_lock); |
| for (i = 0; i < WLAN_MAX_STA_COUNT; i++) { |
| sta = &adapter->aStaInfo[i]; |
| if (sta->isUsed && (ap->uBCStaId != i)) { |
| sta_count++; |
| sta_s20_caps &= |
| sta->sub20_dynamic_channelwidth; |
| } |
| } |
| spin_unlock_bh(&adapter->staInfo_lock); |
| if (sta_count >= 1 && !(sta_s20_caps & sub20_channel_width)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%d STAs connected with sub20 Channelwidth %d", |
| sta_count, sta_s20_caps); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| #endif |
| |
| /** |
| * __hdd_hostapd_open() - HDD Open function for hostapd interface |
| * @dev: pointer to net device |
| * |
| * This is called in response to ifconfig up |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int __hdd_hostapd_open(struct net_device *dev) |
| { |
| hdd_adapter_t *pAdapter = netdev_priv(dev); |
| |
| ENTER(); |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_HOSTAPD_OPEN_REQUEST, NO_SESSION, 0)); |
| |
| if (WLAN_HDD_GET_CTX(pAdapter)->isLoadInProgress || |
| WLAN_HDD_GET_CTX(pAdapter)->isUnloadInProgress) |
| { |
| hddLog(LOGE, FL("Driver load/unload in progress, ignore adapter open")); |
| goto done; |
| } |
| |
| //Enable all Tx queues |
| hddLog(LOG1, FL("Enabling queues")); |
| wlan_hdd_netif_queue_control(pAdapter, |
| WLAN_START_ALL_NETIF_QUEUE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| done: |
| EXIT(); |
| return 0; |
| } |
| |
| /** |
| * hdd_hostapd_open() - SSR wrapper for __hdd_hostapd_open |
| * @dev: pointer to net device |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int hdd_hostapd_open(struct net_device *dev) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __hdd_hostapd_open(dev); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * __hdd_hostapd_stop() - HDD stop function for hostapd interface |
| * @dev: pointer to net_device |
| * |
| * This is called in response to ifconfig down |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int __hdd_hostapd_stop(struct net_device *dev) |
| { |
| ENTER(); |
| |
| if (NULL != dev) { |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| //Stop all tx queues |
| hddLog(LOG1, FL("Disabling queues")); |
| wlan_hdd_netif_queue_control(adapter, WLAN_NETIF_TX_DISABLE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| } |
| |
| EXIT(); |
| return 0; |
| } |
| |
| /** |
| * hdd_hostapd_stop() - SSR wrapper for__hdd_hostapd_stop |
| * @dev: pointer to net_device |
| * |
| * This is called in response to ifconfig down |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| int hdd_hostapd_stop(struct net_device *dev) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __hdd_hostapd_stop(dev); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * __hdd_hostapd_uninit() - HDD uninit function |
| * @dev: pointer to net_device |
| * |
| * This is called during the netdev unregister to uninitialize all data |
| * associated with the device |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static void __hdd_hostapd_uninit(struct net_device *dev) |
| { |
| hdd_adapter_t *adapter = netdev_priv(dev); |
| hdd_context_t *hdd_ctx; |
| |
| ENTER(); |
| |
| if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { |
| hddLog(LOGE, FL("Invalid magic")); |
| return; |
| } |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (NULL == hdd_ctx) { |
| hddLog(LOGE, FL("NULL hdd_ctx")); |
| return; |
| } |
| |
| hdd_deinit_adapter(hdd_ctx, adapter, true); |
| |
| /* after uninit our adapter structure will no longer be valid */ |
| adapter->dev = NULL; |
| adapter->magic = 0; |
| |
| EXIT(); |
| } |
| |
| /** |
| * hdd_hostapd_uninit() - SSR wrapper for __hdd_hostapd_uninit |
| * @dev: pointer to net_device |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static void hdd_hostapd_uninit(struct net_device *dev) |
| { |
| vos_ssr_protect(__func__); |
| __hdd_hostapd_uninit(dev); |
| vos_ssr_unprotect(__func__); |
| } |
| |
| /** |
| * __hdd_hostapd_change_mtu() - change mtu |
| * @dev: pointer to net_device |
| * @new_mtu: new mtu |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int __hdd_hostapd_change_mtu(struct net_device *dev, int new_mtu) |
| { |
| return 0; |
| } |
| |
| /** |
| * hdd_hostapd_change_mtu() - SSR wrapper for __hdd_hostapd_change_mtu |
| * @dev: pointer to net_device |
| * @new_mtu: new mtu |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int hdd_hostapd_change_mtu(struct net_device *dev, int new_mtu) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __hdd_hostapd_change_mtu(dev, new_mtu); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| static int hdd_hostapd_driver_command(hdd_adapter_t *pAdapter, |
| hdd_priv_data_t *priv_data) |
| { |
| tANI_U8 *command = NULL; |
| int ret = 0; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| /* |
| * Note that valid pointers are provided by caller |
| */ |
| |
| ENTER(); |
| |
| if (priv_data->total_len <= 0 || |
| priv_data->total_len > HOSTAPD_IOCTL_COMMAND_STRLEN_MAX) |
| { |
| /* below we allocate one more byte for command buffer. |
| * To avoid addition overflow total_len should be |
| * smaller than INT_MAX. */ |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: integer out of range len %d", |
| __func__, priv_data->total_len); |
| ret = -EFAULT; |
| goto exit; |
| } |
| |
| /* Allocate +1 for '\0' */ |
| command = vos_mem_malloc((priv_data->total_len + 1)); |
| if (!command) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: failed to allocate memory", __func__); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| |
| if (copy_from_user(command, priv_data->buf, priv_data->total_len)) |
| { |
| ret = -EFAULT; |
| goto exit; |
| } |
| |
| /* Make sure the command is NUL-terminated */ |
| command[priv_data->total_len] = '\0'; |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "***HOSTAPD*** : Received %s cmd from Wi-Fi GUI***", command); |
| |
| if (strncmp(command, "P2P_SET_NOA", 11) == 0) |
| { |
| hdd_setP2pNoa(pAdapter->dev, command); |
| } |
| else if (strncmp(command, "P2P_SET_PS", 10) == 0) |
| { |
| hdd_setP2pOpps(pAdapter->dev, command); |
| } |
| else if (strncmp(command, "MIRACAST", 8) == 0) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: Received MIRACAST command", __func__); |
| ret = hdd_drv_cmd_validate(command, 8); |
| if (ret) |
| goto exit; |
| |
| ret = hdd_set_miracast_mode(pAdapter, command); |
| } |
| exit: |
| if (command) |
| { |
| vos_mem_free(command); |
| } |
| EXIT(); |
| return ret; |
| } |
| |
| #ifdef CONFIG_COMPAT |
| static int hdd_hostapd_driver_compat_ioctl(hdd_adapter_t *pAdapter, |
| struct ifreq *ifr) |
| { |
| struct { |
| compat_uptr_t buf; |
| int used_len; |
| int total_len; |
| } compat_priv_data; |
| hdd_priv_data_t priv_data; |
| int ret = 0; |
| |
| /* |
| * Note that pAdapter and ifr have already been verified by caller, |
| * and HDD context has also been validated |
| */ |
| if (copy_from_user(&compat_priv_data, ifr->ifr_data, |
| sizeof(compat_priv_data))) { |
| ret = -EFAULT; |
| goto exit; |
| } |
| priv_data.buf = compat_ptr(compat_priv_data.buf); |
| priv_data.used_len = compat_priv_data.used_len; |
| priv_data.total_len = compat_priv_data.total_len; |
| ret = hdd_hostapd_driver_command(pAdapter, &priv_data); |
| exit: |
| return ret; |
| } |
| #else /* CONFIG_COMPAT */ |
| static int hdd_hostapd_driver_compat_ioctl(hdd_adapter_t *pAdapter, |
| struct ifreq *ifr) |
| { |
| /* will never be invoked */ |
| return 0; |
| } |
| #endif /* CONFIG_COMPAT */ |
| |
| static int hdd_hostapd_driver_ioctl(hdd_adapter_t *pAdapter, struct ifreq *ifr) |
| { |
| hdd_priv_data_t priv_data; |
| int ret = 0; |
| |
| /* |
| * Note that pAdapter and ifr have already been verified by caller, |
| * and HDD context has also been validated |
| */ |
| if (copy_from_user(&priv_data, ifr->ifr_data, sizeof(priv_data))) { |
| ret = -EFAULT; |
| } else { |
| ret = hdd_hostapd_driver_command(pAdapter, &priv_data); |
| } |
| return ret; |
| } |
| |
| /** |
| * __hdd_hostapd_ioctl() - hostapd ioctl |
| * @dev: pointer to net_device |
| * @ifr: pointer to ifreq structure |
| * @cmd: command |
| * |
| * Return; 0 on success, error number otherwise |
| */ |
| static int __hdd_hostapd_ioctl(struct net_device *dev, |
| struct ifreq *ifr, int cmd) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx; |
| int ret; |
| |
| ENTER(); |
| |
| if (dev != pAdapter->dev) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: HDD adapter/dev inconsistency", __func__); |
| ret = -ENODEV; |
| goto exit; |
| } |
| |
| if ((!ifr) || (!ifr->ifr_data)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| FL("ifr or ifr->ifr_data is NULL")); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| ret = wlan_hdd_validate_context(pHddCtx); |
| if (ret) { |
| ret = -EBUSY; |
| goto exit; |
| } |
| |
| switch (cmd) { |
| case (SIOCDEVPRIVATE + 1): |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0)) && defined(CONFIG_X86_64) |
| if (in_compat_syscall()) |
| #else |
| if (is_compat_task()) |
| #endif |
| ret = hdd_hostapd_driver_compat_ioctl(pAdapter, ifr); |
| else |
| ret = hdd_hostapd_driver_ioctl(pAdapter, ifr); |
| break; |
| default: |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: unknown ioctl %d", |
| __func__, cmd); |
| ret = -EINVAL; |
| break; |
| } |
| exit: |
| EXIT(); |
| return ret; |
| } |
| |
| /** |
| * hdd_hostapd_ioctl() - SSR wrapper for __hdd_hostapd_ioctl |
| * @dev: pointer to net_device |
| * @ifr: pointer to ifreq structure |
| * @cmd: command |
| * |
| * Return; 0 on success, error number otherwise |
| */ |
| static int hdd_hostapd_ioctl(struct net_device *dev, |
| struct ifreq *ifr, int cmd) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __hdd_hostapd_ioctl(dev, ifr, cmd); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| |
| #ifdef QCA_HT_2040_COEX |
| VOS_STATUS hdd_set_sap_ht2040_mode(hdd_adapter_t *pHostapdAdapter, |
| tANI_U8 channel_type) |
| { |
| eHalStatus halStatus = eHAL_STATUS_FAILURE; |
| v_PVOID_t hHal = NULL; |
| |
| VOS_TRACE( VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: change HT20/40 mode", __func__); |
| |
| if (WLAN_HDD_SOFTAP == pHostapdAdapter->device_mode) { |
| hHal = WLAN_HDD_GET_HAL_CTX(pHostapdAdapter); |
| if ( NULL == hHal ) { |
| VOS_TRACE( VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR, |
| "%s: Hal ctx is null", __func__); |
| return VOS_STATUS_E_FAULT; |
| } |
| halStatus = sme_SetHT2040Mode(hHal, pHostapdAdapter->sessionId, |
| channel_type, eANI_BOOLEAN_TRUE); |
| if (halStatus == eHAL_STATUS_FAILURE ) { |
| VOS_TRACE( VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to change HT20/40 mode", __func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| } |
| return VOS_STATUS_SUCCESS; |
| } |
| #endif |
| |
| #ifdef FEATURE_WLAN_FORCE_SAP_SCC |
| /**--------------------------------------------------------------------------- |
| \brief hdd_restart_softap() - |
| Restart SAP on STA channel to support |
| STA + SAP concurrency. |
| |
| --------------------------------------------------------------------------*/ |
| void hdd_restart_softap(hdd_context_t *pHddCtx, |
| hdd_adapter_t *pHostapdAdapter) |
| { |
| tHddAvoidFreqList hdd_avoid_freq_list; |
| |
| /* generate vendor specific event */ |
| vos_mem_zero((void *)&hdd_avoid_freq_list, sizeof(tHddAvoidFreqList)); |
| hdd_avoid_freq_list.avoidFreqRange[0].startFreq = |
| vos_chan_to_freq(pHostapdAdapter->sessionCtx.ap.operatingChannel); |
| hdd_avoid_freq_list.avoidFreqRange[0].endFreq = |
| vos_chan_to_freq(pHostapdAdapter->sessionCtx.ap.operatingChannel); |
| hdd_avoid_freq_list.avoidFreqRangeCount = 1; |
| wlan_hdd_send_avoid_freq_event(pHddCtx, &hdd_avoid_freq_list); |
| } |
| #endif /* FEATURE_WLAN_FORCE_SAP_SCC */ |
| |
| /** |
| * __hdd_hostapd_set_mac_address() - set mac address |
| * @dev: pointer to net_device |
| * @addr: mac address |
| * |
| * This function sets the user specified mac address using |
| * the command ifconfig wlanX hw ether <mac address>. |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int __hdd_hostapd_set_mac_address(struct net_device *dev, void *addr) |
| { |
| struct sockaddr *psta_mac_addr = addr; |
| hdd_adapter_t *adapter; |
| hdd_context_t *hdd_ctx; |
| int ret = 0; |
| |
| ENTER(); |
| |
| adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| memcpy(dev->dev_addr, psta_mac_addr->sa_data, ETH_ALEN); |
| EXIT(); |
| return 0; |
| } |
| |
| /** |
| * hdd_hostapd_set_mac_address() - set mac address |
| * @dev: pointer to net_device |
| * @addr: mac address |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int hdd_hostapd_set_mac_address(struct net_device *dev, void *addr) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __hdd_hostapd_set_mac_address(dev, addr); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| void hdd_hostapd_inactivity_timer_cb(v_PVOID_t usrDataForCallback) |
| { |
| struct net_device *dev = (struct net_device *)usrDataForCallback; |
| v_BYTE_t we_custom_event[64]; |
| union iwreq_data wrqu; |
| #ifdef DISABLE_CONCURRENCY_AUTOSAVE |
| VOS_STATUS vos_status; |
| hdd_adapter_t *pHostapdAdapter; |
| hdd_ap_ctx_t *pHddApCtx; |
| #endif /*DISABLE_CONCURRENCY_AUTOSAVE */ |
| |
| /* event_name space-delimiter driver_module_name */ |
| /* Format of the event is "AUTO-SHUT.indication" " " "module_name" */ |
| char * autoShutEvent = "AUTO-SHUT.indication" " " KBUILD_MODNAME; |
| int event_len = strlen(autoShutEvent) + 1; /* For the NULL at the end */ |
| |
| ENTER(); |
| |
| #ifdef DISABLE_CONCURRENCY_AUTOSAVE |
| if (vos_concurrent_open_sessions_running()) |
| { |
| /* |
| This timer routine is going to be called only when AP |
| persona is up. |
| If there are concurrent sessions running we do not want |
| to shut down the Bss.Instead we run the timer again so |
| that if Autosave is enabled next time and other session |
| was down only then we bring down AP |
| */ |
| pHostapdAdapter = netdev_priv(dev); |
| if ((NULL == pHostapdAdapter) || |
| (WLAN_HDD_ADAPTER_MAGIC != pHostapdAdapter->magic)) |
| { |
| hddLog(LOGE, FL("invalid adapter: %pK"), pHostapdAdapter); |
| return; |
| } |
| pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter); |
| vos_status = vos_timer_start( |
| &pHddApCtx->hdd_ap_inactivity_timer, |
| (WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->nAPAutoShutOff |
| * 1000); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| { |
| hddLog(LOGE, FL("Failed to init AP inactivity timer")); |
| } |
| EXIT(); |
| return; |
| } |
| #endif /*DISABLE_CONCURRENCY_AUTOSAVE */ |
| memset(&we_custom_event, '\0', sizeof(we_custom_event)); |
| memcpy(&we_custom_event, autoShutEvent, event_len); |
| |
| memset(&wrqu, 0, sizeof(wrqu)); |
| wrqu.data.length = event_len; |
| |
| hddLog(LOG1, FL("Shutting down AP interface due to inactivity")); |
| wireless_send_event(dev, IWEVCUSTOM, &wrqu, (char *)we_custom_event); |
| |
| EXIT(); |
| } |
| |
| #ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| //This function runs in the timer context of hdd_ap_chan_switch_timer. |
| void hdd_hostapd_chan_switch_cb(v_PVOID_t usrDataForCallback) |
| { |
| hdd_adapter_t *pHostapdAdapter = NULL; |
| hdd_context_t *pHddCtx = NULL; |
| int ret = 0; |
| |
| ENTER(); |
| |
| if(usrDataForCallback) |
| { |
| pHostapdAdapter = (struct hdd_adapter_s *)usrDataForCallback; |
| } |
| else |
| { |
| hddLog(LOGE, FL("hdd_hostapd_chan_switch_cb NULL cb pointer!!\n")); |
| EXIT(); |
| return; |
| } |
| pHddCtx = WLAN_HDD_GET_CTX(pHostapdAdapter); |
| |
| mutex_lock(&pHddCtx->ch_switch_ctx.sap_ch_sw_lock); |
| if(pHddCtx->ch_switch_ctx.sap_chan_sw_pending) |
| { |
| vos_ssr_protect(__func__); |
| ret = hdd_softap_set_channel_change(pHostapdAdapter->dev, pHddCtx->ch_switch_ctx.def_csa_channel_on_disc); |
| vos_ssr_unprotect(__func__); |
| if (ret) |
| { |
| hddLog(LOGE, FL("hdd_softap_set_channel_change failed!!")); |
| } |
| pHddCtx->ch_switch_ctx.sap_chan_sw_pending = 0; |
| } |
| mutex_unlock(&pHddCtx->ch_switch_ctx.sap_ch_sw_lock); |
| |
| EXIT(); |
| } |
| #endif //#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| |
| static VOS_STATUS |
| hdd_change_mcc_go_beacon_interval(hdd_adapter_t *pHostapdAdapter) |
| { |
| eHalStatus halStatus = eHAL_STATUS_FAILURE; |
| v_PVOID_t hHal = NULL; |
| |
| VOS_TRACE( VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: UPDATE Beacon Params", __func__); |
| |
| if(VOS_STA_SAP_MODE == vos_get_conparam ( )){ |
| hHal = WLAN_HDD_GET_HAL_CTX(pHostapdAdapter); |
| if ( NULL == hHal ){ |
| VOS_TRACE( VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR, |
| "%s: Hal ctx is null", __func__); |
| return VOS_STATUS_E_FAULT; |
| } |
| halStatus = sme_ChangeMCCBeaconInterval(hHal, pHostapdAdapter->sessionId); |
| if(halStatus == eHAL_STATUS_FAILURE ){ |
| VOS_TRACE( VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to update Beacon Params", __func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| } |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| void hdd_clear_all_sta(hdd_adapter_t *pHostapdAdapter, v_PVOID_t usrDataForCallback) |
| { |
| v_U8_t staId = 0; |
| struct net_device *dev; |
| struct tagCsrDelStaParams delStaParams; |
| dev = (struct net_device *)usrDataForCallback; |
| |
| hddLog(LOGE, FL("Clearing all the STA entry....")); |
| for (staId = 0; staId < WLAN_MAX_STA_COUNT; staId++) |
| { |
| if ( pHostapdAdapter->aStaInfo[staId].isUsed && |
| ( staId != (WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter))->uBCStaId)) |
| { |
| WLANSAP_PopulateDelStaParams(&pHostapdAdapter->aStaInfo[staId].macAddrSTA.bytes[0], |
| eSIR_MAC_DEAUTH_LEAVING_BSS_REASON, |
| (SIR_MAC_MGMT_DISASSOC >> 4), |
| &delStaParams); |
| //Disconnect all the stations |
| hdd_softap_sta_disassoc(pHostapdAdapter, &delStaParams); |
| } |
| } |
| } |
| |
| static int hdd_stop_bss_link(hdd_adapter_t *pHostapdAdapter, |
| v_PVOID_t usrDataForCallback) |
| { |
| struct net_device *dev; |
| hdd_context_t *pHddCtx = NULL; |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| dev = (struct net_device *)usrDataForCallback; |
| |
| ENTER(); |
| |
| pHddCtx = WLAN_HDD_GET_CTX(pHostapdAdapter); |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return status; |
| |
| if(test_bit(SOFTAP_BSS_STARTED, &pHostapdAdapter->event_flags)) |
| { |
| #ifdef WLAN_FEATURE_MBSSID |
| status = WLANSAP_StopBss(WLAN_HDD_GET_SAP_CTX_PTR(pHostapdAdapter)); |
| #else |
| status = WLANSAP_StopBss((WLAN_HDD_GET_CTX(pHostapdAdapter))->pvosContext); |
| #endif |
| if (VOS_IS_STATUS_SUCCESS(status)) |
| hddLog(LOGE, FL("Deleting SAP/P2P link!!!!!!")); |
| |
| clear_bit(SOFTAP_BSS_STARTED, &pHostapdAdapter->event_flags); |
| wlan_hdd_decr_active_session(pHddCtx, pHostapdAdapter->device_mode); |
| } |
| if (pHddCtx->cfg_ini->apOBSSProtEnabled) |
| vos_runtime_pm_allow_suspend(pHddCtx->runtime_context.obss); |
| EXIT(); |
| return (status == VOS_STATUS_SUCCESS) ? 0 : -EBUSY; |
| } |
| |
| #ifdef SAP_AUTH_OFFLOAD |
| void hdd_set_sap_auth_offload(hdd_adapter_t *pHostapdAdapter, |
| bool enabled) |
| { |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pHostapdAdapter); |
| struct tSirSapOffloadInfo *sap_offload_info = NULL; |
| |
| /* Prepare the request to send to SME */ |
| sap_offload_info = vos_mem_malloc(sizeof(*sap_offload_info)); |
| if (NULL == sap_offload_info) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: could not allocate tSirSapOffloadInfo!", __func__); |
| return; |
| } |
| |
| vos_mem_zero(sap_offload_info, sizeof(*sap_offload_info)); |
| |
| sap_offload_info->vdev_id = pHostapdAdapter->sessionId; |
| sap_offload_info->sap_auth_offload_enable = |
| pHddCtx->cfg_ini->enable_sap_auth_offload && enabled; |
| sap_offload_info->sap_auth_offload_sec_type = |
| pHddCtx->cfg_ini->sap_auth_offload_sec_type; |
| sap_offload_info->key_len = |
| strlen(pHddCtx->cfg_ini->sap_auth_offload_key); |
| |
| if (sap_offload_info->sap_auth_offload_enable) { |
| if (sap_offload_info->key_len < 8 || |
| sap_offload_info->key_len > WLAN_PSK_STRING_LENGTH) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: invalid key length(%d) of WPA security!", __func__, |
| sap_offload_info->key_len); |
| goto end; |
| } |
| } |
| |
| vos_mem_copy(sap_offload_info->key, |
| pHddCtx->cfg_ini->sap_auth_offload_key, |
| sap_offload_info->key_len); |
| if (eHAL_STATUS_SUCCESS != |
| sme_set_sap_auth_offload(pHddCtx->hHal, sap_offload_info)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: sme_set_sap_auth_offload fail!", __func__); |
| goto end; |
| } |
| |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: sme_set_sap_auth_offload successfully!", __func__); |
| |
| end: |
| vos_mem_free(sap_offload_info); |
| return; |
| } |
| |
| |
| /** |
| * hdd_set_client_block_info - get client block info from ini file |
| * @padapter: hdd adapter pointer |
| * |
| * This function reads client block related info from ini file, these |
| * configurations will be sent to fw through wmi. |
| * |
| * Return: 0 on success, otherwise error value |
| */ |
| int hdd_set_client_block_info(hdd_adapter_t *padapter) |
| { |
| hdd_context_t *phddctx = WLAN_HDD_GET_CTX(padapter); |
| struct sblock_info client_block_info; |
| eHalStatus status; |
| |
| /* prepare the request to send to SME */ |
| client_block_info.vdev_id = padapter->sessionId; |
| client_block_info.reconnect_cnt = |
| phddctx->cfg_ini->connect_fail_count; |
| |
| client_block_info.con_fail_duration = |
| phddctx->cfg_ini->connect_fail_duration; |
| |
| client_block_info.block_duration = |
| phddctx->cfg_ini->connect_block_duration; |
| |
| status = sme_set_client_block_info(phddctx->hHal, &client_block_info); |
| if (eHAL_STATUS_FAILURE == status) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: sme_set_client_block_info!", __func__); |
| return -EIO; |
| } |
| |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: sme_set_client_block_info success!", __func__); |
| |
| return 0; |
| } |
| #endif /* SAP_AUTH_OFFLOAD */ |
| |
| /** |
| * hdd_issue_stored_joinreq() - This function will trigger stations's |
| * cached connect request to proceed. |
| * @hdd_ctx: pointer to hdd context. |
| * @sta_adapter: pointer to station adapter. |
| * |
| * This function will call SME to release station's stored/cached connect |
| * request to proceed. |
| * |
| * Return: none. |
| */ |
| static void hdd_issue_stored_joinreq(hdd_adapter_t *sta_adapter, |
| hdd_context_t *hdd_ctx) |
| { |
| tHalHandle hal_handle; |
| uint32_t roam_id = 0; |
| |
| if (NULL == sta_adapter) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Invalid station adapter, ignore issueing join req")); |
| return; |
| } |
| hal_handle = WLAN_HDD_GET_HAL_CTX(sta_adapter); |
| |
| if (true == hdd_is_sta_connection_pending(hdd_ctx)) { |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_ISSUE_JOIN_REQ, |
| sta_adapter->sessionId, roam_id)); |
| if (VOS_STATUS_SUCCESS != |
| sme_issue_stored_joinreq(hal_handle, |
| &roam_id, |
| sta_adapter->sessionId)) { |
| /* change back to NotAssociated */ |
| hdd_connSetConnectionState(sta_adapter, |
| eConnectionState_NotConnected); |
| } |
| hdd_change_sta_conn_pending_status(hdd_ctx, false); |
| } |
| } |
| |
| /** |
| * hdd_update_chandef() - Function to update channel width and center freq |
| * @hostapd_adapter: hostapd adapter |
| * @chandef: cfg80211 chan def |
| * @cb_mode: chan offset |
| * |
| * This function will be called to update channel width and center freq |
| * |
| * Return: None |
| */ |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)) || defined(WITH_BACKPORTS) |
| static inline void |
| hdd_update_chandef(hdd_adapter_t *hostapd_adapter, |
| struct cfg80211_chan_def *chandef, |
| ePhyChanBondState cb_mode) |
| { |
| uint16_t ch_width; |
| hdd_ap_ctx_t *phdd_ap_ctx; |
| uint8_t center_chan, chan; |
| |
| phdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(hostapd_adapter); |
| ch_width = phdd_ap_ctx->sapConfig.acs_cfg.ch_width; |
| |
| switch (ch_width) { |
| case eHT_CHANNEL_WIDTH_20MHZ: |
| case eHT_CHANNEL_WIDTH_40MHZ: |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "ch_width %d, won't update", ch_width); |
| break; |
| case eHT_CHANNEL_WIDTH_80MHZ: |
| chan = vos_freq_to_chan(chandef->chan->center_freq); |
| chandef->width = NL80211_CHAN_WIDTH_80; |
| |
| switch (cb_mode) { |
| case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED: |
| case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW: |
| center_chan = chan + 2; |
| break; |
| case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW: |
| center_chan = chan + 6; |
| break; |
| case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH: |
| case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_CENTERED: |
| center_chan = chan - 2; |
| break; |
| case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH: |
| center_chan = chan - 6; |
| break; |
| default: |
| center_chan = chan; |
| break; |
| } |
| |
| chandef->center_freq1 = vos_chan_to_freq(center_chan); |
| break; |
| case eHT_CHANNEL_WIDTH_160MHZ: |
| default: |
| /* Todo, please add related codes if support 160MHZ or others */ |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "unsupport ch_width %d", ch_width); |
| break; |
| } |
| |
| } |
| #else |
| static inline void |
| hdd_update_chandef(hdd_adapter_t *hostapd_adapter, |
| struct cfg80211_chan_def *chandef, |
| ePhyChanBondState cb_mode) |
| { |
| } |
| #endif |
| |
| /** |
| * hdd_chan_change_notify() - Function to notify hostapd about channel change |
| * @hostapd_adapter hostapd adapter |
| * @dev: Net device structure |
| * @oper_chan: New operating channel |
| * |
| * This function is used to notify hostapd about the channel change |
| * |
| * Return: Success on intimating userspace |
| * |
| */ |
| VOS_STATUS hdd_chan_change_notify(hdd_adapter_t *hostapd_adapter, |
| struct net_device *dev, |
| uint8_t oper_chan) |
| { |
| struct ieee80211_channel *chan; |
| struct cfg80211_chan_def chandef; |
| enum nl80211_channel_type channel_type; |
| eCsrPhyMode phy_mode; |
| ePhyChanBondState cb_mode; |
| uint32_t freq; |
| tHalHandle hal = WLAN_HDD_GET_HAL_CTX(hostapd_adapter); |
| |
| if (NULL == hal) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: hal is NULL", __func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| freq = vos_chan_to_freq(oper_chan); |
| |
| chan = ieee80211_get_channel(hostapd_adapter->wdev.wiphy, freq); |
| |
| if (!chan) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Invalid input frequency for channel conversion", |
| __func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| #ifdef WLAN_FEATURE_MBSSID |
| phy_mode = wlansap_get_phymode(WLAN_HDD_GET_SAP_CTX_PTR(hostapd_adapter)); |
| #else |
| phy_mode = wlansap_get_phymode( |
| (WLAN_HDD_GET_CTX(hostapd_adapter))->pvosContext); |
| #endif |
| |
| if (oper_chan <= 14) |
| cb_mode = sme_GetCBPhyStateFromCBIniValue( |
| sme_GetChannelBondingMode24G(hal)); |
| else |
| cb_mode = sme_GetCBPhyStateFromCBIniValue( |
| sme_GetChannelBondingMode5G(hal)); |
| |
| switch (phy_mode) { |
| case eCSR_DOT11_MODE_11n: |
| case eCSR_DOT11_MODE_11n_ONLY: |
| case eCSR_DOT11_MODE_11ac: |
| case eCSR_DOT11_MODE_11ac_ONLY: |
| if (cb_mode == PHY_SINGLE_CHANNEL_CENTERED) |
| channel_type = NL80211_CHAN_HT20; |
| else if (cb_mode == PHY_DOUBLE_CHANNEL_HIGH_PRIMARY) |
| channel_type = NL80211_CHAN_HT40MINUS; |
| else if (cb_mode == PHY_DOUBLE_CHANNEL_LOW_PRIMARY) |
| channel_type = NL80211_CHAN_HT40PLUS; |
| else |
| channel_type = NL80211_CHAN_HT40PLUS; |
| break; |
| default: |
| channel_type = NL80211_CHAN_NO_HT; |
| break; |
| } |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: phy_mode %d cb_mode %d chann_type %d oper_chan %d", |
| __func__, phy_mode, cb_mode, channel_type, oper_chan); |
| |
| cfg80211_chandef_create(&chandef, chan, channel_type); |
| |
| if ((phy_mode == eCSR_DOT11_MODE_11ac) || |
| (phy_mode == eCSR_DOT11_MODE_11ac_ONLY)) |
| hdd_update_chandef(hostapd_adapter, &chandef, cb_mode); |
| |
| cfg80211_ch_switch_notify(dev, &chandef); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| #ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| VOS_STATUS hdd_send_sap_event(struct net_device *dev, |
| sta_sap_notifications event, |
| struct wlan_sap_csa_info csa_info, |
| struct wireless_dev *wdev) |
| { |
| uint32_t freq = 0, ret; |
| |
| hdd_wlan_get_freq(csa_info.sta_channel, &freq); |
| |
| hddLog(LOG1, FL(" Set Freq %d Chan= %d"), freq, csa_info.sta_channel ); |
| |
| vos_ssr_protect(__func__); |
| ret = hdd_softap_set_channel_change(dev, csa_info.sta_channel); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| VOS_STATUS hdd_sta_state_sap_notify(hdd_context_t *hdd_context, |
| sta_sap_notifications event, |
| struct wlan_sap_csa_info csa_info) |
| { |
| /* Get the HostApd Adapter. If present proceed further. |
| * Check the current state of SAP. If its in active state, get the channel in which it is running. |
| * Verify the channel and band. Based on the event type, take a decision. |
| * If it is a disconnection event and SAP is running in 2.4 band channel, no action should be taken. |
| * If its a connection event and SAP needs to do a CSA to the HomeAP channel. |
| */ |
| |
| hdd_adapter_t *pHostapdAdapter = NULL; |
| hdd_ap_ctx_t *pHddApCtx = NULL; |
| hdd_hostapd_state_t *pHostapdState = NULL; |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| tsap_Config_t *sap_config; |
| uint32_t ret = 0; |
| |
| hddLog(LOGE, FL("%s Entry event = %d channel = %d"), |
| __func__, event, csa_info.sta_channel); |
| |
| if (!hdd_context) { |
| hddLog(LOGE, FL("HDD context is NULL")); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_context); |
| |
| if (ret != 0) { |
| |
| hddLog(LOGE, FL("%s Failed in hdd_validate_context ret=%d"), __func__, ret); |
| return ret; |
| } |
| |
| /*Get the Adapter of SAP*/ |
| pHostapdAdapter = hdd_get_adapter(hdd_context, WLAN_HDD_SOFTAP); |
| |
| if(!pHostapdAdapter) |
| { |
| hddLog(LOGE, FL("Hostapd adapter context is NULL")); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter); |
| |
| pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(pHostapdAdapter); |
| |
| /*Verify the state*/ |
| if(pHostapdState->vosStatus != VOS_STATUS_SUCCESS || |
| pHostapdState->bssState != BSS_START) |
| { |
| hddLog(LOGE, FL("Invalid HostApd State vosStatus=%d bssState=%d"), |
| pHostapdState->vosStatus, pHostapdState->bssState); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| switch(event) |
| { |
| case STA_NOTIFY_DISCONNECTED: |
| { |
| /* check for the operating channel |
| * If operating in 2.4, just ignore and return |
| * else start ACS & find the strongest signal channel and do initiate CSA to that channel. |
| */ |
| if((pHddApCtx->operatingChannel >= 1 && pHddApCtx->operatingChannel <= 14)) |
| { |
| hddLog(LOGE, FL("Hostapd is operating in 2.4Band Channel=%d, Avoid channel switch"), |
| pHddApCtx->operatingChannel); |
| } |
| else |
| { |
| sap_config = &((WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter))->sapConfig); |
| if (VOS_IS_DFS_CH(pHddApCtx->operatingChannel) && |
| ( VOS_IS_DFS_CH(sap_config->channel) || |
| (sap_config->channel == AUTO_CHANNEL_SELECT) )){ |
| hddLog(LOGE, FL("SAP CUR CH %d(DFS) Hostapd Conf CH=%d(%s) Switch to CH %d"), |
| pHddApCtx->operatingChannel, pHddApCtx->operatingChannel, |
| (sap_config->channel == AUTO_CHANNEL_SELECT)?"AUTO":"DFS", 36); |
| hdd_context->ch_switch_ctx.def_csa_channel_on_disc = 36; |
| }else if (VOS_IS_DFS_CH(pHddApCtx->operatingChannel) && |
| !VOS_IS_DFS_CH(sap_config->channel)){ |
| hddLog(LOGE, FL("SAP CUR CH %d(DFS) Hostapd Conf CH=%d(Non-DFS) Switch to %d"), |
| pHddApCtx->operatingChannel, sap_config->channel, sap_config->channel); |
| hdd_context->ch_switch_ctx.def_csa_channel_on_disc = sap_config->channel; //channel from the hostapd |
| }else{ |
| hddLog(LOGE, FL("SAP is operating in 5Ghz Band Non DFS Channel=%d, Avoid channel switch"), |
| pHddApCtx->operatingChannel); |
| return status; |
| } |
| mutex_lock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock); |
| hdd_context->ch_switch_ctx.sap_chan_sw_pending = 1; |
| mutex_unlock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock); |
| |
| //Set the timer to initiate channel switch |
| if(hdd_context->ch_switch_ctx.chan_sw_timer_initialized == VOS_TRUE) |
| { |
| status = vos_timer_start(&hdd_context->ch_switch_ctx.hdd_ap_chan_switch_timer, 10000); |
| if(!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hddLog(LOGE, FL("Failed to start AP channel switch timer!!")); |
| break; |
| } |
| } |
| } |
| } |
| break; |
| case STA_NOTIFY_CONNECTED: |
| { |
| //stop the channel switch timer first |
| if (hdd_context->ch_switch_ctx.hdd_ap_chan_switch_timer.state == VOS_TIMER_STATE_RUNNING) |
| { |
| status = vos_timer_stop(&hdd_context->ch_switch_ctx.hdd_ap_chan_switch_timer); |
| if(!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hddLog(LOGE, FL("Failed to stop AP channel switch timer!!")); |
| break; |
| } |
| } |
| if(pHddApCtx->operatingChannel != csa_info.sta_channel) |
| { |
| mutex_lock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock); |
| hddLog(LOGE, FL("Switching Hostapd to Station channel %d"), csa_info.sta_channel); |
| status = hdd_send_sap_event(pHostapdAdapter->dev, |
| event, |
| csa_info, |
| &pHostapdAdapter->wdev); |
| if(!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hddLog(LOGE, FL("Failed to send channel switch event!!")); |
| mutex_unlock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock); |
| break; |
| } |
| hdd_context->ch_switch_ctx.sap_chan_sw_pending = 0; |
| mutex_unlock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock); |
| } |
| else |
| { |
| hddLog(LOGE, FL("Hostapd and Sta are operating in same channel : %d\n"), |
| pHddApCtx->operatingChannel); |
| } |
| } |
| break; |
| case STA_NOTIFY_CSA: |
| { |
| mutex_lock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock); |
| if(pHddApCtx->operatingChannel != csa_info.sta_channel) |
| { |
| if(!(hdd_context->ch_switch_ctx.is_ch_sw_through_sta_csa && |
| hdd_context->ch_switch_ctx.csa_to_channel == csa_info.sta_channel)) |
| { |
| hdd_context->ch_switch_ctx.is_ch_sw_through_sta_csa = VOS_TRUE; |
| |
| hddLog(LOGE, FL("Switching Hostapd to Station channel %d"), csa_info.sta_channel); |
| status = hdd_send_sap_event(pHostapdAdapter->dev, |
| event, |
| csa_info, |
| &pHostapdAdapter->wdev); |
| if(!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hddLog(LOGE, FL("Failed to send channel switch event!!")); |
| mutex_unlock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock); |
| break; |
| } |
| |
| hdd_context->ch_switch_ctx.csa_to_channel = csa_info.sta_channel; |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s : CSA Sta interface for Channel %d is already notified", |
| __func__, csa_info.sta_channel); |
| } |
| } |
| else |
| { |
| hddLog(LOGE, FL("Hostapd and Sta are operating in same channel : %d\n"), |
| pHddApCtx->operatingChannel); |
| } |
| mutex_unlock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock); |
| } |
| break; |
| default: |
| { |
| hddLog(LOGE, FL("%s Invalid event %d"), __func__, event); |
| } |
| break; |
| } |
| |
| hddLog(LOGE, FL("%s Exit ret = %d"), __func__, status); |
| |
| return status; |
| } |
| #endif //#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| /** |
| * hdd_send_radar_event() - Function to send radar events to user space |
| * @hdd_context: HDD context |
| * @event: Type of radar event |
| * @dfs_info: Structure containing DFS channel and country |
| * @wdev: Wireless device structure |
| * |
| * This function is used to send radar events such as CAC start, CAC |
| * end etc., to userspace |
| * |
| * Return: Success on sending notifying userspace |
| * |
| */ |
| VOS_STATUS hdd_send_radar_event(hdd_context_t *hdd_context, |
| eSapHddEvent event, |
| struct wlan_dfs_info dfs_info, |
| struct wireless_dev *wdev) |
| { |
| |
| struct sk_buff *vendor_event; |
| enum qca_nl80211_vendor_subcmds_index index; |
| uint32_t freq, ret; |
| uint32_t data_size; |
| |
| if (!hdd_context) { |
| hddLog(LOGE, FL("HDD context is NULL")); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| freq = vos_chan_to_freq(dfs_info.channel); |
| |
| switch (event) { |
| case eSAP_DFS_CAC_START: |
| index = |
| QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED_INDEX; |
| data_size = sizeof(uint32_t); |
| break; |
| case eSAP_DFS_CAC_END: |
| index = |
| QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED_INDEX; |
| data_size = sizeof(uint32_t); |
| break; |
| case eSAP_DFS_RADAR_DETECT: |
| index = |
| QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED_INDEX; |
| data_size = sizeof(uint32_t); |
| break; |
| default: |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| vendor_event = cfg80211_vendor_event_alloc(hdd_context->wiphy, |
| wdev, |
| data_size + NLMSG_HDRLEN, |
| index, |
| GFP_KERNEL); |
| if (!vendor_event) { |
| hddLog(LOGE, |
| FL("cfg80211_vendor_event_alloc failed for %d"), index); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| ret = nla_put_u32(vendor_event, NL80211_ATTR_WIPHY_FREQ, freq); |
| |
| if (ret) { |
| hddLog(LOGE, FL("NL80211_ATTR_WIPHY_FREQ put fail")); |
| kfree_skb(vendor_event); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| cfg80211_vendor_event(vendor_event, GFP_KERNEL); |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| #ifdef CONFIG_CNSS |
| static VOS_STATUS hdd_wlan_get_dfs_nol(void *pdfs_list, u16 sdfs_list) |
| { |
| int ret; |
| |
| /* get the dfs nol from cnss */ |
| ret = vos_wlan_get_dfs_nol(pdfs_list, sdfs_list); |
| if (ret > 0) { |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: Get %d bytes of dfs nol from cnss", |
| __func__, ret); |
| return VOS_STATUS_SUCCESS; |
| } else { |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: No dfs nol entry in CNSS, ret: %d", |
| __func__, ret); |
| return VOS_STATUS_E_FAULT; |
| } |
| } |
| #else |
| static VOS_STATUS hdd_wlan_get_dfs_nol(void *pdfs_list, u16 sdfs_list) |
| { |
| return VOS_STATUS_E_FAILURE; |
| } |
| #endif |
| |
| #ifdef CONFIG_CNSS |
| static VOS_STATUS hdd_wlan_set_dfs_nol(const void *pdfs_list, u16 sdfs_list) |
| { |
| int ret; |
| |
| /* set the dfs nol from cnss */ |
| ret = vos_wlan_set_dfs_nol(pdfs_list, sdfs_list); |
| if (ret) { |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: Failed to set dfs nol - ret: %d", |
| __func__, ret); |
| } else { |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: Set %d bytes dfs nol to cnss", |
| __func__, sdfs_list); |
| } |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| #else |
| static VOS_STATUS hdd_wlan_set_dfs_nol(const void *pdfs_list, u16 sdfs_list) |
| { |
| return VOS_STATUS_E_FAILURE; |
| } |
| #endif |
| |
| #ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE |
| /** |
| * hdd_handle_acs_scan_event() - handle acs scan event for SAP |
| * @sap_event: tpSap_Event |
| * @adapter: hdd_adapter_t for SAP |
| * |
| * The function is to handle the eSAP_ACS_SCAN_SUCCESS_EVENT event. |
| * It will update scan result to cfg80211 and start a timer to flush the |
| * cached acs scan result. |
| * |
| * Return: VOS_STATUS_SUCCESS on success, |
| other value on failure |
| */ |
| static VOS_STATUS hdd_handle_acs_scan_event(tpSap_Event sap_event, |
| hdd_adapter_t *adapter) |
| { |
| hdd_context_t *hdd_ctx; |
| struct tsap_acs_scan_complete_event *comp_evt; |
| VOS_STATUS vos_status; |
| int chan_list_size; |
| |
| hdd_ctx = (hdd_context_t*)(adapter->pHddCtx); |
| if (!hdd_ctx) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD context is null")); |
| return VOS_STATUS_E_FAILURE; |
| } |
| comp_evt = &sap_event->sapevt.sap_acs_scan_comp; |
| hdd_ctx->skip_acs_scan_status = eSAP_SKIP_ACS_SCAN; |
| spin_lock(&hdd_ctx->acs_skip_lock); |
| vos_mem_free(hdd_ctx->last_acs_channel_list); |
| hdd_ctx->last_acs_channel_list = NULL; |
| hdd_ctx->num_of_channels = 0; |
| /* cache the previous ACS scan channel list . |
| * If the following OBSS scan chan list is covered by ACS chan list, |
| * we can skip OBSS Scan to save SAP starting total time. |
| */ |
| if (comp_evt->num_of_channels && comp_evt->channellist) { |
| chan_list_size = comp_evt->num_of_channels * |
| sizeof(comp_evt->channellist[0]); |
| hdd_ctx->last_acs_channel_list = vos_mem_malloc( |
| chan_list_size); |
| if (hdd_ctx->last_acs_channel_list) { |
| vos_mem_copy(hdd_ctx->last_acs_channel_list, |
| comp_evt->channellist, |
| chan_list_size); |
| hdd_ctx->num_of_channels = comp_evt->num_of_channels; |
| } |
| } |
| spin_unlock(&hdd_ctx->acs_skip_lock); |
| /* Update ACS scan result to cfg80211. Then OBSS scan can reuse the |
| * scan result. |
| */ |
| if (wlan_hdd_cfg80211_update_bss(hdd_ctx->wiphy, adapter)) |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("NO SCAN result")); |
| |
| hddLog(LOG1, FL("Reusing Last ACS scan result for %d sec"), |
| ACS_SCAN_EXPIRY_TIMEOUT_S); |
| vos_timer_stop( &hdd_ctx->skip_acs_scan_timer); |
| vos_status = vos_timer_start( &hdd_ctx->skip_acs_scan_timer, |
| ACS_SCAN_EXPIRY_TIMEOUT_S * 1000); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| hddLog(LOGE, FL("Failed to start ACS scan expiry timer")); |
| return VOS_STATUS_SUCCESS; |
| } |
| #else |
| static VOS_STATUS hdd_handle_acs_scan_event(tpSap_Event sap_event, |
| hdd_adapter_t *adapter) |
| { |
| return VOS_STATUS_SUCCESS; |
| } |
| #endif |
| |
| /** |
| * get_max_rate_vht() - calculate max rate for VHT mode |
| * @nss: num of streams |
| * @ch_width: channel width |
| * @sgi: short gi |
| * @vht_mcs_map: vht mcs map |
| * |
| * This function calculate max rate for VHT mode |
| * |
| * Return: max rate |
| */ |
| #ifdef WLAN_FEATURE_11AC |
| static int get_max_rate_vht(int nss, int ch_width, int sgi, int vht_mcs_map) |
| { |
| struct index_vht_data_rate_type *supported_vht_mcs_rate; |
| enum data_rate_11ac_max_mcs vht_max_mcs; |
| int maxrate = 0; |
| int maxidx = 0; |
| |
| if (nss == 1) { |
| supported_vht_mcs_rate = supported_vht_mcs_rate_nss1; |
| } else if (nss == 2) { |
| supported_vht_mcs_rate = supported_vht_mcs_rate_nss2; |
| } else { |
| /* Not Supported */ |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: nss %d not supported\n", |
| __func__, nss); |
| return maxrate; |
| } |
| |
| vht_max_mcs = |
| (enum data_rate_11ac_max_mcs) |
| vht_mcs_map & DATA_RATE_11AC_MCS_MASK; |
| |
| if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_7) { |
| maxidx = 7; |
| } else if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_8) { |
| maxidx = 8; |
| } else if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_9) { |
| if (ch_width == eHT_CHANNEL_WIDTH_20MHZ) |
| /* MCS9 is not valid for VHT20 when nss=1,2 */ |
| maxidx = 8; |
| else |
| maxidx = 9; |
| } else { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: vht mcs map %x not supported\n", |
| __func__, |
| vht_mcs_map & DATA_RATE_11AC_MCS_MASK); |
| return maxrate; |
| } |
| |
| if (ch_width == eHT_CHANNEL_WIDTH_20MHZ) { |
| maxrate = |
| supported_vht_mcs_rate[maxidx].supported_VHT20_rate[sgi]; |
| } else if (ch_width == eHT_CHANNEL_WIDTH_40MHZ) { |
| maxrate = |
| supported_vht_mcs_rate[maxidx].supported_VHT40_rate[sgi]; |
| } else if (ch_width == eHT_CHANNEL_WIDTH_80MHZ) { |
| maxrate = |
| supported_vht_mcs_rate[maxidx].supported_VHT80_rate[sgi]; |
| } else { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: ch_width %d not supported\n", |
| __func__, ch_width); |
| return maxrate; |
| } |
| |
| return maxrate; |
| } |
| #else |
| static int get_max_rate_vht(int nss, int ch_width, int sgi, int vht_mcs_map) |
| { |
| return 0; |
| } |
| #endif |
| |
| /** |
| * calculate_max_phy_rate() - calcuate maximum phy rate (kbps) |
| * @mode: phymode: Legacy, 11a/b/g, HT, VHT |
| * @nss: num of stream (maximum num is 2) |
| * @ch_width: channel width |
| * @sgi: short gi enabled or not |
| * @supp_idx: max supported idx |
| * @ext_idx: max extended idx |
| * @ht_mcs_idx: max mcs index for HT |
| * @vht_mcs_map: mcs map for VHT |
| * |
| * return: maximum phy rate in kbps |
| * */ |
| static int calcuate_max_phy_rate(int mode, int nss, int ch_width, |
| int sgi, int supp_idx, int ext_idx, int ht_mcs_idx, |
| int vht_mcs_map) |
| { |
| struct index_data_rate_type *supported_mcs_rate; |
| int maxidx = 12; /*default 6M mode*/ |
| int maxrate = 0, tmprate = 0; |
| int i; |
| |
| /* check supported rates */ |
| if (supp_idx != 0xff && maxidx < supp_idx) |
| maxidx = supp_idx; |
| |
| /* check extended rates */ |
| if (ext_idx != 0xff && maxidx < ext_idx) |
| maxidx = ext_idx; |
| |
| for (i = 0; |
| i < sizeof(supported_data_rate)/sizeof(supported_data_rate[0]); |
| i++) { |
| if (supported_data_rate[i].beacon_rate_index == maxidx) |
| maxrate = supported_data_rate[i].supported_rate[0]; |
| } |
| |
| if (mode == SIR_SME_PHY_MODE_HT) { |
| /* check for HT Mode */ |
| maxidx = ht_mcs_idx; |
| if (nss == 1) { |
| supported_mcs_rate = supported_mcs_rate_nss1; |
| } else if (nss == 2) { |
| supported_mcs_rate = supported_mcs_rate_nss2; |
| } else { |
| /* Not Supported */ |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: nss %d not supported\n", |
| __func__, nss); |
| return maxrate; |
| } |
| |
| if (ch_width == eHT_CHANNEL_WIDTH_20MHZ) { |
| tmprate = |
| supported_mcs_rate[maxidx].supported_rate[0]; |
| if (sgi) |
| tmprate = |
| supported_mcs_rate[maxidx].supported_rate[2]; |
| } else if (ch_width == eHT_CHANNEL_WIDTH_40MHZ) { |
| tmprate = |
| supported_mcs_rate[maxidx].supported_rate[1]; |
| if (sgi) |
| tmprate = |
| supported_mcs_rate[maxidx].supported_rate[3]; |
| } else { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: invalid mode %d ch_width %d\n", |
| __func__, mode, ch_width); |
| return maxrate; |
| } |
| |
| if (maxrate < tmprate) |
| maxrate = tmprate; |
| } |
| |
| if (mode == SIR_SME_PHY_MODE_VHT) { |
| /* check for VHT Mode */ |
| tmprate = get_max_rate_vht(nss, ch_width, sgi, vht_mcs_map); |
| if (maxrate < tmprate) |
| maxrate = tmprate; |
| } |
| |
| return maxrate; |
| } |
| |
| static void hdd_fill_station_info(hdd_station_info_t *stainfo, |
| tSap_StationAssocReassocCompleteEvent *event) |
| { |
| stainfo->nss = event->chan_info.nss; |
| stainfo->rate_flags = event->chan_info.rate_flags; |
| stainfo->sub20_dynamic_channelwidth = |
| event->chan_info.sub20_channelwidth; |
| stainfo->ampdu = event->ampdu; |
| stainfo->sgi_enable = event->sgi_enable; |
| stainfo->tx_stbc = event->tx_stbc; |
| stainfo->rx_stbc = event->rx_stbc; |
| stainfo->ch_width = event->ch_width; |
| stainfo->mode = event->mode; |
| stainfo->max_supp_idx = event->max_supp_idx; |
| stainfo->max_ext_idx = event->max_ext_idx; |
| stainfo->max_mcs_idx = event->max_mcs_idx; |
| stainfo->rx_mcs_map = event->rx_mcs_map; |
| stainfo->tx_mcs_map = event->tx_mcs_map; |
| stainfo->assoc_ts = vos_system_ticks(); |
| stainfo->max_phy_rate = |
| calcuate_max_phy_rate(stainfo->mode, |
| stainfo->nss, |
| stainfo->ch_width, |
| stainfo->sgi_enable, |
| stainfo->max_supp_idx, |
| stainfo->max_ext_idx, |
| stainfo->max_mcs_idx, |
| stainfo->rx_mcs_map); |
| /* expect max_phy_rate report in kbps */ |
| stainfo->max_phy_rate *= 100; |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("cap %d %d %d %d %d %d %d %d %d %x %d"), |
| stainfo->ampdu, |
| stainfo->sgi_enable, |
| stainfo->tx_stbc, |
| stainfo->rx_stbc, |
| stainfo->isQosEnabled, |
| stainfo->ch_width, |
| stainfo->mode, |
| event->wmmEnabled, |
| event->chan_info.nss, |
| event->chan_info.rate_flags, |
| stainfo->max_phy_rate); |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("rate info %d %d %d %d %d"), |
| stainfo->max_supp_idx, |
| stainfo->max_ext_idx, |
| stainfo->max_mcs_idx, |
| stainfo->rx_mcs_map, |
| stainfo->tx_mcs_map); |
| } |
| |
| VOS_STATUS hdd_hostapd_SAPEventCB( tpSap_Event pSapEvent, v_PVOID_t usrDataForCallback) |
| { |
| hdd_adapter_t *pHostapdAdapter; |
| hdd_ap_ctx_t *pHddApCtx; |
| hdd_hostapd_state_t *pHostapdState; |
| struct net_device *dev; |
| eSapHddEvent sapEvent; |
| union iwreq_data wrqu; |
| v_BYTE_t *we_custom_event_generic = NULL; |
| int we_event = 0; |
| int i = 0; |
| v_U8_t staId; |
| VOS_STATUS vos_status; |
| v_BOOL_t bWPSState; |
| v_BOOL_t bAuthRequired = TRUE; |
| tpSap_AssocMacAddr pAssocStasArray = NULL; |
| char unknownSTAEvent[IW_CUSTOM_MAX+1]; |
| char maxAssocExceededEvent[IW_CUSTOM_MAX+1]; |
| v_BYTE_t we_custom_start_event[64]; |
| char *startBssEvent; |
| hdd_context_t *pHddCtx; |
| hdd_scaninfo_t *pScanInfo = NULL; |
| struct iw_michaelmicfailure msg; |
| v_U8_t ignoreCAC = 0; |
| hdd_config_t *cfg = NULL; |
| struct wlan_dfs_info dfs_info; |
| v_U8_t cc_len = WLAN_SVC_COUNTRY_CODE_LEN; |
| hdd_adapter_t *con_sap_adapter; |
| tSap_StationAssocReassocCompleteEvent *event; |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| |
| dev = (struct net_device *)usrDataForCallback; |
| if (!dev) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: usrDataForCallback is null", __func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| pHostapdAdapter = netdev_priv(dev); |
| |
| if ((NULL == pHostapdAdapter) || |
| (WLAN_HDD_ADAPTER_MAGIC != pHostapdAdapter->magic)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "invalid adapter or adapter has invalid magic"); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(pHostapdAdapter); |
| pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter); |
| |
| if (!pSapEvent) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: pSapEvent is null", __func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| sapEvent = pSapEvent->sapHddEventCode; |
| memset(&wrqu, '\0', sizeof(wrqu)); |
| pHddCtx = (hdd_context_t*)(pHostapdAdapter->pHddCtx); |
| |
| if (!pHddCtx) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD context is null")); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| cfg = pHddCtx->cfg_ini; |
| |
| if (!cfg) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD config is null")); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| dfs_info.channel = pHddApCtx->operatingChannel; |
| sme_GetCountryCode(pHddCtx->hHal, dfs_info.country_code, &cc_len); |
| |
| switch(sapEvent) |
| { |
| case eSAP_START_BSS_EVENT : |
| hddLog(LOG1, FL("BSS configured status = %s, channel = %u, bc sta Id = %d"), |
| pSapEvent->sapevt.sapStartBssCompleteEvent.status ? "eSAP_STATUS_FAILURE" : "eSAP_STATUS_SUCCESS", |
| pSapEvent->sapevt.sapStartBssCompleteEvent.operatingChannel, |
| pSapEvent->sapevt.sapStartBssCompleteEvent.staId); |
| |
| pHostapdAdapter->sessionId = |
| pSapEvent->sapevt.sapStartBssCompleteEvent.sessionId; |
| |
| pHostapdState->vosStatus = pSapEvent->sapevt.sapStartBssCompleteEvent.status; |
| vos_status = vos_event_set(&pHostapdState->vosEvent); |
| |
| if (!VOS_IS_STATUS_SUCCESS(vos_status) || pHostapdState->vosStatus) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, ("ERROR: startbss event failed!!")); |
| goto stopbss; |
| } |
| else |
| { |
| #ifdef FEATURE_WLAN_CH_AVOID |
| sme_ChAvoidUpdateReq(pHddCtx->hHal); |
| #endif /* FEATURE_WLAN_CH_AVOID */ |
| |
| pHddApCtx->uBCStaId = pSapEvent->sapevt.sapStartBssCompleteEvent.staId; |
| |
| #ifdef QCA_LL_TX_FLOW_CT |
| if (pHostapdAdapter->tx_flow_timer_initialized == VOS_FALSE) |
| { |
| vos_timer_init(&pHostapdAdapter->tx_flow_control_timer, |
| VOS_TIMER_TYPE_SW, |
| hdd_tx_resume_timer_expired_handler, |
| pHostapdAdapter); |
| pHostapdAdapter->tx_flow_timer_initialized = VOS_TRUE; |
| } |
| WLANTL_RegisterTXFlowControl(pHddCtx->pvosContext, |
| hdd_tx_resume_cb, |
| pHostapdAdapter->sessionId, |
| (void *)pHostapdAdapter); |
| #endif |
| |
| //@@@ need wep logic here to set privacy bit |
| vos_status = hdd_softap_Register_BC_STA(pHostapdAdapter, pHddApCtx->uPrivacy); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) { |
| hddLog(LOGW, FL("Failed to register BC STA %d"), vos_status); |
| hdd_stop_bss_link(pHostapdAdapter, usrDataForCallback); |
| } |
| } |
| #ifdef IPA_OFFLOAD |
| if (hdd_ipa_is_enabled(pHddCtx)) |
| { |
| status = hdd_ipa_wlan_evt(pHostapdAdapter, pHddApCtx->uBCStaId, |
| WLAN_AP_CONNECT, pHostapdAdapter->dev->dev_addr); |
| |
| if (status) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| ("ERROR: WLAN_AP_CONNECT event failed!!")); |
| goto stopbss; |
| } |
| } |
| #endif |
| |
| if (0 != (WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->nAPAutoShutOff) |
| { |
| // AP Inactivity timer init and start |
| vos_status = vos_timer_init( &pHddApCtx->hdd_ap_inactivity_timer, VOS_TIMER_TYPE_SW, |
| hdd_hostapd_inactivity_timer_cb, (v_PVOID_t)dev ); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| hddLog(LOGE, FL("Failed to init AP inactivity timer")); |
| |
| vos_status = vos_timer_start( &pHddApCtx->hdd_ap_inactivity_timer, (WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->nAPAutoShutOff * 1000); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| hddLog(LOGE, FL("Failed to init AP inactivity timer")); |
| |
| } |
| #ifdef FEATURE_WLAN_AUTO_SHUTDOWN |
| wlan_hdd_auto_shutdown_enable(pHddCtx, VOS_TRUE); |
| #endif |
| pHddApCtx->operatingChannel = pSapEvent->sapevt.sapStartBssCompleteEvent.operatingChannel; |
| |
| hdd_hostapd_channel_prevent_suspend(pHostapdAdapter, |
| pHddApCtx->operatingChannel); |
| |
| pHostapdState->bssState = BSS_START; |
| |
| /* Set default key index */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: default key index %hu", __func__, |
| pHddApCtx->wep_def_key_idx); |
| |
| sme_roam_set_default_key_index( |
| WLAN_HDD_GET_HAL_CTX(pHostapdAdapter), |
| pHostapdAdapter->sessionId, |
| pHddApCtx->wep_def_key_idx); |
| |
| //Set group key / WEP key every time when BSS is restarted |
| if( pHddApCtx->groupKey.keyLength ) |
| { |
| status = WLANSAP_SetKeySta( |
| #ifdef WLAN_FEATURE_MBSSID |
| WLAN_HDD_GET_SAP_CTX_PTR(pHostapdAdapter), |
| #else |
| (WLAN_HDD_GET_CTX(pHostapdAdapter))->pvosContext, |
| #endif |
| &pHddApCtx->groupKey); |
| if (!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: WLANSAP_SetKeySta failed", __func__); |
| } |
| } |
| else |
| { |
| for ( i = 0; i < CSR_MAX_NUM_KEY; i++ ) |
| { |
| if ( !pHddApCtx->wepKey[i].keyLength ) |
| continue; |
| |
| status = WLANSAP_SetKeySta( |
| #ifdef WLAN_FEATURE_MBSSID |
| WLAN_HDD_GET_SAP_CTX_PTR(pHostapdAdapter), |
| #else |
| (WLAN_HDD_GET_CTX(pHostapdAdapter))->pvosContext, |
| #endif |
| &pHddApCtx->wepKey[i]); |
| if (!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: WLANSAP_SetKeySta failed idx %d", __func__, i); |
| } |
| } |
| } |
| |
| SPIN_LOCK_BH(&pHddCtx->dfs_lock); |
| pHddCtx->dfs_radar_found = VOS_FALSE; |
| SPIN_UNLOCK_BH(&pHddCtx->dfs_lock); |
| WLANSAP_Get_Dfs_Ignore_CAC(pHddCtx->hHal, &ignoreCAC); |
| if ((NV_CHANNEL_DFS != |
| vos_nv_getChannelEnabledState(pHddApCtx->operatingChannel)) |
| || ignoreCAC |
| || pHddCtx->dev_dfs_cac_status == DFS_CAC_ALREADY_DONE) |
| { |
| pHddApCtx->dfs_cac_block_tx = VOS_FALSE; |
| } else { |
| /* |
| * DFS requirement: Do not transmit during CAC. |
| * This flag will be reset when BSS starts |
| * (if not in a DFS channel) or CAC ends. |
| */ |
| pHddApCtx->dfs_cac_block_tx = VOS_TRUE; |
| } |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED, |
| "The value of dfs_cac_block_tx[%d] for ApCtx[%pK]", |
| pHddApCtx->dfs_cac_block_tx, pHddApCtx); |
| |
| if ((NV_CHANNEL_DFS == |
| vos_nv_getChannelEnabledState(pHddApCtx->operatingChannel)) && |
| (pHddCtx->cfg_ini->IsSapDfsChSifsBurstEnabled == 0)) |
| { |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Setting SIFS Burst disable for DFS channel %d", |
| __func__, pHddApCtx->operatingChannel); |
| |
| if (process_wma_set_command((int)pHostapdAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_BURST_ENABLE, |
| 0, PDEV_CMD)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to Set SIFS Burst for DFS channel %d", |
| __func__, pHddApCtx->operatingChannel); |
| } |
| } |
| |
| //Fill the params for sending IWEVCUSTOM Event with SOFTAP.enabled |
| startBssEvent = "SOFTAP.enabled"; |
| memset(&we_custom_start_event, '\0', sizeof(we_custom_start_event)); |
| memcpy(&we_custom_start_event, startBssEvent, strlen(startBssEvent)); |
| memset(&wrqu, 0, sizeof(wrqu)); |
| wrqu.data.length = strlen(startBssEvent); |
| we_event = IWEVCUSTOM; |
| we_custom_event_generic = we_custom_start_event; |
| hdd_dump_concurrency_info(pHddCtx); |
| #ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| if(pHostapdAdapter->device_mode == WLAN_HDD_SOFTAP) |
| { |
| mutex_lock(&pHddCtx->ch_switch_ctx.sap_ch_sw_lock); |
| if(pHddCtx->ch_switch_ctx.is_ch_sw_through_sta_csa && |
| (pHddApCtx->operatingChannel == pHddCtx->ch_switch_ctx.csa_to_channel)){ |
| hddLog(LOG1, FL("Successfully Channel Switch is done to CH = %d"), |
| pHddApCtx->operatingChannel); |
| pHddCtx->ch_switch_ctx.is_ch_sw_through_sta_csa = VOS_FALSE; |
| } |
| mutex_unlock(&pHddCtx->ch_switch_ctx.sap_ch_sw_lock); |
| } |
| #endif//#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| break; //Event will be sent after Switch-Case stmt |
| |
| case eSAP_STOP_BSS_EVENT: |
| hddLog(LOG1, FL("BSS stop status = %s"),pSapEvent->sapevt.sapStopBssCompleteEvent.status ? |
| "eSAP_STATUS_FAILURE" : "eSAP_STATUS_SUCCESS"); |
| |
| hdd_set_sap_auth_offload(pHostapdAdapter, FALSE); |
| |
| hdd_hostapd_channel_allow_suspend(pHostapdAdapter, |
| pHddApCtx->operatingChannel); |
| |
| //Free up Channel List incase if it is set |
| |
| sapCleanupChannelList(WLAN_HDD_GET_SAP_CTX_PTR(pHostapdAdapter)); |
| |
| pHddApCtx->operatingChannel = 0; //Invalidate the channel info. |
| #ifdef IPA_OFFLOAD |
| if (hdd_ipa_is_enabled(pHddCtx)) |
| { |
| status = hdd_ipa_wlan_evt(pHostapdAdapter, pHddApCtx->uBCStaId, |
| WLAN_AP_DISCONNECT, pHostapdAdapter->dev->dev_addr); |
| |
| if (status) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| ("ERROR: WLAN_AP_DISCONNECT event failed!!")); |
| goto stopbss; |
| } |
| } |
| #endif |
| /* reset the dfs_cac_status and dfs_cac_block_tx flag only when |
| * the last BSS is stopped |
| */ |
| con_sap_adapter = hdd_get_con_sap_adapter(pHostapdAdapter, true); |
| if (!con_sap_adapter) { |
| pHddApCtx->dfs_cac_block_tx = TRUE; |
| pHddCtx->dev_dfs_cac_status = DFS_CAC_NEVER_DONE; |
| } |
| if (pHddCtx->cfg_ini->conc_custom_rule2 && |
| (WLAN_HDD_P2P_GO == pHostapdAdapter->device_mode)) { |
| |
| hdd_adapter_t *sta_adapter = hdd_get_adapter(pHddCtx, |
| WLAN_HDD_INFRA_STATION); |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH, |
| FL("P2PGO is going down now")); |
| hdd_issue_stored_joinreq(sta_adapter, pHddCtx); |
| } |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("bss_stop_reason=%d"), pHddApCtx->bss_stop_reason); |
| if (pHddApCtx->bss_stop_reason != BSS_STOP_DUE_TO_MCC_SCC_SWITCH){ |
| /* when MCC to SCC switching happens, key storage should not be |
| * cleared due to hostapd will not repopulate the original keys |
| */ |
| pHddApCtx->groupKey.keyLength = 0; |
| for (i = 0; i < CSR_MAX_NUM_KEY; i++) |
| pHddApCtx->wepKey[i].keyLength = 0; |
| } |
| |
| /* clear the reason code in case BSS is stopped |
| * in another place |
| */ |
| pHddApCtx->bss_stop_reason = BSS_STOP_REASON_INVALID; |
| goto stopbss; |
| |
| case eSAP_DFS_CAC_START: |
| wlan_hdd_send_svc_nlink_msg(pHddCtx->radio_index, |
| WLAN_SVC_DFS_CAC_START_IND, |
| &dfs_info, |
| sizeof(dfs_info)); |
| pHddCtx->dev_dfs_cac_status = DFS_CAC_IN_PROGRESS; |
| if (VOS_STATUS_SUCCESS != |
| hdd_send_radar_event(pHddCtx, eSAP_DFS_CAC_START, |
| dfs_info, &pHostapdAdapter->wdev)) { |
| hddLog(LOGE, FL("Unable to indicate CAC start NL event")); |
| } else { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("Sent CAC start to user space")); |
| } |
| pHddCtx->dfs_radar_found = VOS_FALSE; |
| break; |
| |
| case eSAP_DFS_CAC_INTERRUPTED: |
| /* |
| * The CAC timer did not run completely and a radar was detected |
| * during the CAC time. This new state will keep the tx path |
| * blocked since we do not want any transmission on the DFS |
| * channel. CAC end will only be reported here since the user |
| * space applications are waiting on CAC end for their state |
| * management. |
| */ |
| if (VOS_STATUS_SUCCESS != |
| hdd_send_radar_event(pHddCtx, eSAP_DFS_CAC_END, |
| dfs_info, &pHostapdAdapter->wdev)) { |
| hddLog(LOGE, |
| FL("Unable to indicate CAC end (interrupted) event")); |
| } else { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("Sent CAC end (interrupted) to user space")); |
| } |
| break; |
| |
| case eSAP_DFS_CAC_END: |
| wlan_hdd_send_svc_nlink_msg(pHddCtx->radio_index, |
| WLAN_SVC_DFS_CAC_END_IND, |
| &dfs_info, |
| sizeof(dfs_info)); |
| pHddApCtx->dfs_cac_block_tx = VOS_FALSE; |
| pHddCtx->dev_dfs_cac_status = DFS_CAC_ALREADY_DONE; |
| if (VOS_STATUS_SUCCESS != |
| hdd_send_radar_event(pHddCtx, eSAP_DFS_CAC_END, |
| dfs_info, &pHostapdAdapter->wdev)) { |
| hddLog(LOGE, FL("Unable to indicate CAC end NL event")); |
| } else { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("Sent CAC end to user space")); |
| } |
| break; |
| |
| case eSAP_DFS_RADAR_DETECT: |
| wlan_hdd_send_svc_nlink_msg(pHddCtx->radio_index, |
| WLAN_SVC_DFS_RADAR_DETECT_IND, |
| &dfs_info, |
| sizeof(dfs_info)); |
| pHddCtx->dev_dfs_cac_status = DFS_CAC_NEVER_DONE; |
| if (VOS_STATUS_SUCCESS != |
| hdd_send_radar_event(pHddCtx, eSAP_DFS_RADAR_DETECT, |
| dfs_info, &pHostapdAdapter->wdev)) { |
| hddLog(LOGE, FL("Unable to indicate Radar detect NL event")); |
| } else { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("Sent radar detected to user space")); |
| } |
| break; |
| |
| case eSAP_DFS_NO_AVAILABLE_CHANNEL: |
| wlan_hdd_send_svc_nlink_msg(pHddCtx->radio_index, |
| WLAN_SVC_DFS_ALL_CHANNEL_UNAVAIL_IND, |
| &dfs_info, |
| sizeof(dfs_info)); |
| break; |
| |
| case eSAP_STA_SET_KEY_EVENT: |
| /* TODO: forward the message to hostapd once implementation |
| is done for now just print */ |
| hddLog(LOG1, FL("SET Key: configured status = %s"),pSapEvent->sapevt.sapStationSetKeyCompleteEvent.status ? |
| "eSAP_STATUS_FAILURE" : "eSAP_STATUS_SUCCESS"); |
| return VOS_STATUS_SUCCESS; |
| case eSAP_STA_DEL_KEY_EVENT: |
| /* TODO: forward the message to hostapd once implementation |
| is done for now just print */ |
| hddLog(LOG1, FL("Event received %s"),"eSAP_STA_DEL_KEY_EVENT"); |
| return VOS_STATUS_SUCCESS; |
| case eSAP_STA_MIC_FAILURE_EVENT: |
| { |
| memset(&msg, '\0', sizeof(msg)); |
| msg.src_addr.sa_family = ARPHRD_ETHER; |
| memcpy(msg.src_addr.sa_data, &pSapEvent->sapevt.sapStationMICFailureEvent.staMac, sizeof(v_MACADDR_t)); |
| hddLog(LOG1, "MIC MAC "MAC_ADDRESS_STR, MAC_ADDR_ARRAY(msg.src_addr.sa_data)); |
| if(pSapEvent->sapevt.sapStationMICFailureEvent.multicast == eSAP_TRUE) |
| msg.flags = IW_MICFAILURE_GROUP; |
| else |
| msg.flags = IW_MICFAILURE_PAIRWISE; |
| memset(&wrqu, 0, sizeof(wrqu)); |
| wrqu.data.length = sizeof(msg); |
| we_event = IWEVMICHAELMICFAILURE; |
| we_custom_event_generic = (v_BYTE_t *)&msg; |
| } |
| /* inform mic failure to nl80211 */ |
| cfg80211_michael_mic_failure(dev, |
| pSapEvent->sapevt. |
| sapStationMICFailureEvent.staMac.bytes, |
| ((pSapEvent->sapevt.sapStationMICFailureEvent.multicast == eSAP_TRUE) ? |
| NL80211_KEYTYPE_GROUP : |
| NL80211_KEYTYPE_PAIRWISE), |
| pSapEvent->sapevt.sapStationMICFailureEvent.keyId, |
| pSapEvent->sapevt.sapStationMICFailureEvent.TSC, |
| GFP_KERNEL); |
| break; |
| |
| case eSAP_STA_ASSOC_EVENT: |
| case eSAP_STA_REASSOC_EVENT: |
| event = &pSapEvent->sapevt.sapStationAssocReassocCompleteEvent; |
| wrqu.addr.sa_family = ARPHRD_ETHER; |
| memcpy(wrqu.addr.sa_data, &event->staMac, |
| sizeof(v_MACADDR_t)); |
| hddLog(LOG1, " associated "MAC_ADDRESS_STR, MAC_ADDR_ARRAY(wrqu.addr.sa_data)); |
| we_event = IWEVREGISTERED; |
| |
| #ifdef WLAN_FEATURE_MBSSID |
| WLANSAP_Get_WPS_State(WLAN_HDD_GET_SAP_CTX_PTR(pHostapdAdapter), &bWPSState); |
| #else |
| WLANSAP_Get_WPS_State((WLAN_HDD_GET_CTX(pHostapdAdapter))->pvosContext, &bWPSState); |
| #endif |
| |
| if ( (eCSR_ENCRYPT_TYPE_NONE == pHddApCtx->ucEncryptType) || |
| ( eCSR_ENCRYPT_TYPE_WEP40_STATICKEY == pHddApCtx->ucEncryptType ) || |
| ( eCSR_ENCRYPT_TYPE_WEP104_STATICKEY == pHddApCtx->ucEncryptType ) ) |
| { |
| bAuthRequired = FALSE; |
| } |
| |
| if (bAuthRequired || bWPSState == eANI_BOOLEAN_TRUE ) |
| { |
| vos_status = hdd_softap_RegisterSTA(pHostapdAdapter, |
| TRUE, |
| pHddApCtx->uPrivacy, |
| event->staId, |
| 0, |
| 0, |
| (v_MACADDR_t *)wrqu.addr.sa_data, |
| event->wmmEnabled); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| hddLog(LOGW, FL("Failed to register STA %d "MAC_ADDRESS_STR""), |
| vos_status, MAC_ADDR_ARRAY(wrqu.addr.sa_data)); |
| } |
| else |
| { |
| vos_status = hdd_softap_RegisterSTA(pHostapdAdapter, |
| FALSE, |
| pHddApCtx->uPrivacy, |
| event->staId, |
| 0, |
| 0, |
| (v_MACADDR_t *)wrqu.addr.sa_data, |
| event->wmmEnabled); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| hddLog(LOGW, FL("Failed to register STA %d "MAC_ADDRESS_STR""), |
| vos_status, MAC_ADDR_ARRAY(wrqu.addr.sa_data)); |
| } |
| |
| if (VOS_IS_STATUS_SUCCESS(vos_status)) { |
| staId = event->staId; |
| hdd_fill_station_info(&pHostapdAdapter->aStaInfo[staId], |
| event); |
| pHostapdAdapter->aStaInfo[staId].ecsa_capable = |
| pSapEvent-> |
| sapevt.sapStationAssocReassocCompleteEvent.ecsa_capable; |
| } |
| |
| #ifdef IPA_OFFLOAD |
| if (hdd_ipa_is_enabled(pHddCtx)) |
| { |
| status = hdd_ipa_wlan_evt(pHostapdAdapter, |
| event->staId, |
| WLAN_CLIENT_CONNECT_EX, |
| event->staMac.bytes); |
| if (status) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, ("ERROR: WLAN_CLIENT_CONNECT_EX event failed!!")); |
| goto stopbss; |
| } |
| } |
| #endif |
| #ifdef QCA_PKT_PROTO_TRACE |
| /* Peer associated, update into trace buffer */ |
| if (pHddCtx->cfg_ini->gEnableDebugLog) |
| { |
| vos_pkt_trace_buf_update("HA:ASSOC"); |
| } |
| #endif /* QCA_PKT_PROTO_TRACE */ |
| |
| DPTRACE(adf_dp_trace_mgmt_pkt(ADF_DP_TRACE_MGMT_PACKET_RECORD, |
| pHostapdAdapter->sessionId, |
| ADF_PROTO_TYPE_MGMT, ADF_PROTO_MGMT_ASSOC)); |
| |
| #ifdef FEATURE_BUS_BANDWIDTH |
| /* start timer in sap/p2p_go */ |
| if (pHddApCtx->bApActive == VOS_FALSE) |
| { |
| SPIN_LOCK_BH(&pHddCtx->bus_bw_lock); |
| pHostapdAdapter->prev_tx_packets = pHostapdAdapter->stats.tx_packets; |
| pHostapdAdapter->prev_rx_packets = pHostapdAdapter->stats.rx_packets; |
| tlshim_get_intra_bss_fwd_pkts_count( |
| pHostapdAdapter->sessionId, |
| &pHostapdAdapter->prev_fwd_tx_packets, |
| &pHostapdAdapter->prev_fwd_rx_packets); |
| pHostapdAdapter->prev_tx_bytes = |
| pHostapdAdapter->stats.tx_bytes; |
| SPIN_UNLOCK_BH(&pHddCtx->bus_bw_lock); |
| hdd_start_bus_bw_compute_timer(pHostapdAdapter); |
| } |
| #endif |
| pHddApCtx->bApActive = VOS_TRUE; |
| // Stop AP inactivity timer |
| if (pHddApCtx->hdd_ap_inactivity_timer.state == VOS_TIMER_STATE_RUNNING) |
| { |
| vos_status = vos_timer_stop(&pHddApCtx->hdd_ap_inactivity_timer); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| hddLog(LOGE, FL("Failed to start AP inactivity timer")); |
| } |
| #ifdef FEATURE_WLAN_AUTO_SHUTDOWN |
| wlan_hdd_auto_shutdown_enable(pHddCtx, VOS_FALSE); |
| #endif |
| vos_wake_lock_timeout_acquire(&pHddCtx->sap_wake_lock, |
| HDD_SAP_WAKE_LOCK_DURATION, |
| WIFI_POWER_EVENT_WAKELOCK_SAP); |
| { |
| v_U16_t iesLen = event->iesLen; |
| |
| if (iesLen <= MAX_ASSOC_IND_IE_LEN ) |
| { |
| struct station_info *stainfo; |
| stainfo = vos_mem_malloc(sizeof(*stainfo)); |
| if (stainfo == NULL) { |
| hddLog(LOGE, FL("alloc station_info failed")); |
| return VOS_STATUS_E_NOMEM; |
| } |
| memset(stainfo, 0, sizeof(*stainfo)); |
| stainfo->assoc_req_ies = |
| (const u8 *)&event->ies[0]; |
| stainfo->assoc_req_ies_len = iesLen; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) |
| /* |
| * After Kernel 4.0, it's no longer need to set |
| * STATION_INFO_ASSOC_REQ_IES flag, as it |
| * changed to use assoc_req_ies_len length to |
| * check the existance of request IE. |
| */ |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,31)) || defined(WITH_BACKPORTS) |
| stainfo->filled |= STATION_INFO_ASSOC_REQ_IES; |
| #endif |
| #endif |
| cfg80211_new_sta(dev, |
| (const u8 *)&event->staMac.bytes[0], |
| stainfo, GFP_KERNEL); |
| vos_mem_free(stainfo); |
| } |
| else |
| { |
| hddLog(LOGE, FL(" Assoc Ie length is too long")); |
| } |
| } |
| |
| pScanInfo = &pHostapdAdapter->scan_info; |
| // Lets do abort scan to ensure smooth authentication for client |
| if ((pScanInfo != NULL) && pScanInfo->mScanPending) |
| { |
| hdd_abort_mac_scan(pHddCtx, pHostapdAdapter->sessionId, |
| eCSR_SCAN_ABORT_DEFAULT); |
| } |
| if (pHostapdAdapter->device_mode == WLAN_HDD_P2P_GO) |
| { |
| /* send peer status indication to oem app */ |
| hdd_SendPeerStatusIndToOemApp( |
| &event->staMac, |
| ePeerConnected, |
| event->timingMeasCap, |
| pHostapdAdapter->sessionId, |
| &event->chan_info, |
| pHostapdAdapter->device_mode); |
| } |
| |
| hdd_wlan_green_ap_add_sta(pHddCtx); |
| |
| if (pHostapdAdapter->device_mode == WLAN_HDD_SOFTAP && |
| !bAuthRequired && bWPSState == eANI_BOOLEAN_FALSE) { |
| uint32_t sub20_channelwidth; |
| |
| if (hdd_hostapd_sub20_channelwidth_can_switch( |
| pHostapdAdapter, &sub20_channelwidth)) |
| WLANSAP_set_sub20_channelwidth_with_csa( |
| WLAN_HDD_GET_SAP_CTX_PTR(pHostapdAdapter), |
| sub20_channelwidth); |
| } |
| break; |
| case eSAP_STA_DISASSOC_EVENT: |
| memcpy(wrqu.addr.sa_data, &pSapEvent->sapevt.sapStationDisassocCompleteEvent.staMac, |
| sizeof(v_MACADDR_t)); |
| hddLog(LOG1, " disassociated "MAC_ADDRESS_STR, MAC_ADDR_ARRAY(wrqu.addr.sa_data)); |
| |
| vos_status = vos_event_set(&pHostapdState->sta_disassoc_event); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "ERROR: Station deauth event reporting failed!!"); |
| |
| if (pSapEvent->sapevt.sapStationDisassocCompleteEvent.reason == eSAP_USR_INITATED_DISASSOC) |
| hddLog(LOG1," User initiated disassociation"); |
| else |
| hddLog(LOG1," MAC initiated disassociation"); |
| we_event = IWEVEXPIRED; |
| vos_status = hdd_softap_GetStaId(pHostapdAdapter, &pSapEvent->sapevt.sapStationDisassocCompleteEvent.staMac, &staId); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, FL("ERROR: HDD Failed to find sta id!!")); |
| return VOS_STATUS_E_FAILURE; |
| } |
| #ifdef IPA_OFFLOAD |
| if (!pHddCtx->isLogpInProgress && hdd_ipa_is_enabled(pHddCtx)) |
| { |
| status = hdd_ipa_wlan_evt(pHostapdAdapter, staId, WLAN_CLIENT_DISCONNECT, |
| pSapEvent->sapevt.sapStationDisassocCompleteEvent.staMac.bytes); |
| |
| if (status) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| ("ERROR: WLAN_CLIENT_DISCONNECT event failed!!")); |
| goto stopbss; |
| } |
| } |
| #endif |
| #ifdef QCA_PKT_PROTO_TRACE |
| /* Peer dis-associated, update into trace buffer */ |
| if (pHddCtx->cfg_ini->gEnableDebugLog) |
| { |
| vos_pkt_trace_buf_update("HA:DISASC"); |
| } |
| #endif /* QCA_PKT_PROTO_TRACE */ |
| |
| DPTRACE(adf_dp_trace_mgmt_pkt(ADF_DP_TRACE_MGMT_PACKET_RECORD, |
| pHostapdAdapter->sessionId, |
| ADF_PROTO_TYPE_MGMT, ADF_PROTO_MGMT_DISASSOC)); |
| |
| hdd_softap_DeregisterSTA(pHostapdAdapter, staId); |
| |
| pHddApCtx->bApActive = VOS_FALSE; |
| SPIN_LOCK_BH( &pHostapdAdapter->staInfo_lock ); |
| for (i = 0; i < WLAN_MAX_STA_COUNT; i++) |
| { |
| if (pHostapdAdapter->aStaInfo[i].isUsed && i != (WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter))->uBCStaId) |
| { |
| pHddApCtx->bApActive = VOS_TRUE; |
| break; |
| } |
| } |
| SPIN_UNLOCK_BH( &pHostapdAdapter->staInfo_lock ); |
| |
| // Start AP inactivity timer if no stations associated with it |
| if ((0 != (WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->nAPAutoShutOff)) |
| { |
| if (pHddApCtx->bApActive == FALSE) |
| { |
| if (pHddApCtx->hdd_ap_inactivity_timer.state == VOS_TIMER_STATE_STOPPED) |
| { |
| vos_status = vos_timer_start(&pHddApCtx->hdd_ap_inactivity_timer, (WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->nAPAutoShutOff * 1000); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| hddLog(LOGE, FL("Failed to init AP inactivity timer")); |
| } |
| else |
| VOS_ASSERT(vos_timer_getCurrentState(&pHddApCtx->hdd_ap_inactivity_timer) == VOS_TIMER_STATE_STOPPED); |
| } |
| } |
| #ifdef FEATURE_WLAN_AUTO_SHUTDOWN |
| wlan_hdd_auto_shutdown_enable(pHddCtx, VOS_TRUE); |
| #endif |
| |
| if (pSapEvent->sapevt.sapStationDisassocCompleteEvent.statusCode == |
| eSIR_SME_SAP_AUTH_OFFLOAD_PEER_UPDATE_STATUS) { |
| /** eSIR_SME_SAP_AUTH_OFFLOAD_PEER_UPDATE_STATUS indicates: |
| * The existing sta connection needs to be updated instead |
| * of clean up the sta. This condition could only happens |
| * when Host SAP sleep with WOW and SAP Auth offload enabled. |
| */ |
| |
| hddLog(LOG1,"SAP peer update sta:Id=%d, Mac="MAC_ADDRESS_STR, |
| pSapEvent->sapevt.sapStationDisassocCompleteEvent.staId, |
| MAC_ADDR_ARRAY(pSapEvent->sapevt. |
| sapStationDisassocCompleteEvent.staMac.bytes)); |
| } else { |
| hddLog(LOG1,"SAP del sta: staId=%d, staMac="MAC_ADDRESS_STR, |
| pSapEvent->sapevt.sapStationDisassocCompleteEvent.staId, |
| MAC_ADDR_ARRAY(pSapEvent->sapevt. |
| sapStationDisassocCompleteEvent.staMac.bytes)); |
| |
| cfg80211_del_sta(dev, |
| (const u8 *)&pSapEvent->sapevt.sapStationDisassocCompleteEvent.staMac.bytes[0], |
| GFP_KERNEL); |
| } |
| |
| //Update the beacon Interval if it is P2P GO |
| vos_status = hdd_change_mcc_go_beacon_interval(pHostapdAdapter); |
| if (VOS_STATUS_SUCCESS != vos_status) |
| { |
| hddLog(LOGE, "%s: failed to update Beacon interval %d", |
| __func__, vos_status); |
| } |
| if (pHostapdAdapter->device_mode == WLAN_HDD_P2P_GO) |
| { |
| /* send peer status indication to oem app */ |
| hdd_SendPeerStatusIndToOemApp( |
| &pSapEvent->sapevt.sapStationDisassocCompleteEvent.staMac, |
| ePeerDisconnected, 0, |
| pHostapdAdapter->sessionId, NULL, |
| pHostapdAdapter->device_mode); |
| } |
| |
| #ifdef FEATURE_BUS_BANDWIDTH |
| /*stop timer in sap/p2p_go */ |
| if (pHddApCtx->bApActive == FALSE) |
| { |
| SPIN_LOCK_BH(&pHddCtx->bus_bw_lock); |
| pHostapdAdapter->prev_tx_packets = 0; |
| pHostapdAdapter->prev_rx_packets = 0; |
| pHostapdAdapter->prev_fwd_tx_packets = 0; |
| pHostapdAdapter->prev_fwd_rx_packets = 0; |
| pHostapdAdapter->prev_tx_bytes = 0; |
| SPIN_UNLOCK_BH(&pHddCtx->bus_bw_lock); |
| hdd_stop_bus_bw_compute_timer(pHostapdAdapter); |
| } |
| #endif |
| |
| hdd_wlan_green_ap_del_sta(pHddCtx); |
| |
| break; |
| case eSAP_WPS_PBC_PROBE_REQ_EVENT: |
| { |
| static const char * message ="MLMEWPSPBCPROBEREQ.indication"; |
| union iwreq_data wreq; |
| |
| down(&pHddApCtx->semWpsPBCOverlapInd); |
| pHddApCtx->WPSPBCProbeReq.probeReqIELen = pSapEvent->sapevt.sapPBCProbeReqEvent.WPSPBCProbeReq.probeReqIELen; |
| |
| vos_mem_copy(pHddApCtx->WPSPBCProbeReq.probeReqIE, pSapEvent->sapevt.sapPBCProbeReqEvent.WPSPBCProbeReq.probeReqIE, |
| pHddApCtx->WPSPBCProbeReq.probeReqIELen); |
| |
| vos_mem_copy(pHddApCtx->WPSPBCProbeReq.peerMacAddr, pSapEvent->sapevt.sapPBCProbeReqEvent.WPSPBCProbeReq.peerMacAddr, sizeof(v_MACADDR_t)); |
| hddLog(LOG1, "WPS PBC probe req "MAC_ADDRESS_STR, MAC_ADDR_ARRAY(pHddApCtx->WPSPBCProbeReq.peerMacAddr)); |
| memset(&wreq, 0, sizeof(wreq)); |
| wreq.data.length = strlen(message); // This is length of message |
| wireless_send_event(dev, IWEVCUSTOM, &wreq, (char *)message); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| case eSAP_ASSOC_STA_CALLBACK_EVENT: |
| pAssocStasArray = pSapEvent->sapevt.sapAssocStaListEvent.pAssocStas; |
| if (pSapEvent->sapevt.sapAssocStaListEvent.noOfAssocSta != 0) |
| { // List of associated stations |
| for (i = 0; i < pSapEvent->sapevt.sapAssocStaListEvent.noOfAssocSta; i++) |
| { |
| hddLog(LOG1,"Associated Sta Num %d:assocId=%d, staId=%d, staMac="MAC_ADDRESS_STR, |
| i+1, |
| pAssocStasArray->assocId, |
| pAssocStasArray->staId, |
| MAC_ADDR_ARRAY(pAssocStasArray->staMac.bytes)); |
| pAssocStasArray++; |
| } |
| } |
| vos_mem_free(pSapEvent->sapevt.sapAssocStaListEvent.pAssocStas);// Release caller allocated memory here |
| pSapEvent->sapevt.sapAssocStaListEvent.pAssocStas = NULL; |
| return VOS_STATUS_SUCCESS; |
| case eSAP_REMAIN_CHAN_READY: |
| hdd_remainChanReadyHandler( pHostapdAdapter ); |
| return VOS_STATUS_SUCCESS; |
| case eSAP_UNKNOWN_STA_JOIN: |
| snprintf(unknownSTAEvent, IW_CUSTOM_MAX, "JOIN_UNKNOWN_STA-%02x:%02x:%02x:%02x:%02x:%02x", |
| pSapEvent->sapevt.sapUnknownSTAJoin.macaddr.bytes[0], |
| pSapEvent->sapevt.sapUnknownSTAJoin.macaddr.bytes[1], |
| pSapEvent->sapevt.sapUnknownSTAJoin.macaddr.bytes[2], |
| pSapEvent->sapevt.sapUnknownSTAJoin.macaddr.bytes[3], |
| pSapEvent->sapevt.sapUnknownSTAJoin.macaddr.bytes[4], |
| pSapEvent->sapevt.sapUnknownSTAJoin.macaddr.bytes[5]); |
| we_event = IWEVCUSTOM; /* Discovered a new node (AP mode). */ |
| wrqu.data.pointer = unknownSTAEvent; |
| wrqu.data.length = strlen(unknownSTAEvent); |
| we_custom_event_generic = (v_BYTE_t *)unknownSTAEvent; |
| hddLog(LOGE,"%s", unknownSTAEvent); |
| break; |
| |
| case eSAP_MAX_ASSOC_EXCEEDED: |
| snprintf(maxAssocExceededEvent, IW_CUSTOM_MAX, "Peer %02x:%02x:%02x:%02x:%02x:%02x denied" |
| " assoc due to Maximum Mobile Hotspot connections reached. Please disconnect" |
| " one or more devices to enable the new device connection", |
| pSapEvent->sapevt.sapMaxAssocExceeded.macaddr.bytes[0], |
| pSapEvent->sapevt.sapMaxAssocExceeded.macaddr.bytes[1], |
| pSapEvent->sapevt.sapMaxAssocExceeded.macaddr.bytes[2], |
| pSapEvent->sapevt.sapMaxAssocExceeded.macaddr.bytes[3], |
| pSapEvent->sapevt.sapMaxAssocExceeded.macaddr.bytes[4], |
| pSapEvent->sapevt.sapMaxAssocExceeded.macaddr.bytes[5]); |
| we_event = IWEVCUSTOM; /* Discovered a new node (AP mode). */ |
| wrqu.data.pointer = maxAssocExceededEvent; |
| wrqu.data.length = strlen(maxAssocExceededEvent); |
| we_custom_event_generic = (v_BYTE_t *)maxAssocExceededEvent; |
| hddLog(LOG1,"%s", maxAssocExceededEvent); |
| break; |
| case eSAP_STA_ASSOC_IND: |
| return VOS_STATUS_SUCCESS; |
| |
| case eSAP_DISCONNECT_ALL_P2P_CLIENT: |
| hddLog(LOG1, FL(" Disconnecting all the P2P Clients....")); |
| hdd_clear_all_sta(pHostapdAdapter, usrDataForCallback); |
| return VOS_STATUS_SUCCESS; |
| |
| case eSAP_MAC_TRIG_STOP_BSS_EVENT : |
| vos_status = hdd_stop_bss_link(pHostapdAdapter, usrDataForCallback); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| hddLog(LOGW, FL("hdd_stop_bss_link failed %d"), vos_status); |
| |
| return VOS_STATUS_SUCCESS; |
| |
| case eSAP_CHANNEL_CHANGE_EVENT: |
| hddLog(LOG1, FL("Received eSAP_CHANNEL_CHANGE_EVENT event")); |
| if (pHostapdState->bssState != BSS_STOP) { |
| /* Prevent suspend for new channel */ |
| hdd_hostapd_channel_prevent_suspend(pHostapdAdapter, |
| pSapEvent->sapevt.sapChSelected.pri_ch); |
| /* Allow suspend for old channel */ |
| hdd_hostapd_channel_allow_suspend(pHostapdAdapter, |
| pHddApCtx->operatingChannel); |
| } |
| /* SME/PE is already updated for new operation channel. So update |
| * HDD layer also here. This resolves issue in AP-AP mode where |
| * AP1 channel is changed due to RADAR then CAC is going on and |
| * START_BSS on new channel has not come to HDD. At this case if |
| * AP2 is start it needs current operation channel for MCC DFS |
| * restiction |
| */ |
| pHddApCtx->operatingChannel = |
| pSapEvent->sapevt.sapChSelected.pri_ch; |
| pHddApCtx->sapConfig.acs_cfg.pri_ch = |
| pSapEvent->sapevt.sapChSelected.pri_ch; |
| pHddApCtx->sapConfig.acs_cfg.ht_sec_ch = |
| pSapEvent->sapevt.sapChSelected.ht_sec_ch; |
| pHddApCtx->sapConfig.acs_cfg.vht_seg0_center_ch = |
| pSapEvent->sapevt.sapChSelected.vht_seg0_center_ch; |
| pHddApCtx->sapConfig.acs_cfg.vht_seg1_center_ch = |
| pSapEvent->sapevt.sapChSelected.vht_seg1_center_ch; |
| pHddApCtx->sapConfig.acs_cfg.ch_width = |
| pSapEvent->sapevt.sapChSelected.ch_width; |
| |
| /* Indicate operating channel change to hostapd |
| * only for non driver override acs |
| */ |
| if (pHostapdAdapter->device_mode == WLAN_HDD_SOFTAP && |
| pHddCtx->cfg_ini->force_sap_acs) |
| return VOS_STATUS_SUCCESS; |
| else |
| return hdd_chan_change_notify(pHostapdAdapter, dev, |
| pSapEvent->sapevt.sapChSelected.pri_ch); |
| case eSAP_ACS_SCAN_SUCCESS_EVENT: |
| return hdd_handle_acs_scan_event(pSapEvent, pHostapdAdapter); |
| case eSAP_DFS_NOL_GET: |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("Received eSAP_DFS_NOL_GET event")); |
| /* get the dfs nol from cnss */ |
| return hdd_wlan_get_dfs_nol( |
| pSapEvent->sapevt.sapDfsNolInfo.pDfsList, |
| pSapEvent->sapevt.sapDfsNolInfo.sDfsList); |
| case eSAP_DFS_NOL_SET: |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("Received eSAP_DFS_NOL_SET event")); |
| /* set the dfs nol to cnss */ |
| return hdd_wlan_set_dfs_nol( |
| pSapEvent->sapevt.sapDfsNolInfo.pDfsList, |
| pSapEvent->sapevt.sapDfsNolInfo.sDfsList); |
| case eSAP_ACS_CHANNEL_SELECTED: |
| hddLog(LOG1, FL("ACS Completed for wlan%d"), |
| pHostapdAdapter->dev->ifindex); |
| clear_bit(ACS_PENDING, &pHostapdAdapter->event_flags); |
|