| /* |
| * 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_main.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. |
| 02/24/10 Sudhir.S.Kohalli Added to support param for SoftAP module |
| 06/03/10 js - Added support to hostapd driven deauth/disassoc/mic failure |
| ==========================================================================*/ |
| |
| /*-------------------------------------------------------------------------- |
| Include Files |
| ------------------------------------------------------------------------*/ |
| #include<net/addrconf.h> |
| #include <wlan_hdd_includes.h> |
| #include <vos_api.h> |
| #include <vos_sched.h> |
| #ifdef WLAN_FEATURE_LPSS |
| #include <vos_utils.h> |
| #endif |
| #include <linux/etherdevice.h> |
| #include <linux/firmware.h> |
| #include <wcnss_api.h> |
| #include <wlan_hdd_tx_rx.h> |
| #include <wniApi.h> |
| #include <wlan_nlink_srv.h> |
| #include <wlan_hdd_cfg.h> |
| #include <wlan_ptt_sock_svc.h> |
| #include <dbglog_host.h> |
| #include <wlan_logging_sock_svc.h> |
| #include <wlan_hdd_wowl.h> |
| #include <wlan_hdd_misc.h> |
| #include <wlan_hdd_wext.h> |
| #include "wlan_hdd_trace.h" |
| #include "vos_types.h" |
| #include "vos_trace.h" |
| |
| #include <linux/wireless.h> |
| #include <net/cfg80211.h> |
| #include <linux/inetdevice.h> |
| #include <net/addrconf.h> |
| #include "wlan_hdd_cfg80211.h" |
| #include "wlan_hdd_p2p.h" |
| #include <linux/rtnetlink.h> |
| #include "sapApi.h" |
| #include <linux/semaphore.h> |
| #include <linux/ctype.h> |
| #include <linux/compat.h> |
| #include <linux/pm_qos.h> |
| #ifdef MSM_PLATFORM |
| #ifdef CONFIG_CNSS |
| #include <soc/qcom/subsystem_restart.h> |
| #endif |
| #endif |
| #include <wlan_hdd_hostapd.h> |
| #include <wlan_hdd_softap_tx_rx.h> |
| #include "cfgApi.h" |
| #include "wlan_hdd_dev_pwr.h" |
| #include "qwlan_version.h" |
| #include "wlan_qct_wda.h" |
| #include "wlan_hdd_tdls.h" |
| #ifdef FEATURE_WLAN_CH_AVOID |
| #include "vos_cnss.h" |
| #include "regdomain_common.h" |
| |
| extern int hdd_hostapd_stop (struct net_device *dev); |
| #endif /* FEATURE_WLAN_CH_AVOID */ |
| |
| #ifdef WLAN_FEATURE_NAN |
| #include "wlan_hdd_nan.h" |
| #endif /* WLAN_FEATURE_NAN */ |
| |
| #include "wlan_hdd_debugfs.h" |
| #include "epping_main.h" |
| #include "wlan_hdd_memdump.h" |
| |
| #include <wlan_hdd_ipa.h> |
| #if defined(HIF_PCI) |
| #include "if_pci.h" |
| #elif defined(HIF_USB) |
| #include "if_usb.h" |
| #elif defined(HIF_SDIO) |
| #include "if_ath_sdio.h" |
| #endif |
| #include "wma.h" |
| #include "ol_fw.h" |
| #include "wlan_hdd_ocb.h" |
| #include "wlan_hdd_tsf.h" |
| #include "tl_shim.h" |
| #include "wlan_hdd_oemdata.h" |
| #include "sirApi.h" |
| |
| #ifdef CNSS_GENL |
| #include <net/cnss_nl.h> |
| #endif |
| |
| #if defined(LINUX_QCMBR) |
| #define SIOCIOCTLTX99 (SIOCDEVPRIVATE+13) |
| #endif |
| |
| #ifdef QCA_ARP_SPOOFING_WAR |
| #include "ol_if_athvar.h" |
| #define HDD_ARP_PACKET_TYPE_OFFSET 12 |
| #endif |
| |
| #ifdef MODULE |
| #define WLAN_MODULE_NAME module_name(THIS_MODULE) |
| #else |
| #define WLAN_MODULE_NAME "wlan" |
| #endif |
| |
| #ifdef TIMER_MANAGER |
| #define TIMER_MANAGER_STR " +TIMER_MANAGER" |
| #else |
| #define TIMER_MANAGER_STR "" |
| #endif |
| |
| #ifdef MEMORY_DEBUG |
| #define MEMORY_DEBUG_STR " +MEMORY_DEBUG" |
| #else |
| #define MEMORY_DEBUG_STR "" |
| #endif |
| |
| #ifdef IPA_UC_OFFLOAD |
| /* If IPA UC data path is enabled, target should reserve extra tx descriptors |
| * for IPA WDI data path. |
| * Then host data path should allow less TX packet pumping in case |
| * IPA WDI data path enabled */ |
| #define WLAN_TFC_IPAUC_TX_DESC_RESERVE 100 |
| #endif /* IPA_UC_OFFLOAD */ |
| |
| /* the Android framework expects this param even though we don't use it */ |
| #define BUF_LEN 20 |
| static char fwpath_buffer[BUF_LEN]; |
| static struct kparam_string fwpath = { |
| .string = fwpath_buffer, |
| .maxlen = BUF_LEN, |
| }; |
| |
| static char *country_code; |
| static int enable_11d = -1; |
| static int enable_dfs_chan_scan = -1; |
| |
| #ifndef MODULE |
| static int wlan_hdd_inited; |
| static char fwpath_mode_local[BUF_LEN]; |
| #endif |
| |
| /* |
| * spinlock for synchronizing asynchronous request/response |
| * (full description of use in wlan_hdd_main.h) |
| */ |
| DEFINE_SPINLOCK(hdd_context_lock); |
| |
| /* |
| * The rate at which the driver sends RESTART event to supplicant |
| * once the function 'vos_wlanRestart()' is called |
| * |
| */ |
| #define WLAN_HDD_RESTART_RETRY_DELAY_MS 5000 /* 5 second */ |
| #define WLAN_HDD_RESTART_RETRY_MAX_CNT 5 /* 5 retries */ |
| |
| /* |
| * Size of Driver command strings from upper layer |
| */ |
| #define SIZE_OF_SETROAMMODE 11 /* size of SETROAMMODE */ |
| #define SIZE_OF_GETROAMMODE 11 /* size of GETROAMMODE */ |
| |
| /* |
| * Ibss prop IE from command will be of size: |
| * size = sizeof(oui) + sizeof(oui_data) + 1(Element ID) + 1(EID Length) |
| * OUI_DATA should be at least 3 bytes long |
| */ |
| #define WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH (3) |
| |
| |
| #if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD) |
| #define TID_MIN_VALUE 0 |
| #define TID_MAX_VALUE 15 |
| #endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */ |
| /* |
| * Maximum buffer size used for returning the data back to user space |
| */ |
| #define WLAN_MAX_BUF_SIZE 1024 |
| #define WLAN_PRIV_DATA_MAX_LEN 8192 |
| /* |
| * Driver miracast parameters 0-Disabled |
| * 1-Source, 2-Sink |
| */ |
| #define WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL 0 |
| #define WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL 2 |
| |
| /* |
| * When ever we need to print IBSSPEERINFOALL for more than 16 STA |
| * we will split the printing. |
| */ |
| #define NUM_OF_STA_DATA_TO_PRINT 16 |
| |
| #define WLAN_NLINK_CESIUM 30 |
| |
| /*Nss - 1, (Nss = 2 for 2x2)*/ |
| #define NUM_OF_SOUNDING_DIMENSIONS 1 |
| |
| /* |
| * Android DRIVER command structures |
| */ |
| struct android_wifi_reassoc_params { |
| unsigned char bssid[18]; |
| int channel; |
| }; |
| |
| #define ANDROID_WIFI_ACTION_FRAME_SIZE 1040 |
| struct android_wifi_af_params { |
| unsigned char bssid[18]; |
| int channel; |
| int dwell_time; |
| int len; |
| unsigned char data[ANDROID_WIFI_ACTION_FRAME_SIZE]; |
| } ; |
| |
| #ifdef WLAN_FEATURE_EXTWOW_SUPPORT |
| #define WLAN_HDD_MAX_TCP_PORT 65535 |
| #define WLAN_WAIT_TIME_READY_TO_EXTWOW 2000 |
| #endif |
| |
| #define AUTO_SUSPEND_DELAY_MS 1500 |
| |
| static vos_wake_lock_t wlan_wake_lock; |
| /* set when SSR is needed after unload */ |
| static e_hdd_ssr_required isSsrRequired = HDD_SSR_NOT_REQUIRED; |
| |
| #define WOW_MAX_FILTER_LISTS 1 |
| #define WOW_MAX_FILTERS_PER_LIST 4 |
| #define WOW_MIN_PATTERN_SIZE 6 |
| #define WOW_MAX_PATTERN_SIZE 64 |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)) || defined(WITH_BACKPORTS) |
| static const struct wiphy_wowlan_support wowlan_support_reg_init = { |
| .flags = WIPHY_WOWLAN_ANY | |
| WIPHY_WOWLAN_MAGIC_PKT | |
| WIPHY_WOWLAN_DISCONNECT | |
| WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | |
| WIPHY_WOWLAN_GTK_REKEY_FAILURE | |
| WIPHY_WOWLAN_EAP_IDENTITY_REQ | |
| WIPHY_WOWLAN_4WAY_HANDSHAKE | |
| WIPHY_WOWLAN_RFKILL_RELEASE, |
| .n_patterns = WOW_MAX_FILTER_LISTS * WOW_MAX_FILTERS_PER_LIST, |
| .pattern_min_len = WOW_MIN_PATTERN_SIZE, |
| .pattern_max_len = WOW_MAX_PATTERN_SIZE, |
| }; |
| #endif |
| |
| /* Internal function declarations */ |
| static int hdd_driver_init(void); |
| static void hdd_driver_exit(void); |
| |
| /* Internal function declarations */ |
| |
| static void hdd_tx_fail_ind_callback(v_U8_t *MacAddr, v_U8_t seqNo); |
| |
| static struct sock *cesium_nl_srv_sock; |
| static v_U16_t cesium_pid; |
| |
| static int hdd_ParseIBSSTXFailEventParams(tANI_U8 *pValue, |
| tANI_U8 *tx_fail_count, |
| tANI_U16 *pid); |
| |
| void wlan_hdd_restart_timer_cb(v_PVOID_t usrDataForCallback); |
| |
| /** |
| * struct init_comp - Driver loading status |
| * @wlan_start_comp: Completion event |
| * @status: Success/Failure |
| */ |
| struct init_comp { |
| struct completion wlan_start_comp; |
| int status; |
| }; |
| static struct init_comp wlan_comp; |
| |
| /* Keep track unload status */ |
| static uint32_t g_current_unload_state; |
| #define TRACK_UNLOAD_STATUS(state) (g_current_unload_state = state) |
| |
| #ifdef QCA_WIFI_FTM |
| extern int hdd_ftm_stop(hdd_context_t *pHddCtx); |
| #endif |
| #ifdef FEATURE_WLAN_AUTO_SHUTDOWN |
| v_VOID_t wlan_hdd_auto_shutdown_cb(v_VOID_t); |
| #endif |
| |
| /* Store WLAN driver version info in a global variable such that crash debugger |
| can extract it from driver debug symbol and crashdump for post processing */ |
| tANI_U8 g_wlan_driver_version[ ] = QWLAN_VERSIONSTR; |
| |
| /** |
| * hdd_device_mode_to_string() - return string conversion of device mode |
| * @device_mode: device mode |
| * |
| * This utility function helps log string conversion of device mode. |
| * |
| * Return: string conversion of device mode, if match found; |
| * "Unknown" otherwise. |
| */ |
| const char* hdd_device_mode_to_string(uint8_t device_mode) |
| { |
| switch (device_mode) { |
| CASE_RETURN_STRING(WLAN_HDD_INFRA_STATION); |
| CASE_RETURN_STRING(WLAN_HDD_SOFTAP); |
| CASE_RETURN_STRING(WLAN_HDD_P2P_CLIENT); |
| CASE_RETURN_STRING(WLAN_HDD_P2P_GO); |
| CASE_RETURN_STRING(WLAN_HDD_MONITOR); |
| CASE_RETURN_STRING(WLAN_HDD_FTM); |
| CASE_RETURN_STRING(WLAN_HDD_IBSS); |
| CASE_RETURN_STRING(WLAN_HDD_P2P_DEVICE); |
| CASE_RETURN_STRING(WLAN_HDD_OCB); |
| CASE_RETURN_STRING(WLAN_HDD_NDI); |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| #ifdef QCA_LL_TX_FLOW_CT |
| |
| /** |
| * wlan_hdd_clean_tx_flow_control_timer - Function cleans tx flow control timer |
| * @hddctx: pointer to hddctx |
| * @hdd_adapter_t: pointer to hdd_adapter_t |
| * |
| * Function deregister's, destroy tx flow control timer |
| * |
| * Return: None |
| */ |
| void wlan_hdd_clean_tx_flow_control_timer(hdd_context_t *hddctx, |
| hdd_adapter_t *adapter) |
| { |
| WLANTL_DeRegisterTXFlowControl(hddctx->pvosContext, |
| adapter->sessionId); |
| if (adapter->tx_flow_timer_initialized == VOS_TRUE) { |
| vos_timer_destroy(&adapter->tx_flow_control_timer); |
| adapter->tx_flow_timer_initialized = VOS_FALSE; |
| } |
| } |
| |
| #endif |
| |
| /** |
| * wlan_hdd_find_opclass() - Find operating class for a channel |
| * @hal: handler to HAL |
| * @channel: channel id |
| * @bw_offset: bandwidth offset |
| * |
| * Function invokes sme api to find the operating class |
| * |
| * Return: operating class |
| */ |
| uint8_t wlan_hdd_find_opclass(tHalHandle hal, uint8_t channel, |
| uint8_t bw_offset) |
| { |
| uint8_t opclass = 0; |
| |
| sme_get_opclass(hal, channel, bw_offset, &opclass); |
| return opclass; |
| } |
| |
| #ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| void hdd_csa_notify_cb |
| ( |
| void *hdd_context, |
| void *indi_param |
| ); |
| #endif//#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| |
| #ifdef FEATURE_GREEN_AP |
| |
| static void hdd_wlan_green_ap_timer_fn(void *phddctx) |
| { |
| hdd_context_t *pHddCtx = (hdd_context_t *)phddctx; |
| hdd_green_ap_ctx_t *green_ap; |
| |
| if (0 != wlan_hdd_validate_context(pHddCtx)) |
| return; |
| |
| green_ap = pHddCtx->green_ap_ctx; |
| |
| if (green_ap) |
| hdd_wlan_green_ap_mc(pHddCtx, green_ap->ps_event); |
| } |
| |
| static VOS_STATUS hdd_wlan_green_ap_attach(hdd_context_t *pHddCtx) |
| { |
| hdd_green_ap_ctx_t *green_ap; |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| green_ap = vos_mem_malloc(sizeof(hdd_green_ap_ctx_t)); |
| |
| if (!green_ap) { |
| hddLog(LOGP, FL("Memory allocation for Green-AP failed!")); |
| status = VOS_STATUS_E_NOMEM; |
| goto error; |
| } |
| |
| vos_mem_zero((void *)green_ap, sizeof(*green_ap)); |
| green_ap->pHddContext = pHddCtx; |
| pHddCtx->green_ap_ctx = green_ap; |
| |
| green_ap->ps_state = GREEN_AP_PS_OFF_STATE; |
| green_ap->ps_event = 0; |
| green_ap->num_nodes = 0; |
| green_ap->ps_on_time = GREEN_AP_PS_ON_TIME; |
| green_ap->ps_delay_time = GREEN_AP_PS_DELAY_TIME; |
| |
| vos_timer_init(&green_ap->ps_timer, |
| VOS_TIMER_TYPE_SW, |
| hdd_wlan_green_ap_timer_fn, |
| (void *)pHddCtx); |
| |
| error: |
| |
| EXIT(); |
| return status; |
| } |
| |
| static VOS_STATUS hdd_wlan_green_ap_deattach(hdd_context_t *pHddCtx) |
| { |
| hdd_green_ap_ctx_t *green_ap = pHddCtx->green_ap_ctx; |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (green_ap == NULL) { |
| hddLog(LOG1, FL("Green-AP is not enabled")); |
| status = VOS_STATUS_E_NOSUPPORT; |
| goto done; |
| } |
| |
| /* check if the timer status is destroyed */ |
| if (VOS_TIMER_STATE_RUNNING == |
| vos_timer_getCurrentState(&green_ap->ps_timer)) |
| { |
| vos_timer_stop(&green_ap->ps_timer); |
| } |
| |
| /* Destroy the Green AP timer */ |
| if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy( |
| &green_ap->ps_timer))) |
| { |
| hddLog(LOG1, FL("Cannot deallocate Green-AP's timer")); |
| } |
| |
| /* release memory */ |
| vos_mem_zero((void *)green_ap, sizeof(*green_ap)); |
| vos_mem_free(green_ap); |
| pHddCtx->green_ap_ctx = NULL; |
| |
| done: |
| |
| EXIT(); |
| return status; |
| } |
| |
| static void hdd_wlan_green_ap_update(hdd_context_t *pHddCtx, |
| hdd_green_ap_ps_state_t state, |
| hdd_green_ap_event_t event) |
| { |
| hdd_green_ap_ctx_t *green_ap = pHddCtx->green_ap_ctx; |
| |
| green_ap->ps_state = state; |
| green_ap->ps_event = event; |
| } |
| |
| static int hdd_wlan_green_ap_enable(hdd_adapter_t *pHostapdAdapter, |
| v_U8_t enable) |
| { |
| int ret = 0; |
| |
| hddLog(LOG1, "%s: Set Green-AP val: %d", __func__, enable); |
| |
| ret = process_wma_set_command( |
| (int)pHostapdAdapter->sessionId, |
| (int)WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID, |
| enable, |
| DBG_CMD); |
| |
| return ret; |
| } |
| |
| void hdd_wlan_green_ap_mc(hdd_context_t *pHddCtx, |
| hdd_green_ap_event_t event) |
| { |
| hdd_green_ap_ctx_t *green_ap = pHddCtx->green_ap_ctx; |
| hdd_adapter_t *pAdapter = NULL; |
| |
| if (green_ap == NULL) |
| return ; |
| |
| hddLog(LOG1, "%s: Green-AP event: %d, state: %d, num_nodes: %d", |
| __func__, event, green_ap->ps_state, green_ap->num_nodes); |
| |
| /* handle the green ap ps event */ |
| switch(event) { |
| case GREEN_AP_PS_START_EVENT: |
| green_ap->ps_enable = 1; |
| break; |
| |
| case GREEN_AP_PS_STOP_EVENT: |
| green_ap->ps_enable = 0; |
| break; |
| |
| case GREEN_AP_ADD_STA_EVENT: |
| green_ap->num_nodes++; |
| break; |
| |
| case GREEN_AP_DEL_STA_EVENT: |
| if (green_ap->num_nodes) |
| green_ap->num_nodes--; |
| break; |
| |
| case GREEN_AP_PS_ON_EVENT: |
| case GREEN_AP_PS_WAIT_EVENT: |
| break; |
| |
| default: |
| hddLog(LOGE, "%s: invalid event %d", __func__, event); |
| break; |
| } |
| |
| pAdapter = hdd_get_adapter (pHddCtx, WLAN_HDD_SOFTAP ); |
| |
| if (pAdapter == NULL) { |
| hddLog(LOGE, FL("Green-AP no SAP adapter")); |
| goto done; |
| } |
| |
| /* Confirm that power save is enabled before doing state transitions */ |
| if (!green_ap->ps_enable) { |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("green ap is disabled")); |
| hdd_wlan_green_ap_update(pHddCtx, |
| GREEN_AP_PS_OFF_STATE, GREEN_AP_PS_WAIT_EVENT); |
| if (hdd_wlan_green_ap_enable(pAdapter, 0)) |
| hddLog(LOGE, FL("failed to set green ap mode")); |
| goto done; |
| } |
| |
| /* handle the green ap ps state */ |
| switch(green_ap->ps_state) { |
| case GREEN_AP_PS_IDLE_STATE: |
| hdd_wlan_green_ap_update(pHddCtx, |
| GREEN_AP_PS_OFF_STATE, GREEN_AP_PS_WAIT_EVENT); |
| break; |
| |
| case GREEN_AP_PS_OFF_STATE: |
| if (!green_ap->num_nodes) { |
| hdd_wlan_green_ap_update(pHddCtx, |
| GREEN_AP_PS_WAIT_STATE, GREEN_AP_PS_WAIT_EVENT); |
| vos_timer_start(&green_ap->ps_timer, |
| green_ap->ps_delay_time); |
| } |
| break; |
| |
| case GREEN_AP_PS_WAIT_STATE: |
| if (!green_ap->num_nodes) { |
| hdd_wlan_green_ap_update(pHddCtx, |
| GREEN_AP_PS_ON_STATE, GREEN_AP_PS_WAIT_EVENT); |
| |
| hdd_wlan_green_ap_enable(pAdapter, 1); |
| |
| if (green_ap->ps_on_time) { |
| hdd_wlan_green_ap_update(pHddCtx, |
| 0, GREEN_AP_PS_WAIT_EVENT); |
| vos_timer_start(&green_ap->ps_timer, |
| green_ap->ps_on_time); |
| } |
| } else { |
| hdd_wlan_green_ap_update(pHddCtx, |
| GREEN_AP_PS_OFF_STATE, GREEN_AP_PS_WAIT_EVENT); |
| } |
| break; |
| |
| case GREEN_AP_PS_ON_STATE: |
| if (green_ap->num_nodes) { |
| if (hdd_wlan_green_ap_enable(pAdapter, 0)) { |
| hddLog(LOGE, FL("FAILED TO SET GREEN-AP mode")); |
| goto done; |
| } |
| hdd_wlan_green_ap_update(pHddCtx, |
| GREEN_AP_PS_OFF_STATE, GREEN_AP_PS_WAIT_EVENT); |
| } else if ((green_ap->ps_event == GREEN_AP_PS_WAIT_EVENT) && |
| (green_ap->ps_on_time)) { |
| |
| /* ps_on_time timeout, switch to ps off */ |
| hdd_wlan_green_ap_update(pHddCtx, |
| GREEN_AP_PS_WAIT_STATE, GREEN_AP_PS_ON_EVENT); |
| |
| if (hdd_wlan_green_ap_enable(pAdapter, 0)) { |
| hddLog(LOGE, FL("FAILED TO SET GREEN-AP mode")); |
| goto done; |
| } |
| |
| vos_timer_start(&green_ap->ps_timer, |
| green_ap->ps_delay_time); |
| } |
| break; |
| |
| default: |
| hddLog(LOGE, "%s: invalid state %d", __func__, green_ap->ps_state); |
| hdd_wlan_green_ap_update(pHddCtx, |
| GREEN_AP_PS_OFF_STATE, GREEN_AP_PS_WAIT_EVENT); |
| break; |
| } |
| |
| done: |
| return; |
| } |
| |
| /** |
| * hdd_wlan_green_ap_init() - Initialize Green AP feature |
| * @hdd_ctx: HDD global context |
| * |
| * Return: none |
| */ |
| void hdd_wlan_green_ap_init(struct hdd_context_s *hdd_ctx) |
| { |
| if (!VOS_IS_STATUS_SUCCESS(hdd_wlan_green_ap_attach(hdd_ctx))) |
| hddLog(LOGE, FL("Failed to allocate Green-AP resource")); |
| } |
| |
| /** |
| * hdd_wlan_green_ap_deinit() - De-initialize Green AP feature |
| * @hdd_ctx: HDD global context |
| * |
| * Return: none |
| */ |
| void hdd_wlan_green_ap_deinit(struct hdd_context_s *hdd_ctx) |
| { |
| if (!VOS_IS_STATUS_SUCCESS(hdd_wlan_green_ap_deattach(hdd_ctx))) |
| hddLog(LOGE, FL("Cannot deallocate Green-AP resource")); |
| } |
| |
| /** |
| * wlan_hdd_set_egap_support() - helper function to set egap support flag |
| * @hdd_ctx: pointer to hdd context |
| * @cfg: pointer to hdd target configuration knob |
| * |
| * Return: None |
| */ |
| void wlan_hdd_set_egap_support(hdd_context_t *hdd_ctx, struct hdd_tgt_cfg *cfg) |
| { |
| if (hdd_ctx && cfg) |
| hdd_ctx->green_ap_ctx->egap_support = cfg->egap_support; |
| } |
| |
| /** |
| * hdd_wlan_is_egap_enabled() - Get Enhance Green AP feature status |
| * @fw_egap_support: flag whether firmware supports egap or not |
| * @cfg: pointer to the struct hdd_config_t |
| * |
| * Return: true if firmware, feature_flag and ini are all enabled the egap |
| */ |
| static bool hdd_wlan_is_egap_enabled(bool fw_egap_support, hdd_config_t *cfg) |
| { |
| /* check if the firmware and ini are both enabled the egap, |
| * and also the feature_flag enable. |
| */ |
| if (fw_egap_support && cfg->enable_egap && |
| cfg->egap_feature_flag) |
| return true; |
| |
| return false; |
| } |
| |
| |
| /** |
| * hdd_wlan_enable_egap() - Enable Enhance Green AP |
| * @hdd_ctx: HDD global context |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| int hdd_wlan_enable_egap(struct hdd_context_s *hdd_ctx) |
| { |
| hdd_config_t *cfg; |
| |
| if (!hdd_ctx) { |
| hddLog(LOGE, FL("hdd context is NULL")); |
| return -EINVAL; |
| } |
| |
| cfg = hdd_ctx->cfg_ini; |
| |
| if (!cfg) { |
| hddLog(LOGE, FL("hdd cfg is NULL")); |
| return -EINVAL; |
| } |
| |
| if (!hdd_ctx->green_ap_ctx) { |
| hddLog(LOGE, FL("green ap context is NULL")); |
| return -EINVAL; |
| } |
| |
| if (!hdd_wlan_is_egap_enabled(hdd_ctx->green_ap_ctx->egap_support, |
| hdd_ctx->cfg_ini)) |
| return -ENOTSUPP; |
| |
| if (VOS_STATUS_SUCCESS != sme_send_egap_conf_params(cfg->enable_egap, |
| cfg->egap_inact_time, |
| cfg->egap_wait_time, |
| cfg->egap_feature_flag)) |
| return -EINVAL; |
| return 0; |
| } |
| |
| /** |
| * hdd_wlan_green_ap_start_bss() - Notify Green AP of Start BSS event |
| * @hdd_ctx: HDD global context |
| * |
| * Return: none |
| */ |
| void hdd_wlan_green_ap_start_bss(struct hdd_context_s *hdd_ctx) |
| { |
| hdd_config_t *cfg; |
| |
| if (!hdd_ctx) { |
| hddLog(LOGE, FL("hdd context is NULL")); |
| return; |
| } |
| |
| cfg = hdd_ctx->cfg_ini; |
| |
| if (!cfg) { |
| hddLog(LOGE, FL("hdd cfg is NULL")); |
| return; |
| } |
| |
| if (!hdd_ctx->green_ap_ctx) { |
| hddLog(LOGE, FL("green ap context is NULL")); |
| return; |
| } |
| |
| if (hdd_wlan_is_egap_enabled(hdd_ctx->green_ap_ctx->egap_support, |
| hdd_ctx->cfg_ini)) |
| return; |
| |
| if ((hdd_ctx->concurrency_mode & VOS_SAP) && |
| !(hdd_ctx->concurrency_mode & (~VOS_SAP)) && |
| cfg->enable2x2 && cfg->enableGreenAP) { |
| hddLog(LOG1, |
| FL("Green AP enabled - sta_con: %d, 2x2: %d, GAP: %d"), |
| (VOS_STA & hdd_ctx->concurrency_mode), |
| cfg->enable2x2, cfg->enableGreenAP); |
| hdd_wlan_green_ap_mc(hdd_ctx, GREEN_AP_PS_START_EVENT); |
| } else { |
| hdd_wlan_green_ap_mc(hdd_ctx, GREEN_AP_PS_STOP_EVENT); |
| hddLog(LOG1, |
| FL("Green AP disabled- sta_con: %d, 2x2: %d, GAP: %d"), |
| (VOS_STA & hdd_ctx->concurrency_mode), |
| cfg->enable2x2, cfg->enableGreenAP); |
| } |
| } |
| |
| /** |
| * hdd_wlan_green_ap_stop_bss() - Notify Green AP of Stop BSS event |
| * @hdd_ctx: HDD global context |
| * |
| * Return: none |
| */ |
| void hdd_wlan_green_ap_stop_bss(struct hdd_context_s *hdd_ctx) |
| { |
| if (!hdd_ctx) { |
| hddLog(LOGE, FL("hdd context is NULL")); |
| return; |
| } |
| |
| if (!hdd_ctx->cfg_ini) { |
| hddLog(LOGE, FL("hdd cfg is NULL")); |
| return; |
| } |
| |
| if (!hdd_ctx->green_ap_ctx) { |
| hddLog(LOGE, FL("green ap context is NULL")); |
| return; |
| } |
| |
| if (hdd_wlan_is_egap_enabled(hdd_ctx->green_ap_ctx->egap_support, |
| hdd_ctx->cfg_ini)) |
| return; |
| |
| /* For AP+AP mode, only trigger GREEN_AP_PS_STOP_EVENT, when the |
| * last AP stops. |
| */ |
| if (1 == (hdd_ctx->no_of_open_sessions[VOS_STA_SAP_MODE])) |
| hdd_wlan_green_ap_mc(hdd_ctx, GREEN_AP_PS_STOP_EVENT); |
| } |
| |
| /** |
| * hdd_wlan_green_ap_add_sta() - Notify Green AP of Add Station event |
| * @hdd_ctx: HDD global context |
| * |
| * Return: none |
| */ |
| void hdd_wlan_green_ap_add_sta(struct hdd_context_s *hdd_ctx) |
| { |
| if (!hdd_ctx) { |
| hddLog(LOGE, FL("hdd context is NULL")); |
| return; |
| } |
| |
| if (!hdd_ctx->cfg_ini) { |
| hddLog(LOGE, FL("hdd cfg is NULL")); |
| return; |
| } |
| |
| if (!hdd_ctx->green_ap_ctx) { |
| hddLog(LOGE, FL("green ap context is NULL")); |
| return; |
| } |
| |
| if (hdd_wlan_is_egap_enabled(hdd_ctx->green_ap_ctx->egap_support, |
| hdd_ctx->cfg_ini)) |
| return; |
| |
| hdd_wlan_green_ap_mc(hdd_ctx, GREEN_AP_ADD_STA_EVENT); |
| } |
| |
| /** |
| * hdd_wlan_green_ap_del_sta() - Notify Green AP of Delete Station event |
| * @hdd_ctx: HDD global context |
| * |
| * Return: none |
| */ |
| void hdd_wlan_green_ap_del_sta(struct hdd_context_s *hdd_ctx) |
| { |
| if (!hdd_ctx) { |
| hddLog(LOGE, FL("hdd context is NULL")); |
| return; |
| } |
| |
| if (!hdd_ctx->cfg_ini) { |
| hddLog(LOGE, FL("hdd cfg is NULL")); |
| return; |
| } |
| |
| if (!hdd_ctx->green_ap_ctx) { |
| hddLog(LOGE, FL("green ap context is NULL")); |
| return; |
| } |
| |
| if (hdd_wlan_is_egap_enabled(hdd_ctx->green_ap_ctx->egap_support, |
| hdd_ctx->cfg_ini)) |
| return; |
| |
| hdd_wlan_green_ap_mc(hdd_ctx, GREEN_AP_DEL_STA_EVENT); |
| } |
| |
| #endif /* FEATURE_GREEN_AP */ |
| |
| /** |
| * hdd_lost_link_info_cb() - callback function to get lost link information |
| * @context: HDD context |
| * @lost_link_info: lost link information |
| * |
| * Return: none |
| */ |
| static void hdd_lost_link_info_cb(void *context, |
| struct sir_lost_link_info *lost_link_info) |
| { |
| hdd_context_t *hdd_ctx = (hdd_context_t *)context; |
| int status; |
| hdd_adapter_t *adapter; |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return; |
| |
| if (NULL == lost_link_info) { |
| hddLog(LOGE, "%s: lost_link_info is NULL", __func__); |
| return; |
| } |
| |
| adapter = hdd_get_adapter_by_vdev(hdd_ctx, lost_link_info->vdev_id); |
| if (NULL == adapter) { |
| hddLog(LOGE, "%s: invalid adapter", __func__); |
| return; |
| } |
| |
| adapter->rssi_on_disconnect = lost_link_info->rssi; |
| hddLog(LOG1, "%s: rssi on disconnect %d", |
| __func__, adapter->rssi_on_disconnect); |
| } |
| |
| /** |
| * __hdd_smps_force_mode_cb() - callback for smps force mode |
| * event |
| * @context: HDD context |
| * @smps_mode_event: smps force mode event info |
| * |
| * Return: none |
| */ |
| static void __hdd_smps_force_mode_cb(void *context, |
| struct sir_smps_force_mode_event *smps_mode_event) |
| { |
| hdd_context_t *hdd_ctx = (hdd_context_t *)context; |
| int status; |
| hdd_adapter_t *adapter; |
| |
| ENTER(); |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return; |
| |
| if (NULL == smps_mode_event) { |
| hddLog(LOGE, FL("smps_mode_event is NULL")); |
| return; |
| } |
| |
| adapter = hdd_get_adapter_by_vdev(hdd_ctx, smps_mode_event->vdev_id); |
| if (NULL == adapter) { |
| hddLog(LOGE, FL("Invalid adapter")); |
| return; |
| } |
| |
| adapter->smps_force_mode_status = smps_mode_event->status; |
| |
| complete(&adapter->smps_force_mode_comp_var); |
| |
| hddLog(LOG1, FL("status %d vdev_id: %d"), |
| smps_mode_event->status, smps_mode_event->vdev_id); |
| } |
| |
| /** |
| * hdd_smps_force_mode_cb() - Wrapper to protect |
| * __hdd_smps_force_mode_cb callback for smps force mode event |
| * @context: HDD context |
| * @smps_mode_event: smps force mode event info |
| * |
| * Return: none |
| */ |
| static void hdd_smps_force_mode_cb(void *context, |
| struct sir_smps_force_mode_event *smps_mode_event) |
| { |
| vos_ssr_protect(__func__); |
| __hdd_smps_force_mode_cb(context, smps_mode_event); |
| vos_ssr_unprotect(__func__); |
| |
| } |
| |
| #if defined (FEATURE_WLAN_MCC_TO_SCC_SWITCH) || defined (FEATURE_WLAN_STA_AP_MODE_DFS_DISABLE) || defined (FEATURE_WLAN_CH_AVOID) |
| /** |
| * wlan_hdd_restart_sap() - to restart SAP in driver internally |
| * @ap_adapter: - Pointer to SAP hdd_adapter_t structure |
| * |
| * wlan_hdd_restart_sap first delete SAP and do cleanup. |
| * After that WLANSAP_StartBss start re-start process of SAP. |
| * |
| * Return: None |
| */ |
| |
| void wlan_hdd_restart_sap(hdd_adapter_t *ap_adapter) |
| { |
| hdd_ap_ctx_t *pHddApCtx; |
| hdd_hostapd_state_t *pHostapdState; |
| VOS_STATUS vos_status; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(ap_adapter); |
| #ifdef CFG80211_DEL_STA_V2 |
| struct station_del_parameters delStaParams; |
| #endif |
| tsap_Config_t *pConfig; |
| void *p_sap_ctx; |
| |
| pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); |
| pConfig = &pHddApCtx->sapConfig; |
| p_sap_ctx = pHddApCtx->sapContext; |
| |
| mutex_lock(&pHddCtx->sap_lock); |
| if (test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags)) { |
| #ifdef CFG80211_DEL_STA_V2 |
| delStaParams.mac = NULL; |
| delStaParams.subtype = SIR_MAC_MGMT_DEAUTH >> 4; |
| delStaParams.reason_code = eCsrForcedDeauthSta; |
| wlan_hdd_cfg80211_del_station(ap_adapter->wdev.wiphy, ap_adapter->dev, |
| &delStaParams); |
| #else |
| wlan_hdd_cfg80211_del_station(ap_adapter->wdev.wiphy, ap_adapter->dev, |
| NULL); |
| #endif |
| hdd_cleanup_actionframe(pHddCtx, ap_adapter); |
| |
| pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(ap_adapter); |
| vos_event_reset(&pHostapdState->stop_bss_event); |
| |
| if (VOS_STATUS_SUCCESS == WLANSAP_StopBss(p_sap_ctx)) { |
| vos_status = vos_wait_single_event(&pHostapdState->stop_bss_event, |
| 10000); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) { |
| hddLog(LOGE, FL("SAP Stop Failed")); |
| goto end; |
| } |
| } |
| clear_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags); |
| wlan_hdd_decr_active_session(pHddCtx, ap_adapter->device_mode); |
| hddLog(LOGE, FL("SAP Stop Success")); |
| #ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| /*this delay is needed to ensure proper resource cleanup of SAP*/ |
| vos_sleep(1000); |
| #endif //#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| if (pHddCtx->cfg_ini->apOBSSProtEnabled) |
| vos_runtime_pm_allow_suspend(pHddCtx->runtime_context.obss); |
| if (0 != wlan_hdd_cfg80211_update_apies(ap_adapter)) { |
| hddLog(LOGE, FL("SAP Not able to set AP IEs")); |
| WLANSAP_ResetSapConfigAddIE(pConfig, eUPDATE_IE_ALL); |
| goto end; |
| } |
| |
| vos_event_reset(&pHostapdState->vosEvent); |
| if (WLANSAP_StartBss(p_sap_ctx, hdd_hostapd_SAPEventCB, pConfig, |
| (v_PVOID_t)ap_adapter->dev) != VOS_STATUS_SUCCESS) { |
| hddLog(LOGE, FL("SAP Start Bss fail")); |
| WLANSAP_ResetSapConfigAddIE(pConfig, eUPDATE_IE_ALL); |
| goto end; |
| } |
| |
| hddLog(LOG1, FL("Waiting for SAP to start")); |
| vos_status = vos_wait_single_event(&pHostapdState->vosEvent, 10000); |
| WLANSAP_ResetSapConfigAddIE(pConfig, eUPDATE_IE_ALL); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) { |
| hddLog(LOGE, FL("SAP Start failed")); |
| goto end; |
| } |
| hddLog(LOGE, FL("SAP Start Success")); |
| set_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags); |
| wlan_hdd_incr_active_session(pHddCtx, ap_adapter->device_mode); |
| pHostapdState->bCommit = TRUE; |
| if (pHddCtx->cfg_ini->apOBSSProtEnabled) |
| vos_runtime_pm_prevent_suspend(pHddCtx->runtime_context.obss); |
| } |
| end: |
| mutex_unlock(&pHddCtx->sap_lock); |
| return; |
| } |
| #endif |
| |
| static int __hdd_netdev_notifier_call(struct notifier_block * nb, |
| unsigned long state, |
| void *data) |
| { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)) |
| struct netdev_notifier_info *dev_notif_info = data; |
| struct net_device *dev = dev_notif_info->dev; |
| #else |
| struct net_device *dev = data; |
| #endif |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx; |
| |
| //Make sure that this callback corresponds to our device. |
| if ((strncmp(dev->name, "wlan", 4)) && |
| (strncmp(dev->name, "softAP", 6)) && |
| (strncmp(dev->name, "p2p", 3))) |
| return NOTIFY_DONE; |
| |
| if ((pAdapter->magic != WLAN_HDD_ADAPTER_MAGIC) || |
| (pAdapter->dev != dev)) { |
| hddLog(LOGE, FL("device adapter is not matching!!!")); |
| return NOTIFY_DONE; |
| } |
| |
| if (!dev->ieee80211_ptr) { |
| hddLog(LOGE, FL("ieee80211_ptr is NULL!!!")); |
| return NOTIFY_DONE; |
| } |
| |
| pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| if (NULL == pHddCtx) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: HDD Context Null Pointer", __func__); |
| VOS_ASSERT(0); |
| return NOTIFY_DONE; |
| } |
| if (pHddCtx->isLogpInProgress) |
| return NOTIFY_DONE; |
| |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: %s New Net Device State = %lu", |
| __func__, dev->name, state); |
| |
| switch (state) { |
| case NETDEV_REGISTER: |
| break; |
| |
| case NETDEV_UNREGISTER: |
| break; |
| |
| case NETDEV_UP: |
| #ifdef FEATURE_WLAN_CH_AVOID |
| sme_ChAvoidUpdateReq(pHddCtx->hHal); |
| #endif |
| break; |
| |
| case NETDEV_DOWN: |
| break; |
| |
| case NETDEV_CHANGE: |
| if(TRUE == pAdapter->isLinkUpSvcNeeded) |
| complete(&pAdapter->linkup_event_var); |
| break; |
| |
| case NETDEV_GOING_DOWN: |
| if( pAdapter->scan_info.mScanPending != FALSE ) |
| { |
| unsigned long rc; |
| INIT_COMPLETION(pAdapter->scan_info.abortscan_event_var); |
| hdd_abort_mac_scan(pAdapter->pHddCtx, pAdapter->sessionId, |
| eCSR_SCAN_ABORT_DEFAULT); |
| rc = wait_for_completion_timeout( |
| &pAdapter->scan_info.abortscan_event_var, |
| msecs_to_jiffies(WLAN_WAIT_TIME_ABORTSCAN)); |
| if (!rc) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Timeout occurred while waiting for abortscan", |
| __func__); |
| } |
| } |
| else |
| { |
| vos_flush_work(&pAdapter->scan_block_work); |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Scan is not Pending from user" , __func__); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| return NOTIFY_DONE; |
| } |
| |
| /** |
| * hdd_netdev_notifier_call() - netdev notifier callback function |
| * @nb: pointer to notifier block |
| * @state: state |
| * @ndev: ndev pointer |
| * |
| * Return: 0 on success, error number otherwise. |
| */ |
| static int hdd_netdev_notifier_call(struct notifier_block * nb, |
| unsigned long state, |
| void *ndev) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __hdd_netdev_notifier_call(nb, state, ndev); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| struct notifier_block hdd_netdev_notifier = { |
| .notifier_call = hdd_netdev_notifier_call, |
| }; |
| |
| /*--------------------------------------------------------------------------- |
| * Function definitions |
| *-------------------------------------------------------------------------*/ |
| void hdd_unregister_mcast_bcast_filter(hdd_context_t *pHddCtx); |
| void hdd_register_mcast_bcast_filter(hdd_context_t *pHddCtx); |
| //variable to hold the insmod parameters |
| static int con_mode; |
| #ifndef MODULE |
| /* current con_mode - used only for statically linked driver |
| * con_mode is changed by user space to indicate a mode change which will |
| * result in calling the module exit and init functions. The module |
| * exit function will clean up based on the value of con_mode prior to it |
| * being changed by user space. So curr_con_mode records the current con_mode |
| * for exit when con_mode becomes the next mode for init |
| */ |
| static int curr_con_mode; |
| #endif |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_vos_trace_enable() - Configure initial VOS Trace enable |
| |
| Called immediately after the cfg.ini is read in order to configure |
| the desired trace levels. |
| |
| \param - moduleId - module whose trace level is being configured |
| \param - bitmask - bitmask of log levels to be enabled |
| |
| \return - void |
| |
| --------------------------------------------------------------------------*/ |
| static void hdd_vos_trace_enable(VOS_MODULE_ID moduleId, v_U32_t bitmask) |
| { |
| VOS_TRACE_LEVEL level; |
| |
| /* if the bitmask is the default value, then a bitmask was not |
| specified in cfg.ini, so leave the logging level alone (it |
| will remain at the "compiled in" default value) */ |
| if (CFG_VOS_TRACE_ENABLE_DEFAULT == bitmask) |
| { |
| return; |
| } |
| |
| /* a mask was specified. start by disabling all logging */ |
| vos_trace_setValue(moduleId, VOS_TRACE_LEVEL_NONE, 0); |
| |
| /* now cycle through the bitmask until all "set" bits are serviced */ |
| level = VOS_TRACE_LEVEL_FATAL; |
| while (0 != bitmask) |
| { |
| if (bitmask & 1) |
| { |
| vos_trace_setValue(moduleId, level, 1); |
| } |
| level++; |
| bitmask >>= 1; |
| } |
| } |
| |
| |
| /* |
| * FUNCTION: wlan_hdd_validate_context |
| * This function is used to check the HDD context |
| */ |
| int wlan_hdd_validate_context(hdd_context_t *pHddCtx) |
| { |
| |
| if (NULL == pHddCtx || NULL == pHddCtx->cfg_ini) { |
| hddLog(LOG1, FL("%pS HDD context is Null"), (void *)_RET_IP_); |
| return -ENODEV; |
| } |
| |
| if (pHddCtx->isLogpInProgress) { |
| hddLog(LOG1, FL("%pS LOGP in Progress. Ignore!!!"), (void *)_RET_IP_); |
| return -EAGAIN; |
| } |
| |
| if ((pHddCtx->isLoadInProgress) || |
| (pHddCtx->isUnloadInProgress)) { |
| hddLog(LOG1, |
| FL("%pS loading: %d unloading:%d in Progress. Ignore!!!"), |
| (void *)_RET_IP_, |
| pHddCtx->isLoadInProgress, |
| pHddCtx->isUnloadInProgress); |
| if (pHddCtx->isUnloadInProgress) |
| hddLog(LOG1, "current unload state: %d", g_current_unload_state); |
| return -EAGAIN; |
| } |
| return 0; |
| } |
| |
| |
| void hdd_checkandupdate_phymode( hdd_context_t *pHddCtx) |
| { |
| hdd_adapter_t *pAdapter = NULL; |
| hdd_station_ctx_t *pHddStaCtx = NULL; |
| eCsrPhyMode phyMode; |
| hdd_config_t *cfg_param = NULL; |
| |
| if (NULL == pHddCtx) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "HDD Context is null !!"); |
| return ; |
| } |
| |
| pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION); |
| if (NULL == pAdapter) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "pAdapter is null !!"); |
| return ; |
| } |
| |
| pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| |
| cfg_param = pHddCtx->cfg_ini; |
| if (NULL == cfg_param) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "cfg_params not available !!"); |
| return ; |
| } |
| |
| phyMode = sme_GetPhyMode(WLAN_HDD_GET_HAL_CTX(pAdapter)); |
| |
| if (!pHddCtx->isVHT80Allowed) |
| { |
| if ((eCSR_DOT11_MODE_AUTO == phyMode) || |
| (eCSR_DOT11_MODE_11ac == phyMode) || |
| (eCSR_DOT11_MODE_11ac_ONLY == phyMode)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "Setting phymode to 11n!!"); |
| sme_SetPhyMode(WLAN_HDD_GET_HAL_CTX(pAdapter), eCSR_DOT11_MODE_11n); |
| } |
| } |
| else |
| { |
| /*New country Supports 11ac as well resetting value back from .ini*/ |
| sme_SetPhyMode(WLAN_HDD_GET_HAL_CTX(pAdapter), |
| hdd_cfg_xlate_to_csr_phy_mode(cfg_param->dot11Mode)); |
| return ; |
| } |
| |
| if ((eConnectionState_Associated == pHddStaCtx->conn_info.connState) && |
| ((eCSR_CFG_DOT11_MODE_11AC_ONLY == pHddStaCtx->conn_info.dot11Mode) || |
| (eCSR_CFG_DOT11_MODE_11AC == pHddStaCtx->conn_info.dot11Mode))) |
| { |
| VOS_STATUS vosStatus; |
| |
| // need to issue a disconnect to CSR. |
| INIT_COMPLETION(pAdapter->disconnect_comp_var); |
| vosStatus = sme_RoamDisconnect(WLAN_HDD_GET_HAL_CTX(pAdapter), |
| pAdapter->sessionId, |
| eCSR_DISCONNECT_REASON_UNSPECIFIED ); |
| |
| if (VOS_STATUS_SUCCESS == vosStatus) |
| { |
| unsigned long rc; |
| |
| rc = wait_for_completion_timeout(&pAdapter->disconnect_comp_var, |
| msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT)); |
| if (!rc) |
| hddLog(LOGE, FL("failure waiting for disconnect_comp_var")); |
| } |
| } |
| } |
| |
| static void |
| hdd_checkandupdate_dfssetting(hdd_adapter_t *pAdapter, char *country_code) |
| { |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| hdd_config_t *cfg_param; |
| |
| if (NULL == pHddCtx) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "HDD Context is null !!"); |
| return ; |
| } |
| |
| cfg_param = pHddCtx->cfg_ini; |
| |
| if (NULL == cfg_param) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "cfg_params not available !!"); |
| return ; |
| } |
| |
| if (NULL != strstr(cfg_param->listOfNonDfsCountryCode, country_code)) |
| { |
| /*New country doesn't support DFS */ |
| sme_UpdateDfsSetting(WLAN_HDD_GET_HAL_CTX(pAdapter), 0); |
| } |
| else |
| { |
| /*New country Supports DFS as well resetting value back from .ini*/ |
| sme_UpdateDfsSetting(WLAN_HDD_GET_HAL_CTX(pAdapter), cfg_param->enableDFSChnlScan); |
| } |
| |
| } |
| |
| /* Function header is left blank intentionally */ |
| static int hdd_parse_setrmcenable_command(tANI_U8 *pValue, tANI_U8 *pRmcEnable) |
| { |
| tANI_U8 *inPtr = pValue; |
| int tempInt; |
| int v = 0; |
| char buf[32]; |
| *pRmcEnable = 0; |
| |
| inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE); |
| |
| if (NULL == inPtr) |
| { |
| return 0; |
| } |
| |
| else if (SPACE_ASCII_VALUE != *inPtr) |
| { |
| return 0; |
| } |
| |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++; |
| |
| if ('\0' == *inPtr) |
| { |
| return 0; |
| } |
| |
| sscanf(inPtr, "%31s ", buf); |
| v = kstrtos32(buf, 10, &tempInt); |
| if ( v < 0) |
| { |
| return -EINVAL; |
| } |
| |
| *pRmcEnable = tempInt; |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "ucRmcEnable: %d", *pRmcEnable); |
| |
| return 0; |
| } |
| |
| /* Function header is left blank intentionally */ |
| static int hdd_parse_setrmcactionperiod_command(tANI_U8 *pValue, |
| tANI_U32 *pActionPeriod) |
| { |
| tANI_U8 *inPtr = pValue; |
| int tempInt; |
| int v = 0; |
| char buf[32]; |
| *pActionPeriod = 0; |
| |
| inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE); |
| |
| if (NULL == inPtr) |
| { |
| return -EINVAL; |
| } |
| |
| else if (SPACE_ASCII_VALUE != *inPtr) |
| { |
| return -EINVAL; |
| } |
| |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++; |
| |
| if ('\0' == *inPtr) |
| { |
| return 0; |
| } |
| |
| sscanf(inPtr, "%31s ", buf); |
| v = kstrtos32(buf, 10, &tempInt); |
| if ( v < 0) |
| { |
| return -EINVAL; |
| } |
| |
| if ( (tempInt < WNI_CFG_RMC_ACTION_PERIOD_FREQUENCY_STAMIN) || |
| (tempInt > WNI_CFG_RMC_ACTION_PERIOD_FREQUENCY_STAMAX) ) |
| { |
| return -EINVAL; |
| } |
| |
| *pActionPeriod = tempInt; |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "uActionPeriod: %d", *pActionPeriod); |
| |
| return 0; |
| } |
| |
| /* Function header is left blank intentionally */ |
| static int hdd_parse_setrmcrate_command(tANI_U8 *pValue, |
| tANI_U32 *pRate, tTxrateinfoflags *pTxFlags) |
| { |
| tANI_U8 *inPtr = pValue; |
| int tempInt; |
| int v = 0; |
| char buf[32]; |
| *pRate = 0; |
| *pTxFlags = 0; |
| |
| inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE); |
| |
| if (NULL == inPtr) |
| { |
| return -EINVAL; |
| } |
| |
| else if (SPACE_ASCII_VALUE != *inPtr) |
| { |
| return -EINVAL; |
| } |
| |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++; |
| |
| if ('\0' == *inPtr) |
| { |
| return 0; |
| } |
| |
| sscanf(inPtr, "%31s ", buf); |
| v = kstrtos32(buf, 10, &tempInt); |
| if ( v < 0) |
| { |
| return -EINVAL; |
| } |
| |
| switch (tempInt) |
| { |
| default: |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "Unsupported rate: %d", tempInt); |
| return -EINVAL; |
| case 0: |
| case 6: |
| case 9: |
| case 12: |
| case 18: |
| case 24: |
| case 36: |
| case 48: |
| case 54: |
| *pTxFlags = eHAL_TX_RATE_LEGACY; |
| *pRate = tempInt * 10; |
| break; |
| case 65: |
| *pTxFlags = eHAL_TX_RATE_HT20; |
| *pRate = tempInt * 10; |
| break; |
| case 72: |
| *pTxFlags = eHAL_TX_RATE_HT20 | eHAL_TX_RATE_SGI; |
| *pRate = 722; |
| break; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "Rate: %d", *pRate); |
| |
| return 0; |
| } |
| |
| |
| /** |
| * hdd_get_ibss_peer_info_cb() - IBSS peer Info request callback |
| * @UserData: Adapter private data |
| * @pPeerInfoRsp: Peer info response |
| * |
| * This is an asynchronous callback function from SME when the peer info |
| * is received |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| void |
| hdd_get_ibss_peer_info_cb(v_VOID_t *pUserData, |
| tSirPeerInfoRspParams *pPeerInfo) |
| { |
| hdd_adapter_t *pAdapter = (hdd_adapter_t *)pUserData; |
| hdd_station_ctx_t *pStaCtx; |
| v_U8_t i; |
| |
| /*Sanity check*/ |
| if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "invalid adapter or adapter has invalid magic"); |
| return; |
| } |
| |
| pStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| |
| if (NULL != pPeerInfo && eHAL_STATUS_SUCCESS == pPeerInfo->status) { |
| /* validate number of peers */ |
| if (pPeerInfo->numPeers > HDD_MAX_NUM_IBSS_STA) { |
| hddLog(LOGW, |
| FL("Limiting num_peers %u to %u"), |
| pPeerInfo->numPeers, HDD_MAX_NUM_IBSS_STA); |
| pPeerInfo->numPeers = HDD_MAX_NUM_IBSS_STA; |
| } |
| |
| pStaCtx->ibss_peer_info.status = pPeerInfo->status; |
| pStaCtx->ibss_peer_info.numPeers = pPeerInfo->numPeers; |
| |
| for (i = 0; i < pPeerInfo->numPeers; i++) { |
| pStaCtx->ibss_peer_info.peerInfoParams[i] = |
| pPeerInfo->peerInfoParams[i]; |
| } |
| } else { |
| hddLog(LOGE, FL("peerInfo %s: status %u, numPeers %u"), |
| pPeerInfo ? "valid" : "null", |
| pPeerInfo ? pPeerInfo->status : eHAL_STATUS_FAILURE, |
| pPeerInfo ? pPeerInfo->numPeers : 0); |
| pStaCtx->ibss_peer_info.numPeers = 0; |
| pStaCtx->ibss_peer_info.status = eHAL_STATUS_FAILURE; |
| } |
| |
| complete(&pAdapter->ibss_peer_info_comp); |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_cfg80211_get_ibss_peer_info_all() - |
| |
| Request function to get IBSS peer info from lower layers |
| |
| \pAdapter -> Adapter context |
| |
| \return - 0 for success non-zero for failure |
| --------------------------------------------------------------------------*/ |
| static |
| VOS_STATUS hdd_cfg80211_get_ibss_peer_info_all(hdd_adapter_t *pAdapter) |
| { |
| tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter); |
| VOS_STATUS retStatus = VOS_STATUS_E_FAILURE; |
| unsigned long rc; |
| |
| INIT_COMPLETION(pAdapter->ibss_peer_info_comp); |
| |
| retStatus = sme_RequestIBSSPeerInfo(hHal, pAdapter, |
| hdd_get_ibss_peer_info_cb, |
| VOS_TRUE, 0xFF); |
| |
| if (VOS_STATUS_SUCCESS == retStatus) |
| { |
| rc = wait_for_completion_timeout |
| (&pAdapter->ibss_peer_info_comp, |
| msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT)); |
| |
| /* status will be 0 if timed out */ |
| if (!rc) { |
| hddLog(VOS_TRACE_LEVEL_WARN, "%s: Warning: IBSS_PEER_INFO_TIMEOUT", |
| __func__); |
| retStatus = VOS_STATUS_E_FAILURE; |
| return retStatus; |
| } |
| } |
| else |
| { |
| hddLog(VOS_TRACE_LEVEL_WARN, |
| "%s: Warning: sme_RequestIBSSPeerInfo Request failed", __func__); |
| } |
| |
| return retStatus; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_cfg80211_get_ibss_peer_info() - |
| |
| Request function to get IBSS peer info from lower layers |
| |
| \pAdapter -> Adapter context |
| \staIdx -> Sta index for which the peer info is requested |
| |
| \return - 0 for success non-zero for failure |
| --------------------------------------------------------------------------*/ |
| static VOS_STATUS |
| hdd_cfg80211_get_ibss_peer_info(hdd_adapter_t *pAdapter, v_U8_t staIdx) |
| { |
| unsigned long rc; |
| tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter); |
| VOS_STATUS retStatus = VOS_STATUS_E_FAILURE; |
| |
| INIT_COMPLETION(pAdapter->ibss_peer_info_comp); |
| |
| retStatus = sme_RequestIBSSPeerInfo(hHal, pAdapter, |
| hdd_get_ibss_peer_info_cb, |
| VOS_FALSE, staIdx); |
| |
| if (VOS_STATUS_SUCCESS == retStatus) |
| { |
| rc = wait_for_completion_timeout |
| (&pAdapter->ibss_peer_info_comp, |
| msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT)); |
| |
| /* status = 0 on timeout */ |
| if (!rc) { |
| hddLog(VOS_TRACE_LEVEL_WARN, "%s: Warning: IBSS_PEER_INFO_TIMEOUT", |
| __func__); |
| retStatus = VOS_STATUS_E_FAILURE; |
| return retStatus; |
| } |
| } |
| else |
| { |
| hddLog(VOS_TRACE_LEVEL_WARN, |
| "%s: Warning: sme_RequestIBSSPeerInfo Request failed", __func__); |
| } |
| |
| return retStatus; |
| } |
| |
| /* Function header is left blank intentionally */ |
| static VOS_STATUS |
| hdd_parse_get_ibss_peer_info(tANI_U8 *pValue, v_MACADDR_t *pPeerMacAddr) |
| { |
| tANI_U8 *inPtr = pValue; |
| size_t inPtrLen = strlen(pValue); |
| |
| inPtr = strnchr(pValue, inPtrLen, SPACE_ASCII_VALUE); |
| |
| if (NULL == inPtr) |
| { |
| return VOS_STATUS_E_FAILURE;; |
| } |
| |
| else if (SPACE_ASCII_VALUE != *inPtr) |
| { |
| return VOS_STATUS_E_FAILURE;; |
| } |
| |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++; |
| |
| if ('\0' == *inPtr) |
| { |
| return VOS_STATUS_E_FAILURE;; |
| } |
| |
| inPtrLen -= (inPtr - pValue); |
| if (inPtrLen < 17) |
| { |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| if (inPtr[2] != ':' || inPtr[5] != ':' || inPtr[8] != ':' || |
| inPtr[11] != ':' || inPtr[14] != ':') |
| { |
| return VOS_STATUS_E_FAILURE;; |
| } |
| sscanf(inPtr, "%2x:%2x:%2x:%2x:%2x:%2x", |
| (unsigned int *)&pPeerMacAddr->bytes[0], |
| (unsigned int *)&pPeerMacAddr->bytes[1], |
| (unsigned int *)&pPeerMacAddr->bytes[2], |
| (unsigned int *)&pPeerMacAddr->bytes[3], |
| (unsigned int *)&pPeerMacAddr->bytes[4], |
| (unsigned int *)&pPeerMacAddr->bytes[5]); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| #ifdef IPA_UC_STA_OFFLOAD |
| static void hdd_set_thermal_level_cb(hdd_context_t *pHddCtx, u_int8_t level) |
| { |
| /* Change IPA to SW path when throttle level greater than 0 */ |
| if (level > THROTTLE_LEVEL_0) |
| hdd_ipa_send_mcc_scc_msg(pHddCtx, TRUE); |
| else |
| /* restore original concurrency mode */ |
| hdd_ipa_send_mcc_scc_msg(pHddCtx, pHddCtx->mcc_mode); |
| } |
| #else |
| static void hdd_set_thermal_level_cb(hdd_context_t *pHddCtx, u_int8_t level) |
| { |
| } |
| #endif |
| |
| #ifdef FEATURE_WLAN_THERMAL_SHUTDOWN |
| static bool |
| hdd_system_suspend_state(hdd_context_t *hdd_ctx) |
| { |
| unsigned long flags; |
| bool s; |
| |
| spin_lock_irqsave(&hdd_ctx->thermal_suspend_lock, flags); |
| s = hdd_ctx->system_suspended; |
| spin_unlock_irqrestore(&hdd_ctx->thermal_suspend_lock, flags); |
| return s; |
| } |
| |
| bool |
| hdd_system_suspend_state_set(hdd_context_t *hdd_ctx, bool state) |
| { |
| unsigned long flags; |
| bool old; |
| |
| spin_lock_irqsave(&hdd_ctx->thermal_suspend_lock, flags); |
| old = hdd_ctx->system_suspended; |
| hdd_ctx->system_suspended = state; |
| spin_unlock_irqrestore(&hdd_ctx->thermal_suspend_lock, flags); |
| |
| return old; |
| } |
| |
| |
| int |
| hdd_thermal_suspend_state(hdd_context_t *hdd_ctx) |
| { |
| unsigned long flags; |
| int s; |
| |
| spin_lock_irqsave(&hdd_ctx->thermal_suspend_lock, flags); |
| s = hdd_ctx->thermal_suspend_state; |
| spin_unlock_irqrestore(&hdd_ctx->thermal_suspend_lock, flags); |
| |
| return s; |
| } |
| |
| static bool |
| hdd_thermal_suspend_transit(hdd_context_t *hdd_ctx, int target, int *old) |
| { |
| unsigned long flags; |
| int s; |
| bool ret = false; |
| |
| spin_lock_irqsave(&hdd_ctx->thermal_suspend_lock, flags); |
| |
| s = hdd_ctx->thermal_suspend_state; |
| if (old) |
| *old = s; |
| |
| switch (target) { |
| case HDD_WLAN_THERMAL_ACTIVE: |
| if (s == HDD_WLAN_THERMAL_RESUMING || |
| s == HDD_WLAN_THERMAL_SUSPENDING) |
| ret = true; |
| break; |
| case HDD_WLAN_THERMAL_SUSPENDING: |
| if (s == HDD_WLAN_THERMAL_ACTIVE) |
| ret = true; |
| break; |
| case HDD_WLAN_THERMAL_SUSPENDED: |
| if (s == HDD_WLAN_THERMAL_SUSPENDING || |
| s == HDD_WLAN_THERMAL_RESUMING) |
| ret = true; |
| break; |
| case HDD_WLAN_THERMAL_RESUMING: |
| if (s == HDD_WLAN_THERMAL_SUSPENDED) |
| ret = true; |
| break; |
| case HDD_WLAN_THERMAL_DEINIT: |
| if (s != HDD_WLAN_THERMAL_DEINIT) |
| ret = true; |
| break; |
| } |
| |
| if (ret) |
| hdd_ctx->thermal_suspend_state = target; |
| |
| spin_unlock_irqrestore(&hdd_ctx->thermal_suspend_lock, flags); |
| return ret; |
| } |
| |
| static void |
| hdd_thermal_suspend_cleanup(hdd_context_t *hdd_ctx) |
| { |
| int state; |
| bool okay; |
| |
| if (!hdd_ctx->thermal_suspend_wq) |
| return; |
| |
| cancel_delayed_work_sync(&hdd_ctx->thermal_suspend_work); |
| okay = hdd_thermal_suspend_transit(hdd_ctx, HDD_WLAN_THERMAL_DEINIT, |
| &state); |
| destroy_workqueue(hdd_ctx->thermal_suspend_wq); |
| hdd_ctx->thermal_suspend_wq = NULL; |
| |
| if (!okay || state == HDD_WLAN_THERMAL_ACTIVE) |
| return; |
| |
| if (state == HDD_WLAN_THERMAL_SUSPENDED) { |
| hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL); |
| __wlan_hdd_cfg80211_resume_wlan(hdd_ctx->wiphy, true); |
| hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL); |
| } |
| } |
| |
| static void |
| hdd_thermal_suspend_work(struct work_struct *work) |
| { |
| hdd_context_t *hdd_ctx = |
| container_of(work, hdd_context_t, thermal_suspend_work.work); |
| struct wiphy *wiphy = hdd_ctx->wiphy; |
| int ret; |
| |
| while (hdd_system_suspend_state(hdd_ctx)) { |
| hddLog(LOG1, FL("Waiting for system resume complete")); |
| schedule_timeout_interruptible(100 * HZ / 1000); |
| } |
| |
| if (hdd_thermal_suspend_transit(hdd_ctx, |
| HDD_WLAN_THERMAL_SUSPENDING, NULL)) { |
| hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL); |
| ret = __wlan_hdd_cfg80211_suspend_wlan(wiphy, NULL, true); |
| hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL); |
| if (ret) { |
| hdd_thermal_suspend_transit(hdd_ctx, |
| HDD_WLAN_THERMAL_ACTIVE, NULL); |
| hddLog(LOGE, FL("Thermal suspend failed: %d"), ret); |
| return; |
| } |
| hdd_thermal_suspend_transit(hdd_ctx, HDD_WLAN_THERMAL_SUSPENDED, |
| NULL); |
| } else if (hdd_thermal_suspend_transit(hdd_ctx, |
| HDD_WLAN_THERMAL_RESUMING, NULL)) { |
| hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL); |
| ret = __wlan_hdd_cfg80211_resume_wlan(wiphy, true); |
| hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL); |
| if (ret) { |
| hdd_thermal_suspend_transit(hdd_ctx, |
| HDD_WLAN_THERMAL_SUSPENDED, NULL); |
| hddLog(LOGE, FL("Thermal resume failed: %d"), ret); |
| return; |
| } |
| hdd_thermal_suspend_transit(hdd_ctx, HDD_WLAN_THERMAL_ACTIVE, |
| NULL); |
| } else { |
| hddLog(LOGE, FL("Should not reach here")); |
| } |
| } |
| |
| /** |
| * hdd_thermal_suspend_queue_work() - Queue a thermal suspend work |
| * @hdd_ctx: Pointer to hdd_context_t |
| * @ms: Delay time in milliseconds to execute the work |
| * |
| * Queue thermal suspend work on the workqueue after delay |
| * |
| * Return: false if work was already on a queue, true otherwise. |
| */ |
| bool |
| hdd_thermal_suspend_queue_work(hdd_context_t *hdd_ctx, unsigned long ms) |
| { |
| hddLog(LOG1, FL("Queue a thermal suspend work, delay %ld ms"), ms); |
| return queue_delayed_work(hdd_ctx->thermal_suspend_wq, |
| &hdd_ctx->thermal_suspend_work, (ms * HZ) / 1000); |
| } |
| |
| static void |
| hdd_thermal_temp_ind_event_cb(hdd_context_t *hdd_ctx, uint32_t degreeC) |
| { |
| if (!hdd_ctx->cfg_ini->thermal_shutdown_auto_enabled) { |
| return; |
| } |
| |
| /* |
| * Here, we only do thermal suspend. |
| * |
| * We can only resume FW elsewhere in two ways: |
| * 1. triggered by wakeup interrupt from FW when it detects T < Tresume |
| * 2. user space app launch thermal resume after suspend as app wants |
| * |
| * Key factor: FW cannot provide temperature when it suspended. |
| */ |
| if ((hdd_thermal_suspend_state(hdd_ctx) == HDD_WLAN_THERMAL_ACTIVE && |
| degreeC >= hdd_ctx->cfg_ini->thermal_suspend_threshold) || |
| (hdd_thermal_suspend_state(hdd_ctx) == HDD_WLAN_THERMAL_SUSPENDED && |
| degreeC < hdd_ctx->cfg_ini->thermal_resume_threshold)) { |
| hdd_thermal_suspend_queue_work(hdd_ctx, 0); |
| } |
| } |
| #else |
| bool |
| hdd_system_suspend_state_set(hdd_context_t *hdd_ctx, bool state) |
| { |
| return TRUE; |
| } |
| |
| static inline void |
| hdd_thermal_suspend_cleanup(hdd_context_t *hdd_ctx) |
| { |
| return; |
| } |
| |
| static inline void |
| hdd_thermal_temp_ind_event_cb(hdd_context_t *hdd_ctx, uint32_t degreeC) |
| { |
| return; |
| } |
| |
| #endif |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_setIbssPowerSaveParams - update IBSS Power Save params to WMA. |
| |
| This function sets the IBSS power save config parameters to WMA |
| which will send it to firmware if FW supports IBSS power save |
| before vdev start. |
| |
| \param - hdd_adapter_t Hdd adapter. |
| |
| \return - VOS_STATUS VOS_STATUS_SUCCESS on Success and VOS_STATUS_E_FAILURE |
| on failure. |
| |
| --------------------------------------------------------------------------*/ |
| VOS_STATUS hdd_setIbssPowerSaveParams(hdd_adapter_t *pAdapter) |
| { |
| int ret; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter ); |
| |
| if (pHddCtx == NULL) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: HDD context is null", __func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| ret = process_wma_set_command(pAdapter->sessionId, |
| WMA_VDEV_IBSS_SET_ATIM_WINDOW_SIZE, |
| pHddCtx->cfg_ini->ibssATIMWinSize, |
| VDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("WMA_VDEV_IBSS_SET_ATIM_WINDOW_SIZE failed %d"), |
| ret); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| ret = process_wma_set_command(pAdapter->sessionId, |
| WMA_VDEV_IBSS_SET_POWER_SAVE_ALLOWED, |
| pHddCtx->cfg_ini->isIbssPowerSaveAllowed, |
| VDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("WMA_VDEV_IBSS_SET_POWER_SAVE_ALLOWED failed %d"), |
| ret); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| ret = process_wma_set_command(pAdapter->sessionId, |
| WMA_VDEV_IBSS_SET_POWER_COLLAPSE_ALLOWED, |
| pHddCtx->cfg_ini->isIbssPowerCollapseAllowed, |
| VDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("WMA_VDEV_IBSS_SET_POWER_COLLAPSE_ALLOWED failed %d"), |
| ret); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| ret = process_wma_set_command(pAdapter->sessionId, |
| WMA_VDEV_IBSS_SET_AWAKE_ON_TX_RX, |
| pHddCtx->cfg_ini->isIbssAwakeOnTxRx, |
| VDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("WMA_VDEV_IBSS_SET_AWAKE_ON_TX_RX failed %d"), |
| ret); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| ret = process_wma_set_command(pAdapter->sessionId, |
| WMA_VDEV_IBSS_SET_INACTIVITY_TIME, |
| pHddCtx->cfg_ini->ibssInactivityCount, |
| VDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("WMA_VDEV_IBSS_SET_INACTIVITY_TIME failed %d"), |
| ret); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| ret = process_wma_set_command(pAdapter->sessionId, |
| WMA_VDEV_IBSS_SET_TXSP_END_INACTIVITY_TIME, |
| pHddCtx->cfg_ini->ibssTxSpEndInactivityTime, |
| VDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("WMA_VDEV_IBSS_SET_TXSP_END_INACTIVITY_TIME failed %d"), |
| ret); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| ret = process_wma_set_command(pAdapter->sessionId, |
| WMA_VDEV_IBSS_PS_SET_WARMUP_TIME_SECS, |
| pHddCtx->cfg_ini->ibssPsWarmupTime, |
| VDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("WMA_VDEV_IBSS_PS_SET_WARMUP_TIME_SECS failed %d"), |
| ret); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| ret = process_wma_set_command(pAdapter->sessionId, |
| WMA_VDEV_IBSS_PS_SET_1RX_CHAIN_IN_ATIM_WINDOW, |
| pHddCtx->cfg_ini->ibssPs1RxChainInAtimEnable, |
| VDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("WMA_VDEV_IBSS_PS_SET_1RX_CHAIN_IN_ATIM_WINDOW failed %d"), |
| ret); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| #if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR) |
| /** |
| * hdd_parse_reassoc_command_data() - HDD Parse reassoc command data |
| * @pValue: Pointer to input data (its a NUL terminated string) |
| * @pTargetApBssid: Pointer to target Ap bssid |
| * @pChannel: Pointer to the Target AP channel |
| * |
| * This function parses the reasoc command data passed in the format |
| * REASSOC<space><bssid><space><channel> |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| static int hdd_parse_reassoc_command_v1_data(const tANI_U8 *pValue, |
| tANI_U8 *pTargetApBssid, |
| tANI_U8 *pChannel) |
| { |
| const tANI_U8 *inPtr = pValue; |
| int tempInt; |
| int v = 0; |
| tANI_U8 tempBuf[32]; |
| /* 12 hexa decimal digits, 5 ':' and '\0' */ |
| tANI_U8 macAddress[18]; |
| |
| |
| inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE); |
| /* no argument after the command */ |
| if (NULL == inPtr) { |
| return -EINVAL; |
| } else if (SPACE_ASCII_VALUE != *inPtr) { |
| /*no space after the command*/ |
| return -EINVAL; |
| } |
| |
| /* Removing empty spaces */ |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++; |
| |
| /* no argument followed by spaces */ |
| if ('\0' == *inPtr) { |
| return -EINVAL; |
| } |
| |
| v = sscanf(inPtr, "%17s", macAddress); |
| if (!((1 == v) && hdd_is_valid_mac_address(macAddress))) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Invalid MAC address or All hex inputs are not read (%d)", v); |
| return -EINVAL; |
| } |
| |
| pTargetApBssid[0] = hdd_parse_hex(macAddress[0]) << 4 | |
| hdd_parse_hex(macAddress[1]); |
| pTargetApBssid[1] = hdd_parse_hex(macAddress[3]) << 4 | |
| hdd_parse_hex(macAddress[4]); |
| pTargetApBssid[2] = hdd_parse_hex(macAddress[6]) << 4 | |
| hdd_parse_hex(macAddress[7]); |
| pTargetApBssid[3] = hdd_parse_hex(macAddress[9]) << 4 | |
| hdd_parse_hex(macAddress[10]); |
| pTargetApBssid[4] = hdd_parse_hex(macAddress[12]) << 4 | |
| hdd_parse_hex(macAddress[13]); |
| pTargetApBssid[5] = hdd_parse_hex(macAddress[15]) << 4 | |
| hdd_parse_hex(macAddress[16]); |
| |
| /* point to the next argument */ |
| inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE); |
| /* no argument after the command */ |
| if (NULL == inPtr) return -EINVAL; |
| |
| /* Removing empty spaces */ |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++; |
| |
| /*no argument followed by spaces*/ |
| if ('\0' == *inPtr) { |
| return -EINVAL; |
| } |
| |
| /*getting the next argument ie the channel number */ |
| v = sscanf(inPtr, "%31s ", tempBuf); |
| if (1 != v) return -EINVAL; |
| |
| /* |
| * Sigma DUT sends connected bssid and channel 0 to indicate |
| * driver to issue reassoc to same AP. |
| * Hence do not treat channel 0 as invalid. |
| */ |
| v = kstrtos32(tempBuf, 10, &tempInt); |
| if ((v < 0) || |
| (tempInt < 0) || |
| (tempInt > WNI_CFG_CURRENT_CHANNEL_STAMAX)) { |
| return -EINVAL; |
| } |
| |
| *pChannel = tempInt; |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| /** |
| * hdd_reassoc() - perform a user space directed reassoc |
| * @pAdapter: Adapter upon which the command was received |
| * @bssid: BSSID with which to reassociate |
| * @channel: channel upon which to reassociate |
| * @src: The source for the trigger of this action |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| int hdd_reassoc(hdd_adapter_t *pAdapter, const tANI_U8 *bssid, |
| const tANI_U8 channel, const handoff_src src) |
| { |
| hdd_station_ctx_t *pHddStaCtx; |
| int ret = 0; |
| |
| pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| |
| /* if not associated, no need to proceed with reassoc */ |
| if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) { |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: Not associated", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| /* if the target bssid is same as currently associated AP, |
| then no need to proceed with reassoc */ |
| if (!memcmp(bssid, pHddStaCtx->conn_info.bssId, sizeof(tSirMacAddr))) { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: Reassoc BSSID is same as currently associated AP bssid", |
| __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| /* Check channel number is a valid channel number */ |
| if (VOS_STATUS_SUCCESS != |
| wlan_hdd_validate_operation_channel(pAdapter, channel)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid Channel %d", |
| __func__, channel); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| /* Proceed with reassoc */ |
| #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD |
| { |
| tCsrHandoffRequest handoffInfo; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| |
| handoffInfo.channel = channel; |
| handoffInfo.src = src; |
| memcpy(handoffInfo.bssid, bssid, sizeof(tSirMacAddr)); |
| sme_HandoffRequest(pHddCtx->hHal, pAdapter->sessionId, &handoffInfo); |
| } |
| #endif |
| exit: |
| return ret; |
| } |
| |
| /* |
| \brief hdd_parse_reassoc_v1() - parse version 1 of the REASSOC command |
| |
| This function parses the v1 REASSOC command with the format |
| |
| REASSOC xx:xx:xx:xx:xx:xx CH |
| |
| Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the |
| BSSID and CH is the ASCII representation of the channel. For |
| example |
| |
| REASSOC 00:0a:0b:11:22:33 48 |
| |
| \param - pAdapter - Adapter upon which the command was received |
| \param - command - ASCII text command that was received |
| |
| \return - 0 for success non-zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static int |
| hdd_parse_reassoc_v1(hdd_adapter_t *pAdapter, const char *command) |
| { |
| tANI_U8 channel = 0; |
| tSirMacAddr bssid; |
| int ret; |
| |
| ret = hdd_parse_reassoc_command_v1_data(command, bssid, &channel); |
| if (ret) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to parse reassoc command data", __func__); |
| } else { |
| ret = hdd_reassoc(pAdapter, bssid, channel, REASSOC); |
| } |
| return ret; |
| } |
| |
| /* |
| \brief hdd_parse_reassoc_v2() - parse version 2 of the REASSOC command |
| |
| This function parses the v2 REASSOC command with the format |
| |
| REASSOC <android_wifi_reassoc_params> |
| |
| \param - pAdapter - Adapter upon which the command was received |
| \param - command - command that was received, ASCII command followed |
| by binary data |
| \param - total_len - total length of the command received |
| |
| \return - 0 for success non-zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static int |
| hdd_parse_reassoc_v2(hdd_adapter_t *pAdapter, |
| const char *command, |
| int total_len) |
| { |
| struct android_wifi_reassoc_params params; |
| tSirMacAddr bssid; |
| int ret; |
| |
| if (total_len < sizeof(params) + 8) { |
| hddLog(LOGE, FL("Invalid command length")); |
| return -EINVAL; |
| } |
| |
| /* The params are located after "REASSOC " */ |
| memcpy(¶ms, command + 8, sizeof(params)); |
| |
| if (!mac_pton(params.bssid, (u8 *)&bssid)) { |
| hddLog(LOGE, "%s: MAC address parsing failed", __func__); |
| ret = -EINVAL; |
| } else { |
| ret = hdd_reassoc(pAdapter, bssid, params.channel, REASSOC); |
| } |
| return ret; |
| } |
| |
| /* |
| \brief hdd_parse_reassoc() - parse the REASSOC command |
| |
| There are two different versions of the REASSOC command. Version 1 |
| of the command contains a parameter list that is ASCII characters |
| whereas version 2 contains a combination of ASCII and binary |
| payload. Determine if a version 1 or a version 2 command is being |
| parsed by examining the parameters, and then dispatch the parser |
| that is appropriate for the command. |
| |
| \param - pAdapter - Adapter upon which the command was received |
| \param - command - command that was received |
| \param - total_len - total length of the command received |
| |
| \return - 0 for success non-zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static int |
| hdd_parse_reassoc(hdd_adapter_t *pAdapter, const char *command, int total_len) |
| { |
| int ret; |
| |
| /* both versions start with "REASSOC " |
| * v1 has a bssid and channel # as an ASCII string |
| * REASSOC xx:xx:xx:xx:xx:xx CH |
| * v2 has a C struct |
| * REASSOC <binary c struct> |
| * |
| * The first field in the v2 struct is also the bssid in ASCII. |
| * But in the case of a v2 message the BSSID is NUL-terminated. |
| * Hence we can peek at that offset to see if this is V1 or V2 |
| * REASSOC xx:xx:xx:xx:xx:xx* |
| * 1111111111222222 |
| * 01234567890123456789012345 |
| */ |
| |
| if (total_len < 26) { |
| hddLog(LOGE, FL("Invalid command (total_len=%d)"), total_len); |
| return -EINVAL; |
| } |
| |
| if (command[25]) { |
| ret = hdd_parse_reassoc_v1(pAdapter, command); |
| } else { |
| ret = hdd_parse_reassoc_v2(pAdapter, command, total_len); |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * hdd_parse_send_action_frame_v1_data() - HDD Parse send action frame data |
| * @pValue: Pointer to input data (its a NUL terminated string) |
| * @pTargetApBssid: Pointer to target Ap bssid |
| * @pChannel: Pointer to the Target AP channel |
| * @pDwellTime: pDwellTime Pointer to the time to stay off-channel |
| * after transmitting action frame |
| * @pBuf: Pointer to data |
| * @pBufLen: Pointer to data length |
| * |
| * This function parses the send action frame data passed in the format |
| * SENDACTIONFRAME<space><bssid><space><channel><space><dwelltime><space><data> |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| static int |
| hdd_parse_send_action_frame_v1_data(const tANI_U8 *pValue, |
| tANI_U8 *pTargetApBssid, |
| tANI_U8 *pChannel, tANI_U8 *pDwellTime, |
| tANI_U8 **pBuf, tANI_U8 *pBufLen) |
| { |
| const tANI_U8 *inPtr = pValue; |
| const tANI_U8 *dataEnd; |
| int tempInt; |
| int j = 0; |
| int i = 0; |
| int v = 0; |
| tANI_U8 tempBuf[32]; |
| tANI_U8 tempByte = 0; |
| /* 12 hexa decimal digits, 5 ':' and '\0' */ |
| tANI_U8 macAddress[18]; |
| |
| inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE); |
| /* no argument after the command */ |
| if (NULL == inPtr) { |
| return -EINVAL; |
| } else if (SPACE_ASCII_VALUE != *inPtr) { |
| /* no space after the command */ |
| return -EINVAL; |
| } |
| |
| /* removing empty spaces */ |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++; |
| |
| /*no argument followed by spaces*/ |
| if ('\0' == *inPtr) { |
| return -EINVAL; |
| } |
| |
| v = sscanf(inPtr, "%17s", macAddress); |
| if (!((1 == v) && hdd_is_valid_mac_address(macAddress))) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Invalid MAC address or All hex inputs are not read (%d)", v); |
| return -EINVAL; |
| } |
| |
| pTargetApBssid[0] = hdd_parse_hex(macAddress[0]) << 4 | |
| hdd_parse_hex(macAddress[1]); |
| pTargetApBssid[1] = hdd_parse_hex(macAddress[3]) << 4 | |
| hdd_parse_hex(macAddress[4]); |
| pTargetApBssid[2] = hdd_parse_hex(macAddress[6]) << 4 | |
| hdd_parse_hex(macAddress[7]); |
| pTargetApBssid[3] = hdd_parse_hex(macAddress[9]) << 4 | |
| hdd_parse_hex(macAddress[10]); |
| pTargetApBssid[4] = hdd_parse_hex(macAddress[12]) << 4 | |
| hdd_parse_hex(macAddress[13]); |
| pTargetApBssid[5] = hdd_parse_hex(macAddress[15]) << 4 | |
| hdd_parse_hex(macAddress[16]); |
| |
| /* point to the next argument */ |
| inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE); |
| /* no argument after the command */ |
| if (NULL == inPtr) return -EINVAL; |
| |
| /* removing empty spaces */ |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++; |
| |
| /* no argument followed by spaces */ |
| if ('\0' == *inPtr) { |
| return -EINVAL; |
| } |
| |
| /* getting the next argument ie the channel number */ |
| v = sscanf(inPtr, "%31s ", tempBuf); |
| if (1 != v) return -EINVAL; |
| |
| v = kstrtos32(tempBuf, 10, &tempInt); |
| if (v < 0 || tempInt < 0 || tempInt > WNI_CFG_CURRENT_CHANNEL_STAMAX) |
| return -EINVAL; |
| |
| *pChannel = tempInt; |
| |
| /* point to the next argument */ |
| inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE); |
| /* no argument after the command */ |
| if (NULL == inPtr) return -EINVAL; |
| /* removing empty spaces */ |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++; |
| |
| /* no argument followed by spaces */ |
| if ('\0' == *inPtr) { |
| return -EINVAL; |
| } |
| |
| /* Getting the next argument ie the dwell time */ |
| v = sscanf(inPtr, "%31s ", tempBuf); |
| if (1 != v) return -EINVAL; |
| |
| v = kstrtos32(tempBuf, 10, &tempInt); |
| if ( v < 0 || tempInt < 0) return -EINVAL; |
| |
| *pDwellTime = tempInt; |
| |
| /* point to the next argument */ |
| inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE); |
| /* no argument after the command */ |
| if (NULL == inPtr) return -EINVAL; |
| /* removing empty spaces */ |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++; |
| |
| /* no argument followed by spaces */ |
| if ('\0' == *inPtr) { |
| return -EINVAL; |
| } |
| |
| /* find the length of data */ |
| dataEnd = inPtr; |
| while('\0' != *dataEnd) { |
| dataEnd++; |
| } |
| *pBufLen = dataEnd - inPtr ; |
| if (*pBufLen <= 0) return -EINVAL; |
| |
| /* Allocate the number of bytes based on the number of input characters |
| whether it is even or odd. |
| if the number of input characters are even, then we need N/2 byte. |
| if the number of input characters are odd, then we need do (N+1)/2 to |
| compensate rounding off. |
| For example, if N = 18, then (18 + 1)/2 = 9 bytes are enough. |
| If N = 19, then we need 10 bytes, hence (19 + 1)/2 = 10 bytes */ |
| *pBuf = vos_mem_malloc((*pBufLen + 1)/2); |
| if (NULL == *pBuf) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: vos_mem_alloc failed ", __func__); |
| return -EINVAL; |
| } |
| |
| /* the buffer received from the upper layer is character buffer, |
| we need to prepare the buffer taking 2 characters in to a U8 hex decimal number |
| for example 7f0000f0...form a buffer to contain 7f in 0th location, 00 in 1st |
| and f0 in 3rd location */ |
| for (i = 0, j = 0; j < *pBufLen; j += 2) { |
| if( j+1 == *pBufLen) { |
| tempByte = hdd_parse_hex(inPtr[j]); |
| } else { |
| tempByte = (hdd_parse_hex(inPtr[j]) << 4) | (hdd_parse_hex(inPtr[j + 1])); |
| } |
| (*pBuf)[i++] = tempByte; |
| } |
| *pBufLen = i; |
| return 0; |
| } |
| |
| /* |
| \brief hdd_sendactionframe() - send a user space supplied action frame |
| |
| \param - pAdapter - Adapter upon which the command was received |
| \param - bssid - BSSID target of the action frame |
| \param - channel - channel upon which to send the frame |
| \param - dwell_time - amount of time to dwell when the frame is sent |
| \param - payload_len - length of the payload |
| \param - payload - payload of the frame |
| |
| \return - 0 for success non-zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static int |
| hdd_sendactionframe(hdd_adapter_t *pAdapter, const tANI_U8 *bssid, |
| const tANI_U8 channel, const tANI_U8 dwell_time, |
| const int payload_len, const tANI_U8 *payload) |
| { |
| struct ieee80211_channel chan; |
| int frame_len, ret = 0; |
| tANI_U8 *frame; |
| struct ieee80211_hdr_3addr *hdr; |
| u64 cookie; |
| hdd_station_ctx_t *pHddStaCtx; |
| hdd_context_t *pHddCtx; |
| tpSirMacVendorSpecificFrameHdr pVendorSpecific = |
| (tpSirMacVendorSpecificFrameHdr) payload; |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS) |
| struct cfg80211_mgmt_tx_params params; |
| #endif |
| pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| |
| /* if not associated, no need to send action frame */ |
| if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) { |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: Not associated", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| /* if the target bssid is different from currently associated AP, |
| then no need to send action frame */ |
| if (memcmp(bssid, pHddStaCtx->conn_info.bssId, VOS_MAC_ADDR_SIZE)) { |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: STA is not associated to this AP", |
| __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| chan.center_freq = sme_ChnToFreq(channel); |
| #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD |
| /* Check if it is specific action frame */ |
| if (pVendorSpecific->category == SIR_MAC_ACTION_VENDOR_SPECIFIC_CATEGORY) { |
| static const tANI_U8 Oui[] = { 0x00, 0x00, 0xf0 }; |
| if (vos_mem_compare(pVendorSpecific->Oui, (void *) Oui, 3)) { |
| /* if the channel number is different from operating channel then |
| no need to send action frame */ |
| if (channel != 0) { |
| if (channel != pHddStaCtx->conn_info.operationChannel) { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: channel(%d) is different from operating channel(%d)", |
| __func__, channel, |
| pHddStaCtx->conn_info.operationChannel); |
| ret = -EINVAL; |
| goto exit; |
| } |
| /* If channel number is specified and same as home channel, |
| * ensure that action frame is sent immediately by cancelling |
| * roaming scans. Otherwise large dwell times may cause long |
| * delays in sending action frames. |
| */ |
| sme_abortRoamScan(pHddCtx->hHal, pAdapter->sessionId); |
| } else { |
| /* 0 is accepted as current home channel, delayed |
| * transmission of action frame is ok. |
| */ |
| chan.center_freq = |
| sme_ChnToFreq(pHddStaCtx->conn_info.operationChannel); |
| } |
| } |
| } |
| #endif //#if WLAN_FEATURE_ROAM_SCAN_OFFLOAD |
| if (chan.center_freq == 0) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s:invalid channel number %d", |
| __func__, channel); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| frame_len = payload_len + 24; |
| frame = vos_mem_malloc(frame_len); |
| if (!frame) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s:memory allocation failed", __func__); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| vos_mem_zero(frame, frame_len); |
| |
| hdr = (struct ieee80211_hdr_3addr *)frame; |
| hdr->frame_control = |
| cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); |
| vos_mem_copy(hdr->addr1, bssid, VOS_MAC_ADDR_SIZE); |
| vos_mem_copy(hdr->addr2, pAdapter->macAddressCurrent.bytes, |
| VOS_MAC_ADDR_SIZE); |
| vos_mem_copy(hdr->addr3, bssid, VOS_MAC_ADDR_SIZE); |
| vos_mem_copy(hdr + 1, payload, payload_len); |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS) |
| params.chan = &chan; |
| params.offchan = 0; |
| params.wait = dwell_time; |
| params.buf = frame; |
| params.len = frame_len; |
| params.no_cck = 1; |
| params.dont_wait_for_ack = 1; |
| ret = wlan_hdd_mgmt_tx(NULL, &pAdapter->wdev, ¶ms, &cookie); |
| #else |
| ret = wlan_hdd_mgmt_tx(NULL, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| &(pAdapter->wdev), |
| #else |
| pAdapter->dev, |
| #endif |
| &chan, 0, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS) |
| NL80211_CHAN_HT20, 1, |
| #endif |
| dwell_time, frame, frame_len, 1, 1, &cookie ); |
| #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)*/ |
| |
| vos_mem_free(frame); |
| exit: |
| return ret; |
| } |
| |
| /* |
| \brief hdd_parse_sendactionframe_v1() - parse version 1 of the |
| SENDACTIONFRAME command |
| |
| This function parses the v1 SENDACTIONFRAME command with the format |
| |
| SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DW xxxxxx |
| |
| Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the |
| BSSID, CH is the ASCII representation of the channel, DW is the |
| ASCII representation of the dwell time, and xxxxxx is the Hex-ASCII |
| payload. For example |
| |
| SENDACTIONFRAME 00:0a:0b:11:22:33 48 40 aabbccddee |
| |
| \param - pAdapter - Adapter upon which the command was received |
| \param - command - ASCII text command that was received |
| |
| \return - 0 for success non-zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static int |
| hdd_parse_sendactionframe_v1(hdd_adapter_t *pAdapter, const char *command) |
| { |
| tANI_U8 channel = 0; |
| tANI_U8 dwell_time = 0; |
| tANI_U8 payload_len = 0; |
| tANI_U8 *payload = NULL; |
| tSirMacAddr bssid; |
| int ret; |
| |
| ret = hdd_parse_send_action_frame_v1_data(command, bssid, &channel, |
| &dwell_time, &payload, |
| &payload_len); |
| if (ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to parse send action frame data", __func__); |
| } else { |
| ret = hdd_sendactionframe(pAdapter, bssid, channel, dwell_time, |
| payload_len, payload); |
| vos_mem_free(payload); |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * hdd_parse_sendactionframe_v2() - parse version 2 of the |
| * SENDACTIONFRAME command |
| * @pAdapter: Adapter upon which the command was received |
| * @command: command that was received, ASCII command followed |
| * by binary data |
| * @total_len: total length of command |
| * |
| * This function parses the v2 SENDACTIONFRAME command with the format |
| * SENDACTIONFRAME <android_wifi_af_params> |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| static int |
| hdd_parse_sendactionframe_v2(hdd_adapter_t *pAdapter, |
| const char *command, int total_len) |
| { |
| struct android_wifi_af_params *params; |
| tSirMacAddr bssid; |
| int ret; |
| int len_wo_payload = 0; |
| |
| /* The params are located after "SENDACTIONFRAME " */ |
| total_len -= 16; |
| len_wo_payload = sizeof(*params) - ANDROID_WIFI_ACTION_FRAME_SIZE; |
| if (total_len <= len_wo_payload) { |
| hddLog(LOGE, FL("Invalid command len")); |
| return -EINVAL; |
| } |
| |
| params = (struct android_wifi_af_params *)(command + 16); |
| |
| if (params->len <= 0 || params->len > ANDROID_WIFI_ACTION_FRAME_SIZE || |
| (params->len > (total_len - len_wo_payload))) { |
| hddLog(LOGE, FL("Invalid payload length: %d"), params->len); |
| return -EINVAL; |
| } |
| |
| if (!mac_pton(params->bssid, (u8 *)&bssid)) { |
| hddLog(LOGE, FL("MAC address parsing failed")); |
| return -EINVAL; |
| } |
| |
| if (params->channel < 0 || |
| params->channel > WNI_CFG_CURRENT_CHANNEL_STAMAX) { |
| hddLog(LOGE, FL("Invalid channel: %d"), params->channel); |
| return -EINVAL; |
| } |
| |
| if (params->dwell_time < 0) { |
| hddLog(LOGE, FL("Invalid dwell_time: %d"), params->dwell_time); |
| return -EINVAL; |
| } |
| |
| ret = hdd_sendactionframe(pAdapter, bssid, params->channel, |
| params->dwell_time, params->len, params->data); |
| |
| return ret; |
| } |
| |
| /* |
| \brief hdd_parse_sendactionframe() - parse the SENDACTIONFRAME command |
| |
| There are two different versions of the SENDACTIONFRAME command. |
| Version 1 of the command contains a parameter list that is ASCII |
| characters whereas version 2 contains a combination of ASCII and |
| binary payload. Determine if a version 1 or a version 2 command is |
| being parsed by examining the parameters, and then dispatch the |
| parser that is appropriate for the version of the command. |
| |
| \param - pAdapter - Adapter upon which the command was received |
| \param - command - command that was received |
| |
| \return - 0 for success non-zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static int |
| hdd_parse_sendactionframe(hdd_adapter_t *pAdapter, const char *command, |
| int total_len) |
| { |
| int ret; |
| |
| /* both versions start with "SENDACTIONFRAME " |
| * v1 has a bssid and other parameters as an ASCII string |
| * SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DWELL LEN FRAME |
| * v2 has a C struct |
| * SENDACTIONFRAME <binary c struct> |
| * |
| * The first field in the v2 struct is also the bssid in ASCII. |
| * But in the case of a v2 message the BSSID is NUL-terminated. |
| * Hence we can peek at that offset to see if this is V1 or V2 |
| * SENDACTIONFRAME xx:xx:xx:xx:xx:xx* |
| * 111111111122222222223333 |
| * 0123456789012345678901234567890123 |
| * |
| * For both the commands, a valid command must have atleast first 34 length |
| * of data. |
| */ |
| if (total_len < 34) { |
| hddLog(LOGE, FL("Invalid command (total_len=%d)"), total_len); |
| return -EINVAL; |
| } |
| |
| if (command[33]) { |
| ret = hdd_parse_sendactionframe_v1(pAdapter, command); |
| } else { |
| ret = hdd_parse_sendactionframe_v2(pAdapter, command, total_len); |
| } |
| |
| return ret; |
| } |
| |
| static void hdd_getBand_helper(hdd_context_t *pHddCtx, int *pBand) |
| { |
| eCsrBand band = -1; |
| sme_GetFreqBand((tHalHandle)(pHddCtx->hHal), &band); |
| switch (band) { |
| case eCSR_BAND_ALL: |
| *pBand = WLAN_HDD_UI_BAND_AUTO; |
| break; |
| |
| case eCSR_BAND_24: |
| *pBand = WLAN_HDD_UI_BAND_2_4_GHZ; |
| break; |
| |
| case eCSR_BAND_5G: |
| *pBand = WLAN_HDD_UI_BAND_5_GHZ; |
| break; |
| |
| default: |
| hddLog(LOGW, FL("Invalid Band %d"), band); |
| *pBand = -1; |
| break; |
| } |
| } |
| |
| |
| /** |
| * hdd_parse_channellist() - HDD Parse channel list |
| * @pValue: Pointer to input data |
| * @pChannelList: Pointer to input channel list |
| * @pNumChannels: Pointer to number of roam scan channels |
| * |
| * This function parses the channel list passed in the format |
| * SETROAMSCANCHANNELS<space><Number of channels><space>Channel 1<space> |
| * Channel 2<space>Channel N |
| * if the Number of channels (N) does not match with the actual number of |
| * channels passed then take the minimum of N and count of (Ch1, Ch2, ...Ch M) |
| * For example, if SETROAMSCANCHANNELS 3 36 40 44 48, only 36, 40 and 44 shall |
| * be taken. |
| * If SETROAMSCANCHANNELS 5 36 40 44 48, ignore 5 and take 36, 40, 44 and 48. |
| * This function does not take care of removing duplicate channels from the list |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| static int |
| hdd_parse_channellist(const tANI_U8 *pValue, tANI_U8 *pChannelList, |
| tANI_U8 *pNumChannels) |
| { |
| const tANI_U8 *inPtr = pValue; |
| int tempInt; |
| int j = 0; |
| int v = 0; |
| char buf[32]; |
| |
| inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE); |
| /* no argument after the command */ |
| if (NULL == inPtr) { |
| return -EINVAL; |
| } else if (SPACE_ASCII_VALUE != *inPtr) { |
| /* no space after the command */ |
| return -EINVAL; |
| } |
| |
| /* removing empty spaces */ |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++; |
| |
| /* no argument followed by spaces */ |
| if ('\0' == *inPtr) { |
| return -EINVAL; |
| } |
| |
| /*getting the first argument ie the number of channels*/ |
| v = sscanf(inPtr, "%31s ", buf); |
| if (1 != v) return -EINVAL; |
| |
| v = kstrtos32(buf, 10, &tempInt); |
| if ((v < 0) || |
| (tempInt <= 0) || |
| (tempInt > WNI_CFG_VALID_CHANNEL_LIST_LEN)) { |
| return -EINVAL; |
| } |
| |
| *pNumChannels = tempInt; |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, |
| "Number of channels are: %d", *pNumChannels); |
| |
| for (j = 0; j < (*pNumChannels); j++) { |
| /* inPtr pointing to the beginning of first space after number of |
| channels*/ |
| inPtr = strpbrk( inPtr, " " ); |
| /* no channel list after the number of channels argument */ |
| if (NULL == inPtr) { |
| if (0 != j) { |
| *pNumChannels = j; |
| return 0; |
| } else { |
| return -EINVAL; |
| } |
| } |
| |
| /* removing empty space */ |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++; |
| |
| /*no channel list after the number of channels argument and spaces*/ |
| if ('\0' == *inPtr) { |
| if (0 != j) { |
| *pNumChannels = j; |
| return 0; |
| } else { |
| return -EINVAL; |
| } |
| } |
| |
| v = sscanf(inPtr, "%31s ", buf); |
| if (1 != v) return -EINVAL; |
| |
| v = kstrtos32(buf, 10, &tempInt); |
| if ((v < 0) || |
| (tempInt <= 0) || |
| (tempInt > WNI_CFG_CURRENT_CHANNEL_STAMAX)) { |
| return -EINVAL; |
| } |
| pChannelList[j] = tempInt; |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, |
| "Channel %d added to preferred channel list", |
| pChannelList[j] ); |
| } |
| |
| return 0; |
| } |
| |
| /* |
| \brief hdd_parse_set_roam_scan_channels_v1() - parse version 1 of the |
| SETROAMSCANCHANNELS command |
| |
| This function parses the v1 SETROAMSCANCHANNELS command with the format |
| |
| SETROAMSCANCHANNELS N C1 C2 ... Cn |
| |
| Where "N" is the ASCII representation of the number of channels and |
| C1 thru Cn is the ASCII representation of the channels. For example |
| |
| SETROAMSCANCHANNELS 4 36 40 44 48 |
| |
| \param - pAdapter - Adapter upon which the command was received |
| \param - command - ASCII text command that was received |
| |
| \return - 0 for success non-zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static int |
| hdd_parse_set_roam_scan_channels_v1(hdd_adapter_t *pAdapter, |
| const char *command) |
| { |
| tANI_U8 channel_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0}; |
| tANI_U8 num_chan = 0; |
| eHalStatus status; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| int ret; |
| |
| ret = hdd_parse_channellist(command, channel_list, &num_chan); |
| if (ret) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to parse channel list information", __func__); |
| goto exit; |
| } |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL, |
| pAdapter->sessionId, num_chan)); |
| |
| if (num_chan > WNI_CFG_VALID_CHANNEL_LIST_LEN) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: number of channels (%d) supported exceeded max (%d)", |
| __func__, num_chan, WNI_CFG_VALID_CHANNEL_LIST_LEN); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| status = sme_ChangeRoamScanChannelList(pHddCtx->hHal, pAdapter->sessionId, |
| channel_list, num_chan); |
| if (eHAL_STATUS_SUCCESS != status) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to update channel list information", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| exit: |
| return ret; |
| } |
| |
| /* |
| \brief hdd_parse_set_roam_scan_channels_v2() - parse version 2 of the |
| SETROAMSCANCHANNELS command |
| |
| This function parses the v2 SETROAMSCANCHANNELS command with the format |
| |
| SETROAMSCANCHANNELS [N][C1][C2][Cn] |
| |
| The command begins with SETROAMSCANCHANNELS followed by a space, but |
| what follows the space is an array of u08 parameters. For example |
| |
| SETROAMSCANCHANNELS [0x04 0x24 0x28 0x2c 0x30] |
| |
| \param - pAdapter - Adapter upon which the command was received |
| \param - command - command that was received, ASCII command followed |
| by binary data |
| |
| \return - 0 for success non-zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static int |
| hdd_parse_set_roam_scan_channels_v2(hdd_adapter_t *pAdapter, |
| const char *command) |
| { |
| const tANI_U8 *value; |
| tANI_U8 channel_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0}; |
| tANI_U8 channel; |
| tANI_U8 num_chan; |
| int i; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| eHalStatus status; |
| int ret = 0; |
| |
| /* array of values begins after "SETROAMSCANCHANNELS " */ |
| value = command + 20; |
| |
| num_chan = *value++; |
| if (num_chan > WNI_CFG_VALID_CHANNEL_LIST_LEN) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: number of channels (%d) supported exceeded max (%d)", |
| __func__, num_chan, WNI_CFG_VALID_CHANNEL_LIST_LEN); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL, |
| pAdapter->sessionId, num_chan)); |
| |
| for (i = 0; i < num_chan; i++) { |
| channel = *value++; |
| if (channel > WNI_CFG_CURRENT_CHANNEL_STAMAX) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: index %d invalid channel %d", __func__, i, channel); |
| ret = -EINVAL; |
| goto exit; |
| } |
| channel_list[i] = channel; |
| } |
| status = sme_ChangeRoamScanChannelList(pHddCtx->hHal, pAdapter->sessionId, |
| channel_list, num_chan); |
| if (eHAL_STATUS_SUCCESS != status) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to update channel list information", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| exit: |
| return ret; |
| } |
| |
| /* |
| \brief hdd_parse_set_roam_scan_channels() - parse the |
| SETROAMSCANCHANNELS command |
| |
| There are two different versions of the SETROAMSCANCHANNELS command. |
| Version 1 of the command contains a parameter list that is ASCII |
| characters whereas version 2 contains a binary payload. Determine |
| if a version 1 or a version 2 command is being parsed by examining |
| the parameters, and then dispatch the parser that is appropriate for |
| the command. |
| |
| \param - pAdapter - Adapter upon which the command was received |
| \param - command - command that was received |
| |
| \return - 0 for success non-zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static int |
| hdd_parse_set_roam_scan_channels(hdd_adapter_t *pAdapter, |
| const char *command) |
| { |
| const char *cursor; |
| char ch; |
| bool v1; |
| int ret; |
| |
| /* start after "SETROAMSCANCHANNELS " */ |
| cursor = command + 20; |
| |
| /* assume we have a version 1 command until proven otherwise */ |
| v1 = true; |
| |
| /* v1 params will only contain ASCII digits and space */ |
| while ((ch = *cursor++) && v1) { |
| if (!(isdigit(ch) || isspace(ch))) { |
| v1 = false; |
| } |
| } |
| if (v1) { |
| ret = hdd_parse_set_roam_scan_channels_v1(pAdapter, command); |
| } else { |
| ret = hdd_parse_set_roam_scan_channels_v2(pAdapter, command); |
| } |
| |
| return ret; |
| } |
| |
| #endif /* WLAN_FEATURE_VOWIFI_11R || FEATURE_WLAN_ESE || FEATURE_WLAN_LFR */ |
| |
| #if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD) |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_parse_plm_cmd() - HDD Parse Plm command |
| |
| This function parses the plm command passed in the format |
| CCXPLMREQ<space><enable><space><dialog_token><space> |
| <meas_token><space><num_of_bursts><space><burst_int><space> |
| <measu duration><space><burst_len><space><desired_tx_pwr> |
| <space><multcast_addr><space><number_of_channels> |
| <space><channel_numbers> |
| |
| \param - pValue Pointer to input data |
| \param - pPlmRequest Pointer to output struct tpSirPlmReq |
| |
| \return - 0 for success non-zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static eHalStatus hdd_parse_plm_cmd(tANI_U8 *pValue, tSirPlmReq *pPlmRequest) |
| { |
| tANI_U8 *cmdPtr = NULL; |
| int count, content = 0, ret = 0; |
| char buf[32]; |
| |
| /* moving to argument list */ |
| cmdPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE); |
| if (NULL == cmdPtr) |
| return eHAL_STATUS_FAILURE; |
| |
| /*no space after the command*/ |
| if (SPACE_ASCII_VALUE != *cmdPtr) |
| return eHAL_STATUS_FAILURE; |
| |
| /*removing empty spaces*/ |
| while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr)) |
| cmdPtr++; |
| |
| /* START/STOP PLM req */ |
| ret = sscanf(cmdPtr, "%31s ", buf); |
| if (1 != ret) return eHAL_STATUS_FAILURE; |
| |
| ret = kstrtos32(buf, 10, &content); |
| if ( ret < 0) return eHAL_STATUS_FAILURE; |
| |
| pPlmRequest->enable = content; |
| cmdPtr = strpbrk(cmdPtr, " "); |
| |
| if (NULL == cmdPtr) |
| return eHAL_STATUS_FAILURE; |
| |
| /*removing empty spaces*/ |
| while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr)) |
| cmdPtr++; |
| |
| /* Dialog token of radio meas req containing meas reqIE */ |
| ret = sscanf(cmdPtr, "%31s ", buf); |
| if (1 != ret) return eHAL_STATUS_FAILURE; |
| |
| ret = kstrtos32(buf, 10, &content); |
| if ( ret < 0) return eHAL_STATUS_FAILURE; |
| |
| pPlmRequest->diag_token = content; |
| hddLog(VOS_TRACE_LEVEL_DEBUG, "diag token %d", pPlmRequest->diag_token); |
| cmdPtr = strpbrk(cmdPtr, " "); |
| |
| if (NULL == cmdPtr) |
| return eHAL_STATUS_FAILURE; |
| |
| /*removing empty spaces*/ |
| while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr)) |
| cmdPtr++; |
| |
| /* measurement token of meas req IE */ |
| ret = sscanf(cmdPtr, "%31s ", buf); |
| if (1 != ret) return eHAL_STATUS_FAILURE; |
| |
| ret = kstrtos32(buf, 10, &content); |
| if ( ret < 0) return eHAL_STATUS_FAILURE; |
| |
| pPlmRequest->meas_token = content; |
| hddLog(VOS_TRACE_LEVEL_DEBUG, "meas token %d", pPlmRequest->meas_token); |
| |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "PLM req %s", pPlmRequest->enable ? "START" : "STOP"); |
| if (pPlmRequest->enable) { |
| |
| cmdPtr = strpbrk(cmdPtr, " "); |
| |
| if (NULL == cmdPtr) |
| return eHAL_STATUS_FAILURE; |
| |
| /*removing empty spaces*/ |
| while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr)) |
| cmdPtr++; |
| |
| /* total number of bursts after which STA stops sending */ |
| ret = sscanf(cmdPtr, "%31s ", buf); |
| if (1 != ret) return eHAL_STATUS_FAILURE; |
| |
| ret = kstrtos32(buf, 10, &content); |
| if ( ret < 0) return eHAL_STATUS_FAILURE; |
| |
| if (content < 0) |
| return eHAL_STATUS_FAILURE; |
| |
| pPlmRequest->numBursts= content; |
| hddLog(VOS_TRACE_LEVEL_DEBUG, "num burst %d", pPlmRequest->numBursts); |
| cmdPtr = strpbrk(cmdPtr, " "); |
| |
| if (NULL == cmdPtr) |
| return eHAL_STATUS_FAILURE; |
| |
| /* removing empty spaces */ |
| while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr)) |
| cmdPtr++; |
| |
| /* burst interval in seconds */ |
| ret = sscanf(cmdPtr, "%31s ", buf); |
| if (1 != ret) return eHAL_STATUS_FAILURE; |
| |
| ret = kstrtos32(buf, 10, &content); |
| if ( ret < 0) return eHAL_STATUS_FAILURE; |
| |
| if (content <= 0) |
| return eHAL_STATUS_FAILURE; |
| |
| pPlmRequest->burstInt = content; |
| hddLog(VOS_TRACE_LEVEL_DEBUG, "burst Int %d", pPlmRequest->burstInt); |
| cmdPtr = strpbrk(cmdPtr, " "); |
| |
| if (NULL == cmdPtr) |
| return eHAL_STATUS_FAILURE; |
| |
| /*removing empty spaces*/ |
| while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr)) |
| cmdPtr++; |
| |
| /* Meas dur in TU's,STA goes off-ch and transmit PLM bursts */ |
| ret = sscanf(cmdPtr, "%31s ", buf); |
| if (1 != ret) return eHAL_STATUS_FAILURE; |
| |
| ret = kstrtos32(buf, 10, &content); |
| if ( ret < 0) return eHAL_STATUS_FAILURE; |
| |
| if (content <= 0) |
| return eHAL_STATUS_FAILURE; |
| |
| pPlmRequest->measDuration = content; |
| hddLog(VOS_TRACE_LEVEL_DEBUG, "measDur %d", pPlmRequest->measDuration); |
| cmdPtr = strpbrk(cmdPtr, " "); |
| |
| if (NULL == cmdPtr) |
| return eHAL_STATUS_FAILURE; |
| |
| /*removing empty spaces*/ |
| while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr)) |
| cmdPtr++; |
| |
| /* burst length of PLM bursts */ |
| ret = sscanf(cmdPtr, "%31s ", buf); |
| if (1 != ret) return eHAL_STATUS_FAILURE; |
| |
| ret = kstrtos32(buf, 10, &content); |
| if ( ret < 0) return eHAL_STATUS_FAILURE; |
| |
| if (content <= 0) |
| return eHAL_STATUS_FAILURE; |
| |
| pPlmRequest->burstLen = content; |
| hddLog(VOS_TRACE_LEVEL_DEBUG, "burstLen %d", pPlmRequest->burstLen); |
| cmdPtr = strpbrk(cmdPtr, " "); |
| |
| if (NULL == cmdPtr) |
| return eHAL_STATUS_FAILURE; |
| |
| /*removing empty spaces*/ |
| while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr)) |
| cmdPtr++; |
| |
| /* desired tx power for transmission of PLM bursts */ |
| ret = sscanf(cmdPtr, "%31s ", buf); |
| if (1 != ret) return eHAL_STATUS_FAILURE; |
| |
| ret = kstrtos32(buf, 10, &content); |
| if ( ret < 0) return eHAL_STATUS_FAILURE; |
| |
| if (content <= 0) |
| return eHAL_STATUS_FAILURE; |
| |
| pPlmRequest->desiredTxPwr = content; |
| hddLog(VOS_TRACE_LEVEL_DEBUG, |
| "desiredTxPwr %d", pPlmRequest->desiredTxPwr); |
| |
| for (count = 0; count < VOS_MAC_ADDR_SIZE; count++) |
| { |
| cmdPtr = strpbrk(cmdPtr, " "); |
| |
| if (NULL == cmdPtr) |
| return eHAL_STATUS_FAILURE; |
| |
| /*removing empty spaces*/ |
| while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr)) |
| cmdPtr++; |
| |
| ret = sscanf(cmdPtr, "%31s ", buf); |
| if (1 != ret) return eHAL_STATUS_FAILURE; |
| |
| ret = kstrtos32(buf, 16, &content); |
| if ( ret < 0) return eHAL_STATUS_FAILURE; |
| |
| pPlmRequest->macAddr[count] = content; |
| } |
| |
| hddLog(VOS_TRACE_LEVEL_DEBUG, "MC addr "MAC_ADDRESS_STR, |
| MAC_ADDR_ARRAY(pPlmRequest->macAddr)); |
| |
| cmdPtr = strpbrk(cmdPtr, " "); |
| |
| if (NULL == cmdPtr) |
| return eHAL_STATUS_FAILURE; |
| |
| /*removing empty spaces*/ |
| while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr)) |
| cmdPtr++; |
| |
| /* number of channels */ |
| ret = sscanf(cmdPtr, "%31s ", buf); |
| if (1 != ret) return eHAL_STATUS_FAILURE; |
| |
| ret = kstrtos32(buf, 10, &content); |
| if ( ret < 0) return eHAL_STATUS_FAILURE; |
| |
| if (content < 0) |
| return eHAL_STATUS_FAILURE; |
| |
| content = VOS_MIN(content, WNI_CFG_VALID_CHANNEL_LIST_LEN); |
| pPlmRequest->plmNumCh = content; |
| hddLog(LOG1, FL("Numch: %d"), pPlmRequest->plmNumCh); |
| |
| /* Channel numbers */ |
| for (count = 0; count < pPlmRequest->plmNumCh; count++) |
| { |
| cmdPtr = strpbrk(cmdPtr, " "); |
| |
| if (NULL == cmdPtr) |
| return eHAL_STATUS_FAILURE; |
| |
| /*removing empty spaces*/ |
| while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr)) |
| cmdPtr++; |
| |
| ret = sscanf(cmdPtr, "%31s ", buf); |
| if (1 != ret) return eHAL_STATUS_FAILURE; |
| |
| ret = kstrtos32(buf, 10, &content); |
| if (ret < 0 || content <= 0 || |
| content > WNI_CFG_CURRENT_CHANNEL_STAMAX) |
| return eHAL_STATUS_FAILURE; |
| |
| pPlmRequest->plmChList[count]= content; |
| hddLog(VOS_TRACE_LEVEL_DEBUG, " ch- %d", |
| pPlmRequest->plmChList[count]); |
| } |
| } /* If PLM START */ |
| |
| return eHAL_STATUS_SUCCESS; |
| } |
| #endif |
| #ifdef WLAN_FEATURE_EXTWOW_SUPPORT |
| static void wlan_hdd_ready_to_extwow(void *callbackContext, |
| boolean is_success) |
| { |
| hdd_context_t *pHddCtx = (hdd_context_t *)callbackContext; |
| int rc; |
| |
| rc = wlan_hdd_validate_context(pHddCtx); |
| if (0 != rc) |
| return; |
| |
| pHddCtx->ext_wow_should_suspend = is_success; |
| complete(&pHddCtx->ready_to_extwow); |
| } |
| |
| static int hdd_enable_ext_wow(hdd_adapter_t *pAdapter, |
| tpSirExtWoWParams arg_params) |
| { |
| tSirExtWoWParams params; |
| eHalStatus halStatus = eHAL_STATUS_FAILURE; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter); |
| int rc; |
| |
| vos_mem_copy(¶ms, arg_params, sizeof(params)); |
| |
| INIT_COMPLETION(pHddCtx->ready_to_extwow); |
| |
| halStatus = sme_ConfigureExtWoW(hHal, ¶ms, |
| &wlan_hdd_ready_to_extwow, pHddCtx); |
| if (eHAL_STATUS_SUCCESS != halStatus) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("sme_ConfigureExtWoW returned failure %d"), halStatus); |
| return -EPERM; |
| } |
| |
| rc = wait_for_completion_timeout(&pHddCtx->ready_to_extwow, |
| msecs_to_jiffies(WLAN_WAIT_TIME_READY_TO_EXTWOW)); |
| if (!rc) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to get ready to extwow", __func__); |
| return -EPERM; |
| } |
| |
| if (pHddCtx->ext_wow_should_suspend) { |
| if (pHddCtx->cfg_ini->extWowGotoSuspend) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received ready to ExtWoW. Going to suspend", __func__); |
| |
| wlan_hdd_cfg80211_suspend_wlan(pHddCtx->wiphy, NULL); |
| wlan_hif_pci_suspend(); |
| } |
| } else { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Received ready to ExtWoW failure", __func__); |
| return -EPERM; |
| } |
| |
| return 0; |
| } |
| |
| static int hdd_enable_ext_wow_parser(hdd_adapter_t *pAdapter, int vdev_id, |
| int value) |
| { |
| tSirExtWoWParams params; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| int rc; |
| |
| rc = wlan_hdd_validate_context(pHddCtx); |
| if (0 != rc) |
| return -EINVAL; |
| |
| if (value < EXT_WOW_TYPE_APP_TYPE1 || value > EXT_WOW_TYPE_APP_TYPE1_2 ) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid type")); |
| return -EINVAL; |
| } |
| |
| if (value == EXT_WOW_TYPE_APP_TYPE1 && |
| pHddCtx->is_extwow_app_type1_param_set) |
| params.type = value; |
| else if (value == EXT_WOW_TYPE_APP_TYPE2 && |
| pHddCtx->is_extwow_app_type2_param_set) |
| params.type = value; |
| else if (value == EXT_WOW_TYPE_APP_TYPE1_2 && |
| pHddCtx->is_extwow_app_type1_param_set && |
| pHddCtx->is_extwow_app_type2_param_set) |
| params.type = value; |
| else { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Set app params before enable it value %d"),value); |
| return -EINVAL; |
| } |
| |
| params.vdev_id = vdev_id; |
| params.wakeup_pin_num = pHddCtx->cfg_ini->extWowApp1WakeupPinNumber | |
| (pHddCtx->cfg_ini->extWowApp2WakeupPinNumber << 8); |
| |
| return hdd_enable_ext_wow(pAdapter, ¶ms); |
| } |
| |
| static int hdd_set_app_type1_params(tHalHandle hHal, |
| tpSirAppType1Params arg_params) |
| { |
| tSirAppType1Params params; |
| eHalStatus halStatus = eHAL_STATUS_FAILURE; |
| |
| vos_mem_copy(¶ms, arg_params, sizeof(params)); |
| |
| halStatus = sme_ConfigureAppType1Params(hHal, ¶ms); |
| if (eHAL_STATUS_SUCCESS != halStatus) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("sme_ConfigureAppType1Params returned failure %d"), halStatus); |
| return -EPERM; |
| } |
| |
| return 0; |
| } |
| |
| static int hdd_set_app_type1_parser(hdd_adapter_t *pAdapter, |
| char *arg, int len) |
| { |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter); |
| char id[20], password[20]; |
| tSirAppType1Params params; |
| int rc, i; |
| |
| rc = wlan_hdd_validate_context(pHddCtx); |
| if (0 != rc) |
| return -EINVAL; |
| |
| if (2 != sscanf(arg, "%8s %16s", id, password)) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| FL("Invalid Number of arguments")); |
| return -EINVAL; |
| } |
| |
| memset(¶ms, 0, sizeof(tSirAppType1Params)); |
| params.vdev_id = pAdapter->sessionId; |
| for (i = 0; i < ETHER_ADDR_LEN; i++) |
| params.wakee_mac_addr[i] = pAdapter->macAddressCurrent.bytes[i]; |
| |
| params.id_length = strlen(id); |
| vos_mem_copy(params.identification_id, id, params.id_length); |
| params.pass_length = strlen(password); |
| vos_mem_copy(params.password, password, params.pass_length); |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: %d %pM %.8s %u %.16s %u", |
| __func__, params.vdev_id, params.wakee_mac_addr, |
| params.identification_id, params.id_length, |
| params.password, params.pass_length ); |
| |
| return hdd_set_app_type1_params(hHal, ¶ms); |
| } |
| |
| static int hdd_set_app_type2_params(tHalHandle hHal, |
| tpSirAppType2Params arg_params) |
| { |
| tSirAppType2Params params; |
| eHalStatus halStatus = eHAL_STATUS_FAILURE; |
| |
| vos_mem_copy(¶ms, arg_params, sizeof(params)); |
| |
| halStatus = sme_ConfigureAppType2Params(hHal, ¶ms); |
| if (eHAL_STATUS_SUCCESS != halStatus) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("sme_ConfigureAppType2Params returned failure %d"), halStatus); |
| return -EPERM; |
| } |
| |
| return 0; |
| } |
| |
| static int hdd_set_app_type2_parser(hdd_adapter_t *pAdapter, |
| char *arg, int len) |
| { |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter); |
| char mac_addr[20], rc4_key[20]; |
| unsigned int gateway_mac[6], i; |
| tSirAppType2Params params; |
| int ret; |
| |
| ret = wlan_hdd_validate_context(pHddCtx); |
| if (0 != ret) |
| return -EINVAL; |
| |
| memset(¶ms, 0, sizeof(tSirAppType2Params)); |
| |
| ret = sscanf(arg, "%17s %16s %x %x %x %u %u %hu %hu %u %u %u %u %u %u", |
| mac_addr, rc4_key, (unsigned int *)¶ms.ip_id, |
| (unsigned int*)¶ms.ip_device_ip, |
| (unsigned int*)¶ms.ip_server_ip, |
| (unsigned int*)¶ms.tcp_seq, (unsigned int*)¶ms.tcp_ack_seq, |
| (uint16_t*)¶ms.tcp_src_port, |
| (uint16_t*)¶ms.tcp_dst_port, |
| (unsigned int*)¶ms.keepalive_init, |
| (unsigned int*)¶ms.keepalive_min, |
| (unsigned int*)¶ms.keepalive_max, |
| (unsigned int*)¶ms.keepalive_inc, |
| (unsigned int*)¶ms.tcp_tx_timeout_val, |
| (unsigned int*)¶ms.tcp_rx_timeout_val); |
| |
| |
| if (ret != 15 && ret != 7) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Invalid Number of arguments"); |
| return -EINVAL; |
| } |
| |
| if (6 != sscanf(mac_addr, "%02x:%02x:%02x:%02x:%02x:%02x", &gateway_mac[0], |
| &gateway_mac[1], &gateway_mac[2], &gateway_mac[3], |
| &gateway_mac[4], &gateway_mac[5])) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Invalid MacAddress Input %s", mac_addr); |
| return -EINVAL; |
| } |
| |
| if (params.tcp_src_port > WLAN_HDD_MAX_TCP_PORT || |
| params.tcp_dst_port > WLAN_HDD_MAX_TCP_PORT) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Invalid TCP Port Number"); |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < ETHER_ADDR_LEN; i++) |
| params.gateway_mac[i] = (uint8_t) gateway_mac[i]; |
| |
| params.rc4_key_len = strlen(rc4_key); |
| vos_mem_copy(params.rc4_key, rc4_key, params.rc4_key_len); |
| |
| params.vdev_id = pAdapter->sessionId; |
| params.tcp_src_port = (params.tcp_src_port != 0)? |
| params.tcp_src_port : pHddCtx->cfg_ini->extWowApp2TcpSrcPort; |
| params.tcp_dst_port = (params.tcp_dst_port != 0)? |
| params.tcp_dst_port : pHddCtx->cfg_ini->extWowApp2TcpDstPort; |
| params.keepalive_init = (params.keepalive_init != 0)? |
| params.keepalive_init : pHddCtx->cfg_ini->extWowApp2KAInitPingInterval; |
| params.keepalive_min = (params.keepalive_min != 0)? |
| params.keepalive_min : pHddCtx->cfg_ini->extWowApp2KAMinPingInterval; |
| params.keepalive_max = (params.keepalive_max != 0)? |
| params.keepalive_max : pHddCtx->cfg_ini->extWowApp2KAMaxPingInterval; |
| params.keepalive_inc = (params.keepalive_inc != 0)? |
| params.keepalive_inc : pHddCtx->cfg_ini->extWowApp2KAIncPingInterval; |
| params.tcp_tx_timeout_val = (params.tcp_tx_timeout_val != 0)? |
| params.tcp_tx_timeout_val : pHddCtx->cfg_ini->extWowApp2TcpTxTimeout; |
| params.tcp_rx_timeout_val = (params.tcp_rx_timeout_val != 0)? |
| params.tcp_rx_timeout_val : pHddCtx->cfg_ini->extWowApp2TcpRxTimeout; |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: %pM %.16s %u %u %u %u %u %u %u %u %u %u %u %u %u", |
| __func__, gateway_mac, rc4_key, params.ip_id, params.ip_device_ip, |
| params.ip_server_ip, params.tcp_seq, params.tcp_ack_seq, |
| params.tcp_src_port, params.tcp_dst_port, params.keepalive_init, |
| params.keepalive_min, params.keepalive_max, |
| params.keepalive_inc, params.tcp_tx_timeout_val, |
| params.tcp_rx_timeout_val); |
| |
| return hdd_set_app_type2_params(hHal, ¶ms); |
| } |
| |
| #endif |
| |
| int wlan_hdd_set_mc_rate(hdd_adapter_t *pAdapter, int targetRate) |
| { |
| tSirRateUpdateInd rateUpdate = {0}; |
| eHalStatus status; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| hdd_config_t *pConfig = NULL; |
| |
| if (pHddCtx == NULL) { |
| hddLog(LOGE, FL("HDD context is null")); |
| return -EINVAL; |
| } |
| |
| if ((WLAN_HDD_IBSS != pAdapter->device_mode) && |
| (WLAN_HDD_SOFTAP != pAdapter->device_mode) && |
| (WLAN_HDD_INFRA_STATION != pAdapter->device_mode)) { |
| hddLog(LOGE, FL("Received SETMCRATE cmd in invalid device mode %s(%d)"), |
| hdd_device_mode_to_string(pAdapter->device_mode), |
| pAdapter->device_mode); |
| hddLog(LOGE, |
| FL("SETMCRATE cmd is allowed only in STA, IBSS or SOFTAP mode")); |
| return -EINVAL; |
| } |
| |
| pConfig = pHddCtx->cfg_ini; |
| rateUpdate.nss = (pConfig->enable2x2 == 0) ? 0 : 1; |
| rateUpdate.dev_mode = pAdapter->device_mode; |
| rateUpdate.mcastDataRate24GHz = targetRate; |
| rateUpdate.mcastDataRate24GHzTxFlag = 1; |
| rateUpdate.mcastDataRate5GHz = targetRate; |
| rateUpdate.bcastDataRate = -1; |
| memcpy(rateUpdate.bssid, pAdapter->macAddressCurrent.bytes, |
| sizeof(rateUpdate.bssid)); |
| hddLog(LOG1, FL("MC Target rate %d, mac = %pM, dev_mode %s(%d)"), |
| rateUpdate.mcastDataRate24GHz, rateUpdate.bssid, |
| hdd_device_mode_to_string(pAdapter->device_mode), |
| pAdapter->device_mode); |
| |
| status = sme_SendRateUpdateInd(pHddCtx->hHal, &rateUpdate); |
| if (eHAL_STATUS_SUCCESS != status) { |
| hddLog(LOGE, FL("SETMCRATE failed")); |
| return -EFAULT; |
| } |
| return 0; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_parse_setmaxtxpower_command() - HDD Parse MAXTXPOWER command |
| |
| This function parses the MAXTXPOWER command passed in the format |
| MAXTXPOWER<space>X(Tx power in dbm) |
| For example input commands: |
| 1) MAXTXPOWER -8 -> This is translated into set max TX power to -8 dbm |
| 2) MAXTXPOWER -23 -> This is translated into set max TX power to -23 dbm |
| |
| \param - pValue Pointer to MAXTXPOWER command |
| \param - pDbm Pointer to tx power |
| |
| \return - 0 for success non-zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static int hdd_parse_setmaxtxpower_command(tANI_U8 *pValue, int *pTxPower) |
| { |
| tANI_U8 *inPtr = pValue; |
| int tempInt; |
| int v = 0; |
| *pTxPower = 0; |
| |
| inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE); |
| /*no argument after the command*/ |
| if (NULL == inPtr) |
| { |
| return -EINVAL; |
| } |
| |
| /*no space after the command*/ |
| else if (SPACE_ASCII_VALUE != *inPtr) |
| { |
| return -EINVAL; |
| } |
| |
| /*removing empty spaces*/ |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++; |
| |
| /*no argument followed by spaces*/ |
| if ('\0' == *inPtr) |
| { |
| return 0; |
| } |
| |
| v = kstrtos32(inPtr, 10, &tempInt); |
| |
| /* Range checking for passed parameter */ |
| if ( (tempInt < HDD_MIN_TX_POWER) || |
| (tempInt > HDD_MAX_TX_POWER) ) |
| { |
| return -EINVAL; |
| } |
| |
| *pTxPower = tempInt; |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "SETMAXTXPOWER: %d", *pTxPower); |
| |
| return 0; |
| } /*End of hdd_parse_setmaxtxpower_command*/ |
| |
| |
| static int hdd_get_dwell_time(hdd_config_t *pCfg, tANI_U8 *command, char *extra, tANI_U8 n, tANI_U8 *len) |
| { |
| int ret = 0; |
| |
| if (!pCfg || !command || !extra || !len) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: argument passed for GETDWELLTIME is incorrect", __func__); |
| ret = -EINVAL; |
| return ret; |
| } |
| |
| if (strncmp(command, "GETDWELLTIME ACTIVE MAX", 23) == 0) |
| { |
| *len = scnprintf(extra, n, "GETDWELLTIME ACTIVE MAX %u\n", |
| (int)pCfg->nActiveMaxChnTime); |
| return ret; |
| } |
| else if (strncmp(command, "GETDWELLTIME ACTIVE MIN", 23) == 0) |
| { |
| *len = scnprintf(extra, n, "GETDWELLTIME ACTIVE MIN %u\n", |
| (int)pCfg->nActiveMinChnTime); |
| return ret; |
| } |
| else if (strncmp(command, "GETDWELLTIME PASSIVE MAX", 24) == 0) |
| { |
| *len = scnprintf(extra, n, "GETDWELLTIME PASSIVE MAX %u\n", |
| (int)pCfg->nPassiveMaxChnTime); |
| return ret; |
| } |
| else if (strncmp(command, "GETDWELLTIME PASSIVE MIN", 24) == 0) |
| { |
| *len = scnprintf(extra, n, "GETDWELLTIME PASSIVE MIN %u\n", |
| (int)pCfg->nPassiveMinChnTime); |
| return ret; |
| } |
| else if (strncmp(command, "GETDWELLTIME", 12) == 0) |
| { |
| *len = scnprintf(extra, n, "GETDWELLTIME %u \n", |
| (int)pCfg->nActiveMaxChnTime); |
| return ret; |
| } |
| else |
| { |
| ret = -EINVAL; |
| } |
| |
| return ret; |
| } |
| |
| int hdd_drv_cmd_validate(tANI_U8 *command, int len) |
| { |
| if (command[len] != ' ') |
| return -EINVAL; |
| |
| return 0; |
| } |
| |
| static int hdd_set_dwell_time(hdd_adapter_t *pAdapter, tANI_U8 *command) |
| { |
| tHalHandle hHal; |
| hdd_config_t *pCfg; |
| tANI_U8 *value = command; |
| tSmeConfigParams smeConfig; |
| int val = 0, ret = 0, temp = 0; |
| |
| if (!pAdapter || !command || !(pCfg = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini) |
| || !(hHal = (WLAN_HDD_GET_HAL_CTX(pAdapter)))) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: argument passed for SETDWELLTIME is incorrect", __func__); |
| ret = -EINVAL; |
| return ret; |
| } |
| |
| vos_mem_zero(&smeConfig, sizeof(smeConfig)); |
| sme_GetConfigParam(hHal, &smeConfig); |
| |
| if (strncmp(command, "SETDWELLTIME ACTIVE MAX", 23) == 0 ) |
| { |
| if (hdd_drv_cmd_validate(command, 23)) |
| return -EINVAL; |
| |
| value = value + 24; |
| temp = kstrtou32(value, 10, &val); |
| if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_MIN || |
| val > CFG_ACTIVE_MAX_CHANNEL_TIME_MAX ) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: argument passed for SETDWELLTIME ACTIVE MAX is incorrect", __func__); |
| ret = -EFAULT; |
| return ret; |
| } |
| pCfg->nActiveMaxChnTime = val; |
| smeConfig.csrConfig.nActiveMaxChnTime = val; |
| sme_UpdateConfig(hHal, &smeConfig); |
| } |
| else if (strncmp(command, "SETDWELLTIME ACTIVE MIN", 23) == 0) |
| { |
| if (hdd_drv_cmd_validate(command, 23)) |
| return -EINVAL; |
| |
| value = value + 24; |
| temp = kstrtou32(value, 10, &val); |
| if (temp !=0 || val < CFG_ACTIVE_MIN_CHANNEL_TIME_MIN || |
| val > CFG_ACTIVE_MIN_CHANNEL_TIME_MAX ) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: argument passed for SETDWELLTIME ACTIVE MIN is incorrect", __func__); |
| ret = -EFAULT; |
| return ret; |
| } |
| pCfg->nActiveMinChnTime = val; |
| smeConfig.csrConfig.nActiveMinChnTime = val; |
| sme_UpdateConfig(hHal, &smeConfig); |
| } |
| else if (strncmp(command, "SETDWELLTIME PASSIVE MAX", 24) == 0) |
| { |
| if (hdd_drv_cmd_validate(command, 24)) |
| return -EINVAL; |
| |
| value = value + 25; |
| temp = kstrtou32(value, 10, &val); |
| if (temp != 0 || val < CFG_PASSIVE_MAX_CHANNEL_TIME_MIN || |
| val > CFG_PASSIVE_MAX_CHANNEL_TIME_MAX ) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: argument passed for SETDWELLTIME PASSIVE MAX is incorrect", __func__); |
| ret = -EFAULT; |
| return ret; |
| } |
| pCfg->nPassiveMaxChnTime = val; |
| smeConfig.csrConfig.nPassiveMaxChnTime = val; |
| sme_UpdateConfig(hHal, &smeConfig); |
| } |
| else if (strncmp(command, "SETDWELLTIME PASSIVE MIN", 24) == 0) |
| { |
| if (hdd_drv_cmd_validate(command, 24)) |
| return -EINVAL; |
| |
| value = value + 25; |
| temp = kstrtou32(value, 10, &val); |
| if (temp != 0 || val < CFG_PASSIVE_MIN_CHANNEL_TIME_MIN || |
| val > CFG_PASSIVE_MIN_CHANNEL_TIME_MAX ) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: argument passed for SETDWELLTIME PASSIVE MIN is incorrect", __func__); |
| ret = -EFAULT; |
| return ret; |
| } |
| pCfg->nPassiveMinChnTime = val; |
| smeConfig.csrConfig.nPassiveMinChnTime = val; |
| sme_UpdateConfig(hHal, &smeConfig); |
| } |
| else if (strncmp(command, "SETDWELLTIME", 12) == 0) |
| { |
| if (hdd_drv_cmd_validate(command, 12)) |
| return -EINVAL; |
| |
| value = value + 13; |
| temp = kstrtou32(value, 10, &val); |
| if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_MIN || |
| val > CFG_ACTIVE_MAX_CHANNEL_TIME_MAX ) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: argument passed for SETDWELLTIME is incorrect", __func__); |
| ret = -EFAULT; |
| return ret; |
| } |
| pCfg->nActiveMaxChnTime = val; |
| smeConfig.csrConfig.nActiveMaxChnTime = val; |
| sme_UpdateConfig(hHal, &smeConfig); |
| } |
| else |
| { |
| ret = -EINVAL; |
| } |
| |
| return ret; |
| } |
| /** |
| * hdd_indicate_mgmt_frame() - Wrapper to indicate management frame to |
| * user space |
| * @frame_ind: Management frame data to be informed. |
| * |
| * This function is used to indicate management frame to |
| * user space |
| * |
| * Return: None |
| * |
| */ |
| void hdd_indicate_mgmt_frame(tSirSmeMgmtFrameInd *frame_ind) |
| { |
| hdd_context_t *hdd_ctx; |
| hdd_adapter_t *adapter; |
| v_CONTEXT_t vos_context; |
| int i; |
| |
| /* Get the global VOSS context.*/ |
| vos_context = vos_get_global_context(VOS_MODULE_ID_SYS, NULL); |
| if (!vos_context) { |
| hddLog(LOGE, FL("Global VOS context is Null")); |
| return; |
| } |
| /* Get the HDD context.*/ |
| hdd_ctx = |
| (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, vos_context); |
| |
| if (0 != wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| |
| if (HDD_SESSION_ID_ANY == frame_ind->sessionId) { |
| for (i = 0; i < HDD_SESSION_MAX; i++) { |
| adapter = |
| hdd_get_adapter_by_sme_session_id(hdd_ctx, i); |
| if (adapter) |
| break; |
| } |
| } else { |
| adapter = hdd_get_adapter_by_sme_session_id(hdd_ctx, |
| frame_ind->sessionId); |
| } |
| |
| if ((NULL != adapter) && |
| (WLAN_HDD_ADAPTER_MAGIC == adapter->magic)) |
| __hdd_indicate_mgmt_frame(adapter, |
| frame_ind->frame_len, |
| frame_ind->frameBuf, |
| frame_ind->frameType, |
| frame_ind->rxChan, |
| frame_ind->rxRssi); |
| return; |
| } |
| |
| static void hdd_GetLink_statusCB(v_U8_t status, void *pContext) |
| { |
| struct statsContext *pLinkContext; |
| hdd_adapter_t *pAdapter; |
| |
| if (NULL == pContext) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Bad pContext [%pK]", |
| __func__, pContext); |
| return; |
| } |
| |
| pLinkContext = pContext; |
| pAdapter = pLinkContext->pAdapter; |
| |
| spin_lock(&hdd_context_lock); |
| |
| if ((NULL == pAdapter) || (LINK_STATUS_MAGIC != pLinkContext->magic)) { |
| /* the caller presumably timed out so there is nothing we can do */ |
| spin_unlock(&hdd_context_lock); |
| hddLog(VOS_TRACE_LEVEL_WARN, |
| "%s: Invalid context, pAdapter [%pK] magic [%08x]", |
| __func__, pAdapter, pLinkContext->magic); |
| return; |
| } |
| |
| /* context is valid so caller is still waiting */ |
| |
| /* paranoia: invalidate the magic */ |
| pLinkContext->magic = 0; |
| |
| /* copy over the status */ |
| pAdapter->linkStatus = status; |
| |
| /* notify the caller */ |
| complete(&pLinkContext->completion); |
| |
| /* serialization is complete */ |
| spin_unlock(&hdd_context_lock); |
| } |
| |
| /** |
| * hdd_get_fw_state_cb() - validates the context and notifies the caller |
| * @callback_context: caller context |
| * |
| * Return: none |
| */ |
| static void hdd_get_fw_state_cb(void *callback_context) |
| { |
| struct statsContext *context; |
| hdd_adapter_t *adapter; |
| |
| if (NULL == callback_context) { |
| hddLog(LOGE, FL("Bad pContext [%pK]"), callback_context); |
| return; |
| } |
| |
| context = callback_context; |
| adapter = context->pAdapter; |
| |
| spin_lock(&hdd_context_lock); |
| |
| if ((NULL == adapter) || (FW_STATUS_MAGIC != context->magic)) { |
| /* the caller presumably timed out so there is |
| * nothing we can do |
| */ |
| spin_unlock(&hdd_context_lock); |
| hddLog(LOGE, FL("Invalid context, Adapter [%pK] magic [%08x]"), |
| adapter, context->magic); |
| return; |
| } |
| |
| /* context is valid so caller is still waiting */ |
| |
| /* paranoia: invalidate the magic */ |
| context->magic = 0; |
| |
| /* notify the caller */ |
| complete(&context->completion); |
| |
| /* serialization is complete */ |
| spin_unlock(&hdd_context_lock); |
| } |
| |
| /** |
| * wlan_hdd_get_fw_state() - get firmware state |
| * @adapter: pointer to the adapter |
| * |
| * This function sends a request to firmware and waits |
| * on a timer to invoke the callback. if the callback is invoked then |
| * true will be returned or otherwise fail status will be returned. |
| * |
| * Return: true, firmware is active. |
| * false, firmware is in bad state. |
| */ |
| bool wlan_hdd_get_fw_state(hdd_adapter_t *adapter) |
| { |
| hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| struct statsContext context; |
| eHalStatus hstatus = eHAL_STATUS_SUCCESS; |
| unsigned long rc; |
| bool fw_active = true; |
| |
| if (wlan_hdd_validate_context(hdd_ctx) != 0) |
| return false; |
| |
| init_completion(&context.completion); |
| context.pAdapter = adapter; |
| context.magic = FW_STATUS_MAGIC; |
| hstatus = sme_get_fw_state(WLAN_HDD_GET_HAL_CTX(adapter), |
| hdd_get_fw_state_cb, |
| &context); |
| |
| if (eHAL_STATUS_SUCCESS != hstatus) { |
| hddLog(LOGE, FL("Unable to retrieve firmware status")); |
| fw_active = false; |
| } else { |
| /* request is sent -- wait for the response */ |
| rc = wait_for_completion_timeout(&context.completion, |
| msecs_to_jiffies(WLAN_WAIT_TIME_LINK_STATUS)); |
| if (!rc) { |
| hddLog(LOGE, |
| FL("SME timed out while retrieving firmware status")); |
| fw_active = false; |
| } |
| } |
| |
| spin_lock(&hdd_context_lock); |
| context.magic = 0; |
| spin_unlock(&hdd_context_lock); |
| |
| return fw_active; |
| } |
| |
| /** |
| * wlan_hdd_get_link_status() - get link status |
| * @pAdapter: pointer to the adapter |
| * |
| * This function sends a request to query the link status and waits |
| * on a timer to invoke the callback. if the callback is invoked then |
| * latest link status shall be returned or otherwise cached value |
| * will be returned. |
| * |
| * Return: On success, link status shall be returned. |
| * On error or not associated, link status 0 will be returned. |
| */ |
| static int wlan_hdd_get_link_status(hdd_adapter_t *pAdapter) |
| { |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| struct statsContext context; |
| eHalStatus hstatus; |
| unsigned long rc; |
| |
| if (pHddCtx->isLogpInProgress) { |
| hddLog(LOGW, FL("LOGP in Progress. Ignore!!!")); |
| return 0; |
| } |
| |
| if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) { |
| /* If not associated, then expected link status return value is 0 */ |
| hddLog(LOG1, FL("Not associated!")); |
| return 0; |
| } |
| |
| init_completion(&context.completion); |
| context.pAdapter = pAdapter; |
| context.magic = LINK_STATUS_MAGIC; |
| hstatus = sme_getLinkStatus(WLAN_HDD_GET_HAL_CTX(pAdapter), |
| hdd_GetLink_statusCB, |
| &context, |
| pAdapter->sessionId); |
| if (eHAL_STATUS_SUCCESS != hstatus) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Unable to retrieve link status", __func__); |
| /* return a cached value */ |
| } else { |
| /* request is sent -- wait for the response */ |
| rc = wait_for_completion_timeout(&context.completion, |
| msecs_to_jiffies(WLAN_WAIT_TIME_LINK_STATUS)); |
| if (!rc) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("SME timed out while retrieving link status")); |
| } |
| } |
| |
| spin_lock(&hdd_context_lock); |
| context.magic = 0; |
| spin_unlock(&hdd_context_lock); |
| |
| /* either callback updated pAdapter stats or it has cached data */ |
| return pAdapter->linkStatus; |
| } |
| |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| static void hdd_wma_send_fastreassoc_cmd(int sessionId, tSirMacAddr bssid, |
| int channel) |
| { |
| t_wma_roam_invoke_cmd *fastreassoc; |
| vos_msg_t msg = {0}; |
| |
| fastreassoc = vos_mem_malloc(sizeof(*fastreassoc)); |
| if (NULL == fastreassoc) { |
| hddLog(LOGE, FL("vos_mem_alloc failed for fastreassoc")); |
| return; |
| } |
| fastreassoc->vdev_id = sessionId; |
| fastreassoc->channel = channel; |
| fastreassoc->bssid[0] = bssid[0]; |
| fastreassoc->bssid[1] = bssid[1]; |
| fastreassoc->bssid[2] = bssid[2]; |
| fastreassoc->bssid[3] = bssid[3]; |
| fastreassoc->bssid[4] = bssid[4]; |
| fastreassoc->bssid[5] = bssid[5]; |
| |
| msg.type = SIR_HAL_ROAM_INVOKE; |
| msg.reserved = 0; |
| msg.bodyptr = fastreassoc; |
| if (VOS_STATUS_SUCCESS != vos_mq_post_message(VOS_MODULE_ID_WDA, |
| &msg)) { |
| vos_mem_free(fastreassoc); |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| FL("Not able to post ROAM_INVOKE_CMD message to WMA")); |
| } |
| } |
| #endif |
| |
| /** |
| * hdd_set_miracast_mode() - function used to set the miracast mode value |
| * @pAdapter: pointer to the adapter of the interface. |
| * @command: pointer to the command buffer "MIRACAST <value>". |
| * Return: 0 on success -EINVAL on failure. |
| */ |
| int hdd_set_miracast_mode(hdd_adapter_t *pAdapter, tANI_U8 *command) |
| { |
| tHalHandle hHal; |
| tANI_U8 filterType = 0; |
| hdd_context_t *pHddCtx = NULL; |
| tANI_U8 *value; |
| int ret; |
| eHalStatus ret_status; |
| |
| pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| if (0 != wlan_hdd_validate_context(pHddCtx)) |
| return -EINVAL; |
| |
| hHal = pHddCtx->hHal; |
| |
| value = command + 9; |
| |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &filterType); |
| if (ret < 0) { |
| /* If the input value is greater than max value of datatype, |
| * then also kstrtou8 fails |
| */ |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: kstrtou8 failed range", __func__); |
| return -EINVAL; |
| } |
| |
| /* Filtertype value should be either 0-Disabled, 1-Source, 2-sink */ |
| if ((filterType < WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL ) || |
| (filterType > WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Accepted Values are 0 to 2." |
| "0-Disabled, 1-Source, 2-Sink", __func__); |
| return -EINVAL; |
| } |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: miracast mode %hu", __func__, filterType); |
| pHddCtx->miracast_value = filterType; |
| |
| ret_status = sme_set_miracast(hHal, filterType); |
| if (eHAL_STATUS_SUCCESS != ret_status) { |
| hddLog(LOGE, "Failed to set miracast"); |
| return -EBUSY; |
| } |
| |
| if (hdd_is_mcc_in_24G(pHddCtx)) { |
| return hdd_set_mas(pAdapter, filterType); |
| } |
| |
| return 0; |
| } |
| |
| /* Function header is left blank intentionally */ |
| static int hdd_parse_set_ibss_oui_data_command(uint8_t *command, uint8_t *ie, |
| int32_t *oui_length, int32_t limit) |
| { |
| uint8_t len; |
| uint8_t data; |
| |
| while ((SPACE_ASCII_VALUE == *command) && ('\0' != *command)) { |
| command++; |
| limit--; |
| } |
| |
| len = 2; |
| |
| while ((SPACE_ASCII_VALUE != *command) && ('\0' != *command) && |
| (limit > 1)) { |
| sscanf(command, "%02x", (unsigned int *)&data); |
| ie[len++] = data; |
| command += 2; |
| limit -= 2; |
| } |
| |
| *oui_length = len - 2; |
| |
| while ((SPACE_ASCII_VALUE == *command) && ('\0' != *command)) { |
| command++; |
| limit--; |
| } |
| |
| while ((SPACE_ASCII_VALUE != *command) && ('\0' != *command) && |
| (limit > 1)) { |
| sscanf(command, "%02x", (unsigned int *)&data); |
| ie[len++] = data; |
| command += 2; |
| limit -= 2; |
| } |
| |
| ie[0] = IE_EID_VENDOR; |
| ie[1] = len - 2; |
| |
| return len; |
| } |
| |
| |
| /** |
| * hdd_set_mas() - Function to set MAS value to UMAC |
| * @adapter: Pointer to HDD adapter |
| * @mas_value: 0-Disable, 1-Enable MAS |
| * |
| * This function passes down the value of MAS to UMAC |
| * |
| * Return: Configuration message posting status, SUCCESS or Fail |
| * |
| */ |
| int32_t hdd_set_mas(hdd_adapter_t *adapter, tANI_U8 mas_value) |
| { |
| hdd_context_t *hdd_ctx = NULL; |
| eHalStatus ret_status; |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (!hdd_ctx) |
| return -EFAULT; |
| |
| if (mas_value) { |
| /* Miracast is ON. Disable MAS and configure P2P quota */ |
| if (hdd_ctx->cfg_ini->enableMCCAdaptiveScheduler) { |
| if (cfgSetInt(hdd_ctx->hHal, |
| WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED, 0) |
| != eSIR_SUCCESS) { |
| hddLog(LOGE, |
| "Could not pass on WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED to CCM"); |
| } |
| |
| ret_status = sme_set_mas(false); |
| if (eHAL_STATUS_SUCCESS != ret_status) { |
| hddLog(LOGE, "Failed to disable MAS"); |
| return -EBUSY; |
| } |
| } |
| |
| /* Config p2p quota */ |
| if (adapter->device_mode == WLAN_HDD_INFRA_STATION) |
| hdd_wlan_set_mcc_p2p_quota(adapter, |
| 100 - HDD_DEFAULT_MCC_P2P_QUOTA); |
| else if (adapter->device_mode == WLAN_HDD_P2P_GO) |
| hdd_wlan_go_set_mcc_p2p_quota(adapter, |
| HDD_DEFAULT_MCC_P2P_QUOTA); |
| else |
| hdd_wlan_set_mcc_p2p_quota(adapter, |
| HDD_DEFAULT_MCC_P2P_QUOTA); |
| } else { |
| /* Reset p2p quota */ |
| if (adapter->device_mode == WLAN_HDD_P2P_GO) |
| hdd_wlan_go_set_mcc_p2p_quota(adapter, |
| HDD_RESET_MCC_P2P_QUOTA); |
| else |
| hdd_wlan_set_mcc_p2p_quota(adapter, |
| HDD_RESET_MCC_P2P_QUOTA); |
| |
| /* Miracast is OFF. Enable MAS and reset P2P quota */ |
| if (hdd_ctx->cfg_ini->enableMCCAdaptiveScheduler) { |
| if (cfgSetInt(hdd_ctx->hHal, |
| WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED, 1) |
| != eSIR_SUCCESS) { |
| hddLog(LOGE, "Could not pass on WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED to CCM"); |
| } |
| |
| /* Enable MAS */ |
| ret_status = sme_set_mas(true); |
| if (eHAL_STATUS_SUCCESS != ret_status) { |
| hddLog(LOGE, "Unable to enable MAS"); |
| return -EBUSY; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_is_mcc_in_24G() - Function to check for MCC in 2.4GHz |
| * @hdd_ctx: Pointer to HDD context |
| * |
| * This function is used to check for MCC operation in 2.4GHz band. |
| * STA, P2P and SAP adapters are only considered. |
| * |
| * Return: Non zero value if MCC is detected in 2.4GHz band |
| * |
| */ |
| uint8_t hdd_is_mcc_in_24G(hdd_context_t *hdd_ctx) |
| { |
| VOS_STATUS status; |
| hdd_adapter_t *hdd_adapter = NULL; |
| hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL; |
| uint8_t ret = 0; |
| hdd_station_ctx_t *sta_ctx; |
| hdd_ap_ctx_t *ap_ctx; |
| uint8_t ch1 = 0, ch2 = 0; |
| uint8_t channel = 0; |
| hdd_hostapd_state_t *hostapd_state; |
| |
| status = hdd_get_front_adapter(hdd_ctx, &adapter_node); |
| |
| /* loop through all adapters and check MCC for STA,P2P,SAP adapters */ |
| while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) { |
| hdd_adapter = adapter_node->pAdapter; |
| |
| if (!((hdd_adapter->device_mode >= WLAN_HDD_INFRA_STATION) |
| || (hdd_adapter->device_mode |
| <= WLAN_HDD_P2P_GO))) { |
| /* skip for other adapters */ |
| status = hdd_get_next_adapter(hdd_ctx, |
| adapter_node, &next); |
| adapter_node = next; |
| continue; |
| } else { |
| if (WLAN_HDD_INFRA_STATION == |
| hdd_adapter->device_mode || |
| WLAN_HDD_P2P_CLIENT == |
| hdd_adapter->device_mode) { |
| sta_ctx = |
| WLAN_HDD_GET_STATION_CTX_PTR( |
| hdd_adapter); |
| if (eConnectionState_Associated == |
| sta_ctx->conn_info.connState) |
| channel = |
| sta_ctx->conn_info. |
| operationChannel; |
| } else if (WLAN_HDD_P2P_GO == |
| hdd_adapter->device_mode || |
| WLAN_HDD_SOFTAP == |
| hdd_adapter->device_mode) { |
| ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(hdd_adapter); |
| hostapd_state = |
| WLAN_HDD_GET_HOSTAP_STATE_PTR( |
| hdd_adapter); |
| if (hostapd_state->bssState == BSS_START && |
| hostapd_state->vosStatus == |
| VOS_STATUS_SUCCESS) |
| channel = ap_ctx->operatingChannel; |
| } |
| |
| if ((ch1 == 0) || |
| ((ch2 != 0) && (ch2 != channel))) { |
| ch1 = channel; |
| } else if ((ch2 == 0) || |
| ((ch1 != 0) && (ch1 != channel))) { |
| ch2 = channel; |
| } |
| |
| if ((ch1 != 0 && ch2 != 0) && (ch1 != ch2) && |
| ((ch1 <= SIR_11B_CHANNEL_END) && |
| (ch2 <= SIR_11B_CHANNEL_END))) { |
| hddLog(LOGE, |
| "MCC in 2.4Ghz on channels %d and %d", |
| ch1, ch2); |
| return 1; |
| } |
| |
| status = hdd_get_next_adapter(hdd_ctx, |
| adapter_node, &next); |
| adapter_node = next; |
| } |
| } |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_get_link_speed() - get link speed |
| * @pAdapter: pointer to the adapter |
| * @link_speed: pointer to link speed |
| * |
| * This function fetches per bssid link speed. |
| * |
| * Return: if associated, link speed shall be returned. |
| * if not associated, link speed of 0 is returned. |
| * On error, error number will be returned. |
| */ |
| int wlan_hdd_get_link_speed(hdd_adapter_t *sta_adapter, uint32_t *link_speed) |
| { |
| hdd_context_t *hddctx = WLAN_HDD_GET_CTX(sta_adapter); |
| hdd_station_ctx_t *hdd_stactx = WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter); |
| int ret; |
| |
| ret = wlan_hdd_validate_context(hddctx); |
| if (0 != ret) |
| return ret; |
| |
| if (eConnectionState_Associated != hdd_stactx->conn_info.connState) { |
| /* we are not connected so we don't have a classAstats */ |
| *link_speed = 0; |
| } else { |
| VOS_STATUS status; |
| tSirMacAddr bssid; |
| |
| vos_mem_copy(bssid, hdd_stactx->conn_info.bssId, VOS_MAC_ADDR_SIZE); |
| |
| status = wlan_hdd_get_linkspeed_for_peermac(sta_adapter, bssid); |
| if (!VOS_IS_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, FL("Unable to retrieve SME linkspeed")); |
| return -EINVAL; |
| } |
| *link_speed = sta_adapter->ls_stats.estLinkSpeed; |
| /* linkspeed in units of 500 kbps */ |
| *link_speed = (*link_speed) / 500; |
| } |
| return 0; |
| } |
| |
| #if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD) |
| /** |
| * hdd_parse_ese_beacon_req() - Parse ese beacon request |
| * @pValue: Pointer to input data (its a NUL terminated string) |
| * @pEseBcnReq: pEseBcnReq output pointer to store parsed ie information |
| * |
| * This function parses the ese beacon request passed in the format |
| * CCXBEACONREQ<space><Number of fields><space><Measurement token> |
| * <space>Channel 1<space>Scan Mode <space>Meas Duration<space>Channel N |
| * <space>Scan Mode N<space>Meas Duration N |
| * if the Number of bcn req fields (N) does not match with the actual number of |
| * fields passed then take N. |
| * <Meas Token><Channel><Scan Mode> and <Meas Duration> are treated as one pair |
| * For example, CCXBEACONREQ 2 1 1 1 30 2 44 0 40. |
| * This function does not take care of removing duplicate channels from the list |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| static VOS_STATUS hdd_parse_ese_beacon_req(tANI_U8 *pValue, |
| tCsrEseBeaconReq *pEseBcnReq) |
| { |
| tANI_U8 *inPtr = pValue; |
| uint8_t input = 0; |
| uint32_t tempInt = 0; |
| int j = 0, i = 0, v = 0; |
| char buf[32]; |
| |
| inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE); |
| /* no argument after the command */ |
| if (NULL == inPtr) { |
| return -EINVAL; |
| } else if (SPACE_ASCII_VALUE != *inPtr) { |
| /* no space after the command */ |
| return -EINVAL; |
| } |
| |
| /* removing empty spaces */ |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++; |
| |
| /* no argument followed by spaces */ |
| if ('\0' == *inPtr) return -EINVAL; |
| |
| /* Getting the first argument ie Number of IE fields */ |
| v = sscanf(inPtr, "%31s ", buf); |
| if (1 != v) return -EINVAL; |
| |
| v = kstrtou8(buf, 10, &input); |
| if (v < 0) return -EINVAL; |
| |
| input = VOS_MIN(input, SIR_ESE_MAX_MEAS_IE_REQS); |
| pEseBcnReq->numBcnReqIe = input; |
| |
| hddLog(LOG1, "Number of Bcn Req Ie fields: %d", pEseBcnReq->numBcnReqIe); |
| |
| for (j = 0; j < (pEseBcnReq->numBcnReqIe); j++) { |
| for (i = 0; i < 4; i++) { |
| /* inPtr pointing to the beginning of first space after number of |
| ie fields */ |
| inPtr = strpbrk( inPtr, " " ); |
| /* no ie data after the number of ie fields argument */ |
| if (NULL == inPtr) return -EINVAL; |
| |
| /* removing empty space */ |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++; |
| |
| /* no ie data after the number of ie fields argument and spaces */ |
| if ('\0' == *inPtr) return -EINVAL; |
| |
| v = sscanf(inPtr, "%31s ", buf); |
| if (1 != v) return -EINVAL; |
| |
| v = kstrtou32(buf, 10, &tempInt); |
| if (v < 0) return -EINVAL; |
| |
| switch (i) { |
| case 0: /* Measurement token */ |
| if (!tempInt) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Invalid Measurement Token: %d", tempInt); |
| return -EINVAL; |
| } |
| pEseBcnReq->bcnReq[j].measurementToken = tempInt; |
| break; |
| |
| case 1: /* Channel number */ |
| if ((!tempInt) || |
| (tempInt > WNI_CFG_CURRENT_CHANNEL_STAMAX)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Invalid Channel Number: %d", tempInt); |
| return -EINVAL; |
| } |
| pEseBcnReq->bcnReq[j].channel = tempInt; |
| break; |
| |
| case 2: /* Scan mode */ |
| if ((tempInt < eSIR_PASSIVE_SCAN) || |
| (tempInt > eSIR_BEACON_TABLE)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Invalid Scan Mode: %d Expected{0|1|2}", tempInt); |
| return -EINVAL; |
| } |
| pEseBcnReq->bcnReq[j].scanMode= tempInt; |
| break; |
| |
| case 3: /* Measurement duration */ |
| if (((!tempInt) && |
| (pEseBcnReq->bcnReq[j].scanMode != eSIR_BEACON_TABLE)) || |
| ((pEseBcnReq->bcnReq[j].scanMode == eSIR_BEACON_TABLE))) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Invalid Measurement Duration: %d", tempInt); |
| return -EINVAL; |
| } |
| pEseBcnReq->bcnReq[j].measurementDuration = tempInt; |
| break; |
| } |
| } |
| } |
| |
| for (j = 0; j < pEseBcnReq->numBcnReqIe; j++) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "Index(%d) Measurement Token(%u) Channel(%u) Scan Mode(%u) Measurement Duration(%u)", |
| j, |
| pEseBcnReq->bcnReq[j].measurementToken, |
| pEseBcnReq->bcnReq[j].channel, |
| pEseBcnReq->bcnReq[j].scanMode, |
| pEseBcnReq->bcnReq[j].measurementDuration); |
| } |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| static void hdd_GetTsmStatsCB( tAniTrafStrmMetrics tsmMetrics, |
| const tANI_U32 staId, |
| void *pContext ) |
| { |
| struct statsContext *pStatsContext = NULL; |
| hdd_adapter_t *pAdapter = NULL; |
| |
| if (NULL == pContext) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Bad param, pContext [%pK]", |
| __func__, pContext); |
| return; |
| } |
| |
| /* there is a race condition that exists between this callback |
| function and the caller since the caller could time out either |
| before or while this code is executing. we use a spinlock to |
| serialize these actions */ |
| spin_lock(&hdd_context_lock); |
| |
| pStatsContext = pContext; |
| pAdapter = pStatsContext->pAdapter; |
| if ((NULL == pAdapter) || (STATS_CONTEXT_MAGIC != pStatsContext->magic)) { |
| /* the caller presumably timed out so there is nothing we can do */ |
| spin_unlock(&hdd_context_lock); |
| hddLog(VOS_TRACE_LEVEL_WARN, |
| "%s: Invalid context, pAdapter [%pK] magic [%08x]", |
| __func__, pAdapter, pStatsContext->magic); |
| return; |
| } |
| |
| /* context is valid so caller is still waiting */ |
| |
| /* paranoia: invalidate the magic */ |
| pStatsContext->magic = 0; |
| |
| /* copy over the tsm stats */ |
| pAdapter->tsmStats.UplinkPktQueueDly = tsmMetrics.UplinkPktQueueDly; |
| vos_mem_copy(pAdapter->tsmStats.UplinkPktQueueDlyHist, |
| tsmMetrics.UplinkPktQueueDlyHist, |
| sizeof(pAdapter->tsmStats.UplinkPktQueueDlyHist)/ |
| sizeof(pAdapter->tsmStats.UplinkPktQueueDlyHist[0])); |
| pAdapter->tsmStats.UplinkPktTxDly = tsmMetrics.UplinkPktTxDly; |
| pAdapter->tsmStats.UplinkPktLoss = tsmMetrics.UplinkPktLoss; |
| pAdapter->tsmStats.UplinkPktCount = tsmMetrics.UplinkPktCount; |
| pAdapter->tsmStats.RoamingCount = tsmMetrics.RoamingCount; |
| pAdapter->tsmStats.RoamingDly = tsmMetrics.RoamingDly; |
| |
| /* notify the caller */ |
| complete(&pStatsContext->completion); |
| |
| /* serialization is complete */ |
| spin_unlock(&hdd_context_lock); |
| } |
| |
| static VOS_STATUS hdd_get_tsm_stats(hdd_adapter_t *pAdapter, |
| const tANI_U8 tid, |
| tAniTrafStrmMetrics* pTsmMetrics) |
| { |
| hdd_station_ctx_t *pHddStaCtx = NULL; |
| eHalStatus hstatus; |
| VOS_STATUS vstatus = VOS_STATUS_SUCCESS; |
| unsigned long rc; |
| struct statsContext context; |
| hdd_context_t *pHddCtx = NULL; |
| |
| if (NULL == pAdapter) { |
| hddLog(LOGE, FL("pAdapter is NULL")); |
| return VOS_STATUS_E_FAULT; |
| } |
| |
| pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| |
| /* we are connected prepare our callback context */ |
| init_completion(&context.completion); |
| context.pAdapter = pAdapter; |
| context.magic = STATS_CONTEXT_MAGIC; |
| |
| /* query tsm stats */ |
| hstatus = sme_GetTsmStats(pHddCtx->hHal, hdd_GetTsmStatsCB, |
| pHddStaCtx->conn_info.staId[ 0 ], |
| pHddStaCtx->conn_info.bssId, |
| &context, pHddCtx->pvosContext, tid); |
| if (eHAL_STATUS_SUCCESS != hstatus) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Unable to retrieve statistics", |
| __func__); |
| vstatus = VOS_STATUS_E_FAULT; |
| } else { |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout(&context.completion, |
| msecs_to_jiffies(WLAN_WAIT_TIME_STATS)); |
| if (!rc) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: SME timed out while retrieving statistics", |
| __func__); |
| vstatus = VOS_STATUS_E_TIMEOUT; |
| } |
| } |
| |
| /* either we never sent a request, we sent a request and received a |
| response or we sent a request and timed out. if we never sent a |
| request or if we sent a request and got a response, we want to |
| clear the magic out of paranoia. if we timed out there is a |
| race condition such that the callback function could be |
| executing at the same time we are. of primary concern is if the |
| callback function had already verified the "magic" but had not |
| yet set the completion variable when a timeout occurred. we |
| serialize these activities by invalidating the magic while |
| holding a shared spinlock which will cause us to block if the |
| callback is currently executing */ |
| spin_lock(&hdd_context_lock); |
| context.magic = 0; |
| spin_unlock(&hdd_context_lock); |
| |
| if (VOS_STATUS_SUCCESS == vstatus) { |
| pTsmMetrics->UplinkPktQueueDly = pAdapter->tsmStats.UplinkPktQueueDly; |
| vos_mem_copy(pTsmMetrics->UplinkPktQueueDlyHist, |
| pAdapter->tsmStats.UplinkPktQueueDlyHist, |
| sizeof(pAdapter->tsmStats.UplinkPktQueueDlyHist)/ |
| sizeof(pAdapter->tsmStats.UplinkPktQueueDlyHist[0])); |
| pTsmMetrics->UplinkPktTxDly = pAdapter->tsmStats.UplinkPktTxDly; |
| pTsmMetrics->UplinkPktLoss = pAdapter->tsmStats.UplinkPktLoss; |
| pTsmMetrics->UplinkPktCount = pAdapter->tsmStats.UplinkPktCount; |
| pTsmMetrics->RoamingCount = pAdapter->tsmStats.RoamingCount; |
| pTsmMetrics->RoamingDly = pAdapter->tsmStats.RoamingDly; |
| } |
| return vstatus; |
| } |
| |
| /** |
| * hdd_parse_get_cckm_ie() - HDD Parse and fetch the CCKM IE |
| * @pValue: Pointer to input data |
| * @pCckmIe: Pointer to output cckm Ie |
| * @pCckmIeLen: Pointer to output cckm ie length |
| * |
| * This function parses the SETCCKM IE command |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| static VOS_STATUS |
| hdd_parse_get_cckm_ie(tANI_U8 *pValue, tANI_U8 **pCckmIe, tANI_U8 *pCckmIeLen) |
| { |
| tANI_U8 *inPtr = pValue; |
| tANI_U8 *dataEnd; |
| int j = 0; |
| int i = 0; |
| tANI_U8 tempByte = 0; |
| inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE); |
| /* no argument after the command */ |
| if (NULL == inPtr) { |
| return -EINVAL; |
| } else if (SPACE_ASCII_VALUE != *inPtr) { |
| /* no space after the command */ |
| return -EINVAL; |
| } |
| /* removing empty spaces */ |
| while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++; |
| /* no argument followed by spaces */ |
| if ('\0' == *inPtr) { |
| return -EINVAL; |
| } |
| /* find the length of data */ |
| dataEnd = inPtr; |
| while('\0' != *dataEnd) { |
| dataEnd++; |
| ++(*pCckmIeLen); |
| } |
| if (*pCckmIeLen <= 0) return -EINVAL; |
| /* |
| * Allocate the number of bytes based on the number of input characters |
| * whether it is even or odd. |
| * if the number of input characters are even, then we need N/2 byte. |
| * if the number of input characters are odd, then we need do (N+1)/2 to |
| * compensate rounding off. |
| * For example, if N = 18, then (18 + 1)/2 = 9 bytes are enough. |
| * If N = 19, then we need 10 bytes, hence (19 + 1)/2 = 10 bytes |
| */ |
| *pCckmIe = vos_mem_malloc((*pCckmIeLen + 1)/2); |
| if (NULL == *pCckmIe) { |
| hddLog(LOGP, FL("vos_mem_alloc failed")); |
| return -ENOMEM; |
| } |
| vos_mem_zero(*pCckmIe, (*pCckmIeLen + 1)/2); |
| /* |
| * the buffer received from the upper layer is character buffer, |
| * we need to prepare the buffer taking 2 characters in to a U8 hex |
| * decimal number for example 7f0000f0...form a buffer to contain |
| * 7f in 0th location, 00 in 1st and f0 in 3rd location |
| */ |
| for (i = 0, j = 0; j < *pCckmIeLen; j += 2) { |
| tempByte = (hdd_parse_hex(inPtr[j]) << 4) |
| | (hdd_parse_hex(inPtr[j + 1])); |
| (*pCckmIe)[i++] = tempByte; |
| } |
| *pCckmIeLen = i; |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| #endif /*FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */ |
| |
| /** |
| * drv_cmd_set_fcc_channel() - handle fcc constraint request |
| * @hdd_ctx: HDD context |
| * @cmd: command ptr |
| * @cmd_len: command len |
| * |
| * Return: status |
| */ |
| static int drv_cmd_set_fcc_channel(hdd_adapter_t *adapter, uint8_t *cmd, |
| uint8_t cmd_len) |
| { |
| uint8_t *value; |
| uint8_t fcc_constraint; |
| eHalStatus status; |
| int ret = 0; |
| hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| |
| value = cmd + cmd_len + 1; |
| |
| ret = kstrtou8(value, 10, &fcc_constraint); |
| if ((ret < 0) || (fcc_constraint > 1)) { |
| /* |
| * If the input value is greater than max value of datatype, |
| * then also it is a failure |
| */ |
| hddLog(LOGE, FL("value out of range")); |
| return -EINVAL; |
| } |
| |
| status = sme_handle_set_fcc_channel(hdd_ctx->hHal, !fcc_constraint, |
| adapter->scan_info.mScanPending); |
| if (status != eHAL_STATUS_SUCCESS) |
| ret = -EPERM; |
| |
| return ret; |
| } |
| |
| /** |
| * hdd_set_rx_filter() - set RX filter |
| * @adapter: Pointer to adapter |
| * @action: Filter action |
| * @pattern: Address pattern |
| * |
| * Address pattern is most significant byte of address for example |
| * 0x01 for IPV4 multicast address |
| * 0x33 for IPV6 multicast address |
| * 0xFF for broadcast address |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int hdd_set_rx_filter(hdd_adapter_t *adapter, bool action, |
| uint8_t pattern) |
| { |
| int ret; |
| uint8_t i, j; |
| tHalHandle handle; |
| tSirRcvFltMcAddrList *filter; |
| hdd_context_t* hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| handle = hdd_ctx->hHal; |
| |
| if (NULL == handle) { |
| hddLog(LOGE, FL("HAL Handle is NULL")); |
| return -EINVAL; |
| } |
| |
| if (!hdd_ctx->cfg_ini->fEnableMCAddrList) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("mc addr ini is disabled")); |
| return -EINVAL; |
| } |
| |
| /* |
| * If action is false it means start dropping packets |
| * Set addr_filter_pattern which will be used when sending |
| * MC/BC address list to target |
| */ |
| if (!action) |
| adapter->addr_filter_pattern = pattern; |
| else |
| adapter->addr_filter_pattern = 0; |
| |
| if (((adapter->device_mode == WLAN_HDD_INFRA_STATION) || |
| (adapter->device_mode == WLAN_HDD_P2P_CLIENT)) && |
| adapter->mc_addr_list.mc_cnt && |
| hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { |
| |
| |
| filter = vos_mem_malloc(sizeof(*filter)); |
| if (NULL == filter) { |
| hddLog(LOGE, FL("Could not allocate Memory")); |
| return -ENOMEM; |
| } |
| vos_mem_zero(filter, sizeof(*filter)); |
| filter->action = action; |
| for (i = 0, j = 0; i < adapter->mc_addr_list.mc_cnt; i++) { |
| if (!memcmp(&adapter->mc_addr_list.addr[i * ETH_ALEN], |
| &pattern, 1)) { |
| memcpy(filter->multicastAddr[j], |
| &adapter->mc_addr_list.addr[i * ETH_ALEN], |
| ETH_ALEN); |
| hddLog(LOG1, "%s RX filter : addr =" |
| MAC_ADDRESS_STR, |
| action ? "setting" : "clearing", |
| MAC_ADDR_ARRAY(filter->multicastAddr[j])); |
| j++; |
| } |
| if (j == SIR_MAX_NUM_MULTICAST_ADDRESS) |
| break; |
| } |
| filter->ulMulticastAddrCnt = j; |
| /* Set rx filter */ |
| sme_8023MulticastList(handle, adapter->sessionId, filter); |
| vos_mem_free(filter); |
| } else { |
| hddLog(LOGW, FL("mode %d mc_cnt %d"), |
| adapter->device_mode, adapter->mc_addr_list.mc_cnt); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_driver_rxfilter_comand_handler() - RXFILTER driver command handler |
| * @command: Pointer to input string driver command |
| * @adapter: Pointer to adapter |
| * @action: Action to enable/disable filtering |
| * |
| * If action == false |
| * Start filtering out data packets based on type |
| * RXFILTER-REMOVE 0 -> Start filtering out unicast data packets |
| * RXFILTER-REMOVE 1 -> Start filtering out broadcast data packets |
| * RXFILTER-REMOVE 2 -> Start filtering out IPV4 mcast data packets |
| * RXFILTER-REMOVE 3 -> Start filtering out IPV6 mcast data packets |
| * |
| * if action == true |
| * Stop filtering data packets based on type |
| * RXFILTER-ADD 0 -> Stop filtering unicast data packets |
| * RXFILTER-ADD 1 -> Stop filtering broadcast data packets |
| * RXFILTER-ADD 2 -> Stop filtering IPV4 mcast data packets |
| * RXFILTER-ADD 3 -> Stop filtering IPV6 mcast data packets |
| * |
| * Current implementation only supports IPV4 address filtering by |
| * selectively allowing IPV4 multicast data packest based on |
| * address list received in .ndo_set_rx_mode |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int hdd_driver_rxfilter_comand_handler(uint8_t *command, |
| hdd_adapter_t *adapter, |
| bool action) |
| { |
| int ret = 0; |
| uint8_t *value; |
| uint8_t type; |
| |
| value = command; |
| /* Skip space after RXFILTER-REMOVE OR RXFILTER-ADD based on action */ |
| if (!action) |
| value = command + 16; |
| else |
| value = command + 13; |
| ret = kstrtou8(value, 10, &type); |
| if (ret < 0) { |
| hddLog(LOGE, |
| FL("kstrtou8 failed invalid input value %d"), type); |
| return -EINVAL; |
| } |
| |
| switch (type) { |
| case 2: |
| /* Set rx filter for IPV4 multicast data packets */ |
| ret = hdd_set_rx_filter(adapter, action, 0x01); |
| break; |
| default: |
| hddLog(LOG1, FL("Unsupported RXFILTER type %d"), type); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * hdd_parse_setantennamode_command() - HDD Parse SETANTENNAMODE |
| * command |
| * @value: Pointer to SETANTENNAMODE command |
| * @mode: Pointer to antenna mode |
| * |
| * This function parses the SETANTENNAMODE command passed in the format |
| * SETANTENNAMODE<space>mode |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| static int hdd_parse_setantennamode_command(const uint8_t *value, |
| int *mode) |
| { |
| const uint8_t *in_ptr = value; |
| int tmp, v; |
| char arg1[32]; |
| *mode = 0; |
| |
| in_ptr = strnchr(value, strlen(value), SPACE_ASCII_VALUE); |
| |
| /* no argument after the command */ |
| if (NULL == in_ptr) { |
| hddLog(LOGE, FL("No argument after the command")); |
| return -EINVAL; |
| } |
| |
| /* no space after the command */ |
| if (SPACE_ASCII_VALUE != *in_ptr) { |
| hddLog(LOGE, FL("No space after the command")); |
| return -EINVAL; |
| } |
| |
| /* remove empty spaces */ |
| while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr)) |
| in_ptr++; |
| |
| /* no argument followed by spaces */ |
| if ('\0' == *in_ptr) { |
| hddLog(LOGE, FL("No argument followed by spaces")); |
| return -EINVAL; |
| } |
| |
| /* get the argument i.e. antenna mode */ |
| v = sscanf(in_ptr, "%31s ", arg1); |
| if (1 != v) { |
| hddLog(LOGE, FL("argument retrieval from cmd string failed")); |
| return -EINVAL; |
| } |
| |
| v = kstrtos32(arg1, 10, &tmp); |
| if (v < 0) { |
| hddLog(LOGE, FL("argument string to integer conversion failed")); |
| return -EINVAL; |
| } |
| *mode = tmp; |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_is_supported_chain_mask_2x2() - Verify if supported chain |
| * mask is 2x2 mode |
| * @hdd_ctx: Pointer to hdd contex |
| * |
| * Return: true if supported chain mask 2x2 else false |
| */ |
| static bool hdd_is_supported_chain_mask_2x2(hdd_context_t *hdd_ctx) |
| { |
| hdd_config_t *config = hdd_ctx->cfg_ini; |
| |
| if (hdd_ctx->per_band_chainmask_supp == 0x01) { |
| return (((hdd_ctx->supp_2g_chain_mask & 0x03) |
| == 0x03) || |
| ((hdd_ctx->supp_5g_chain_mask & 0x03) |
| == 0x03)) ? true : false; |
| } |
| |
| return (config->enable2x2 == 0x01) ? true : false; |
| } |
| |
| /** |
| * hdd_is_supported_chain_mask_1x1() - Verify if the supported |
| * chain mask is 1x1 |
| * @hdd_ctx: Pointer to hdd contex |
| * |
| * Return: true if supported chain mask 1x1 else false |
| */ |
| static bool hdd_is_supported_chain_mask_1x1(hdd_context_t *hdd_ctx) |
| { |
| hdd_config_t *config = hdd_ctx->cfg_ini; |
| |
| if (hdd_ctx->per_band_chainmask_supp == 0x01) { |
| return ((hdd_ctx->supp_2g_chain_mask <= 0x02) && |
| (hdd_ctx->supp_5g_chain_mask <= 0x02)) ? |
| true : false; |
| } |
| |
| return (!config->enable2x2) ? true : false; |
| } |
| |
| /** |
| * switch_antenna_mode_non_conn_state() - Dynamic switch to 1x1 |
| * antenna mode when there are no connections |
| * @hdd_ctx: Pointer to hdd contex |
| * @adapter: Pointer to hdd adapter |
| * @chains: Number of TX/RX chains to set |
| * |
| * Return: 0 if success else non zero |
| */ |
| static int switch_antenna_mode_non_conn_state(hdd_context_t *hdd_ctx, |
| hdd_adapter_t *adapter, |
| uint8_t chains) |
| { |
| int ret; |
| eHalStatus hal_status; |
| bool enable_smps; |
| int smps_mode; |
| |
| ret = wlan_hdd_update_txrx_chain_mask(hdd_ctx, |
| (chains == 2) ? 0x3 : 0x1); |
| |
| if (0 != ret) { |
| hddLog(LOGE, |
| FL("Failed to update chain mask: %d"), |
| chains); |
| return ret; |
| } |
| |
| /* Update HT SMPS as static/disabled in the SME configuration |
| * If there is STA connection followed by dynamic switch |
| * to 1x1 protocol stack would include SM power save IE as |
| * static in the assoc mgmt frame and after association |
| * SMPS force mode command will be sent to FW to initiate |
| * SMPS action frames to AP. In this case, SMPS force mode |
| * command event can be expected from firmware with the |
| * TX status of SMPS action frames. Inclusion of SM power |
| * save IE and sending of SMPS action frames will not happen |
| * for switch to 2x2 mode. But SME config should still be |
| * updated to disabled. |
| */ |
| adapter->smps_force_mode_status = 0; |
| |
| enable_smps = (chains == 1) ? true : false; |
| smps_mode = (chains == 1) ? HDD_SMPS_MODE_STATIC : |
| HDD_SMPS_MODE_DISABLED; |
| |
| hal_status = sme_update_mimo_power_save(hdd_ctx->hHal, |
| enable_smps, smps_mode); |
| if (eHAL_STATUS_SUCCESS != hal_status) { |
| hddLog(LOG1, |
| FL("Update MIMO power SME config failed: %d"), |
| hal_status); |
| return -EFAULT; |
| } |
| |
| hddLog(LOG1, FL("Updated SME config enable smps: %d mode: %d"), |
| enable_smps, smps_mode); |
| |
| return 0; |
| } |
| |
| /** |
| * switch_to_1x1_connected_sta_state() - Dynamic switch to 1x1 |
| * antenna mode in standalone station |
| * @hdd_ctx: Pointer to hdd contex |
| * @adapter: Pointer to hdd adapter |
| * |
| * Return: 0 if success else non zero |
| */ |
| static int switch_to_1x1_connected_sta_state(hdd_context_t *hdd_ctx, |
| hdd_adapter_t *adapter) |
| { |
| int ret; |
| eHalStatus hal_status; |
| bool send_smps; |
| |
| /* Check TDLS status and update antenna mode */ |
| ret = wlan_hdd_tdls_antenna_switch(hdd_ctx, adapter, |
| HDD_ANTENNA_MODE_1X1); |
| if (0 != ret) |
| return ret; |
| |
| /* If intersection of sta and AP NSS is 1x1 then |
| * skip SMPS indication to AP. Only update the chain mask |
| * and other configuration. |
| */ |
| send_smps = sme_is_sta_smps_allowed(hdd_ctx->hHal, |
| adapter->sessionId); |
| if (!send_smps) { |
| hddLog(LOGE, FL("Need not indicate SMPS to AP")); |
| goto chain_mask; |
| } |
| |
| INIT_COMPLETION(adapter->smps_force_mode_comp_var); |
| |
| hddLog(LOG1, FL("Send SMPS force mode command")); |
| ret = process_wma_set_command((int)adapter->sessionId, |
| WMI_STA_SMPS_FORCE_MODE_CMDID, |
| WMI_SMPS_FORCED_MODE_STATIC, |
| VDEV_CMD); |
| if (0 != ret) { |
| hddLog(LOGE, |
| FL("Failed to send SMPS force mode to static")); |
| return ret; |
| } |
| |
| /* Block on SMPS force mode event only for mode static */ |
| ret = wait_for_completion_timeout( |
| &adapter->smps_force_mode_comp_var, |
| msecs_to_jiffies(WLAN_WAIT_SMPS_FORCE_MODE)); |
| if (!ret) { |
| hddLog(LOGE, |
| FL("SMPS force mode event timeout: %d"), |
| ret); |
| return -EFAULT; |
| } |
| ret = adapter->smps_force_mode_status; |
| adapter->smps_force_mode_status = 0; |
| if (0 != ret) { |
| hddLog(LOGE, FL("SMPS force mode status: %d "), |
| ret); |
| return ret; |
| } |
| |
| chain_mask: |
| hddLog(LOG1, FL("Update chain mask to 1x1")); |
| ret = wlan_hdd_update_txrx_chain_mask(hdd_ctx, 1); |
| if (0 != ret) { |
| hddLog(LOGE, FL("Failed to switch to 1x1 mode")); |
| return ret; |
| } |
| |
| /* Update SME SM power save config */ |
| hal_status = sme_update_mimo_power_save(hdd_ctx->hHal, |
| true, HDD_SMPS_MODE_STATIC); |
| if (eHAL_STATUS_SUCCESS != hal_status) { |
| hddLog(LOG1, |
| FL("Failed to update SMPS config to static: %d"), |
| hal_status); |
| return -EFAULT; |
| } |
| |
| hddLog(LOG1, FL("Successfully switched to 1x1 mode")); |
| return 0; |
| } |
| |
| /** |
| * switch_to_2x2_connected_sta_state() - Dynamic switch to 2x2 |
| * antenna mode in standalone station |
| * @hdd_ctx: Pointer to hdd contex |
| * @adapter: Pointer to hdd adapter |
| * |
| * Return: 0 if success else non zero |
| */ |
| static int switch_to_2x2_connected_sta_state(hdd_context_t *hdd_ctx, |
| hdd_adapter_t *adapter) |
| { |
| int ret; |
| eHalStatus hal_status; |
| bool send_smps; |
| |
| /* Check TDLS status and update antenna mode */ |
| ret = wlan_hdd_tdls_antenna_switch(hdd_ctx, adapter, |
| HDD_ANTENNA_MODE_2X2); |
| if (0 != ret) |
| return ret; |
| |
| hddLog(LOG1, FL("Update chain mask to 2x2")); |
| ret = wlan_hdd_update_txrx_chain_mask(hdd_ctx, 3); |
| if (0 != ret) { |
| hddLog(LOGE, FL("Failed to switch to 2x2 mode")); |
| return ret; |
| } |
| |
| /* If intersection of sta and AP NSS is 1x1 then |
| * skip SMPS indication to AP. |
| */ |
| send_smps = sme_is_sta_smps_allowed(hdd_ctx->hHal, |
| adapter->sessionId); |
| if (!send_smps) { |
| hddLog(LOGE, FL("Need not indicate SMPS to AP")); |
| goto exit; |
| } |
| |
| hddLog(LOG1, FL("Send SMPS force mode command ")); |
| |
| /* No need to block on SMPS force mode event when |
| * the mode switch is 2x2 since the chain mask |
| * has already been updated to 2x2 |
| */ |
| ret = process_wma_set_command((int)adapter->sessionId, |
| WMI_STA_SMPS_FORCE_MODE_CMDID, |
| WMI_SMPS_FORCED_MODE_DISABLED, |
| VDEV_CMD); |
| if (0 != ret) { |
| hddLog(LOGE, |
| FL("Failed to send SMPS force mode to disabled")); |
| return ret; |
| } |
| |
| exit: |
| /* Update SME SM power save config */ |
| hal_status = sme_update_mimo_power_save(hdd_ctx->hHal, |
| false, HDD_SMPS_MODE_DISABLED); |
| if (eHAL_STATUS_SUCCESS != hal_status) { |
| hddLog(LOG1, |
| FL("Failed to update SMPS config to disable: %d"), |
| hal_status); |
| return -EFAULT; |
| } |
| |
| hddLog(LOG1, FL("Successfully switched to 2x2 mode")); |
| return 0; |
| } |
| |
| /** |
| * hdd_decide_dynamic_chain_mask() - Dynamically decide and set chain mask |
| * for STA |
| * @hdd_ctx: Pointer to hdd context |
| * @forced_mode: set to valid mode to force the mode |
| * |
| * This function decides the chain mask to set considering number of |
| * active sessions, type of active sessions and concurrency. |
| * If forced_mode is valid, chan mask is set to the forced_mode. |
| * |
| * Return: void |
| */ |
| void hdd_decide_dynamic_chain_mask(hdd_context_t *hdd_ctx, |
| enum antenna_mode forced_mode) |
| { |
| int ret; |
| hdd_adapter_t *sta_adapter; |
| hdd_station_ctx_t *hdd_sta_ctx; |
| enum antenna_mode mode; |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| |
| if (!hdd_ctx->cfg_ini->enable_dynamic_sta_chainmask || |
| !hdd_is_supported_chain_mask_2x2(hdd_ctx)) { |
| hddLog(LOG1, |
| FL("dynamic antenna mode in INI is %d or 2x2 not supported"), |
| hdd_ctx->cfg_ini->enable_dynamic_sta_chainmask); |
| return; |
| } |
| hddLog(LOG1, FL("Current antenna mode: %d"), |
| hdd_ctx->current_antenna_mode); |
| |
| if (HDD_ANTENNA_MODE_INVALID != forced_mode) { |
| mode = forced_mode; |
| } else if (!wlan_hdd_get_active_session_count(hdd_ctx)) { |
| /* If no active connection set 1x1 */ |
| mode = HDD_ANTENNA_MODE_1X1; |
| } else if (1 == wlan_hdd_get_active_session_count(hdd_ctx) && |
| hdd_ctx->no_of_active_sessions[WLAN_HDD_INFRA_STATION]) { |
| sta_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_INFRA_STATION); |
| if (!sta_adapter) { |
| hddLog(LOGE, FL("Sta adapter null!!")); |
| return; |
| } |
| hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter); |
| |
| if (!hdd_connIsConnected(hdd_sta_ctx)) { |
| hddLog(LOGE, FL("Sta not connected")); |
| return; |
| } |
| /* |
| * In case only sta is connected set value depending |
| * on AP capability. |
| */ |
| mode = hdd_sta_ctx->conn_info.nss; |
| } else { |
| /* If non sta is active or concurrency set 2x2 */ |
| mode = HDD_ANTENNA_MODE_2X2; |
| } |
| |
| if (mode != hdd_ctx->current_antenna_mode) { |
| |
| ret = wlan_hdd_update_txrx_chain_mask(hdd_ctx, |
| (HDD_ANTENNA_MODE_2X2 == mode) ? 0x3 : 0x1); |
| |
| if (0 != ret) { |
| hddLog(LOGE, |
| FL("Failed to update chain mask: %d"), |
| mode); |
| return; |
| } |
| hdd_ctx->current_antenna_mode = mode; |
| |
| hddLog(LOG1, FL("Antenna mode updated to: %d"), |
| hdd_ctx->current_antenna_mode); |
| } |
| } |
| |
| /** |
| * drv_cmd_set_antenna_mode() - SET ANTENNA MODE driver command |
| * handler |
| * @hdd_ctx: Pointer to hdd context |
| * @cmd: Pointer to input command |
| * @command_len: Command length |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| static int drv_cmd_set_antenna_mode(hdd_adapter_t *adapter, |
| uint8_t *command, |
| uint8_t cmd_len) |
| { |
| int ret; |
| int mode; |
| uint8_t *value = command; |
| hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| |
| if ((hdd_ctx->concurrency_mode > 1) || |
| (hdd_ctx->no_of_active_sessions[WLAN_HDD_INFRA_STATION] > 1)) { |
| hddLog(LOGE, FL("Operation invalid in non sta or concurrent mode")); |
| ret = -EPERM; |
| goto exit; |
| } |
| |
| ret = hdd_parse_setantennamode_command(value, &mode); |
| if (0 != ret) { |
| hddLog(LOGE, FL("Invalid SETANTENNA command")); |
| goto exit; |
| } |
| |
| hddLog(LOG1, FL("Request to switch antenna mode to: %d"), mode); |
| |
| if (hdd_ctx->current_antenna_mode == mode) { |
| hddLog(LOGE, FL("System already in the requested mode")); |
| ret = 0; |
| goto exit; |
| } |
| |
| if ((HDD_ANTENNA_MODE_2X2 == mode) && |
| (!hdd_is_supported_chain_mask_2x2(hdd_ctx))) { |
| hddLog(LOGE, FL("System does not support 2x2 mode")); |
| ret = -EPERM; |
| goto exit; |
| } |
| |
| if ((HDD_ANTENNA_MODE_1X1 == mode) && |
| hdd_is_supported_chain_mask_1x1(hdd_ctx)) { |
| hddLog(LOGE, FL("System already in 1x1 mode")); |
| ret = 0; |
| goto exit; |
| } |
| |
| /* Non connected state */ |
| if (0 == wlan_hdd_get_active_session_count(hdd_ctx)) { |
| hddLog(LOG1, |
| FL("Switch to %d x %d in non connected state"), |
| mode, mode); |
| |
| ret = switch_antenna_mode_non_conn_state( |
| hdd_ctx, adapter, mode); |
| if (0 != ret) { |
| hddLog(LOGE, |
| FL("Failed to switch to %d x %d mode"), |
| mode, mode); |
| goto exit; |
| } |
| |
| hdd_ctx->current_antenna_mode = (mode == 1) ? |
| HDD_ANTENNA_MODE_1X1 : HDD_ANTENNA_MODE_2X2; |
| |
| } else if ((hdd_ctx->concurrency_mode <= 1) && |
| (hdd_ctx->no_of_active_sessions[ |
| WLAN_HDD_INFRA_STATION] <= 1)) { |
| hddLog(LOG1, |
| FL("Switch to %d x %d in connected sta state"), |
| mode, mode); |
| |
| if (HDD_ANTENNA_MODE_1X1 == mode) { |
| ret = switch_to_1x1_connected_sta_state( |
| hdd_ctx, adapter); |
| if (0 != ret) { |
| hddLog(LOGE, |
| FL("Failed to switch to 1x1 mode")); |
| goto exit; |
| } |
| hdd_ctx->current_antenna_mode = |
| HDD_ANTENNA_MODE_1X1; |
| |
| } else if (HDD_ANTENNA_MODE_2X2 == mode) { |
| ret = switch_to_2x2_connected_sta_state( |
| hdd_ctx, adapter); |
| if (0 != ret) { |
| hddLog(LOGE, |
| FL("Failed to switch to 2x2 mode")); |
| goto exit; |
| } |
| hdd_ctx->current_antenna_mode = |
| HDD_ANTENNA_MODE_2X2; |
| } |
| } |
| |
| /* Update the user requested nss in the mac context. |
| * This will be used in tdls protocol engine to form tdls |
| * Management frames. |
| */ |
| sme_update_user_configured_nss( |
| hdd_ctx->hHal, |
| hdd_ctx->current_antenna_mode); |
| |
| exit: |
| #ifdef FEATURE_WLAN_TDLS |
| /* Reset tdls NSS flags */ |
| if (hdd_ctx->tdls_nss_switch_in_progress && |
| hdd_ctx->tdls_nss_teardown_complete) { |
| hdd_ctx->tdls_nss_switch_in_progress = false; |
| hdd_ctx->tdls_nss_teardown_complete = false; |
| } |
| |
| hddLog(LOG1, |
| FL("tdls_nss_switch_in_progress: %d tdls_nss_teardown_complete: %d"), |
| hdd_ctx->tdls_nss_switch_in_progress, |
| hdd_ctx->tdls_nss_teardown_complete); |
| #endif |
| hddLog(LOG1, FL("Set antenna status: %d current mode: %d"), |
| ret, hdd_ctx->current_antenna_mode); |
| return ret; |
| } |
| |
| /** |
| * drv_cmd_get_antenna_mode() - GET ANTENNA MODE driver command |
| * handler |
| * @adapter: Pointer to hdd adapter |
| * @hdd_ctx: Pointer to hdd context |
| * @command: Pointer to input command |
| * @command_len: length of the command |
| * @priv_data: private data coming with the driver command |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| static inline int drv_cmd_get_antenna_mode(hdd_adapter_t *adapter, |
| hdd_context_t *hdd_ctx, |
| uint8_t *command, |
| uint8_t command_len, |
| hdd_priv_data_t *priv_data) |
| { |
| uint32_t antenna_mode = 0; |
| char extra[32]; |
| uint8_t len = 0; |
| |
| antenna_mode = hdd_ctx->current_antenna_mode; |
| len = scnprintf(extra, sizeof(extra), "%s %d", command, |
| antenna_mode); |
| len = VOS_MIN(priv_data->total_len, len + 1); |
| if (copy_to_user(priv_data->buf, &extra, len)) { |
| hddLog(LOGE, FL("Failed to copy data to user buffer")); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| static int hdd_driver_command(hdd_adapter_t *pAdapter, |
| hdd_priv_data_t *ppriv_data) |
| { |
| hdd_priv_data_t priv_data; |
| tANI_U8 *command = NULL; |
| int ret = 0; |
| |
| ENTER(); |
| |
| 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 |
| */ |
| |
| /* copy to local struct to avoid numerous changes to legacy code */ |
| priv_data = *ppriv_data; |
| |
| if (priv_data.total_len <= 0 || |
| priv_data.total_len > WLAN_PRIV_DATA_MAX_LEN) |
| { |
| hddLog(VOS_TRACE_LEVEL_WARN, |
| "%s:invalid priv_data.total_len(%d)!!!", __func__, |
| priv_data.total_len); |
| ret = -EINVAL; |
| 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'; |
| |
| /* at one time the following block of code was conditional. braces |
| * have been retained to avoid re-indenting the legacy code |
| */ |
| { |
| hdd_context_t *pHddCtx = (hdd_context_t*)pAdapter->pHddCtx; |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: Received %s cmd from Wi-Fi GUI***", __func__, command); |
| |
| if (strncmp(command, "P2P_DEV_ADDR", 12) == 0 ) |
| { |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_P2P_DEV_ADDR_IOCTL, |
| pAdapter->sessionId, (unsigned) |
| (*(pHddCtx->p2pDeviceAddress.bytes+2)<<24 | |
| *(pHddCtx->p2pDeviceAddress.bytes+3)<<16 | |
| *(pHddCtx->p2pDeviceAddress.bytes+4)<<8 | |
| *(pHddCtx->p2pDeviceAddress.bytes+5)))); |
| if (copy_to_user(priv_data.buf, pHddCtx->p2pDeviceAddress.bytes, |
| sizeof(tSirMacAddr))) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| } |
| } |
| else if (strncmp(command, "SETBAND", 7) == 0) |
| { |
| tANI_U8 *ptr = command ; |
| |
| if (hdd_drv_cmd_validate(command, 7)) |
| goto exit; |
| |
| /* Change band request received */ |
| |
| /* First 8 bytes will have "SETBAND " and |
| * 9 byte will have band setting value */ |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: SetBandCommand Info comm %s UL %d, TL %d", |
| __func__, command, priv_data.used_len, priv_data.total_len); |
| /* Change band request received */ |
| ret = hdd_setBand_helper(pAdapter->dev, ptr); |
| } |
| else if (strncmp(command, "SETWMMPS", 8) == 0) |
| { |
| tANI_U8 *ptr = command; |
| |
| if (hdd_drv_cmd_validate(command, 8)) |
| goto exit; |
| |
| ret = hdd_wmmps_helper(pAdapter, ptr); |
| } |
| else if (strncasecmp(command, "COUNTRY", 7) == 0) |
| { |
| eHalStatus status; |
| unsigned long rc; |
| char *country_code; |
| |
| if (hdd_drv_cmd_validate(command, 7)) |
| goto exit; |
| |
| country_code = command + 8; |
| |
| INIT_COMPLETION(pAdapter->change_country_code); |
| hdd_checkandupdate_dfssetting(pAdapter, country_code); |
| |
| status = |
| sme_ChangeCountryCode(pHddCtx->hHal, |
| (void *)(tSmeChangeCountryCallback) |
| wlan_hdd_change_country_code_callback, |
| country_code, pAdapter, |
| pHddCtx->pvosContext, |
| eSIR_TRUE, eSIR_TRUE); |
| if (status == eHAL_STATUS_SUCCESS) |
| { |
| rc = wait_for_completion_timeout( |
| &pAdapter->change_country_code, |
| msecs_to_jiffies(WLAN_WAIT_TIME_COUNTRY)); |
| if (!rc) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: SME while setting country code timed out", |
| __func__); |
| } |
| } |
| else |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: SME Change Country code fail, status=%d", |
| __func__, status); |
| ret = -EINVAL; |
| } |
| |
| } |
| else if (strncmp(command, "SETSUSPENDMODE", 14) == 0) |
| { |
| } |
| #ifdef WLAN_FEATURE_NEIGHBOR_ROAMING |
| else if (strncmp(command, "SETROAMTRIGGER", 14) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_S8 rssi = 0; |
| tANI_U8 lookUpThreshold = CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_DEFAULT; |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| |
| if (hdd_drv_cmd_validate(command, 14)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETROAMTRIGGER<delimiter> */ |
| value = value + 15; |
| |
| /* Convert the value from ascii to integer */ |
| ret = kstrtos8(value, 10, &rssi); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed Input value may be out of range[%d - %d]", |
| __func__, |
| CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN, |
| CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| lookUpThreshold = abs(rssi); |
| |
| if ((lookUpThreshold < CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN) || |
| (lookUpThreshold > CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Neighbor lookup threshold value %d is out of range" |
| " (Min: %d Max: %d)", lookUpThreshold, |
| CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN, |
| CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_SETROAMTRIGGER_IOCTL, |
| pAdapter->sessionId, lookUpThreshold)); |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to Set Roam trigger" |
| " (Neighbor lookup threshold) = %d", __func__, lookUpThreshold); |
| |
| pHddCtx->cfg_ini->nNeighborLookupRssiThreshold = lookUpThreshold; |
| status = sme_setNeighborLookupRssiThreshold(pHddCtx->hHal, |
| pAdapter->sessionId, |
| lookUpThreshold); |
| if (eHAL_STATUS_SUCCESS != status) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to set roam trigger, try again", __func__); |
| ret = -EPERM; |
| goto exit; |
| } |
| |
| /* Set Reassoc threshold to (lookup rssi threshold + 5 dBm) */ |
| pHddCtx->cfg_ini->nNeighborReassocRssiThreshold = |
| lookUpThreshold + 5; |
| sme_setNeighborReassocRssiThreshold(pHddCtx->hHal, |
| pAdapter->sessionId, |
| lookUpThreshold + 5); |
| } |
| else if (strncmp(command, "GETROAMTRIGGER", 14) == 0) |
| { |
| tANI_U8 lookUpThreshold = |
| sme_getNeighborLookupRssiThreshold(pHddCtx->hHal); |
| int rssi = (-1) * lookUpThreshold; |
| char extra[32]; |
| tANI_U8 len = 0; |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_GETROAMTRIGGER_IOCTL, |
| pAdapter->sessionId, lookUpThreshold)); |
| len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "SETROAMSCANPERIOD", 17) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 roamScanPeriod = 0; |
| tANI_U16 neighborEmptyScanRefreshPeriod = CFG_EMPTY_SCAN_REFRESH_PERIOD_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 17)) |
| goto exit; |
| |
| /* input refresh period is in terms of seconds */ |
| /* Move pointer to ahead of SETROAMSCANPERIOD<delimiter> */ |
| value = value + 18; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &roamScanPeriod); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed Input value may be out of range[%d - %d]", |
| __func__, |
| (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN/1000), |
| (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX/1000)); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if ((roamScanPeriod < (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN/1000)) || |
| (roamScanPeriod > (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX/1000))) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Roam scan period value %d is out of range" |
| " (Min: %d Max: %d)", roamScanPeriod, |
| (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN/1000), |
| (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX/1000)); |
| ret = -EINVAL; |
| goto exit; |
| } |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_SETROAMSCANPERIOD_IOCTL, |
| pAdapter->sessionId, roamScanPeriod)); |
| neighborEmptyScanRefreshPeriod = roamScanPeriod * 1000; |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to Set roam scan period" |
| " (Empty Scan refresh period) = %d", __func__, roamScanPeriod); |
| |
| pHddCtx->cfg_ini->nEmptyScanRefreshPeriod = neighborEmptyScanRefreshPeriod; |
| sme_UpdateEmptyScanRefreshPeriod(pHddCtx->hHal, |
| pAdapter->sessionId, |
| neighborEmptyScanRefreshPeriod); |
| } |
| else if (strncmp(command, "GETROAMSCANPERIOD", 17) == 0) |
| { |
| tANI_U16 nEmptyScanRefreshPeriod = |
| sme_getEmptyScanRefreshPeriod(pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_GETROAMSCANPERIOD_IOCTL, |
| pAdapter->sessionId, nEmptyScanRefreshPeriod)); |
| len = scnprintf(extra, sizeof(extra), "%s %d", |
| "GETROAMSCANPERIOD", (nEmptyScanRefreshPeriod/1000)); |
| /* Returned value is in units of seconds */ |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "SETROAMSCANREFRESHPERIOD", 24) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 roamScanRefreshPeriod = 0; |
| tANI_U16 neighborScanRefreshPeriod = CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 24)) |
| goto exit; |
| |
| /* input refresh period is in terms of seconds */ |
| /* Move pointer to ahead of SETROAMSCANREFRESHPERIOD<delimiter> */ |
| value = value + 25; |
| |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &roamScanRefreshPeriod); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed Input value may be out of range[%d - %d]", |
| __func__, |
| (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN/1000), |
| (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX/1000)); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if ((roamScanRefreshPeriod < (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN/1000)) || |
| (roamScanRefreshPeriod > (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX/1000))) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Neighbor scan results refresh period value %d is out of range" |
| " (Min: %d Max: %d)", roamScanRefreshPeriod, |
| (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN/1000), |
| (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX/1000)); |
| ret = -EINVAL; |
| goto exit; |
| } |
| neighborScanRefreshPeriod = roamScanRefreshPeriod * 1000; |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to Set roam scan refresh period" |
| " (Scan refresh period) = %d", __func__, roamScanRefreshPeriod); |
| |
| pHddCtx->cfg_ini->nNeighborResultsRefreshPeriod = neighborScanRefreshPeriod; |
| sme_setNeighborScanRefreshPeriod(pHddCtx->hHal, |
| pAdapter->sessionId, |
| neighborScanRefreshPeriod); |
| } |
| else if (strncmp(command, "GETROAMSCANREFRESHPERIOD", 24) == 0) |
| { |
| tANI_U16 value = sme_getNeighborScanRefreshPeriod(pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", |
| "GETROAMSCANREFRESHPERIOD", (value/1000)); |
| /* Returned value is in units of seconds */ |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| #ifdef FEATURE_WLAN_LFR |
| /* SETROAMMODE */ |
| else if (strncmp(command, "SETROAMMODE", SIZE_OF_SETROAMMODE) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_BOOLEAN roamMode = CFG_LFR_FEATURE_ENABLED_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, SIZE_OF_SETROAMMODE)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETROAMMODE<delimiter> */ |
| value = value + SIZE_OF_SETROAMMODE + 1; |
| |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &roamMode); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed range [%d - %d]", __func__, |
| CFG_LFR_FEATURE_ENABLED_MIN, |
| CFG_LFR_FEATURE_ENABLED_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| if ((roamMode < CFG_LFR_FEATURE_ENABLED_MIN) || |
| (roamMode > CFG_LFR_FEATURE_ENABLED_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Roam Mode value %d is out of range" |
| " (Min: %d Max: %d)", roamMode, |
| CFG_LFR_FEATURE_ENABLED_MIN, |
| CFG_LFR_FEATURE_ENABLED_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG, |
| "%s: Received Command to Set Roam Mode = %d", __func__, roamMode); |
| /* |
| * Note that |
| * SETROAMMODE 0 is to enable LFR while |
| * SETROAMMODE 1 is to disable LFR, but |
| * NotifyIsFastRoamIniFeatureEnabled 0/1 is to enable/disable. |
| * So, we have to invert the value to call sme_UpdateIsFastRoamIniFeatureEnabled. |
| */ |
| if (CFG_LFR_FEATURE_ENABLED_MIN == roamMode) |
| roamMode = CFG_LFR_FEATURE_ENABLED_MAX; /* Roam enable */ |
| else |
| roamMode = CFG_LFR_FEATURE_ENABLED_MIN; /* Roam disable */ |
| |
| pHddCtx->cfg_ini->isFastRoamIniFeatureEnabled = roamMode; |
| /* LFR2 is dependent on Fast Roam. So, enable/disable LFR2 |
| * variable. if Fast Roam has been changed from disabled to enabled, |
| * then enable LFR2 and send the LFR START command to the firmware. |
| * Otherwise, send the LFR STOP command to the firmware and then |
| * disable LFR2.The sequence is different. |
| */ |
| if (roamMode) { |
| pHddCtx->cfg_ini->isRoamOffloadScanEnabled = roamMode; |
| sme_UpdateRoamScanOffloadEnabled((tHalHandle)(pHddCtx->hHal), |
| pHddCtx->cfg_ini->isRoamOffloadScanEnabled); |
| sme_UpdateIsFastRoamIniFeatureEnabled(pHddCtx->hHal, |
| pAdapter->sessionId, |
| roamMode); |
| } else { |
| sme_UpdateIsFastRoamIniFeatureEnabled(pHddCtx->hHal, |
| pAdapter->sessionId, |
| roamMode); |
| pHddCtx->cfg_ini->isRoamOffloadScanEnabled = roamMode; |
| sme_UpdateRoamScanOffloadEnabled((tHalHandle)(pHddCtx->hHal), |
| pHddCtx->cfg_ini->isRoamOffloadScanEnabled); |
| } |
| } |
| /* GETROAMMODE */ |
| else if (strncmp(command, "GETROAMMODE", SIZE_OF_GETROAMMODE) == 0) |
| { |
| tANI_BOOLEAN roamMode = sme_getIsLfrFeatureEnabled(pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| /* |
| * roamMode value shall be inverted because the semantics is |
| * different. |
| */ |
| if (CFG_LFR_FEATURE_ENABLED_MIN == roamMode) |
| roamMode = CFG_LFR_FEATURE_ENABLED_MAX; |
| else |
| roamMode = CFG_LFR_FEATURE_ENABLED_MIN; |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", command, roamMode); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| #endif |
| #endif |
| #if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR) |
| else if (strncmp(command, "SETROAMDELTA", 12) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 roamRssiDiff = CFG_ROAM_RSSI_DIFF_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 12)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETROAMDELTA<delimiter> */ |
| value = value + 13; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &roamRssiDiff); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed range [%d - %d]", __func__, |
| CFG_ROAM_RSSI_DIFF_MIN, |
| CFG_ROAM_RSSI_DIFF_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if ((roamRssiDiff < CFG_ROAM_RSSI_DIFF_MIN) || |
| (roamRssiDiff > CFG_ROAM_RSSI_DIFF_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Roam rssi diff value %d is out of range" |
| " (Min: %d Max: %d)", roamRssiDiff, |
| CFG_ROAM_RSSI_DIFF_MIN, |
| CFG_ROAM_RSSI_DIFF_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to Set roam rssi diff = %d", __func__, roamRssiDiff); |
| |
| pHddCtx->cfg_ini->RoamRssiDiff = roamRssiDiff; |
| sme_UpdateRoamRssiDiff(pHddCtx->hHal, |
| pAdapter->sessionId, roamRssiDiff); |
| } |
| else if (strncmp(command, "GETROAMDELTA", 12) == 0) |
| { |
| tANI_U8 roamRssiDiff = sme_getRoamRssiDiff(pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_GETROAMDELTA_IOCTL, |
| pAdapter->sessionId, roamRssiDiff)); |
| len = scnprintf(extra, sizeof(extra), "%s %d", |
| command, roamRssiDiff); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| #endif |
| #if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR) |
| else if (strncmp(command, "GETBAND", 7) == 0) |
| { |
| int band = -1; |
| char extra[32]; |
| tANI_U8 len = 0; |
| hdd_getBand_helper(pHddCtx, &band); |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_GETBAND_IOCTL, |
| pAdapter->sessionId, band)); |
| len = scnprintf(extra, sizeof(extra), "%s %d", command, band); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "SETROAMSCANCHANNELS ", 20) == 0) |
| { |
| if (hdd_drv_cmd_validate(command, 20)) |
| goto exit; |
| |
| ret = hdd_parse_set_roam_scan_channels(pAdapter, command); |
| } |
| else if (strncmp(command, "GETROAMSCANCHANNELS", 19) == 0) |
| { |
| tANI_U8 ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0}; |
| tANI_U8 numChannels = 0; |
| tANI_U8 j = 0; |
| char extra[128] = {0}; |
| int len; |
| |
| if (eHAL_STATUS_SUCCESS != sme_getRoamScanChannelList( |
| pHddCtx->hHal, |
| ChannelList, |
| &numChannels, |
| pAdapter->sessionId)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: failed to get roam scan channel list", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_GETROAMSCANCHANNELS_IOCTL, |
| pAdapter->sessionId, numChannels)); |
| /* output channel list is of the format |
| [Number of roam scan channels][Channel1][Channel2]... */ |
| /* copy the number of channels in the 0th index */ |
| len = scnprintf(extra, sizeof(extra), "%s %d", command, numChannels); |
| for (j = 0; (j < numChannels) && len <= sizeof(extra); j++) |
| { |
| len += scnprintf(extra + len, sizeof(extra) - len, " %d", |
| ChannelList[j]); |
| } |
| |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "GETCCXMODE", 10) == 0) |
| { |
| tANI_BOOLEAN eseMode = sme_getIsEseFeatureEnabled(pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| /* Check if the features OKC/ESE/11R are supported simultaneously, |
| then this operation is not permitted (return FAILURE) */ |
| if (eseMode && |
| hdd_is_okc_mode_enabled(pHddCtx) && |
| sme_getIsFtFeatureEnabled(pHddCtx->hHal)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "%s: OKC/ESE/11R are supported simultaneously" |
| " hence this operation is not permitted!", __func__); |
| ret = -EPERM; |
| goto exit; |
| } |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", |
| "GETCCXMODE", eseMode); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "GETOKCMODE", 10) == 0) |
| { |
| tANI_BOOLEAN okcMode = hdd_is_okc_mode_enabled(pHddCtx); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| /* Check if the features OKC/ESE/11R are supported simultaneously, |
| then this operation is not permitted (return FAILURE) */ |
| if (okcMode && |
| sme_getIsEseFeatureEnabled(pHddCtx->hHal) && |
| sme_getIsFtFeatureEnabled(pHddCtx->hHal)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "%s: OKC/ESE/11R are supported simultaneously" |
| " hence this operation is not permitted!", __func__); |
| ret = -EPERM; |
| goto exit; |
| } |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", |
| "GETOKCMODE", okcMode); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "GETFASTROAM", 11) == 0) |
| { |
| tANI_BOOLEAN lfrMode = sme_getIsLfrFeatureEnabled(pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", |
| "GETFASTROAM", lfrMode); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "GETFASTTRANSITION", 17) == 0) |
| { |
| tANI_BOOLEAN ft = sme_getIsFtFeatureEnabled(pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", |
| "GETFASTTRANSITION", ft); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "SETROAMSCANCHANNELMINTIME", 25) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 minTime = CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 25)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETROAMSCANCHANNELMINTIME<delimiter> */ |
| value = value + 26; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &minTime); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed range [%d - %d]", __func__, |
| CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN, |
| CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| if ((minTime < CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN) || |
| (minTime > CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "scan min channel time value %d is out of range" |
| " (Min: %d Max: %d)", minTime, |
| CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN, |
| CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_SETROAMSCANCHANNELMINTIME_IOCTL, |
| pAdapter->sessionId, minTime)); |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to change channel min time = %d", __func__, minTime); |
| |
| pHddCtx->cfg_ini->nNeighborScanMinChanTime = minTime; |
| sme_setNeighborScanMinChanTime(pHddCtx->hHal, |
| minTime, pAdapter->sessionId); |
| } |
| else if (strncmp(command, "SENDACTIONFRAME", 15) == 0) |
| { |
| if (hdd_drv_cmd_validate(command, 15)) |
| goto exit; |
| |
| ret = hdd_parse_sendactionframe(pAdapter, command, |
| priv_data.total_len); |
| } |
| else if (strncmp(command, "GETROAMSCANCHANNELMINTIME", 25) == 0) |
| { |
| tANI_U16 val = sme_getNeighborScanMinChanTime( |
| pHddCtx->hHal, |
| pAdapter->sessionId); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| /* value is interms of msec */ |
| len = scnprintf(extra, sizeof(extra), "%s %d", |
| "GETROAMSCANCHANNELMINTIME", val); |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_GETROAMSCANCHANNELMINTIME_IOCTL, |
| pAdapter->sessionId, val)); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "SETSCANCHANNELTIME", 18) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U16 maxTime = CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 18)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETSCANCHANNELTIME<delimiter> */ |
| value = value + 19; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou16(value, 10, &maxTime); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou16 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou16 failed range [%d - %d]", __func__, |
| CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN, |
| CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if ((maxTime < CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN) || |
| (maxTime > CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "lfr mode value %d is out of range" |
| " (Min: %d Max: %d)", maxTime, |
| CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN, |
| CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to change channel max time = %d", __func__, maxTime); |
| |
| pHddCtx->cfg_ini->nNeighborScanMaxChanTime = maxTime; |
| sme_setNeighborScanMaxChanTime(pHddCtx->hHal, |
| pAdapter->sessionId, maxTime); |
| } |
| else if (strncmp(command, "GETSCANCHANNELTIME", 18) == 0) |
| { |
| tANI_U16 val = sme_getNeighborScanMaxChanTime(pHddCtx->hHal, |
| pAdapter->sessionId); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| /* value is interms of msec */ |
| len = scnprintf(extra, sizeof(extra), "%s %d", |
| "GETSCANCHANNELTIME", val); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "SETSCANHOMETIME", 15) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U16 val = CFG_NEIGHBOR_SCAN_TIMER_PERIOD_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 15)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETSCANHOMETIME<delimiter> */ |
| value = value + 16; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou16(value, 10, &val); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou16 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou16 failed range [%d - %d]", __func__, |
| CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN, |
| CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if ((val < CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN) || |
| (val > CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "scan home time value %d is out of range" |
| " (Min: %d Max: %d)", val, |
| CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN, |
| CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to change scan home time = %d", __func__, val); |
| |
| pHddCtx->cfg_ini->nNeighborScanPeriod = val; |
| sme_setNeighborScanPeriod(pHddCtx->hHal, |
| pAdapter->sessionId, val); |
| } |
| else if (strncmp(command, "GETSCANHOMETIME", 15) == 0) |
| { |
| tANI_U16 val = sme_getNeighborScanPeriod(pHddCtx->hHal, |
| pAdapter->sessionId); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| /* value is interms of msec */ |
| len = scnprintf(extra, sizeof(extra), "%s %d", |
| "GETSCANHOMETIME", val); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "SETROAMINTRABAND", 16) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 val = CFG_ROAM_INTRA_BAND_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 16)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETROAMINTRABAND<delimiter> */ |
| value = value + 17; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &val); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed range [%d - %d]", __func__, |
| CFG_ROAM_INTRA_BAND_MIN, |
| CFG_ROAM_INTRA_BAND_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if ((val < CFG_ROAM_INTRA_BAND_MIN) || |
| (val > CFG_ROAM_INTRA_BAND_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "intra band mode value %d is out of range" |
| " (Min: %d Max: %d)", val, |
| CFG_ROAM_INTRA_BAND_MIN, |
| CFG_ROAM_INTRA_BAND_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to change intra band = %d", __func__, val); |
| |
| pHddCtx->cfg_ini->nRoamIntraBand = val; |
| sme_setRoamIntraBand(pHddCtx->hHal, val); |
| } |
| else if (strncmp(command, "GETROAMINTRABAND", 16) == 0) |
| { |
| tANI_U16 val = sme_getRoamIntraBand(pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| /* value is interms of msec */ |
| len = scnprintf(extra, sizeof(extra), "%s %d", |
| "GETROAMINTRABAND", val); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "SETSCANNPROBES", 14) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 nProbes = CFG_ROAM_SCAN_N_PROBES_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 14)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETSCANNPROBES<delimiter> */ |
| value = value + 15; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &nProbes); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed range [%d - %d]", __func__, |
| CFG_ROAM_SCAN_N_PROBES_MIN, |
| CFG_ROAM_SCAN_N_PROBES_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if ((nProbes < CFG_ROAM_SCAN_N_PROBES_MIN) || |
| (nProbes > CFG_ROAM_SCAN_N_PROBES_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "NProbes value %d is out of range" |
| " (Min: %d Max: %d)", nProbes, |
| CFG_ROAM_SCAN_N_PROBES_MIN, |
| CFG_ROAM_SCAN_N_PROBES_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to Set nProbes = %d", __func__, nProbes); |
| |
| pHddCtx->cfg_ini->nProbes = nProbes; |
| sme_UpdateRoamScanNProbes(pHddCtx->hHal, pAdapter->sessionId, |
| nProbes); |
| } |
| else if (strncmp(command, "GETSCANNPROBES", 14) == 0) |
| { |
| tANI_U8 val = sme_getRoamScanNProbes(pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", command, val); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "SETSCANHOMEAWAYTIME", 19) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U16 homeAwayTime = CFG_ROAM_SCAN_HOME_AWAY_TIME_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 19)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETSCANHOMEAWAYTIME<delimiter> */ |
| /* input value is in units of msec */ |
| value = value + 20; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou16(value, 10, &homeAwayTime); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed range [%d - %d]", __func__, |
| CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN, |
| CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if ((homeAwayTime < CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN) || |
| (homeAwayTime > CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "homeAwayTime value %d is out of range" |
| " (Min: %d Max: %d)", homeAwayTime, |
| CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN, |
| CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to Set scan away time = %d", __func__, homeAwayTime); |
| if (pHddCtx->cfg_ini->nRoamScanHomeAwayTime != homeAwayTime) |
| { |
| pHddCtx->cfg_ini->nRoamScanHomeAwayTime = homeAwayTime; |
| sme_UpdateRoamScanHomeAwayTime(pHddCtx->hHal, |
| pAdapter->sessionId, |
| homeAwayTime, eANI_BOOLEAN_TRUE); |
| } |
| } |
| else if (strncmp(command, "GETSCANHOMEAWAYTIME", 19) == 0) |
| { |
| tANI_U16 val = sme_getRoamScanHomeAwayTime(pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", command, val); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "REASSOC", 7) == 0) |
| { |
| if (hdd_drv_cmd_validate(command, 7)) |
| goto exit; |
| |
| ret = hdd_parse_reassoc(pAdapter, command, priv_data.total_len); |
| } |
| else if (strncmp(command, "SETWESMODE", 10) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_BOOLEAN wesMode = CFG_ENABLE_WES_MODE_NAME_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 10)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETWESMODE<delimiter> */ |
| value = value + 11; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &wesMode); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed range [%d - %d]", __func__, |
| CFG_ENABLE_WES_MODE_NAME_MIN, |
| CFG_ENABLE_WES_MODE_NAME_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if ((wesMode < CFG_ENABLE_WES_MODE_NAME_MIN) || |
| (wesMode > CFG_ENABLE_WES_MODE_NAME_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "WES Mode value %d is out of range" |
| " (Min: %d Max: %d)", wesMode, |
| CFG_ENABLE_WES_MODE_NAME_MIN, |
| CFG_ENABLE_WES_MODE_NAME_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to Set WES Mode rssi diff = %d", __func__, wesMode); |
| |
| pHddCtx->cfg_ini->isWESModeEnabled = wesMode; |
| sme_UpdateWESMode(pHddCtx->hHal, wesMode, pAdapter->sessionId); |
| } |
| else if (strncmp(command, "GETWESMODE", 10) == 0) |
| { |
| tANI_BOOLEAN wesMode = sme_GetWESMode(pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", command, wesMode); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "SETOPPORTUNISTICRSSIDIFF", 24) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 nOpportunisticThresholdDiff = CFG_OPPORTUNISTIC_SCAN_THRESHOLD_DIFF_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 24)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETOPPORTUNISTICRSSIDIFF<delimiter> */ |
| value = value + 25; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &nOpportunisticThresholdDiff); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed.", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to Set Opportunistic Threshold diff = %d", |
| __func__, |
| nOpportunisticThresholdDiff); |
| |
| sme_SetRoamOpportunisticScanThresholdDiff(pHddCtx->hHal, |
| pAdapter->sessionId, |
| nOpportunisticThresholdDiff); |
| } |
| else if (strncmp(command, "GETOPPORTUNISTICRSSIDIFF", 24) == 0) |
| { |
| tANI_S8 val = sme_GetRoamOpportunisticScanThresholdDiff( |
| pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", command, val); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "SETROAMRESCANRSSIDIFF", 21) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 nRoamRescanRssiDiff = CFG_ROAM_RESCAN_RSSI_DIFF_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 21)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETROAMRESCANRSSIDIFF<delimiter> */ |
| value = value + 22; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &nRoamRescanRssiDiff); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed.", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to Set Roam Rescan RSSI Diff = %d", |
| __func__, |
| nRoamRescanRssiDiff); |
| sme_SetRoamRescanRssiDiff(pHddCtx->hHal, |
| pAdapter->sessionId, |
| nRoamRescanRssiDiff); |
| } |
| else if (strncmp(command, "GETROAMRESCANRSSIDIFF", 21) == 0) |
| { |
| tANI_U8 val = sme_GetRoamRescanRssiDiff(pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", command, val); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| #endif /* WLAN_FEATURE_VOWIFI_11R || FEATURE_WLAN_ESE || FEATURE_WLAN_LFR */ |
| #ifdef FEATURE_WLAN_LFR |
| else if (strncmp(command, "SETFASTROAM", 11) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 lfrMode = CFG_LFR_FEATURE_ENABLED_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 11)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETFASTROAM<delimiter> */ |
| value = value + 12; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &lfrMode); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed range [%d - %d]", __func__, |
| CFG_LFR_FEATURE_ENABLED_MIN, |
| CFG_LFR_FEATURE_ENABLED_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if ((lfrMode < CFG_LFR_FEATURE_ENABLED_MIN) || |
| (lfrMode > CFG_LFR_FEATURE_ENABLED_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "lfr mode value %d is out of range" |
| " (Min: %d Max: %d)", lfrMode, |
| CFG_LFR_FEATURE_ENABLED_MIN, |
| CFG_LFR_FEATURE_ENABLED_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to change lfr mode = %d", __func__, lfrMode); |
| |
| pHddCtx->cfg_ini->isFastRoamIniFeatureEnabled = lfrMode; |
| sme_UpdateIsFastRoamIniFeatureEnabled(pHddCtx->hHal, |
| pAdapter->sessionId, |
| lfrMode); |
| } |
| #endif |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| else if (strncmp(command, "SETFASTTRANSITION", 17) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 ft = CFG_FAST_TRANSITION_ENABLED_NAME_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 17)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETFASTROAM<delimiter> */ |
| value = value + 18; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &ft); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed range [%d - %d]", __func__, |
| CFG_FAST_TRANSITION_ENABLED_NAME_MIN, |
| CFG_FAST_TRANSITION_ENABLED_NAME_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if ((ft < CFG_FAST_TRANSITION_ENABLED_NAME_MIN) || |
| (ft > CFG_FAST_TRANSITION_ENABLED_NAME_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "ft mode value %d is out of range" |
| " (Min: %d Max: %d)", ft, |
| CFG_FAST_TRANSITION_ENABLED_NAME_MIN, |
| CFG_FAST_TRANSITION_ENABLED_NAME_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to change ft mode = %d", __func__, ft); |
| |
| pHddCtx->cfg_ini->isFastTransitionEnabled = ft; |
| sme_UpdateFastTransitionEnabled(pHddCtx->hHal, ft); |
| } |
| |
| else if (strncmp(command, "FASTREASSOC", 11) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 channel = 0; |
| tSirMacAddr targetApBssid; |
| tHalHandle hHal; |
| v_U32_t roamId = 0; |
| tCsrRoamModifyProfileFields modProfileFields; |
| #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD |
| tCsrHandoffRequest handoffInfo; |
| #endif |
| hdd_station_ctx_t *pHddStaCtx = NULL; |
| pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| hHal = WLAN_HDD_GET_HAL_CTX(pAdapter); |
| |
| /* if not associated, no need to proceed with reassoc */ |
| if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s:Not associated!",__func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| ret = hdd_parse_reassoc_command_v1_data(value, targetApBssid, |
| &channel); |
| if (ret) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to parse reassoc command data", __func__); |
| goto exit; |
| } |
| |
| /* if the target bssid is same as currently associated AP, |
| issue reassoc to same AP */ |
| if (VOS_TRUE == vos_mem_compare(targetApBssid, |
| pHddStaCtx->conn_info.bssId, sizeof(tSirMacAddr))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s:Reassoc BSSID is same as currently associated AP bssid",__func__); |
| sme_GetModifyProfileFields(hHal, pAdapter->sessionId, |
| &modProfileFields); |
| sme_RoamReassoc(hHal, pAdapter->sessionId, |
| NULL, modProfileFields, &roamId, 1); |
| ret = 0; |
| goto exit; |
| } |
| |
| /* Check channel number is a valid channel number */ |
| if(VOS_STATUS_SUCCESS != |
| wlan_hdd_validate_operation_channel(pAdapter, channel)) |
| { |
| hddLog(LOGE, FL("Invalid Channel [%d]"), channel); |
| |
| ret = -EINVAL; |
| goto exit; |
| } |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| if (pHddCtx->cfg_ini->isRoamOffloadEnabled) { |
| hdd_wma_send_fastreassoc_cmd((int)pAdapter->sessionId, targetApBssid, |
| (int) channel); |
| goto exit; |
| } |
| #endif |
| /* Proceed with reassoc */ |
| #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD |
| handoffInfo.channel = channel; |
| handoffInfo.src = FASTREASSOC; |
| vos_mem_copy(handoffInfo.bssid, targetApBssid, sizeof(tSirMacAddr)); |
| sme_HandoffRequest(pHddCtx->hHal, pAdapter->sessionId, &handoffInfo); |
| #endif |
| } |
| #endif |
| #if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD) |
| else if (strncmp(command, "CCXPLMREQ", 9) == 0) |
| { |
| tANI_U8 *value = command; |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| tpSirPlmReq pPlmRequest; |
| |
| pPlmRequest = vos_mem_malloc(sizeof(tSirPlmReq)); |
| if (NULL == pPlmRequest){ |
| ret = -ENOMEM; |
| goto exit; |
| } |
| |
| status = hdd_parse_plm_cmd(value, pPlmRequest); |
| if (eHAL_STATUS_SUCCESS != status){ |
| vos_mem_free(pPlmRequest); |
| pPlmRequest = NULL; |
| ret = -EINVAL; |
| goto exit; |
| } |
| pPlmRequest->sessionId = pAdapter->sessionId; |
| |
| status = sme_SetPlmRequest(pHddCtx->hHal, pPlmRequest); |
| if (eHAL_STATUS_SUCCESS != status) |
| { |
| vos_mem_free(pPlmRequest); |
| pPlmRequest = NULL; |
| ret = -EINVAL; |
| goto exit; |
| } |
| } |
| #endif |
| #ifdef FEATURE_WLAN_ESE |
| else if (strncmp(command, "SETCCXMODE", 10) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 eseMode = CFG_ESE_FEATURE_ENABLED_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 10)) |
| goto exit; |
| |
| /* Check if the features OKC/ESE/11R are supported simultaneously, |
| then this operation is not permitted (return FAILURE) */ |
| if (sme_getIsEseFeatureEnabled(pHddCtx->hHal) && |
| hdd_is_okc_mode_enabled(pHddCtx) && |
| sme_getIsFtFeatureEnabled(pHddCtx->hHal)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "%s: OKC/ESE/11R are supported simultaneously" |
| " hence this operation is not permitted!", __func__); |
| ret = -EPERM; |
| goto exit; |
| } |
| |
| /* Move pointer to ahead of SETCCXMODE<delimiter> */ |
| value = value + 11; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &eseMode); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed range [%d - %d]", __func__, |
| CFG_ESE_FEATURE_ENABLED_MIN, |
| CFG_ESE_FEATURE_ENABLED_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| if ((eseMode < CFG_ESE_FEATURE_ENABLED_MIN) || |
| (eseMode > CFG_ESE_FEATURE_ENABLED_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Ese mode value %d is out of range" |
| " (Min: %d Max: %d)", eseMode, |
| CFG_ESE_FEATURE_ENABLED_MIN, |
| CFG_ESE_FEATURE_ENABLED_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to change ese mode = %d", __func__, eseMode); |
| |
| pHddCtx->cfg_ini->isEseIniFeatureEnabled = eseMode; |
| sme_UpdateIsEseFeatureEnabled(pHddCtx->hHal, pAdapter->sessionId, |
| eseMode); |
| } |
| #endif |
| else if (strncmp(command, "SETROAMSCANCONTROL", 18) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_BOOLEAN roamScanControl = 0; |
| |
| if (hdd_drv_cmd_validate(command, 18)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETROAMSCANCONTROL<delimiter> */ |
| value = value + 19; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &roamScanControl); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed ", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to Set roam scan control = %d", __func__, roamScanControl); |
| |
| if (0 != roamScanControl) |
| { |
| ret = 0; /* return success but ignore param value "TRUE" */ |
| goto exit; |
| } |
| |
| sme_SetRoamScanControl(pHddCtx->hHal, |
| pAdapter->sessionId, roamScanControl); |
| } |
| #ifdef FEATURE_WLAN_OKC |
| else if (strncmp(command, "SETOKCMODE", 10) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 okcMode = CFG_OKC_FEATURE_ENABLED_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 10)) |
| goto exit; |
| |
| /* Check if the features OKC/ESE/11R are supported simultaneously, |
| then this operation is not permitted (return FAILURE) */ |
| if (sme_getIsEseFeatureEnabled(pHddCtx->hHal) && |
| hdd_is_okc_mode_enabled(pHddCtx) && |
| sme_getIsFtFeatureEnabled(pHddCtx->hHal)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "%s: OKC/ESE/11R are supported simultaneously" |
| " hence this operation is not permitted!", __func__); |
| ret = -EPERM; |
| goto exit; |
| } |
| |
| /* Move pointer to ahead of SETOKCMODE<delimiter> */ |
| value = value + 11; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &okcMode); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, then also |
| kstrtou8 fails */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed range [%d - %d]", __func__, |
| CFG_OKC_FEATURE_ENABLED_MIN, |
| CFG_OKC_FEATURE_ENABLED_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if ((okcMode < CFG_OKC_FEATURE_ENABLED_MIN) || |
| (okcMode > CFG_OKC_FEATURE_ENABLED_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Okc mode value %d is out of range" |
| " (Min: %d Max: %d)", okcMode, |
| CFG_OKC_FEATURE_ENABLED_MIN, |
| CFG_OKC_FEATURE_ENABLED_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to change okc mode = %d", __func__, okcMode); |
| pHddCtx->cfg_ini->isOkcIniFeatureEnabled = okcMode; |
| } |
| #endif /* FEATURE_WLAN_OKC */ |
| else if (strncmp(command, "BTCOEXMODE", 10) == 0 ) |
| { |
| char *bcMode; |
| int ret; |
| |
| if (hdd_drv_cmd_validate(command, 10)) |
| goto exit; |
| |
| bcMode = command + 11; |
| if ('1' == *bcMode) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG, |
| FL("BTCOEXMODE %d"), *bcMode); |
| pHddCtx->btCoexModeSet = TRUE; |
| ret = wlan_hdd_scan_abort(pAdapter); |
| if (ret < 0) { |
| hddLog(LOGE, |
| FL("Failed to abort existing scan status:%d"), ret); |
| } |
| } |
| else if ('2' == *bcMode) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG, |
| FL("BTCOEXMODE %d"), *bcMode); |
| pHddCtx->btCoexModeSet = FALSE; |
| } |
| } |
| else if (strncmp(command, "GETROAMSCANCONTROL", 18) == 0) |
| { |
| tANI_BOOLEAN roamScanControl = sme_GetRoamScanControl(pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", |
| command, roamScanControl); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| #ifdef WLAN_FEATURE_PACKET_FILTERING |
| else if (strncmp(command, "ENABLE_PKTFILTER_IPV6", 21) == 0) |
| { |
| tANI_U8 filterType = 0; |
| tANI_U8 *value = command; |
| |
| if (hdd_drv_cmd_validate(command, 21)) |
| goto exit; |
| |
| /* Move pointer to ahead of ENABLE_PKTFILTER_IPV6<delimiter> */ |
| value = value + 22; |
| |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &filterType); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, |
| * then also kstrtou8 fails |
| */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed range ", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if (filterType != 0 && filterType != 1) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Accepted Values are 0 and 1 ", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| wlan_hdd_setIPv6Filter(WLAN_HDD_GET_CTX(pAdapter), filterType, |
| pAdapter->sessionId); |
| } |
| #endif |
| else if (strncmp(command, "BTCOEXMODE", 10) == 0 ) |
| { |
| char *bcMode; |
| |
| if (hdd_drv_cmd_validate(command, 10)) |
| goto exit; |
| |
| bcMode = command + 11; |
| if ('1' == *bcMode) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG, |
| FL("BTCOEXMODE %d"), *bcMode); |
| pHddCtx->btCoexModeSet = TRUE; |
| } |
| else if ('2' == *bcMode) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG, |
| FL("BTCOEXMODE %d"), *bcMode); |
| pHddCtx->btCoexModeSet = FALSE; |
| } |
| } |
| else if (strncmp(command, "SCAN-ACTIVE", 11) == 0) |
| { |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| hddLog(LOG1, FL("making default scan to ACTIVE")); |
| pHddCtx->ioctl_scan_mode = eSIR_ACTIVE_SCAN; |
| } |
| else if (strncmp(command, "SCAN-PASSIVE", 12) == 0) |
| { |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| hddLog(LOG1, FL("making default scan to PASSIVE")); |
| pHddCtx->ioctl_scan_mode = eSIR_PASSIVE_SCAN; |
| } |
| else if (strncmp(command, "GETDWELLTIME", 12) == 0) |
| { |
| hdd_config_t *pCfg = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini; |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| memset(extra, 0, sizeof(extra)); |
| ret = hdd_get_dwell_time(pCfg, command, extra, sizeof(extra), &len); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (ret != 0 || copy_to_user(priv_data.buf, &extra, len)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| ret = len; |
| } |
| else if (strncmp(command, "SETDWELLTIME", 12) == 0) |
| { |
| ret = hdd_set_dwell_time(pAdapter, command); |
| } |
| else if ( strncasecmp(command, "MIRACAST", 8) == 0 ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received MIRACAST command", __func__); |
| |
| if (hdd_drv_cmd_validate(command, 8)) |
| goto exit; |
| |
| ret = hdd_set_miracast_mode(pAdapter, command); |
| } |
| else if ((strncasecmp(command, "SETIBSSBEACONOUIDATA", 20) == 0) && |
| (WLAN_HDD_IBSS == pAdapter->device_mode)) |
| { |
| int i = 0; |
| uint8_t *ibss_ie; |
| int32_t command_len; |
| int32_t oui_length = 0; |
| uint8_t *value = command; |
| uint32_t ibss_ie_length; |
| tSirModifyIE ibssModifyIE; |
| tCsrRoamProfile *pRoamProfile; |
| hdd_wext_state_t *pWextState = |
| WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter); |
| |
| int status; |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: received command %s", __func__, ((char *) value)); |
| |
| /* validate argument of command */ |
| if (strlen(value) <= 21) { |
| hddLog(LOGE, |
| FL("No arguements in command length %zu"), strlen(value)); |
| ret = -EFAULT; |
| goto exit; |
| } |
| |
| if (hdd_drv_cmd_validate(command, 20)) |
| goto exit; |
| |
| /* moving to arguments of commands */ |
| value = value + 21; |
| command_len = strlen(value); |
| |
| /* oui_data can't be less than 3 bytes */ |
| if (command_len <= (2 * WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH)) { |
| hddLog(LOGE, |
| FL("Invalid SETIBSSBEACONOUIDATA command length %d"), |
| command_len); |
| ret = -EFAULT; |
| goto exit; |
| } |
| |
| ibss_ie = vos_mem_malloc(command_len); |
| if (!ibss_ie) { |
| hddLog(LOGE, |
| FL("Could not allocate memory for command length %d"), |
| command_len); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| vos_mem_zero(ibss_ie, command_len); |
| |
| ibss_ie_length = hdd_parse_set_ibss_oui_data_command(value, ibss_ie, |
| &oui_length, command_len); |
| if (ibss_ie_length < (2 * WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH)) { |
| hddLog(LOGE, FL("Could not parse command %s return length %d"), |
| value, ibss_ie_length); |
| ret = -EFAULT; |
| vos_mem_free(ibss_ie); |
| goto exit; |
| } |
| |
| pRoamProfile = &pWextState->roamProfile; |
| |
| vos_mem_copy(ibssModifyIE.bssid, |
| pRoamProfile->BSSIDs.bssid, |
| VOS_MAC_ADDR_SIZE); |
| |
| ibssModifyIE.smeSessionId = pAdapter->sessionId; |
| ibssModifyIE.notify = TRUE; |
| ibssModifyIE.ieID = IE_EID_VENDOR; |
| ibssModifyIE.ieIDLen = ibss_ie_length; |
| ibssModifyIE.ieBufferlength = ibss_ie_length; |
| ibssModifyIE.pIEBuffer = ibss_ie; |
| ibssModifyIE.oui_length = oui_length; |
| |
| hddLog(LOGW, FL("ibss_ie length %d oui_length %d ibss_ie:"), |
| ibss_ie_length, oui_length); |
| while (i < ibssModifyIE.ieBufferlength) { |
| hddLog(LOGW, FL("0x%x"), ibss_ie[i++]); |
| } |
| |
| /* Probe Bcn modification */ |
| sme_ModifyAddIE(WLAN_HDD_GET_HAL_CTX(pAdapter), |
| &ibssModifyIE, |
| eUPDATE_IE_PROBE_BCN); |
| |
| /* Populating probe resp frame */ |
| sme_ModifyAddIE(WLAN_HDD_GET_HAL_CTX(pAdapter), |
| &ibssModifyIE, |
| eUPDATE_IE_PROBE_RESP); |
| /* free ibss_ie */ |
| vos_mem_free(ibss_ie); |
| |
| status = sme_SendCesiumEnableInd( (tHalHandle)(pHddCtx->hHal), |
| pAdapter->sessionId ); |
| if (VOS_STATUS_SUCCESS != status) { |
| hddLog(LOGE, FL("cesium enable indication failed %d"), |
| status); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| } |
| else if (strncasecmp(command, "SETRMCENABLE", 12) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 ucRmcEnable = 0; |
| int status; |
| |
| if ((WLAN_HDD_IBSS != pAdapter->device_mode) && |
| (WLAN_HDD_SOFTAP != pAdapter->device_mode)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Received SETRMCENABLE command in invalid mode %d" |
| "SETRMCENABLE command is only allowed in IBSS or SOFTAP mode", |
| pAdapter->device_mode); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| status = hdd_parse_setrmcenable_command(value, &ucRmcEnable); |
| if (status) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Invalid SETRMCENABLE command "); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: ucRmcEnable %d ", __func__, ucRmcEnable); |
| |
| if (TRUE == ucRmcEnable) { |
| status = sme_enable_rmc((tHalHandle)(pHddCtx->hHal), |
| pAdapter->sessionId); |
| } |
| else if(FALSE == ucRmcEnable) { |
| status = sme_disable_rmc((tHalHandle)(pHddCtx->hHal), |
| pAdapter->sessionId); |
| } |
| else |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Invalid SETRMCENABLE command %d", ucRmcEnable); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if (VOS_STATUS_SUCCESS != status) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: SETRMC %d failed status %d", __func__, ucRmcEnable, |
| status); |
| ret = -EINVAL; |
| goto exit; |
| } |
| } |
| else if (strncasecmp(command, "SETRMCACTIONPERIOD", 18) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U32 uActionPeriod = 0; |
| int status; |
| |
| if ((WLAN_HDD_IBSS != pAdapter->device_mode) && |
| (WLAN_HDD_SOFTAP != pAdapter->device_mode)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Received SETRMC command in invalid mode %d" |
| "SETRMC command is only allowed in IBSS or SOFTAP mode", |
| pAdapter->device_mode); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| status = hdd_parse_setrmcactionperiod_command(value, &uActionPeriod); |
| if (status) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Invalid SETRMCACTIONPERIOD command "); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: uActionPeriod %d ", __func__, uActionPeriod); |
| |
| if (ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_RMC_ACTION_PERIOD_FREQUENCY, |
| uActionPeriod, NULL, eANI_BOOLEAN_FALSE)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Could not set SETRMCACTIONPERIOD %d", __func__, uActionPeriod); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| status = sme_SendRmcActionPeriod( (tHalHandle)(pHddCtx->hHal), |
| pAdapter->sessionId ); |
| if (VOS_STATUS_SUCCESS != status) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Could not send cesium enable indication %d", status); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| } |
| else if (strncasecmp(command, "GETIBSSPEERINFOALL", 18) == 0) |
| { |
| /* Peer Info All Command */ |
| int status = eHAL_STATUS_SUCCESS; |
| hdd_station_ctx_t *pHddStaCtx = NULL; |
| char *extra = NULL; |
| int idx = 0, length = 0; |
| uint8_t mac_addr[VOS_MAC_ADDR_SIZE]; |
| v_U32_t numOfBytestoPrint = 0; |
| |
| if (WLAN_HDD_IBSS == pAdapter->device_mode) |
| { |
| pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: pAdapter is not valid for this device mode", |
| __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received GETIBSSPEERINFOALL Command", __func__); |
| |
| /* Handle the command */ |
| status = hdd_cfg80211_get_ibss_peer_info_all(pAdapter); |
| if (VOS_STATUS_SUCCESS == status) |
| { |
| /* The variable extra needed to be allocated on the heap since |
| * amount of memory required to copy the data for 32 devices |
| * exceeds the size of 1024 bytes of default stack size. On |
| * 64 bit devices, the default max stack size of 2048 bytes |
| */ |
| extra = vos_mem_malloc(WLAN_MAX_BUF_SIZE); |
| |
| if (NULL == extra) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s:kmalloc failed", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| /* Copy number of stations */ |
| length = scnprintf( extra, WLAN_MAX_BUF_SIZE, "%d ", |
| pHddStaCtx->ibss_peer_info.numPeers); |
| numOfBytestoPrint = length; |
| for (idx = 0; idx < pHddStaCtx->ibss_peer_info.numPeers; idx++) { |
| int8_t rssi; |
| uint32_t tx_rate; |
| |
| vos_mem_copy(mac_addr, |
| pHddStaCtx->ibss_peer_info.peerInfoParams[idx].mac_addr, |
| sizeof(mac_addr)); |
| |
| tx_rate = pHddStaCtx->ibss_peer_info.peerInfoParams[idx].txRate; |
| /* Only lower 3 bytes are rate info. Mask of the MSByte */ |
| tx_rate &= 0x00FFFFFF; |
| |
| rssi = pHddStaCtx->ibss_peer_info.peerInfoParams[idx].rssi; |
| |
| length += scnprintf((extra + length), |
| WLAN_MAX_BUF_SIZE - length, |
| "%02x:%02x:%02x:%02x:%02x:%02x %d %d ", |
| mac_addr[0], mac_addr[1], mac_addr[2], |
| mac_addr[3], mac_addr[4], mac_addr[5], |
| tx_rate, rssi); |
| /* |
| * VOS_TRACE() macro has limitation of 512 bytes for the print |
| * buffer. Hence printing the data in two chunks. The first |
| * chunk will have the data for 16 devices and the second |
| * chunk will have the rest. |
| */ |
| if (idx < NUM_OF_STA_DATA_TO_PRINT) |
| numOfBytestoPrint = length; |
| } |
| |
| /* |
| * Copy the data back into buffer, if the data to copy is |
| * more than 512 bytes than we will split the data and do |
| * it in two shots |
| */ |
| if (copy_to_user(priv_data.buf, extra, numOfBytestoPrint)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Copy into user data buffer failed ", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| /* This overwrites the last space, which we already copied */ |
| extra[numOfBytestoPrint - 1] = '\0'; |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED, |
| "%s", extra); |
| |
| if (length > numOfBytestoPrint) |
| { |
| if (copy_to_user(priv_data.buf + numOfBytestoPrint, |
| extra + numOfBytestoPrint, |
| length - numOfBytestoPrint + 1)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Copy into user data buffer failed ", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED, |
| "%s", &extra[numOfBytestoPrint]); |
| } |
| |
| /* Free temporary buffer */ |
| vos_mem_free(extra); |
| } |
| |
| else |
| { |
| /* Command failed, log error */ |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: GETIBSSPEERINFOALL command failed with status code %d", |
| __func__, status); |
| ret = -EINVAL; |
| goto exit; |
| } |
| ret = 0; |
| } |
| else if(strncasecmp(command, "GETIBSSPEERINFO", 15) == 0) |
| { |
| /* Peer Info <Peer Addr> command */ |
| tANI_U8 *value = command; |
| VOS_STATUS status; |
| hdd_station_ctx_t *pHddStaCtx = NULL; |
| char extra[128] = { 0 }; |
| v_U32_t length = 0; |
| v_U8_t staIdx = 0; |
| v_MACADDR_t peerMacAddr; |
| |
| if (WLAN_HDD_IBSS == pAdapter->device_mode) |
| { |
| pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: pAdapter is not valid for this device mode", |
| __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| /* if there are no peers, no need to continue with the command */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received GETIBSSPEERINFO Command", __func__); |
| |
| if (eConnectionState_IbssConnected != pHddStaCtx->conn_info.connState) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s:No IBSS Peers coalesced", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| /* Parse the incoming command buffer */ |
| status = hdd_parse_get_ibss_peer_info(value, &peerMacAddr); |
| if (VOS_STATUS_SUCCESS != status) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Invalid GETIBSSPEERINFO command", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| /* Get station index for the peer mac address and sanitize it */ |
| hdd_get_peer_sta_id(pHddStaCtx, &peerMacAddr, &staIdx); |
| |
| if (staIdx > HDD_MAX_NUM_IBSS_STA) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Invalid StaIdx %d returned", __func__, staIdx); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| /* Handle the command */ |
| status = hdd_cfg80211_get_ibss_peer_info(pAdapter, staIdx); |
| if (VOS_STATUS_SUCCESS == status) |
| { |
| v_U32_t txRate = pHddStaCtx->ibss_peer_info.peerInfoParams[0].txRate; |
| /* Only lower 3 bytes are rate info. Mask of the MSByte */ |
| txRate &= 0x00FFFFFF; |
| |
| length = scnprintf( extra, sizeof(extra), "%d %d", (int)txRate, |
| (int)pHddStaCtx->ibss_peer_info.peerInfoParams[0].rssi); |
| |
| /* Copy the data back into buffer */ |
| if (copy_to_user(priv_data.buf, &extra, length+ 1)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: copy data to user buffer failed GETIBSSPEERINFO command", |
| __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else |
| { |
| /* Command failed, log error */ |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: GETIBSSPEERINFO command failed with status code %d", |
| __func__, status); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| /* Success ! */ |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED, |
| "%s", priv_data.buf); |
| ret = 0; |
| } |
| else if (strncmp(command, "SETRMCTXRATE", 12) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U32 uRate = 0; |
| tTxrateinfoflags txFlags = 0; |
| tSirRateUpdateInd rateUpdateParams = {0}; |
| int status; |
| hdd_config_t *pConfig = pHddCtx->cfg_ini; |
| |
| if ((WLAN_HDD_IBSS != pAdapter->device_mode) && |
| (WLAN_HDD_SOFTAP != pAdapter->device_mode)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Received SETRMCTXRATE command in invalid mode %d" |
| "SETRMC command is only allowed in IBSS or SOFTAP mode", |
| pAdapter->device_mode); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| status = hdd_parse_setrmcrate_command(value, &uRate, &txFlags); |
| if (status) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Invalid SETRMCTXRATE command "); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: uRate %d ", __func__, uRate); |
| |
| /* -1 implies ignore this param */ |
| rateUpdateParams.ucastDataRate = -1; |
| |
| /* |
| * Fill the user specified RMC rate param |
| * and the derived tx flags. |
| */ |
| rateUpdateParams.nss = (pConfig->enable2x2 == 0) ? 0 : 1; |
| rateUpdateParams.reliableMcastDataRate = uRate; |
| rateUpdateParams.reliableMcastDataRateTxFlag = txFlags; |
| rateUpdateParams.dev_mode = pAdapter->device_mode; |
| rateUpdateParams.bcastDataRate = -1; |
| memcpy(rateUpdateParams.bssid, pAdapter->macAddressCurrent.bytes, |
| sizeof(rateUpdateParams.bssid)); |
| status = sme_SendRateUpdateInd((tHalHandle)(pHddCtx->hHal), |
| &rateUpdateParams); |
| } |
| else if (strncasecmp(command, "SETIBSSTXFAILEVENT", 18) == 0) |
| { |
| char *value; |
| tANI_U8 tx_fail_count = 0; |
| tANI_U16 pid = 0; |
| |
| value = command; |
| |
| ret = hdd_ParseIBSSTXFailEventParams(value, &tx_fail_count, &pid); |
| |
| if (0 != ret) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: Failed to parse SETIBSSTXFAILEVENT arguments", |
| __func__); |
| goto exit; |
| } |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: tx_fail_cnt=%hhu, pid=%hu", |
| __func__, tx_fail_count, pid); |
| |
| if (0 == tx_fail_count) |
| { |
| // Disable TX Fail Indication |
| if (eHAL_STATUS_SUCCESS == |
| sme_TXFailMonitorStartStopInd(pHddCtx->hHal, |
| tx_fail_count, |
| NULL)) |
| { |
| cesium_pid = 0; |
| } |
| else |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to disable TX Fail Event ", __func__); |
| ret = -EINVAL; |
| } |
| } |
| else |
| { |
| if (eHAL_STATUS_SUCCESS == |
| sme_TXFailMonitorStartStopInd(pHddCtx->hHal, |
| tx_fail_count, |
| (void*)hdd_tx_fail_ind_callback)) |
| { |
| cesium_pid = pid; |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Registered Cesium pid %u", __func__, |
| cesium_pid); |
| } |
| else |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to enable TX Fail Monitoring", __func__); |
| ret = -EINVAL; |
| } |
| } |
| } |
| #if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD) |
| else if (strncmp(command, "SETCCXROAMSCANCHANNELS", 22) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0}; |
| tANI_U8 numChannels = 0; |
| eHalStatus status; |
| ret = hdd_parse_channellist(value, ChannelList, &numChannels); |
| if (ret) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to parse channel list information", __func__); |
| goto exit; |
| } |
| if (numChannels > WNI_CFG_VALID_CHANNEL_LIST_LEN) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, |
| VOS_TRACE_LEVEL_ERROR, |
| "%s: number of channels (%d) supported exceeded max (%d)", |
| __func__, |
| numChannels, |
| WNI_CFG_VALID_CHANNEL_LIST_LEN); |
| ret = -EINVAL; |
| goto exit; |
| } |
| status = sme_SetEseRoamScanChannelList(pHddCtx->hHal, |
| pAdapter->sessionId, |
| ChannelList, |
| numChannels); |
| if (eHAL_STATUS_SUCCESS != status) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to update channel list information", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "GETTSMSTATS", 11) == 0) |
| { |
| tANI_U8 *value = command; |
| char extra[128] = {0}; |
| int len = 0; |
| tANI_U8 tid = 0; |
| hdd_station_ctx_t *pHddStaCtx = NULL; |
| tAniTrafStrmMetrics tsmMetrics; |
| |
| if (hdd_drv_cmd_validate(command, 11)) |
| goto exit; |
| |
| pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| /* if not associated, return error */ |
| if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, |
| VOS_TRACE_LEVEL_ERROR, |
| "%s:Not associated!", |
| __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| /* Move pointer to ahead of GETTSMSTATS<delimiter> */ |
| value = value + 12; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &tid); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of datatype, |
| * then also |
| * kstrtou8 fails |
| */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed range [%d - %d]", __func__, |
| TID_MIN_VALUE, |
| TID_MAX_VALUE); |
| ret = -EINVAL; |
| goto exit; |
| } |
| if ((tid < TID_MIN_VALUE) || (tid > TID_MAX_VALUE)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "tid value %d is out of range" |
| " (Min: %d Max: %d)", tid, |
| TID_MIN_VALUE, |
| TID_MAX_VALUE); |
| ret = -EINVAL; |
| goto exit; |
| } |
| VOS_TRACE( VOS_MODULE_ID_HDD, |
| VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to get tsm stats tid = %d", |
| __func__, |
| tid); |
| if (VOS_STATUS_SUCCESS != hdd_get_tsm_stats(pAdapter, tid, &tsmMetrics)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to get tsm stats", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| VOS_TRACE( VOS_MODULE_ID_HDD, |
| VOS_TRACE_LEVEL_INFO, |
| "UplinkPktQueueDly(%d)" |
| "UplinkPktQueueDlyHist[0](%d)" |
| "UplinkPktQueueDlyHist[1](%d)" |
| "UplinkPktQueueDlyHist[2](%d)" |
| "UplinkPktQueueDlyHist[3](%d)" |
| "UplinkPktTxDly(%u)" |
| "UplinkPktLoss(%d)" |
| "UplinkPktCount(%d)" |
| "RoamingCount(%d)" |
| "RoamingDly(%d)", |
| tsmMetrics.UplinkPktQueueDly, |
| tsmMetrics.UplinkPktQueueDlyHist[0], |
| tsmMetrics.UplinkPktQueueDlyHist[1], |
| tsmMetrics.UplinkPktQueueDlyHist[2], |
| tsmMetrics.UplinkPktQueueDlyHist[3], |
| tsmMetrics.UplinkPktTxDly, |
| tsmMetrics.UplinkPktLoss, |
| tsmMetrics.UplinkPktCount, |
| tsmMetrics.RoamingCount, |
| tsmMetrics.RoamingDly); |
| /* Output TSM stats is of the format |
| * GETTSMSTATS [PktQueueDly] |
| * [PktQueueDlyHist[0]]:[PktQueueDlyHist[1]] ...[RoamingDly] |
| * eg., GETTSMSTATS 10 1:0:0:161 20 1 17 8 39800 */ |
| len = scnprintf(extra, |
| sizeof(extra), |
| "%s %d %d:%d:%d:%d %u %d %d %d %d", |
| command, |
| tsmMetrics.UplinkPktQueueDly, |
| tsmMetrics.UplinkPktQueueDlyHist[0], |
| tsmMetrics.UplinkPktQueueDlyHist[1], |
| tsmMetrics.UplinkPktQueueDlyHist[2], |
| tsmMetrics.UplinkPktQueueDlyHist[3], |
| tsmMetrics.UplinkPktTxDly, |
| tsmMetrics.UplinkPktLoss, |
| tsmMetrics.UplinkPktCount, |
| tsmMetrics.RoamingCount, |
| tsmMetrics.RoamingDly); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "SETCCKMIE", 9) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 *cckmIe = NULL; |
| tANI_U8 cckmIeLen = 0; |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| status = hdd_parse_get_cckm_ie(value, &cckmIe, &cckmIeLen); |
| if (eHAL_STATUS_SUCCESS != status) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to parse cckm ie data", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| if (cckmIeLen > DOT11F_IE_RSN_MAX_LEN) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: CCKM Ie input length is more than max[%d]", __func__, |
| DOT11F_IE_RSN_MAX_LEN); |
| if (NULL != cckmIe) |
| { |
| vos_mem_free(cckmIe); |
| cckmIe = NULL; |
| } |
| ret = -EINVAL; |
| goto exit; |
| } |
| sme_SetCCKMIe(pHddCtx->hHal, pAdapter->sessionId, cckmIe, cckmIeLen); |
| if (NULL != cckmIe) |
| { |
| vos_mem_free(cckmIe); |
| cckmIe = NULL; |
| } |
| } |
| else if (strncmp(command, "CCXBEACONREQ", 12) == 0) |
| { |
| tANI_U8 *value = command; |
| tCsrEseBeaconReq eseBcnReq; |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| |
| status = hdd_parse_ese_beacon_req(value, &eseBcnReq); |
| if (eHAL_STATUS_SUCCESS != status) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to parse ese beacon req", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if (!hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))) { |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("Not associated")); |
| hdd_indicateEseBcnReportNoResults (pAdapter, |
| eseBcnReq.bcnReq[0].measurementToken, |
| 0x02, //BIT(1) set for measurement done |
| 0); // no BSS |
| goto exit; |
| } |
| |
| status = sme_SetEseBeaconRequest(pHddCtx->hHal, |
| pAdapter->sessionId, |
| &eseBcnReq); |
| |
| if (eHAL_STATUS_RESOURCES == status) { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("sme_SetEseBeaconRequest failed (%d)," |
| " a request already in progress"), status); |
| ret = -EBUSY; |
| goto exit; |
| } else if (eHAL_STATUS_SUCCESS != status) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, |
| VOS_TRACE_LEVEL_ERROR, |
| "%s: sme_SetEseBeaconRequest failed (%d)", |
| __func__, |
| status); |
| ret = -EINVAL; |
| goto exit; |
| } |
| } |
| #endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */ |
| else if (strncmp(command, "SETMCRATE", 9) == 0) |
| { |
| tANI_U8 *value = command; |
| int targetRate; |
| |
| if (hdd_drv_cmd_validate(command, 9)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETMCRATE<delimiter> */ |
| /* input value is in units of hundred kbps */ |
| value = value + 10; |
| /* Convert the value from ascii to integer, decimal base */ |
| ret = kstrtouint(value, 10, &targetRate); |
| ret = wlan_hdd_set_mc_rate(pAdapter, targetRate); |
| } |
| else if (strncmp(command, "MAXTXPOWER", 10) == 0) |
| { |
| int status; |
| int txPower; |
| VOS_STATUS vosStatus; |
| eHalStatus smeStatus; |
| tANI_U8 *value = command; |
| hdd_adapter_t *pAdapter; |
| tSirMacAddr bssid = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; |
| tSirMacAddr selfMac = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| |
| status = hdd_parse_setmaxtxpower_command(value, &txPower); |
| if (status) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Invalid MAXTXPOWER command "); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| vosStatus = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == vosStatus ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| /* Assign correct self MAC address */ |
| vos_mem_copy(bssid, pAdapter->macAddressCurrent.bytes, |
| VOS_MAC_ADDR_SIZE); |
| vos_mem_copy(selfMac, pAdapter->macAddressCurrent.bytes, |
| VOS_MAC_ADDR_SIZE); |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, "Device mode %d max tx power %d" |
| " selfMac: " MAC_ADDRESS_STR " bssId: " MAC_ADDRESS_STR " ", |
| pAdapter->device_mode, txPower, MAC_ADDR_ARRAY(selfMac), |
| MAC_ADDR_ARRAY(bssid)); |
| smeStatus = sme_SetMaxTxPower((tHalHandle)(pHddCtx->hHal), bssid, |
| selfMac, txPower); |
| if (eHAL_STATUS_SUCCESS != status) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s:Set max tx power failed", |
| __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: Set max tx power success", |
| __func__); |
| vosStatus = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext); |
| pAdapterNode = pNext; |
| } |
| } |
| else if (strncmp(command, "SETDFSSCANMODE", 14) == 0) |
| { |
| tANI_U8 *value = command; |
| tANI_U8 dfsScanMode = CFG_ROAMING_DFS_CHANNEL_DEFAULT; |
| |
| if (hdd_drv_cmd_validate(command, 14)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETDFSSCANMODE<delimiter> */ |
| value = value + 15; |
| /* Convert the value from ascii to integer */ |
| ret = kstrtou8(value, 10, &dfsScanMode); |
| if (ret < 0) |
| { |
| /* If the input value is greater than max value of |
| * datatype, then also kstrtou8 fails |
| */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: kstrtou8 failed range [%d - %d]", __func__, |
| CFG_ROAMING_DFS_CHANNEL_MIN, |
| CFG_ROAMING_DFS_CHANNEL_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if ((dfsScanMode < CFG_ROAMING_DFS_CHANNEL_MIN) || |
| (dfsScanMode > CFG_ROAMING_DFS_CHANNEL_MAX)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "dfsScanMode value %d is out of range" |
| " (Min: %d Max: %d)", dfsScanMode, |
| CFG_ROAMING_DFS_CHANNEL_MIN, |
| CFG_ROAMING_DFS_CHANNEL_MAX); |
| ret = -EINVAL; |
| goto exit; |
| } |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Received Command to Set DFS Scan Mode = %d", |
| __func__, dfsScanMode); |
| |
| /* When DFS scanning is disabled, the DFS channels need to be |
| * removed from the operation of device. |
| */ |
| ret = wlan_hdd_disable_dfs_chan_scan(pHddCtx, pAdapter, |
| (dfsScanMode == CFG_ROAMING_DFS_CHANNEL_DISABLED)); |
| if (ret < 0) { |
| /* Some conditions prevented it from disabling DFS channels |
| */ |
| hddLog(LOGE, |
| FL("disable/enable DFS channel request was denied")); |
| goto exit; |
| } |
| |
| pHddCtx->cfg_ini->allowDFSChannelRoam = dfsScanMode; |
| sme_UpdateDFSScanMode(pHddCtx->hHal, pAdapter->sessionId, |
| dfsScanMode); |
| } |
| else if (strncmp(command, "GETDFSSCANMODE", 14) == 0) |
| { |
| tANI_U8 dfsScanMode = sme_GetDFSScanMode(pHddCtx->hHal); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", command, dfsScanMode); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| else if (strncmp(command, "GETLINKSTATUS", 13) == 0) { |
| int value = wlan_hdd_get_link_status(pAdapter); |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", command, value); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: failed to copy data to user buffer", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } |
| #ifdef WLAN_FEATURE_EXTWOW_SUPPORT |
| else if (strncmp(command, "ENABLEEXTWOW", 12) == 0) { |
| |
| tANI_U8 *value = command; |
| int set_value; |
| |
| if (hdd_drv_cmd_validate(command, 12)) |
| goto exit; |
| |
| /* Move pointer to ahead of ENABLEEXTWOW*/ |
| value += 12; |
| if (!(sscanf(value, "%d", &set_value))) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| FL("No input identified")); |
| ret = -EINVAL; |
| goto exit; |
| } |
| ret = hdd_enable_ext_wow_parser(pAdapter, |
| pAdapter->sessionId, set_value); |
| |
| } else if (strncmp(command, "SETAPP1PARAMS", 13) == 0) { |
| tANI_U8 *value = command; |
| |
| if (hdd_drv_cmd_validate(command, 13)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETAPP1PARAMS*/ |
| value += 13; |
| ret = hdd_set_app_type1_parser(pAdapter, |
| value, strlen(value)); |
| if (ret >= 0) |
| pHddCtx->is_extwow_app_type1_param_set = TRUE; |
| |
| } else if (strncmp(command, "SETAPP2PARAMS", 13) == 0) { |
| tANI_U8 *value = command; |
| |
| if (hdd_drv_cmd_validate(command, 13)) |
| goto exit; |
| |
| /* Move pointer to ahead of SETAPP2PARAMS*/ |
| value += 13; |
| ret = hdd_set_app_type2_parser(pAdapter, |
| value, strlen(value)); |
| if (ret >= 0) |
| pHddCtx->is_extwow_app_type2_param_set = TRUE; |
| } |
| #endif |
| #ifdef FEATURE_WLAN_TDLS |
| else if (strncmp(command, "TDLSSECONDARYCHANNELOFFSET", 26) == 0) { |
| tANI_U8 *value = command; |
| int set_value; |
| |
| if (hdd_drv_cmd_validate(command, 26)) |
| goto exit; |
| |
| /* Move pointer to point the string */ |
| value += 26; |
| if (!(sscanf(value, "%d", &set_value))) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| FL("No input identified")); |
| ret = -EINVAL; |
| goto exit; |
| } |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| FL("Tdls offchannel offset:%d"), |
| set_value); |
| ret = hdd_set_tdls_secoffchanneloffset(pHddCtx, set_value); |
| } else if (strncmp(command, "TDLSOFFCHANNELMODE", 18) == 0) { |
| tANI_U8 *value = command; |
| int set_value; |
| |
| if (hdd_drv_cmd_validate(command, 18)) |
| goto exit; |
| |
| /* Move pointer to point the string */ |
| value += 18; |
| if (!(sscanf(value, "%d", &set_value))) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| FL("No input identified")); |
| ret = -EINVAL; |
| goto exit; |
| } |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| FL("Tdls offchannel mode:%d"), |
| set_value); |
| ret = hdd_set_tdls_offchannelmode(pAdapter, set_value); |
| } else if (strncmp(command, "TDLSOFFCHANNEL", 14) == 0) { |
| tANI_U8 *value = command; |
| int set_value; |
| |
| if (hdd_drv_cmd_validate(command, 14)) |
| goto exit; |
| |
| /* Move pointer to point the string */ |
| value += 14; |
| ret = sscanf(value, "%d", &set_value); |
| if (ret != 1) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Wrong value is given for hdd_set_tdls_offchannel"); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if (VOS_IS_DFS_CH(set_value)) { |
| hddLog(LOGE, |
| FL("DFS channel %d is passed for hdd_set_tdls_offchannel"), |
| set_value); |
| ret = -EINVAL; |
| goto exit; |
| } |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| FL("Tdls offchannel num: %d"), set_value); |
| ret = hdd_set_tdls_offchannel(pHddCtx, set_value); |
| } else if (strncmp(command, "TDLSSCAN", 8) == 0) { |
| uint8_t *value = command; |
| int set_value; |
| |
| if (hdd_drv_cmd_validate(command, 8)) |
| goto exit; |
| |
| /* Move pointer to point the string */ |
| value += 8; |
| ret = sscanf(value, "%d", &set_value); |
| if (ret != 1) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "Wrong value is given for tdls_scan_type"); |
| ret = -EINVAL; |
| goto exit; |
| } |
| hddLog(LOG1, FL("Tdls scan type val: %d"), |
| set_value); |
| ret = hdd_set_tdls_scan_type(pHddCtx, set_value); |
| } |
| #endif |
| else if (strncasecmp(command, "RSSI", 4) == 0) { |
| v_S7_t s7Rssi = 0; |
| char extra[32]; |
| tANI_U8 len = 0; |
| |
| wlan_hdd_get_rssi(pAdapter, &s7Rssi); |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", command, s7Rssi); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| hddLog(LOGE, FL("Failed to copy data to user buffer")); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } else if (strncasecmp(command, "LINKSPEED", 9) == 0) { |
| uint32_t link_speed = 0; |
| char extra[32]; |
| uint8_t len = 0; |
| |
| ret = wlan_hdd_get_link_speed(pAdapter, &link_speed); |
| if (0 != ret) |
| goto exit; |
| |
| len = scnprintf(extra, sizeof(extra), "%s %d", command, link_speed); |
| len = VOS_MIN(priv_data.total_len, len + 1); |
| if (copy_to_user(priv_data.buf, &extra, len)) { |
| hddLog(LOGE, FL("Failed to copy data to user buffer")); |
| ret = -EFAULT; |
| goto exit; |
| } |
| } else if (strncasecmp(command, "SET_FCC_CHANNEL", 15) == 0) { |
| /* |
| * this command wld be called by user-space when it detects WLAN |
| * ON after airplane mode is set. When APM is set, WLAN turns off. |
| * But it can be turned back on. Otherwise; when APM is turned back |
| * off, WLAN wld turn back on. So at that point the command is |
| * expected to come down. 0 means disable, 1 means enable. The |
| * constraint is removed when parameter 1 is set or different |
| * country code is set |
| */ |
| |
| if (hdd_drv_cmd_validate(command, 15)) |
| goto exit; |
| |
| ret = drv_cmd_set_fcc_channel(pAdapter, command, 15); |
| |
| } else if (strncmp(command, "RXFILTER-REMOVE", 15) == 0) { |
| |
| if (hdd_drv_cmd_validate(command, 15)) |
| goto exit; |
| |
| ret = hdd_driver_rxfilter_comand_handler(command, pAdapter, false); |
| |
| } else if (strncmp(command, "RXFILTER-ADD", 12) == 0) { |
| |
| if (hdd_drv_cmd_validate(command, 12)) |
| goto exit; |
| |
| ret = hdd_driver_rxfilter_comand_handler(command, pAdapter, true); |
| |
| } else if (strncasecmp(command, "SETANTENNAMODE", 14) == 0) { |
| ret = drv_cmd_set_antenna_mode(pAdapter, command, 14); |
| hddLog(LOG1, FL("set antenna mode ret: %d"), ret); |
| } else if (strncasecmp(command, "GETANTENNAMODE", 14) == 0) { |
| |
| ret = drv_cmd_get_antenna_mode(pAdapter, pHddCtx, command, |
| 14, &priv_data); |
| } else if (strncmp(command, "STOP", 4) == 0) { |
| hddLog(LOG1, FL("STOP command")); |
| pHddCtx->driver_being_stopped = true; |
| } else { |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_UNSUPPORTED_IOCTL, |
| pAdapter->sessionId, 0)); |
| hddLog( VOS_TRACE_LEVEL_WARN, "%s: Unsupported GUI command %s", |
| __func__, command); |
| } |
| |
| } |
| exit: |
| if (command) |
| { |
| vos_mem_free(command); |
| } |
| EXIT(); |
| return ret; |
| } |
| |
| #ifdef CONFIG_COMPAT |
| static int hdd_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_driver_command(pAdapter, &priv_data); |
| exit: |
| return ret; |
| } |
| #else /* CONFIG_COMPAT */ |
| static int hdd_driver_compat_ioctl(hdd_adapter_t *pAdapter, struct ifreq *ifr) |
| { |
| /* will never be invoked */ |
| return 0; |
| } |
| #endif /* CONFIG_COMPAT */ |
| |
| static int hdd_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_driver_command(pAdapter, &priv_data); |
| } |
| return ret; |
| } |
| |
| /** |
| * __hdd_ioctl() - HDD ioctl handler |
| * @dev: pointer to net_device structure |
| * @ifr: pointer to ifreq structure |
| * @cmd: ioctl command |
| * |
| * Return: 0 for success and error number for failure. |
| */ |
| static int __hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx; |
| long ret = 0; |
| |
| ENTER(); |
| |
| if (dev != pAdapter->dev) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%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, |
| "%s: invalid data", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| #if defined(QCA_WIFI_FTM) && defined(LINUX_QCMBR) |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| if (SIOCIOCTLTX99 == cmd) { |
| ret = wlan_hdd_qcmbr_unified_ioctl(pAdapter, ifr); |
| goto exit; |
| } |
| } |
| #endif |
| |
| 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_driver_compat_ioctl(pAdapter, ifr); |
| else |
| ret = hdd_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_ioctl() - Wrapper function to protect __hdd_ioctl() function from SSR |
| * @dev: pointer to net_device structure |
| * @ifr: pointer to ifreq structure |
| * @cmd: ioctl command |
| * |
| * Return: 0 for success and error number for failure. |
| */ |
| static int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __hdd_ioctl(dev, ifr, cmd); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| |
| /* |
| * Mac address for multiple virtual interface is found as following |
| * i) The mac address of the first interface is just the actual hw mac address. |
| * ii) MSM 3 or 4 bits of byte5 of the actual mac address are used to |
| * define the mac address for the remaining interfaces and locally |
| * administered bit is set. INTF_MACADDR_MASK is based on the number of |
| * supported virtual interfaces, right now this is 0x07 (meaning 8 interface). |
| * Byte[3] of second interface will be hw_macaddr[3](bit5..7) + 1, |
| * for third interface it will be hw_macaddr[3](bit5..7) + 2, etc. |
| */ |
| |
| void hdd_update_macaddr(hdd_config_t *cfg_ini, v_MACADDR_t hw_macaddr) |
| { |
| int8_t i; |
| u_int8_t macaddr_b3, tmp_br3; |
| |
| vos_mem_copy(cfg_ini->intfMacAddr[0].bytes, hw_macaddr.bytes, |
| VOS_MAC_ADDR_SIZE); |
| for (i = 1; i < VOS_MAX_CONCURRENCY_PERSONA; i++) { |
| vos_mem_copy(cfg_ini->intfMacAddr[i].bytes, hw_macaddr.bytes, |
| VOS_MAC_ADDR_SIZE); |
| macaddr_b3 = cfg_ini->intfMacAddr[i].bytes[3]; |
| tmp_br3 = ((macaddr_b3 >> 4 & INTF_MACADDR_MASK) + i) & |
| INTF_MACADDR_MASK; |
| macaddr_b3 += tmp_br3; |
| |
| /* XOR-ing bit-24 of the mac address. This will give enough |
| * mac address range before collision |
| */ |
| macaddr_b3 ^= (1 << 7); |
| |
| /* Set locally administered bit */ |
| cfg_ini->intfMacAddr[i].bytes[0] |= 0x02; |
| cfg_ini->intfMacAddr[i].bytes[3] = macaddr_b3; |
| hddLog(VOS_TRACE_LEVEL_INFO, "cfg_ini->intfMacAddr[%d]: " |
| MAC_ADDRESS_STR, i, |
| MAC_ADDR_ARRAY(cfg_ini->intfMacAddr[i].bytes)); |
| } |
| } |
| |
| static void hdd_update_tgt_services(hdd_context_t *hdd_ctx, |
| struct hdd_tgt_services *cfg) |
| { |
| hdd_config_t *cfg_ini = hdd_ctx->cfg_ini; |
| |
| /* Set up UAPSD */ |
| cfg_ini->apUapsdEnabled &= cfg->uapsd; |
| |
| #ifdef WLAN_FEATURE_11AC |
| /* 11AC mode support */ |
| if ((cfg_ini->dot11Mode == eHDD_DOT11_MODE_11ac || |
| cfg_ini->dot11Mode == eHDD_DOT11_MODE_11ac_ONLY) && |
| !cfg->en_11ac) |
| cfg_ini->dot11Mode = eHDD_DOT11_MODE_AUTO; |
| #endif /* #ifdef WLAN_FEATURE_11AC */ |
| |
| /* ARP offload: override user setting if invalid */ |
| cfg_ini->fhostArpOffload &= cfg->arp_offload; |
| |
| #ifdef FEATURE_WLAN_SCAN_PNO |
| /* PNO offload */ |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: PNO Capability in f/w = %d", |
| __func__,cfg->pno_offload); |
| if (cfg->pno_offload) |
| cfg_ini->PnoOffload = TRUE; |
| #endif |
| sme_set_lte_coex_supp(hdd_ctx->hHal, |
| cfg->lte_coex_ant_share); |
| hdd_ctx->per_band_chainmask_supp = cfg->per_band_chainmask_supp; |
| sme_set_per_band_chainmask_supp(hdd_ctx->hHal, |
| cfg->per_band_chainmask_supp); |
| #ifdef FEATURE_WLAN_TDLS |
| cfg_ini->fEnableTDLSSupport &= cfg->en_tdls; |
| cfg_ini->fEnableTDLSOffChannel = cfg_ini->fEnableTDLSOffChannel && |
| cfg->en_tdls_offchan; |
| cfg_ini->fEnableTDLSBufferSta = cfg_ini->fEnableTDLSBufferSta && |
| cfg->en_tdls_uapsd_buf_sta; |
| if (cfg_ini->fTDLSUapsdMask && cfg->en_tdls_uapsd_sleep_sta) |
| { |
| cfg_ini->fEnableTDLSSleepSta = TRUE; |
| } |
| else |
| { |
| cfg_ini->fEnableTDLSSleepSta = FALSE; |
| } |
| #endif |
| sme_set_bcon_offload_supp(hdd_ctx->hHal, cfg->beacon_offload); |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| cfg_ini->isRoamOffloadEnabled &= cfg->en_roam_offload; |
| #endif |
| |
| #ifdef SAP_AUTH_OFFLOAD |
| cfg_ini->enable_sap_auth_offload &= cfg->sap_auth_offload_service; |
| #endif |
| cfg_ini->sap_get_peer_info &= cfg->get_peer_info_enabled; |
| } |
| |
| /** |
| * hdd_update_chain_mask_vdev_nss() - sets the chain mask and vdev nss |
| * @hdd_ctx: HDD context |
| * @cfg: Pointer to target services. |
| * |
| * Sets the chain masks for 2G and 5G bands based on target supported |
| * values and INI values. And sets the Nss per vdev type based on INI |
| * and configured chain mask value. |
| * |
| * Return: None |
| */ |
| static void hdd_update_chain_mask_vdev_nss(hdd_context_t *hdd_ctx, |
| struct hdd_tgt_services *cfg) |
| { |
| hdd_config_t *cfg_ini = hdd_ctx->cfg_ini; |
| uint8_t chain_mask_rx, chain_mask_tx, ret; |
| uint8_t max_supp_nss = 1; |
| |
| cfg_ini->enable2x2 = 0; |
| chain_mask_rx = cfg->chain_mask_2g & cfg_ini->chain_mask_2g_rx; |
| chain_mask_tx = cfg->chain_mask_2g & cfg_ini->chain_mask_2g_tx; |
| if (!chain_mask_rx) |
| chain_mask_rx = cfg->chain_mask_2g; |
| if (!chain_mask_tx) |
| chain_mask_tx = chain_mask_rx; |
| hddLog(LOG1, |
| FL("set 2G chain mask rx %d tx %d"), |
| chain_mask_rx, chain_mask_tx); |
| ret = process_wma_set_command(0, WMI_PDEV_PARAM_RX_CHAIN_MASK_2G, |
| chain_mask_rx, PDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: set WMI_PDEV_PARAM_RX_CHAIN_MASK_2G failed %d", |
| __func__, ret); |
| } |
| ret = process_wma_set_command(0, WMI_PDEV_PARAM_TX_CHAIN_MASK_2G, |
| chain_mask_tx, PDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: WMI_PDEV_PARAM_TX_CHAIN_MASK_2G set failed %d", |
| __func__, ret); |
| } |
| if (((chain_mask_rx & 0x3) == 0x3) || |
| ((chain_mask_tx & 0x3) == 0x3)) |
| max_supp_nss++; |
| |
| if (max_supp_nss == 2) |
| cfg_ini->enable2x2 = 1; |
| sme_update_vdev_type_nss(hdd_ctx->hHal, max_supp_nss, |
| cfg_ini->vdev_type_nss_2g, eCSR_BAND_24); |
| |
| if (chain_mask_rx >= chain_mask_tx) |
| hdd_ctx->supp_2g_chain_mask = chain_mask_rx; |
| else |
| hdd_ctx->supp_2g_chain_mask = chain_mask_tx; |
| |
| max_supp_nss = 1; |
| chain_mask_rx = cfg->chain_mask_5g & cfg_ini->chain_mask_5g_rx; |
| chain_mask_tx = cfg->chain_mask_5g & cfg_ini->chain_mask_5g_tx; |
| if (!chain_mask_rx) |
| chain_mask_rx = cfg->chain_mask_5g; |
| if (!chain_mask_tx) |
| chain_mask_tx = chain_mask_rx; |
| hddLog(LOG1, |
| FL("set 5G chain mask rx %d tx %d"), |
| chain_mask_rx, chain_mask_tx); |
| ret = process_wma_set_command(0, WMI_PDEV_PARAM_RX_CHAIN_MASK_5G, |
| chain_mask_rx, PDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: set WMI_PDEV_PARAM_RX_CHAIN_MASK_5G failed %d", |
| __func__, ret); |
| } |
| ret = process_wma_set_command(0, WMI_PDEV_PARAM_TX_CHAIN_MASK_5G, |
| chain_mask_tx, PDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: WMI_PDEV_PARAM_TX_CHAIN_MASK_5G set failed %d", |
| __func__, ret); |
| } |
| if (((chain_mask_rx & 0x3) == 0x3) || |
| ((chain_mask_tx & 0x3) == 0x3)) |
| max_supp_nss++; |
| |
| if (max_supp_nss == 2) |
| cfg_ini->enable2x2 = 1; |
| sme_update_vdev_type_nss(hdd_ctx->hHal, max_supp_nss, |
| cfg_ini->vdev_type_nss_5g, eCSR_BAND_5G); |
| if (chain_mask_rx >= chain_mask_tx) |
| hdd_ctx->supp_5g_chain_mask = chain_mask_rx; |
| else |
| hdd_ctx->supp_5g_chain_mask = chain_mask_tx; |
| hddLog(LOG1, FL("Supported chain mask 2G: %d 5G: %d"), |
| hdd_ctx->supp_2g_chain_mask, |
| hdd_ctx->supp_5g_chain_mask); |
| } |
| |
| static void hdd_update_tgt_ht_cap(hdd_context_t *hdd_ctx, |
| struct hdd_tgt_ht_cap *cfg) |
| { |
| eHalStatus status; |
| tANI_U32 value, val32; |
| tANI_U16 val16; |
| hdd_config_t *pconfig = hdd_ctx->cfg_ini; |
| tSirMacHTCapabilityInfo *phtCapInfo; |
| tANI_U8 mcs_set[SIZE_OF_SUPPORTED_MCS_SET]; |
| uint8_t enable_tx_stbc; |
| |
| /* check and update RX STBC */ |
| if (pconfig->enableRxSTBC && !cfg->ht_rx_stbc) |
| pconfig->enableRxSTBC = cfg->ht_rx_stbc; |
| |
| /* get the MPDU density */ |
| status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_MPDU_DENSITY, &value); |
| |
| if (status != eHAL_STATUS_SUCCESS) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get MPDU DENSITY", |
| __func__); |
| value = 0; |
| } |
| |
| /* |
| * MPDU density: |
| * override user's setting if value is larger |
| * than the one supported by target |
| */ |
| if (value > cfg->mpdu_density) { |
| status = ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_MPDU_DENSITY, |
| cfg->mpdu_density, |
| NULL, eANI_BOOLEAN_FALSE); |
| |
| if (status == eHAL_STATUS_FAILURE) |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set MPDU DENSITY to CCM", |
| __func__); |
| } |
| |
| /* get the HT capability info*/ |
| status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_HT_CAP_INFO, &val32); |
| if (eHAL_STATUS_SUCCESS != status) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get HT capability info", |
| __func__); |
| return; |
| } |
| val16 = (tANI_U16)val32; |
| phtCapInfo = (tSirMacHTCapabilityInfo *)&val16; |
| |
| /* Set the LDPC capability */ |
| phtCapInfo->advCodingCap = cfg->ht_rx_ldpc; |
| |
| |
| if (pconfig->ShortGI20MhzEnable && !cfg->ht_sgi_20) |
| pconfig->ShortGI20MhzEnable = cfg->ht_sgi_20; |
| |
| if (pconfig->ShortGI40MhzEnable && !cfg->ht_sgi_40) |
| pconfig->ShortGI40MhzEnable = cfg->ht_sgi_40; |
| |
| hdd_ctx->num_rf_chains = cfg->num_rf_chains; |
| hdd_ctx->ht_tx_stbc_supported = cfg->ht_tx_stbc; |
| |
| enable_tx_stbc = pconfig->enableTxSTBC; |
| |
| if (pconfig->enable2x2 && (hdd_ctx->per_band_chainmask_supp || |
| (!hdd_ctx->per_band_chainmask_supp && (cfg->num_rf_chains == 2)))) |
| { |
| pconfig->enable2x2 = 1; |
| } |
| else |
| { |
| pconfig->enable2x2 = 0; |
| enable_tx_stbc = 0; |
| |
| /* 1x1 */ |
| /* Update Rx Highest Long GI data Rate */ |
| if (ccmCfgSetInt(hdd_ctx->hHal, |
| WNI_CFG_VHT_RX_HIGHEST_SUPPORTED_DATA_RATE, |
| VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1, NULL, |
| eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE) |
| { |
| hddLog(LOGE, "Could not pass on " |
| "WNI_CFG_VHT_RX_HIGHEST_SUPPORTED_DATA_RATE to CCM"); |
| } |
| |
| /* Update Tx Highest Long GI data Rate */ |
| if (ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_VHT_TX_HIGHEST_SUPPORTED_DATA_RATE, |
| VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_1_1, NULL, |
| eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE) |
| { |
| hddLog(LOGE, "Could not pass on " |
| "HDD_VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1 to CCM"); |
| } |
| } |
| if (!(cfg->ht_tx_stbc && pconfig->enable2x2)) |
| { |
| enable_tx_stbc = 0; |
| } |
| phtCapInfo->txSTBC = enable_tx_stbc; |
| val32 = val16; |
| status = ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_HT_CAP_INFO, |
| val32, NULL, eANI_BOOLEAN_FALSE); |
| if (status != eHAL_STATUS_SUCCESS) |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set HT capability to CCM", |
| __func__); |
| #define WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES 0xff |
| value = SIZE_OF_SUPPORTED_MCS_SET; |
| if (ccmCfgGetStr(hdd_ctx->hHal, WNI_CFG_SUPPORTED_MCS_SET, mcs_set, |
| &value) == eHAL_STATUS_SUCCESS) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO, |
| "%s: Read MCS rate set", __func__); |
| |
| if (pconfig->enable2x2) |
| { |
| for (value = 0; value < 2; value++) |
| mcs_set[value] = WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES; |
| |
| status = ccmCfgSetStr(hdd_ctx->hHal, WNI_CFG_SUPPORTED_MCS_SET, |
| mcs_set, SIZE_OF_SUPPORTED_MCS_SET, NULL, |
| eANI_BOOLEAN_FALSE); |
| if (status == eHAL_STATUS_FAILURE) |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set MCS SET to CCM", __func__); |
| } |
| } |
| #undef WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES |
| } |
| |
| #ifdef WLAN_FEATURE_11AC |
| static void hdd_update_tgt_vht_cap(hdd_context_t *hdd_ctx, |
| struct hdd_tgt_vht_cap *cfg) |
| { |
| eHalStatus status; |
| tANI_U32 value = 0; |
| hdd_config_t *pconfig = hdd_ctx->cfg_ini; |
| tANI_U32 temp = 0; |
| |
| /* Get the current MPDU length */ |
| status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_MAX_MPDU_LENGTH, &value); |
| |
| if (status != eHAL_STATUS_SUCCESS) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get MPDU LENGTH", |
| __func__); |
| value = 0; |
| } |
| |
| /* |
| * VHT max MPDU length: |
| * override if user configured value is too high |
| * that the target cannot support |
| */ |
| if (value > cfg->vht_max_mpdu) { |
| status = ccmCfgSetInt(hdd_ctx->hHal, |
| WNI_CFG_VHT_MAX_MPDU_LENGTH, |
| cfg->vht_max_mpdu, NULL, |
| eANI_BOOLEAN_FALSE); |
| |
| if (status == eHAL_STATUS_FAILURE) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set VHT MAX MPDU LENGTH", |
| __func__); |
| } |
| } |
| |
| /* Get the current supported chan width */ |
| status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_SUPPORTED_CHAN_WIDTH_SET, |
| &value); |
| |
| if (status != eHAL_STATUS_SUCCESS) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get MPDU LENGTH", |
| __func__); |
| value = 0; |
| } |
| |
| /* |
| * Update VHT supported chan width: |
| * if user setting is invalid, override it with |
| * target capability |
| */ |
| if ((value == eHT_CHANNEL_WIDTH_80MHZ && |
| !(cfg->supp_chan_width & eHT_CHANNEL_WIDTH_80MHZ)) || |
| (value == eHT_CHANNEL_WIDTH_160MHZ && |
| !(cfg->supp_chan_width & eHT_CHANNEL_WIDTH_160MHZ))) { |
| u_int32_t width = eHT_CHANNEL_WIDTH_20MHZ; |
| |
| if (cfg->supp_chan_width & eHT_CHANNEL_WIDTH_160MHZ) |
| width = eHT_CHANNEL_WIDTH_160MHZ; |
| else if (cfg->supp_chan_width & eHT_CHANNEL_WIDTH_80MHZ) |
| width = eHT_CHANNEL_WIDTH_80MHZ; |
| |
| status = ccmCfgSetInt(hdd_ctx->hHal, |
| WNI_CFG_VHT_SUPPORTED_CHAN_WIDTH_SET, |
| width, NULL, eANI_BOOLEAN_FALSE); |
| |
| if (status == eHAL_STATUS_FAILURE) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set VHT SUPPORTED CHAN WIDTH", |
| __func__); |
| } |
| } |
| |
| ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_BASIC_MCS_SET, &temp); |
| temp = (temp & VHT_MCS_1x1) | pconfig->vhtRxMCS; |
| |
| if (pconfig->enable2x2) |
| temp = (temp & VHT_MCS_2x2) | (pconfig->vhtRxMCS2x2 << 2); |
| |
| if (ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_VHT_BASIC_MCS_SET, temp, NULL, |
| eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE) { |
| hddLog(LOGE, "Could not pass on WNI_CFG_VHT_BASIC_MCS_SET to CCM"); |
| } |
| |
| ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_RX_MCS_MAP, &temp); |
| temp = (temp & VHT_MCS_1x1) | pconfig->vhtRxMCS; |
| if (pconfig->enable2x2) |
| temp = (temp & VHT_MCS_2x2) | (pconfig->vhtRxMCS2x2 << 2); |
| |
| if (ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_VHT_RX_MCS_MAP, temp, NULL, |
| eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE) { |
| hddLog(LOGE, "Could not pass on WNI_CFG_VHT_RX_MCS_MAP to CCM"); |
| } |
| |
| ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_TX_MCS_MAP, &temp); |
| temp = (temp & VHT_MCS_1x1) | pconfig->vhtTxMCS; |
| if (pconfig->enable2x2) |
| temp = (temp & VHT_MCS_2x2) | (pconfig->vhtTxMCS2x2 << 2); |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, |
| "vhtRxMCS2x2 - %x temp - %u enable2x2 %d", |
| pconfig->vhtRxMCS2x2, temp, pconfig->enable2x2); |
| |
| if (ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_VHT_TX_MCS_MAP, temp, NULL, |
| eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE) { |
| hddLog(LOGE, "Could not pass on WNI_CFG_VHT_TX_MCS_MAP to CCM"); |
| } |
| /* Get the current RX LDPC setting */ |
| status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_LDPC_CODING_CAP, &value); |
| |
| if (status != eHAL_STATUS_SUCCESS) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get VHT LDPC CODING CAP", |
| __func__); |
| value = 0; |
| } |
| |
| /* Set the LDPC capability */ |
| if (value && !cfg->vht_rx_ldpc) { |
| status = ccmCfgSetInt(hdd_ctx->hHal, |
| WNI_CFG_VHT_LDPC_CODING_CAP, |
| cfg->vht_rx_ldpc, NULL, |
| eANI_BOOLEAN_FALSE); |
| |
| if (status == eHAL_STATUS_FAILURE) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set VHT LDPC CODING CAP to CCM", |
| __func__); |
| } |
| } |
| |
| /* Get current GI 80 value */ |
| status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_SHORT_GI_80MHZ, &value); |
| |
| if (status != eHAL_STATUS_SUCCESS) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get SHORT GI 80MHZ", |
| __func__); |
| value = 0; |
| } |
| |
| /* set the Guard interval 80MHz */ |
| if (value && !cfg->vht_short_gi_80) { |
| status = ccmCfgSetInt(hdd_ctx->hHal, |
| WNI_CFG_VHT_SHORT_GI_80MHZ, |
| cfg->vht_short_gi_80, NULL, |
| eANI_BOOLEAN_FALSE); |
| |
| if (status == eHAL_STATUS_FAILURE) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set SHORT GI 80MHZ to CCM", |
| __func__); |
| } |
| } |
| |
| /* Get current GI 160 value */ |
| status = ccmCfgGetInt(hdd_ctx->hHal, |
| WNI_CFG_VHT_SHORT_GI_160_AND_80_PLUS_80MHZ, |
| &value); |
| |
| if (status != eHAL_STATUS_SUCCESS) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get SHORT GI 80 & 160", |
| __func__); |
| value = 0; |
| } |
| |
| /* set the Guard interval 160MHz */ |
| if (value && !cfg->vht_short_gi_160) { |
| status = ccmCfgSetInt(hdd_ctx->hHal, |
| WNI_CFG_VHT_SHORT_GI_160_AND_80_PLUS_80MHZ, |
| cfg->vht_short_gi_160, NULL, |
| eANI_BOOLEAN_FALSE); |
| |
| if (status == eHAL_STATUS_FAILURE) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set SHORT GI 80 & 160 to CCM", |
| __func__); |
| } |
| } |
| |
| /* Get VHT TX STBC cap */ |
| status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_TXSTBC, &value); |
| |
| if (status != eHAL_STATUS_SUCCESS) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get VHT TX STBC", |
| __func__); |
| value = 0; |
| } |
| |
| /* VHT TX STBC cap */ |
| if (value && !cfg->vht_tx_stbc) { |
| status = ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_VHT_TXSTBC, |
| cfg->vht_tx_stbc, NULL, |
| eANI_BOOLEAN_FALSE); |
| |
| if (status == eHAL_STATUS_FAILURE) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set the VHT TX STBC to CCM", |
| __func__); |
| } |
| } |
| |
| /* Get VHT RX STBC cap */ |
| status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_RXSTBC, &value); |
| |
| if (status != eHAL_STATUS_SUCCESS) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get VHT RX STBC", |
| __func__); |
| value = 0; |
| } |
| |
| /* VHT RX STBC cap */ |
| if (value && !cfg->vht_rx_stbc) { |
| status = ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_VHT_RXSTBC, |
| cfg->vht_rx_stbc, NULL, |
| eANI_BOOLEAN_FALSE); |
| |
| if (status == eHAL_STATUS_FAILURE) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set the VHT RX STBC to CCM", |
| __func__); |
| } |
| } |
| |
| /* Get VHT SU Beamformer cap */ |
| status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_SU_BEAMFORMER_CAP, |
| &value); |
| |
| if (status != eHAL_STATUS_SUCCESS) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get VHT SU BEAMFORMER CAP", |
| __func__); |
| value = 0; |
| } |
| |
| /* set VHT SU Beamformer cap */ |
| if (value && !cfg->vht_su_bformer) { |
| status = ccmCfgSetInt(hdd_ctx->hHal, |
| WNI_CFG_VHT_SU_BEAMFORMER_CAP, |
| cfg->vht_su_bformer, NULL, |
| eANI_BOOLEAN_FALSE); |
| |
| if (status == eHAL_STATUS_FAILURE) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set VHT SU BEAMFORMER CAP", |
| __func__); |
| } |
| } |
| |
| /* check and update SU BEAMFORMEE capability*/ |
| if (pconfig->enableTxBF && !cfg->vht_su_bformee) |
| pconfig->enableTxBF = cfg->vht_su_bformee; |
| |
| status = ccmCfgSetInt(hdd_ctx->hHal, |
| WNI_CFG_VHT_SU_BEAMFORMEE_CAP, |
| pconfig->enableTxBF, NULL, |
| eANI_BOOLEAN_FALSE); |
| |
| if (status == eHAL_STATUS_FAILURE) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set VHT SU BEAMFORMEE CAP", |
| __func__); |
| } |
| |
| /* Get VHT MU Beamformer cap */ |
| status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_MU_BEAMFORMER_CAP, |
| &value); |
| |
| if (status != eHAL_STATUS_SUCCESS) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get VHT MU BEAMFORMER CAP", |
| __func__); |
| value = 0; |
| } |
| |
| /* set VHT MU Beamformer cap */ |
| if (value && !cfg->vht_mu_bformer) { |
| status = ccmCfgSetInt(hdd_ctx->hHal, |
| WNI_CFG_VHT_MU_BEAMFORMER_CAP, |
| cfg->vht_mu_bformer, NULL, |
| eANI_BOOLEAN_FALSE); |
| |
| if (status == eHAL_STATUS_FAILURE) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set the VHT MU BEAMFORMER CAP to CCM", |
| __func__); |
| } |
| } |
| |
| /* Get VHT MU Beamformee cap */ |
| status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_MU_BEAMFORMEE_CAP, |
| &value); |
| |
| if (status != eHAL_STATUS_SUCCESS) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get VHT MU BEAMFORMEE CAP", |
| __func__); |
| value = 0; |
| } |
| |
| /* check and update MU BEAMFORMEE capability*/ |
| if (pconfig->enableMuBformee && !cfg->vht_mu_bformee) |
| pconfig->enableMuBformee = cfg->vht_mu_bformee; |
| |
| /* set VHT MU Beamformee cap */ |
| if (value && !cfg->vht_mu_bformee) { |
| status = ccmCfgSetInt(hdd_ctx->hHal, |
| WNI_CFG_VHT_MU_BEAMFORMEE_CAP, |
| pconfig->enableMuBformee, NULL, |
| eANI_BOOLEAN_FALSE); |
| |
| if (status == eHAL_STATUS_FAILURE) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set VHT MU BEAMFORMER CAP", |
| __func__); |
| } |
| } |
| |
| /* Get VHT MAX AMPDU Len exp */ |
| status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_AMPDU_LEN_EXPONENT, |
| &value); |
| |
| if (status != eHAL_STATUS_SUCCESS) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get VHT AMPDU LEN", |
| __func__); |
| value = 0; |
| } |
| |
| /* |
| * VHT max AMPDU len exp: |
| * override if user configured value is too high |
| * that the target cannot support. |
| * Even though Rome publish ampdu_len=7, it can |
| * only support 4 because of some h/w bug. |
| */ |
| |
| if (value > cfg->vht_max_ampdu_len_exp) { |
| status = ccmCfgSetInt(hdd_ctx->hHal, |
| WNI_CFG_VHT_AMPDU_LEN_EXPONENT, |
| cfg->vht_max_ampdu_len_exp, NULL, |
| eANI_BOOLEAN_FALSE); |
| |
| if (status == eHAL_STATUS_FAILURE) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set the VHT AMPDU LEN EXP", |
| __func__); |
| } |
| } |
| |
| /* Get VHT TXOP PS CAP */ |
| status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_TXOP_PS, &value); |
| |
| if (status != eHAL_STATUS_SUCCESS) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get VHT TXOP PS", |
| __func__); |
| value = 0; |
| } |
| |
| /* set VHT TXOP PS cap */ |
| if (value && !cfg->vht_txop_ps) { |
| status = ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_VHT_TXOP_PS, |
| cfg->vht_txop_ps, NULL, |
| eANI_BOOLEAN_FALSE); |
| |
| if (status == eHAL_STATUS_FAILURE) { |
| VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: could not set the VHT TXOP PS", |
| __func__); |
| } |
| } |
| |
| hddLog(LOG1, "enable2x2 %d ", pconfig->enable2x2); |
| if (pconfig->enable2x2) |
| { |
| if (ccmCfgSetInt(hdd_ctx->hHal, |
| WNI_CFG_VHT_NUM_SOUNDING_DIMENSIONS, |
| NUM_OF_SOUNDING_DIMENSIONS, NULL, |
| eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE) { |
| hddLog(LOGE, |
| "Could not set WNI_CFG_VHT_NUM_SOUNDING_DIMENSIONS to CCM"); |
| } |
| } |
| } |
| #endif /* #ifdef WLAN_FEATURE_11AC */ |
| |
| #ifdef FEATURE_WLAN_RA_FILTERING |
| static void hdd_update_ra_rate_limit(hdd_context_t *hdd_ctx, |
| struct hdd_tgt_cfg *cfg) |
| { |
| hdd_ctx->cfg_ini->IsRArateLimitEnabled = cfg->is_ra_rate_limit_enabled; |
| } |
| #else |
| static void hdd_update_ra_rate_limit(hdd_context_t *hdd_ctx, |
| struct hdd_tgt_cfg *cfg) |
| { |
| } |
| #endif |
| |
| void hdd_update_tgt_cfg(void *context, void *param) |
| { |
| hdd_context_t *hdd_ctx = (hdd_context_t *)context; |
| struct hdd_tgt_cfg *cfg = (struct hdd_tgt_cfg *)param; |
| tANI_U8 temp_band_cap; |
| |
| if (hdd_cfg_is_sub20_channel_width_enabled(hdd_ctx) && |
| cfg->sub_20_support == 0) { |
| hddLog(VOS_TRACE_LEVEL_WARN, |
| FL("requests 5/10M, target not support")); |
| hdd_ctx->cfg_ini->sub_20_channel_width = 0; |
| } |
| |
| /* first store the INI band capability */ |
| temp_band_cap = hdd_ctx->cfg_ini->nBandCapability; |
| |
| hdd_ctx->cfg_ini->nBandCapability = cfg->band_cap; |
| |
| /* now overwrite the target band capability with INI |
| setting if INI setting is a subset */ |
| |
| if ((hdd_ctx->cfg_ini->nBandCapability == eCSR_BAND_ALL) && |
| (temp_band_cap != eCSR_BAND_ALL)) |
| hdd_ctx->cfg_ini->nBandCapability = temp_band_cap; |
| else if ((hdd_ctx->cfg_ini->nBandCapability != eCSR_BAND_ALL) && |
| (temp_band_cap != eCSR_BAND_ALL) && |
| (hdd_ctx->cfg_ini->nBandCapability != temp_band_cap)) { |
| hddLog(VOS_TRACE_LEVEL_WARN, |
| FL("ini BandCapability not supported by the target")); |
| } |
| |
| if (!hdd_ctx->isLogpInProgress) { |
| hdd_ctx->reg.reg_domain = cfg->reg_domain; |
| hdd_ctx->reg.eeprom_rd_ext = cfg->eeprom_rd_ext; |
| } |
| |
| /* This can be extended to other configurations like ht, vht cap... */ |
| |
| if (!vos_is_macaddr_zero(&cfg->hw_macaddr)) |
| { |
| vos_mem_copy(&hdd_ctx->hw_macaddr, &cfg->hw_macaddr, |
| VOS_MAC_ADDR_SIZE); |
| } |
| else { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: HW MAC is zero", __func__); |
| } |
| |
| hdd_ctx->target_fw_version = cfg->target_fw_version; |
| |
| hdd_ctx->max_intf_count = cfg->max_intf_count; |
| |
| #ifdef WLAN_FEATURE_LPSS |
| hdd_ctx->lpss_support = cfg->lpss_support; |
| #endif |
| |
| wlan_hdd_set_egap_support(hdd_ctx, cfg); |
| |
| hdd_ctx->ap_arpns_support = cfg->ap_arpns_support; |
| hdd_update_tgt_services(hdd_ctx, &cfg->services); |
| if (hdd_ctx->per_band_chainmask_supp) |
| hdd_update_chain_mask_vdev_nss(hdd_ctx, &cfg->services); |
| else |
| sme_set_vdev_nss(hdd_ctx->hHal, hdd_ctx->cfg_ini->enable2x2); |
| |
| hdd_update_tgt_ht_cap(hdd_ctx, &cfg->ht_cap); |
| |
| #ifdef WLAN_FEATURE_11AC |
| hdd_update_tgt_vht_cap(hdd_ctx, &cfg->vht_cap); |
| #endif /* #ifdef WLAN_FEATURE_11AC */ |
| |
| hdd_ctx->current_antenna_mode = |
| hdd_is_supported_chain_mask_2x2(hdd_ctx) ? |
| HDD_ANTENNA_MODE_2X2 : HDD_ANTENNA_MODE_1X1; |
| hddLog(LOG1, FL("Current antenna mode: %d"), |
| hdd_ctx->current_antenna_mode); |
| hdd_ctx->cfg_ini->fine_time_meas_cap &= cfg->fine_time_measurement_cap; |
| hdd_ctx->fine_time_meas_cap_target = cfg->fine_time_measurement_cap; |
| hddLog(LOG1, FL("fine_time_measurement_cap: 0x%x"), |
| hdd_ctx->cfg_ini->fine_time_meas_cap); |
| |
| hddLog(LOG1, FL("Target BPF %d Host BPF %d"), |
| cfg->bpf_enabled, hdd_ctx->cfg_ini->bpf_packet_filter_enable); |
| hdd_ctx->bpf_enabled = (cfg->bpf_enabled && |
| hdd_ctx->cfg_ini->bpf_packet_filter_enable); |
| hdd_update_ra_rate_limit(hdd_ctx, cfg); |
| |
| /* |
| * If BPF is enabled, maxWowFilters set to WMA_STA_WOW_DEFAULT_PTRN_MAX |
| * because we need atleast WMA_STA_WOW_DEFAULT_PTRN_MAX free slots to |
| * configure the STA mode wow pattern. |
| */ |
| if (hdd_ctx->bpf_enabled) |
| hdd_ctx->cfg_ini->maxWoWFilters = WMA_STA_WOW_DEFAULT_PTRN_MAX; |
| hdd_ctx->wmi_max_len = cfg->wmi_max_len; |
| |
| /* Configure NAN datapath features */ |
| hdd_nan_datapath_target_config(hdd_ctx, cfg); |
| |
| hdd_ctx->max_mc_addr_list = cfg->max_mc_addr_list; |
| } |
| |
| void hdd_update_dfs_cac_block_tx_flag(void *context, bool cac_block_tx) |
| { |
| hdd_context_t *hdd_ctx = (hdd_context_t *)context; |
| hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL; |
| hdd_adapter_t *adapter; |
| VOS_STATUS status; |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| if (hdd_ctx->cfg_ini->disableDFSChSwitch) |
| return; |
| status = hdd_get_front_adapter(hdd_ctx, &adapter_node); |
| while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) { |
| adapter = adapter_node->pAdapter; |
| if (WLAN_HDD_SOFTAP == adapter->device_mode || |
| WLAN_HDD_P2P_GO == adapter->device_mode) |
| WLAN_HDD_GET_AP_CTX_PTR(adapter)->dfs_cac_block_tx = |
| cac_block_tx; |
| |
| status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next); |
| adapter_node = next; |
| } |
| } |
| |
| /* This function is invoked in atomic context when a radar |
| * is found on the SAP current operating channel and Data |
| * Tx from netif has to be stopped to honor the DFS regulations. |
| * Actions: Stop the netif Tx queues,Indicate Radar present |
| * in HDD context for future usage. |
| */ |
| bool hdd_dfs_indicate_radar(void *context, void *param) |
| { |
| hdd_context_t *pHddCtx= (hdd_context_t *)context; |
| struct hdd_dfs_radar_ind *hdd_radar_event = |
| (struct hdd_dfs_radar_ind*)param; |
| hdd_adapter_list_node_t *pAdapterNode = NULL, |
| *pNext = NULL; |
| hdd_adapter_t *pAdapter; |
| VOS_STATUS status; |
| |
| if (!pHddCtx || !hdd_radar_event || pHddCtx->cfg_ini->disableDFSChSwitch) |
| return true; |
| |
| if (VOS_TRUE == hdd_radar_event->dfs_radar_status) |
| { |
| spin_lock_bh(&pHddCtx->dfs_lock); |
| if (pHddCtx->dfs_radar_found) |
| { |
| /* Application already triggered channel switch |
| * on current channel, so return here |
| */ |
| spin_unlock_bh(&pHddCtx->dfs_lock); |
| return false; |
| } |
| |
| pHddCtx->dfs_radar_found = VOS_TRUE; |
| spin_unlock_bh(&pHddCtx->dfs_lock); |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| if (WLAN_HDD_SOFTAP == pAdapter->device_mode || |
| WLAN_HDD_P2P_GO == pAdapter->device_mode) |
| { |
| WLAN_HDD_GET_AP_CTX_PTR(pAdapter)->dfs_cac_block_tx = VOS_TRUE; |
| } |
| |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| } |
| |
| return true; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_is_valid_mac_address() - Validate MAC address |
| |
| This function validates whether the given MAC address is valid or not |
| Expected MAC address is of the format XX:XX:XX:XX:XX:XX |
| where X is the hexa decimal digit character and separated by ':' |
| This algorithm works even if MAC address is not separated by ':' |
| |
| This code checks given input string mac contains exactly 12 hexadecimal digits. |
| and a separator colon : appears in the input string only after |
| an even number of hex digits. |
| |
| \param - pMacAddr pointer to the input MAC address |
| \return - 1 for valid and 0 for invalid |
| |
| --------------------------------------------------------------------------*/ |
| |
| v_BOOL_t hdd_is_valid_mac_address(const tANI_U8 *pMacAddr) |
| { |
| int xdigit = 0; |
| int separator = 0; |
| while (*pMacAddr) |
| { |
| if (isxdigit(*pMacAddr)) |
| { |
| xdigit++; |
| } |
| else if (':' == *pMacAddr) |
| { |
| if (0 == xdigit || ((xdigit / 2) - 1) != separator) |
| break; |
| |
| ++separator; |
| } |
| else |
| { |
| /* Invalid MAC found */ |
| return 0; |
| } |
| ++pMacAddr; |
| } |
| return (xdigit == 12 && (separator == 5 || separator == 0)); |
| } |
| |
| /** |
| * __hdd_open() - HDD Open function |
| * @dev: pointer to net_device structure |
| * |
| * This is called in response to ifconfig up |
| * |
| * Return: 0 for success and error number for failure |
| */ |
| static int __hdd_open(struct net_device *dev) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| v_BOOL_t in_standby = TRUE; |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_OPEN_REQUEST, |
| pAdapter->sessionId, pAdapter->device_mode)); |
| |
| /* Don't validate for load/unload and logp as if we return |
| failure we may endup in scan/connection related issues */ |
| if (NULL == pHddCtx || NULL == pHddCtx->cfg_ini) { |
| hddLog(LOG1, FL("HDD context is Null")); |
| return -ENODEV; |
| } |
| |
| pHddCtx->driver_being_stopped = false; |
| |
| status = hdd_get_front_adapter (pHddCtx, &pAdapterNode); |
| while ((NULL != pAdapterNode) && (VOS_STATUS_SUCCESS == status)) { |
| if (test_bit(DEVICE_IFACE_OPENED, &pAdapterNode->pAdapter->event_flags)) { |
| hddLog(LOG1, FL("chip already out of standby")); |
| in_standby = FALSE; |
| break; |
| } else { |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| } |
| |
| if (TRUE == in_standby) { |
| if (VOS_STATUS_SUCCESS != wlan_hdd_exit_lowpower(pHddCtx, pAdapter)) { |
| hddLog(LOGE, FL("Failed to bring wlan out of power save")); |
| return -EINVAL; |
| } |
| } |
| |
| set_bit(DEVICE_IFACE_OPENED, &pAdapter->event_flags); |
| if (hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))) { |
| hddLog(LOG1, FL("Enabling Tx Queues")); |
| /* Enable TX queues only when we are connected */ |
| wlan_hdd_netif_queue_control(pAdapter, |
| WLAN_START_ALL_NETIF_QUEUE, |
| WLAN_CONTROL_PATH); |
| } |
| |
| /* Enable carrier and transmit queues for NDI */ |
| if (WLAN_HDD_IS_NDI(pAdapter)) { |
| hddLog(LOG1, FL("Enabling Tx Queues")); |
| wlan_hdd_netif_queue_control(pAdapter, |
| WLAN_START_ALL_NETIF_QUEUE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_open() - Wrapper function for __hdd_open to protect it from SSR |
| * @dev: pointer to net_device structure |
| * |
| * This is called in response to ifconfig up |
| * |
| * Return: 0 for success and error number for failure |
| */ |
| static int hdd_open(struct net_device *dev) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __hdd_open(dev); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * __hdd_mon_open() - HDD monitor open |
| * @dev: pointer to net_device |
| * |
| * Return: 0 for success and error number for failure |
| */ |
| static int __hdd_mon_open(struct net_device *dev) |
| { |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| int ret; |
| VOS_STATUS vos_status; |
| eHalStatus hal_status; |
| WLAN_STADescType sta_desc = {0}; |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_OPEN_REQUEST, |
| adapter->sessionId, adapter->device_mode)); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| /* peer is created wma_vdev_attach->wma_create_peer */ |
| vos_status = WLANTL_RegisterSTAClient(hdd_ctx->pvosContext, |
| hdd_mon_rx_packet_cbk, |
| &sta_desc, 0); |
| if (VOS_STATUS_SUCCESS != vos_status) { |
| hddLog(LOGE, |
| FL("WLANTL_RegisterSTAClient() failed to register. Status= %d [0x%08X]"), |
| vos_status, vos_status); |
| goto exit; |
| } |
| |
| hal_status = sme_create_mon_session(hdd_ctx->hHal, |
| adapter->macAddressCurrent.bytes); |
| if (eHAL_STATUS_SUCCESS != hal_status) { |
| hddLog(LOGE, |
| FL("sme_create_mon_session() failed to register. Status= %d [0x%08X]"), |
| hal_status, hal_status); |
| goto exit; |
| } |
| return ret; |
| exit: |
| return -EIO; |
| } |
| |
| /** |
| * hdd_mon_open() - SSR wrapper function for __hdd_mon_open |
| * @dev: pointer to net_device |
| * |
| * Return: 0 for success and error number for failure |
| */ |
| static int hdd_mon_open(struct net_device *dev) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __hdd_mon_open(dev); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| int wlan_hdd_monitor_mode_enable(hdd_context_t *hdd_ctx, bool enable) |
| { |
| int ret = 0; |
| |
| ret = process_wma_set_command( |
| 0, |
| GEN_PDEV_MONITOR_MODE, |
| enable, |
| GEN_CMD); |
| |
| if (ret) { |
| hddLog(LOGE, |
| FL("send monitor enable cmd fail, ret %d"), |
| ret); |
| return ret; |
| } |
| |
| hdd_ctx->is_mon_enable = enable; |
| |
| return ret; |
| } |
| |
| /** |
| * __hdd_vir_mon_open() - HDD monitor open |
| * @dev: pointer to net_device |
| * |
| * Return: 0 for success and error number for failure |
| */ |
| static int __hdd_vir_mon_open(struct net_device *dev) |
| { |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| hdd_adapter_t *sta_adapter = NULL; |
| int ret; |
| VOS_STATUS vos_status; |
| |
| if (!hdd_ctx->cfg_ini->mon_on_sta_enable) { |
| hddLog(LOGE, |
| FL("monitor feature for STA is not enabled")); |
| goto exit; |
| } |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_OPEN_REQUEST, |
| adapter->sessionId, adapter->device_mode)); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| /* register monitor RX callback to TLSHIM and OL */ |
| vos_status = tl_register_vir_mon_cb(hdd_ctx->pvosContext, |
| hdd_vir_mon_rx_cbk); |
| |
| if (VOS_STATUS_SUCCESS != vos_status) { |
| hddLog(LOGE, |
| FL("failed to register monitor cbk")); |
| goto exit; |
| } |
| |
| /* get STA adapter to check if STA is connected */ |
| sta_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_INFRA_STATION); |
| |
| if ((NULL == sta_adapter) || |
| (WLAN_HDD_ADAPTER_MAGIC != sta_adapter->magic)) { |
| hddLog(LOGE, |
| FL("STA adapter is not existed.")); |
| return ret; |
| } |
| |
| if (hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter)) && |
| (false == hdd_ctx->is_mon_enable)) { |
| /* send WMI cmd to enable monitor function */ |
| ret = wlan_hdd_monitor_mode_enable(hdd_ctx, true); |
| |
| /* disable Sta BMPS */ |
| if (hdd_ctx->cfg_ini->enablePowersaveOffload) |
| sme_PsOffloadDisablePowerSave( |
| WLAN_HDD_GET_HAL_CTX(sta_adapter), |
| NULL, |
| NULL, |
| sta_adapter->sessionId); |
| |
| #if defined(FEATURE_WLAN_LFR) && defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD) |
| if (hdd_ctx->cfg_ini->isFastRoamIniFeatureEnabled && |
| hdd_ctx->cfg_ini->isRoamOffloadScanEnabled) |
| sme_stopRoaming(WLAN_HDD_GET_HAL_CTX(sta_adapter), |
| sta_adapter->sessionId, 0); |
| #endif |
| } |
| |
| return ret; |
| exit: |
| return -EIO; |
| } |
| |
| /** |
| * __hdd_vir_mon_stop() - HDD monitor stop |
| * @dev: pointer to net_device |
| * |
| * Return: 0 for success and error number for failure |
| */ |
| static int __hdd_vir_mon_stop(struct net_device *dev) |
| { |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| hdd_adapter_t *sta_adapter = NULL; |
| int ret; |
| VOS_STATUS vos_status = VOS_STATUS_SUCCESS; |
| |
| if (!hdd_ctx->cfg_ini->mon_on_sta_enable) { |
| hddLog(LOGE, |
| FL("monitor feature for STA is not enabled")); |
| goto exit; |
| } |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_OPEN_REQUEST, |
| adapter->sessionId, adapter->device_mode)); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| /* if monitor is enabled already, |
| * send WMI cmd to disable target monitor. |
| */ |
| if (true == hdd_ctx->is_mon_enable) { |
| ret = wlan_hdd_monitor_mode_enable(hdd_ctx, false); |
| |
| /* get STA adapter to check if STA is connected */ |
| sta_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_INFRA_STATION); |
| |
| if (sta_adapter && |
| (WLAN_HDD_ADAPTER_MAGIC == sta_adapter->magic) && |
| hdd_connIsConnected( |
| WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter))) { |
| /* enable BMPS power save */ |
| if (hdd_ctx->cfg_ini->enablePowersaveOffload) |
| sme_PsOffloadEnablePowerSave( |
| WLAN_HDD_GET_HAL_CTX(sta_adapter), |
| sta_adapter->sessionId); |
| |
| #if defined(FEATURE_WLAN_LFR) && defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD) |
| /* enable roaming */ |
| if (hdd_ctx->cfg_ini->isFastRoamIniFeatureEnabled && |
| hdd_ctx->cfg_ini->isRoamOffloadScanEnabled) |
| sme_startRoaming(WLAN_HDD_GET_HAL_CTX(sta_adapter), |
| sta_adapter->sessionId, |
| REASON_CONNECT); |
| #endif |
| } |
| } |
| |
| /* Deregister monitor RX callback to TLSHIM and OL */ |
| vos_status = tl_deregister_vir_mon_cb(hdd_ctx->pvosContext); |
| |
| if (VOS_STATUS_SUCCESS != vos_status) { |
| hddLog(LOGE, |
| FL("failed to deregister monitor cbk")); |
| goto exit; |
| } |
| |
| return ret; |
| exit: |
| return -EIO; |
| } |
| |
| /** |
| * hdd_vir_mon_open() |
| * @dev: pointer to net_device |
| * |
| * Return: 0 for success and error number for failure |
| */ |
| static int hdd_vir_mon_open(struct net_device *dev) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| __hdd_vir_mon_open(dev); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * hdd_vir_mon_stop() |
| * @dev: pointer to net_device |
| * |
| * Return: 0 for success and error number for failure |
| */ |
| static int hdd_vir_mon_stop(struct net_device *dev) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| __hdd_vir_mon_stop(dev); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| #ifdef MODULE |
| /** |
| * wlan_hdd_stop_enter_lowpower() - Enter low power mode |
| * @hdd_ctx: HDD context |
| * |
| * For module, when all the interfaces are down, enter low power mode. |
| */ |
| void wlan_hdd_stop_enter_lowpower(hdd_context_t *hdd_ctx) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: All Interfaces are Down entering standby", |
| __func__); |
| if (VOS_STATUS_SUCCESS != wlan_hdd_enter_lowpower(hdd_ctx)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to put wlan in power save", |
| __func__); |
| } |
| } |
| |
| /** |
| * wlan_hdd_stop_can_enter_lowpower() - Enter low power mode |
| * @adapter: Adapter context |
| * |
| * Check if hardware can enter low power mode when all the interfaces are down. |
| */ |
| static inline int wlan_hdd_stop_can_enter_lowpower(hdd_adapter_t *adapter) |
| { |
| /* SoftAP ifaces should never go in power save mode making |
| * sure same here. |
| */ |
| if ((WLAN_HDD_SOFTAP == adapter->device_mode) || |
| (WLAN_HDD_MONITOR == adapter->device_mode) || |
| (WLAN_HDD_P2P_GO == adapter->device_mode)) |
| return 0; |
| |
| return 1; |
| } |
| #else |
| |
| /** |
| * kickstart_driver_handler() - Work queue handler for kickstart_driver |
| * |
| * Use worker queue to exit if it is not possible to call kickstart_driver() |
| * directly in the caller context like in interface down context |
| */ |
| static void kickstart_driver_handler(struct work_struct *work) |
| { |
| hdd_driver_exit(); |
| wlan_hdd_inited = 0; |
| } |
| |
| static DECLARE_WORK(kickstart_driver_work, kickstart_driver_handler); |
| |
| /** |
| * kickstart_driver() - Initialize and Clean-up driver |
| * @load: True: initialize, False: Clean-up driver |
| * @mode_change: tell if last mode and current mode is same or not |
| * |
| * Delayed driver initialization when driver is statically linked and Clean-up |
| * when all the interfaces are down or any other condition which requires to |
| * save power by bringing down hardware. |
| * This routine is invoked when module parameter fwpath is modified from user |
| * space to signal the initialization of the WLAN driver or when all the |
| * interfaces are down and user space no longer need WLAN interfaces. Userspace |
| * needs to write to fwpath again to get the WLAN interfaces |
| * |
| * Return: 0 on success, non zero on failure |
| */ |
| static int kickstart_driver(bool load, bool mode_change) |
| { |
| int ret_status; |
| |
| pr_info("%s: load: %d wlan_hdd_inited: %d, mode_change: %d caller: %pf\n", |
| __func__, load, wlan_hdd_inited, |
| mode_change, (void *)_RET_IP_); |
| |
| /* Make sure unload and load are synchronized */ |
| flush_work(&kickstart_driver_work); |
| |
| /* No-Op, If unload requested even though driver is not loaded */ |
| if (!load && !wlan_hdd_inited) |
| return 0; |
| |
| /* Unload is requested */ |
| if (!load && wlan_hdd_inited) { |
| schedule_work(&kickstart_driver_work); |
| return 0; |
| } |
| |
| if (!wlan_hdd_inited) { |
| ret_status = hdd_driver_init(); |
| wlan_hdd_inited = ret_status ? 0 : 1; |
| return ret_status; |
| } |
| |
| if (load && wlan_hdd_inited && !mode_change) { |
| ret_status = 0; |
| } else { |
| hdd_driver_exit(); |
| |
| msleep(200); |
| |
| ret_status = hdd_driver_init(); |
| wlan_hdd_inited = ret_status ? 0 : 1; |
| } |
| |
| return ret_status; |
| } |
| |
| /** |
| * wlan_hdd_stop_enter_lowpower() - Enter low power mode |
| * @hdd_ctx: HDD context |
| * |
| * For static driver, when all the interfaces are down, enter low power mode by |
| * bringing down WLAN hardware. |
| */ |
| void wlan_hdd_stop_enter_lowpower(hdd_context_t *hdd_ctx) |
| { |
| bool ready; |
| |
| /* Do not clean up n/w ifaces if we are in DRIVER STOP phase or else |
| * DRIVER START will fail and Wi-Fi will not resume successfully |
| */ |
| if (hdd_ctx && !hdd_ctx->driver_being_stopped) { |
| ready = vos_is_load_unload_ready(__func__); |
| if (!ready) { |
| VOS_ASSERT(0); |
| return; |
| } |
| |
| vos_load_unload_protect(__func__); |
| kickstart_driver(false, false); |
| vos_load_unload_unprotect(__func__); |
| } |
| } |
| |
| /** |
| * wlan_hdd_stop_can_enter_lowpower() - Enter low power mode |
| * @adapter: Adapter context |
| * |
| * Check if hardware can enter low power mode when all the interfaces are down. |
| * For static driver, hardware can enter low power mode for all types of |
| * interfaces. |
| * |
| * Return: true for power save allowed and false for power save not allowed |
| */ |
| static inline bool wlan_hdd_stop_can_enter_lowpower(hdd_adapter_t *adapter) |
| { |
| hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| |
| /* In static driver case, we need to distinguish between WiFi OFF and |
| * DRIVER STOP. In both cases "ifconfig down" is happening. In OFF case, |
| * want to allow lowest power mode and driver cleanup. In case of DRIVER |
| * STOP do not want to allow power collapse for GO/SAP case. STOP |
| * behavior is now identical across both DLKM and Static driver case. |
| */ |
| if (hdd_ctx && !hdd_ctx->driver_being_stopped) |
| return true; |
| else if ((WLAN_HDD_SOFTAP == adapter->device_mode) || |
| (WLAN_HDD_MONITOR == adapter->device_mode) || |
| (WLAN_HDD_P2P_GO == adapter->device_mode)) |
| return false; |
| else |
| return true; |
| } |
| #endif |
| |
| /** |
| * __hdd_stop() - HDD stop function |
| * @dev: pointer to net_device structure |
| * |
| * This is called in response to ifconfig down |
| * |
| * Return: 0 for success and error number for failure |
| */ |
| static int __hdd_stop(struct net_device *dev) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| v_BOOL_t enter_standby = TRUE; |
| int ret; |
| |
| ENTER(); |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_STOP_REQUEST, |
| pAdapter->sessionId, pAdapter->device_mode)); |
| |
| ret = wlan_hdd_validate_context(pHddCtx); |
| if (0 != ret) |
| return ret; |
| |
| /* Nothing to be done if the interface is not opened */ |
| if (VOS_FALSE == test_bit(DEVICE_IFACE_OPENED, &pAdapter->event_flags)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: NETDEV Interface is not OPENED", __func__); |
| return -ENODEV; |
| } |
| |
| /* Make sure the interface is marked as closed */ |
| clear_bit(DEVICE_IFACE_OPENED, &pAdapter->event_flags); |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: Disabling OS Tx queues", __func__); |
| |
| /* Disable TX on the interface, after this hard_start_xmit() will not |
| * be called on that interface |
| */ |
| hddLog(LOG1, FL("Disabling queues")); |
| wlan_hdd_netif_queue_control(pAdapter, WLAN_NETIF_TX_DISABLE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| |
| /* |
| * NAN data interface is different in some sense. The traffic on NDI is |
| * bursty in nature and depends on the need to transfer. The service layer |
| * may down the interface after the usage and up again when required. |
| * In some sense, the NDI is expected to be available (like SAP) iface |
| * until NDI delete request is issued by the service layer. |
| * Skip BSS termination and adapter deletion for NAN Data interface (NDI). |
| */ |
| if (WLAN_HDD_IS_NDI(pAdapter)) |
| return 0; |
| |
| /* The interface is marked as down for outside world (aka kernel) |
| * But the driver is pretty much alive inside. The driver needs to |
| * tear down the existing connection on the netdev (session) |
| * cleanup the data pipes and wait until the control plane is stabilized |
| * for this interface. The call also needs to wait until the above |
| * mentioned actions are completed before returning to the caller. |
| * Notice that the hdd_stop_adapter is requested not to close the session |
| * That is intentional to be able to scan if it is a STA/P2P interface |
| */ |
| hdd_stop_adapter(pHddCtx, pAdapter, VOS_FALSE); |
| |
| /* DeInit the adapter. This ensures datapath cleanup as well */ |
| hdd_deinit_adapter(pHddCtx, pAdapter, true); |
| |
| /* SoftAP ifaces should never go in power save mode |
| making sure same here. */ |
| if (!wlan_hdd_stop_can_enter_lowpower(pAdapter)) |
| { |
| /* SoftAP mode, so return from here */ |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: In SAP MODE", __func__); |
| EXIT(); |
| return 0; |
| } |
| |
| /* Find if any iface is up. If any iface is up then can't put device to |
| * sleep/power save mode |
| */ |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| while ( (NULL != pAdapterNode) && (VOS_STATUS_SUCCESS == status) ) |
| { |
| if (test_bit(DEVICE_IFACE_OPENED, &pAdapterNode->pAdapter->event_flags)) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: Still other ifaces are up cannot " |
| "put device to sleep", __func__); |
| enter_standby = FALSE; |
| break; |
| } |
| else |
| { |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| } |
| |
| if (TRUE == enter_standby) |
| wlan_hdd_stop_enter_lowpower(pHddCtx); |
| |
| EXIT(); |
| return 0; |
| } |
| |
| /** |
| * hdd_stop() - Wrapper function for __hdd_stop to protect it from SSR |
| * @dev: pointer to net_device structure |
| * |
| * This is called in response to ifconfig down |
| * |
| * Return: 0 for success and error number for failure |
| */ |
| static int hdd_stop (struct net_device *dev) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __hdd_stop(dev); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * __hdd_uninit() - HDD uninit function |
| * @dev: pointer to net_device structure |
| * |
| * This is called during the netdev unregister to uninitialize all data |
| * associated with the device |
| * |
| * Return: none |
| */ |
| static void __hdd_uninit(struct net_device *dev) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| |
| ENTER(); |
| |
| if (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic) { |
| hddLog(LOGP, FL("Invalid magic")); |
| return; |
| } |
| |
| if (NULL == pAdapter->pHddCtx) { |
| hddLog(LOGP, FL("NULL pHddCtx")); |
| return; |
| } |
| |
| if (dev != pAdapter->dev) |
| hddLog(LOGP, FL("Invalid device reference")); |
| |
| hdd_deinit_adapter(pAdapter->pHddCtx, pAdapter, true); |
| |
| /* After uninit our adapter structure will no longer be valid */ |
| pAdapter->dev = NULL; |
| pAdapter->magic = 0; |
| pAdapter->pHddCtx = NULL; |
| |
| EXIT(); |
| } |
| |
| /** |
| * hdd_uninit() - Wrapper function to protect __hdd_uninit from SSR |
| * @dev: pointer to net_device structure |
| * |
| * This is called during the netdev unregister to uninitialize all data |
| * associated with the device |
| * |
| * Return: none |
| */ |
| static void hdd_uninit(struct net_device *dev) |
| { |
| vos_ssr_protect(__func__); |
| __hdd_uninit(dev); |
| vos_ssr_unprotect(__func__); |
| } |
| |
| |
| /**--------------------------------------------------------------------------- |
| \brief hdd_full_pwr_cbk() - HDD full power callback function |
| |
| This is the function invoked by SME to inform the result of a full power |
| request issued by HDD |
| |
| \param - callback context - Pointer to cookie |
| status - result of request |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| void hdd_full_pwr_cbk(void *callbackContext, eHalStatus status) |
| { |
| hdd_context_t *pHddCtx = (hdd_context_t*)callbackContext; |
| |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH,"HDD full Power callback status = %d", status); |
| if(&pHddCtx->full_pwr_comp_var) |
| { |
| complete(&pHddCtx->full_pwr_comp_var); |
| } |
| } |
| |
| static void hdd_tx_fail_ind_callback(v_U8_t *MacAddr, v_U8_t seqNo) |
| { |
| int payload_len; |
| struct sk_buff *skb; |
| struct nlmsghdr *nlh; |
| v_U8_t *data; |
| |
| payload_len = ETH_ALEN; |
| |
| if (0 == cesium_pid || cesium_nl_srv_sock == NULL) { |
| hddLog(LOGE, FL("cesium process not registered, pid: %d, nl_sock: %pK"), |
| cesium_pid, cesium_nl_srv_sock); |
| return; |
| } |
| |
| if ((skb = nlmsg_new(payload_len,GFP_ATOMIC)) == NULL) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: nlmsg_new() failed for msg size[%d]", |
| __func__, NLMSG_SPACE(payload_len)); |
| return; |
| } |
| |
| nlh = nlmsg_put(skb, cesium_pid, seqNo, 0, payload_len, NLM_F_REQUEST); |
| |
| if (NULL == nlh) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: nlmsg_put() failed for msg size[%d]", |
| __func__, NLMSG_SPACE(payload_len)); |
| |
| kfree_skb(skb); |
| return; |
| } |
| |
| data = nlmsg_data(nlh); |
| memcpy(data, MacAddr, ETH_ALEN); |
| |
| if (nlmsg_unicast(cesium_nl_srv_sock, skb, cesium_pid) < 0) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: nlmsg_unicast() failed for msg size[%d]", |
| __func__, NLMSG_SPACE(payload_len)); |
| } |
| |
| return; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| \brief hdd_ParseuserParams - return a pointer to the next argument |
| |
| \return - status |
| |
| --------------------------------------------------------------------------*/ |
| static int hdd_ParseUserParams(tANI_U8 *pValue, tANI_U8 **ppArg) |
| { |
| tANI_U8 *pVal; |
| |
| pVal = strchr(pValue, ' '); |
| |
| if (NULL == pVal) |
| { |
| /* no argument remains */ |
| return -EINVAL; |
| } |
| else if (SPACE_ASCII_VALUE != *pVal) |
| { |
| /* no space after the current argument */ |
| return -EINVAL; |
| } |
| |
| pVal++; |
| |
| /* remove empty spaces */ |
| while ((SPACE_ASCII_VALUE == *pVal) && ('\0' != *pVal)) |
| { |
| pVal++; |
| } |
| |
| /* no argument followed by spaces */ |
| if ('\0' == *pVal) |
| { |
| return -EINVAL; |
| } |
| |
| *ppArg = pVal; |
| |
| return 0; |
| } |
| |
| /**---------------------------------------------------------------------------- |
| \brief hdd_ParseIBSSTXFailEventParams - Parse params for SETIBSSTXFAILEVENT |
| |
| \return - status |
| |
| ------------------------------------------------------------------------------*/ |
| static int hdd_ParseIBSSTXFailEventParams(tANI_U8 *pValue, |
| tANI_U8 *tx_fail_count, |
| tANI_U16 *pid) |
| { |
| tANI_U8 *param = NULL; |
| int ret; |
| |
| ret = hdd_ParseUserParams(pValue, ¶m); |
| |
| if (0 == ret && NULL != param) |
| { |
| if (1 != sscanf(param, "%hhu", tx_fail_count)) |
| { |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| else |
| { |
| goto done; |
| } |
| |
| if (0 == *tx_fail_count) |
| { |
| *pid = 0; |
| goto done; |
| } |
| |
| pValue = param; |
| pValue++; |
| |
| ret = hdd_ParseUserParams(pValue, ¶m); |
| |
| if (0 == ret) |
| { |
| if (1 != sscanf(param, "%hu", pid)) |
| { |
| ret = -EINVAL; |
| goto done; |
| } |
| } |
| else |
| { |
| goto done; |
| } |
| |
| done: |
| return ret; |
| } |
| |
| static int hdd_open_cesium_nl_sock(void) |
| { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) |
| struct netlink_kernel_cfg cfg = { |
| .groups = WLAN_NLINK_MCAST_GRP_ID, |
| .input = NULL |
| }; |
| #endif |
| int ret = 0; |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) |
| cesium_nl_srv_sock = netlink_kernel_create(&init_net, WLAN_NLINK_CESIUM, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)) |
| THIS_MODULE, |
| #endif |
| &cfg); |
| #else |
| cesium_nl_srv_sock = netlink_kernel_create(&init_net, WLAN_NLINK_CESIUM, |
| WLAN_NLINK_MCAST_GRP_ID, NULL, NULL, THIS_MODULE); |
| #endif |
| |
| if (cesium_nl_srv_sock == NULL) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "NLINK: cesium netlink_kernel_create failed"); |
| ret = -ECONNREFUSED; |
| } |
| |
| return ret; |
| } |
| |
| static void hdd_close_cesium_nl_sock(void) |
| { |
| if (NULL != cesium_nl_srv_sock) |
| { |
| netlink_kernel_release(cesium_nl_srv_sock); |
| cesium_nl_srv_sock = NULL; |
| } |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_get_cfg_file_size() - |
| |
| This function reads the configuration file using the request firmware |
| API and returns the configuration file size. |
| |
| \param - pCtx - Pointer to the adapter . |
| - pFileName - Pointer to the file name. |
| - pBufSize - Pointer to the buffer size. |
| |
| \return - 0 for success, non zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| |
| VOS_STATUS hdd_get_cfg_file_size(v_VOID_t *pCtx, char *pFileName, v_SIZE_t *pBufSize) |
| { |
| int status; |
| hdd_context_t *pHddCtx = (hdd_context_t*)pCtx; |
| |
| ENTER(); |
| |
| status = request_firmware(&pHddCtx->fw, pFileName, pHddCtx->parent_dev); |
| |
| if(status || !pHddCtx->fw || !pHddCtx->fw->data) { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: CFG download failed",__func__); |
| status = VOS_STATUS_E_FAILURE; |
| } |
| else { |
| *pBufSize = pHddCtx->fw->size; |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: CFG size = %d", __func__, *pBufSize); |
| release_firmware(pHddCtx->fw); |
| pHddCtx->fw = NULL; |
| } |
| |
| EXIT(); |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_read_cfg_file() - |
| |
| This function reads the configuration file using the request firmware |
| API and returns the cfg data and the buffer size of the configuration file. |
| |
| \param - pCtx - Pointer to the adapter . |
| - pFileName - Pointer to the file name. |
| - pBuffer - Pointer to the data buffer. |
| - pBufSize - Pointer to the buffer size. |
| |
| \return - 0 for success, non zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| |
| VOS_STATUS hdd_read_cfg_file(v_VOID_t *pCtx, char *pFileName, |
| v_VOID_t *pBuffer, v_SIZE_t *pBufSize) |
| { |
| int status; |
| hdd_context_t *pHddCtx = (hdd_context_t*)pCtx; |
| |
| ENTER(); |
| |
| status = request_firmware(&pHddCtx->fw, pFileName, pHddCtx->parent_dev); |
| |
| if(status || !pHddCtx->fw || !pHddCtx->fw->data) { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: CFG download failed",__func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| else { |
| if(*pBufSize != pHddCtx->fw->size) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Caller sets invalid CFG " |
| "file size", __func__); |
| release_firmware(pHddCtx->fw); |
| pHddCtx->fw = NULL; |
| return VOS_STATUS_E_FAILURE; |
| } |
| else { |
| if(pBuffer) { |
| vos_mem_copy(pBuffer,pHddCtx->fw->data,*pBufSize); |
| } |
| release_firmware(pHddCtx->fw); |
| pHddCtx->fw = NULL; |
| } |
| } |
| |
| EXIT(); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| /** |
| * __hdd_set_mac_address() - HDD set mac address |
| * @dev: pointer to net_device structure |
| * @addr: Pointer to the sockaddr |
| * |
| * This function sets the user specified mac address using |
| * the command ifconfig wlanX hw ether <mac adress>. |
| * |
| * Return: 0 for success. |
| */ |
| static int __hdd_set_mac_address(struct net_device *dev, void *addr) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *hdd_ctx; |
| struct sockaddr *psta_mac_addr = addr; |
| int ret; |
| |
| ENTER(); |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(pAdapter); |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| memcpy(&pAdapter->macAddressCurrent, psta_mac_addr->sa_data, ETH_ALEN); |
| memcpy(dev->dev_addr, psta_mac_addr->sa_data, ETH_ALEN); |
| |
| EXIT(); |
| return 0; |
| } |
| |
| /** |
| * hdd_set_mac_address() - Wrapper function to protect __hdd_set_mac_address() |
| * function from SSR |
| * @dev: pointer to net_device structure |
| * @addr: Pointer to the sockaddr |
| * |
| * This function sets the user specified mac address using |
| * the command ifconfig wlanX hw ether <mac adress>. |
| * |
| * Return: 0 for success. |
| */ |
| static int hdd_set_mac_address(struct net_device *dev, void *addr) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __hdd_set_mac_address(dev, addr); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| tANI_U8* wlan_hdd_get_intf_addr(hdd_context_t* pHddCtx) |
| { |
| int i; |
| for ( i = 0; i < VOS_MAX_CONCURRENCY_PERSONA; i++) |
| { |
| if( 0 == ((pHddCtx->cfg_ini->intfAddrMask) & (1 << i))) |
| break; |
| } |
| |
| if( VOS_MAX_CONCURRENCY_PERSONA == i) |
| return NULL; |
| |
| pHddCtx->cfg_ini->intfAddrMask |= (1 << i); |
| return &pHddCtx->cfg_ini->intfMacAddr[i].bytes[0]; |
| } |
| |
| void wlan_hdd_release_intf_addr(hdd_context_t* pHddCtx, tANI_U8* releaseAddr) |
| { |
| int i; |
| for ( i = 0; i < VOS_MAX_CONCURRENCY_PERSONA; i++) |
| { |
| if ( !memcmp(releaseAddr, &pHddCtx->cfg_ini->intfMacAddr[i].bytes[0], 6) ) |
| { |
| pHddCtx->cfg_ini->intfAddrMask &= ~(1 << i); |
| break; |
| } |
| } |
| return; |
| } |
| |
| #ifdef WLAN_FEATURE_PACKET_FILTERING |
| /** |
| * __hdd_set_multicast_list() - set the multicast address list |
| * @dev: pointer to net_device |
| * |
| * Return: none |
| */ |
| static void __hdd_set_multicast_list(struct net_device *dev) |
| { |
| static const uint8_t ipv6_router_solicitation[] = |
| {0x33, 0x33, 0x00, 0x00, 0x00, 0x02}; |
| hdd_adapter_t *pAdapter; |
| hdd_context_t *pHddCtx; |
| int mc_count; |
| int i = 0; |
| struct netdev_hw_addr *ha; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) |
| return; |
| |
| pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| if (0 != wlan_hdd_validate_context(pHddCtx)) |
| return; |
| |
| if (!pHddCtx->cfg_ini->fEnableMCAddrList) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("mc addr list ini is disabled")); |
| return; |
| } |
| |
| /* Delete already configured multicast address list */ |
| if (0 < pAdapter->mc_addr_list.mc_cnt) |
| if (wlan_hdd_set_mc_addr_list(pAdapter, false)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("failed to clear mc addr list")); |
| return; |
| } |
| |
| |
| if (dev->flags & IFF_ALLMULTI) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: allow all multicast frames", __func__); |
| pAdapter->mc_addr_list.mc_cnt = 0; |
| } |
| else |
| { |
| mc_count = netdev_mc_count(dev); |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: mc_count : %u, max_mc_addr_list : %d", |
| __func__, mc_count, pHddCtx->max_mc_addr_list); |
| |
| if (mc_count > pHddCtx->max_mc_addr_list) { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: No free filter available; allow all multicast frames", |
| __func__); |
| pAdapter->mc_addr_list.mc_cnt = 0; |
| return; |
| } |
| |
| netdev_for_each_mc_addr(ha, dev) { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("ha_addr[%d] "MAC_ADDRESS_STR), |
| i, MAC_ADDR_ARRAY(ha->addr)); |
| |
| if (i == mc_count) |
| break; |
| /* |
| * Skip following addresses: |
| * 1)IPv6 router solicitation address |
| * 2)Any other address pattern if its set during RXFILTER REMOVE |
| * driver command based on addr_filter_pattern |
| */ |
| if ((!memcmp(ha->addr, ipv6_router_solicitation, ETH_ALEN)) || |
| (pAdapter->addr_filter_pattern && (!memcmp(ha->addr, |
| &pAdapter->addr_filter_pattern, 1)))) { |
| hddLog(LOG1, FL("MC/BC filtering Skip addr ="MAC_ADDRESS_STR), |
| MAC_ADDR_ARRAY(ha->addr)); |
| |
| continue; |
| } |
| |
| memset(&(pAdapter->mc_addr_list.addr[i * ETH_ALEN]), 0, ETH_ALEN); |
| memcpy(&(pAdapter->mc_addr_list.addr[i * ETH_ALEN]), |
| ha->addr, ETH_ALEN); |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: mlist[%d] = "MAC_ADDRESS_STR, |
| __func__, i, |
| MAC_ADDR_ARRAY(&pAdapter->mc_addr_list.addr[i * ETH_ALEN])); |
| pAdapter->mc_addr_list.mc_cnt++; |
| i++; |
| } |
| } |
| |
| /* Configure the updated multicast address list */ |
| if (wlan_hdd_set_mc_addr_list(pAdapter, true)) |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("failed to set mc addr list")); |
| |
| EXIT(); |
| return; |
| } |
| |
| /** |
| * hdd_set_multicast_list() - SSR wrapper function for __hdd_set_multicast_list |
| * @dev: pointer to net_device |
| * |
| * Return: none |
| */ |
| static void hdd_set_multicast_list(struct net_device *dev) |
| { |
| vos_ssr_protect(__func__); |
| __hdd_set_multicast_list(dev); |
| vos_ssr_unprotect(__func__); |
| } |
| #endif |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_select_queue() - |
| |
| This function is registered with the Linux OS for network |
| core to decide which queue to use first. |
| |
| \param - dev - Pointer to the WLAN device. |
| - skb - Pointer to OS packet (sk_buff). |
| \return - ac, Queue Index/access category corresponding to UP in IP header |
| |
| --------------------------------------------------------------------------*/ |
| static v_U16_t hdd_select_queue(struct net_device *dev, |
| struct sk_buff *skb |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)) |
| , void *accel_priv |
| #endif |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) |
| , select_queue_fallback_t fallback |
| #endif |
| ) |
| { |
| return hdd_wmm_select_queue(dev, skb); |
| } |
| |
| static struct net_device_ops wlan_drv_ops = { |
| .ndo_open = hdd_open, |
| .ndo_stop = hdd_stop, |
| .ndo_uninit = hdd_uninit, |
| .ndo_start_xmit = hdd_hard_start_xmit, |
| .ndo_tx_timeout = hdd_tx_timeout, |
| .ndo_get_stats = hdd_stats, |
| .ndo_do_ioctl = hdd_ioctl, |
| .ndo_set_mac_address = hdd_set_mac_address, |
| .ndo_select_queue = hdd_select_queue, |
| #ifdef WLAN_FEATURE_PACKET_FILTERING |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(3,1,0)) || defined(WITH_BACKPORTS) |
| .ndo_set_rx_mode = hdd_set_multicast_list, |
| #else |
| .ndo_set_multicast_list = hdd_set_multicast_list, |
| #endif //LINUX_VERSION_CODE |
| #endif |
| }; |
| static struct net_device_ops wlan_mon_drv_ops = { |
| .ndo_open = hdd_mon_open, |
| .ndo_stop = hdd_stop, |
| #ifdef CONFIG_HL_SUPPORT |
| .ndo_start_xmit = hdd_hard_start_xmit, |
| #endif |
| .ndo_get_stats = hdd_stats, |
| }; |
| |
| static struct net_device_ops wlan_mon_dev_ops = { |
| .ndo_open = hdd_vir_mon_open, |
| .ndo_stop = hdd_vir_mon_stop, |
| }; |
| |
| void hdd_set_station_ops( struct net_device *pWlanDev ) |
| { |
| if (VOS_MONITOR_MODE == hdd_get_conparam()) |
| pWlanDev->netdev_ops = &wlan_mon_drv_ops; |
| else |
| pWlanDev->netdev_ops = &wlan_drv_ops; |
| } |
| |
| void hdd_set_monitor_ops(struct net_device *pwlan_dev) |
| { |
| pwlan_dev->netdev_ops = &wlan_mon_dev_ops; |
| } |
| |
| static void mon_mode_ether_setup(struct net_device *dev) |
| { |
| dev->header_ops = NULL; |
| dev->type = ARPHRD_IEEE80211_RADIOTAP; |
| dev->hard_header_len = ETH_HLEN; |
| dev->mtu = ETH_DATA_LEN; |
| dev->addr_len = ETH_ALEN; |
| dev->tx_queue_len = 1000; /* Ethernet wants good queues */ |
| dev->flags = IFF_BROADCAST|IFF_MULTICAST; |
| dev->priv_flags |= IFF_TX_SKB_SHARING; |
| |
| memset(dev->broadcast, 0xFF, ETH_ALEN); |
| } |
| |
| #ifdef FEATURE_RUNTIME_PM |
| /** |
| * hdd_runtime_suspend_init() - API to initialize runtime pm context |
| * @hdd_ctx: HDD Context |
| * |
| * The API initializes the context to prevent runtime pm for various use |
| * cases like scan, roc, dfs. |
| * This API can be extended to initialize the context to prevent runtime pm |
| * |
| * Return: void |
| */ |
| void hdd_runtime_suspend_init(hdd_context_t *hdd_ctx) |
| { |
| struct hdd_runtime_pm_context *context = &hdd_ctx->runtime_context; |
| |
| context->scan = vos_runtime_pm_prevent_suspend_init("scan"); |
| context->roc = vos_runtime_pm_prevent_suspend_init("roc"); |
| context->dfs = vos_runtime_pm_prevent_suspend_init("dfs"); |
| context->obss = vos_runtime_pm_prevent_suspend_init("obss"); |
| } |
| |
| /** |
| * hdd_runtime_suspend_deinit() - API to deinit runtime pm context |
| * @hdd_ctx: HDD context |
| * |
| * The API deinit the context to prevent runtime pm. |
| * |
| * Return: void |
| */ |
| void hdd_runtime_suspend_deinit(hdd_context_t *hdd_ctx) |
| { |
| struct hdd_runtime_pm_context *context = &hdd_ctx->runtime_context; |
| |
| vos_runtime_pm_prevent_suspend_deinit(context->scan); |
| context->scan = NULL; |
| vos_runtime_pm_prevent_suspend_deinit(context->roc); |
| context->roc = NULL; |
| vos_runtime_pm_prevent_suspend_deinit(context->dfs); |
| context->dfs = NULL; |
| vos_runtime_pm_prevent_suspend_deinit(context->obss); |
| context->obss = NULL; |
| } |
| |
| /** |
| * hdd_adapter_runtime_suspend_init() - API to init runtime pm context/adapter |
| * @adapter: Interface Adapter |
| * |
| * API is used to init the context to prevent runtime pm/adapter |
| * |
| * Return: void |
| */ |
| static void |
| hdd_adapter_runtime_suspend_init(hdd_adapter_t *adapter) |
| { |
| struct hdd_adapter_pm_context *context = &adapter->runtime_context; |
| |
| context->connect = vos_runtime_pm_prevent_suspend_init("connect"); |
| } |
| |
| /** |
| * hdd_adapter_runtime_suspend_denit() - API to deinit runtime pm/adapter |
| * @adapter: Interface Adapter |
| * |
| * API is used to deinit the context to prevent runtime pm/adapter |
| * |
| * Return: void |
| */ |
| static void hdd_adapter_runtime_suspend_denit(hdd_adapter_t *adapter) |
| { |
| struct hdd_adapter_pm_context *context = &adapter->runtime_context; |
| |
| vos_runtime_pm_prevent_suspend_deinit(context->connect); |
| context->connect = NULL; |
| } |
| |
| #else |
| void hdd_runtime_suspend_init(hdd_context_t *hdd_ctx) { } |
| void hdd_runtime_suspend_deinit(hdd_context_t *hdd_ctx) { } |
| static inline void |
| hdd_adapter_runtime_suspend_init(hdd_adapter_t *adapter) { } |
| static inline void |
| hdd_adapter_runtime_suspend_denit(hdd_adapter_t *adapter) { } |
| #endif |
| |
| /** |
| * hdd_adapter_init_action_frame_random_mac() - Initialze attributes needed for |
| * randomization of SA in management action frames |
| * @adapter: Pointer to adapter |
| * |
| * Return: None |
| */ |
| static void hdd_adapter_init_action_frame_random_mac(hdd_adapter_t *adapter) |
| { |
| spin_lock_init(&adapter->random_mac_lock); |
| vos_mem_zero(adapter->random_mac, sizeof(adapter->random_mac)); |
| } |
| |
| static hdd_adapter_t* hdd_alloc_station_adapter(hdd_context_t *pHddCtx, |
| tSirMacAddr macAddr, |
| unsigned char name_assign_type, |
| const char* name) |
| { |
| struct net_device *pWlanDev = NULL; |
| hdd_adapter_t *pAdapter = NULL; |
| /* |
| * cfg80211 initialization and registration.... |
| */ |
| pWlanDev = alloc_netdev_mq(sizeof( hdd_adapter_t ), |
| name, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)) || defined(WITH_BACKPORTS) |
| name_assign_type, |
| #endif |
| (VOS_MONITOR_MODE == vos_get_conparam()? |
| mon_mode_ether_setup : ether_setup), |
| NUM_TX_QUEUES); |
| |
| if(pWlanDev != NULL) |
| { |
| |
| //Save the pointer to the net_device in the HDD adapter |
| pAdapter = (hdd_adapter_t*) netdev_priv( pWlanDev ); |
| |
| vos_mem_zero( pAdapter, sizeof( hdd_adapter_t ) ); |
| |
| pAdapter->dev = pWlanDev; |
| pAdapter->pHddCtx = pHddCtx; |
| pAdapter->magic = WLAN_HDD_ADAPTER_MAGIC; |
| |
| vos_event_init(&pAdapter->scan_info.scan_finished_event); |
| pAdapter->scan_info.scan_pending_option = WEXT_SCAN_PENDING_GIVEUP; |
| |
| pAdapter->offloads_configured = FALSE; |
| pAdapter->isLinkUpSvcNeeded = FALSE; |
| pAdapter->higherDtimTransition = eANI_BOOLEAN_TRUE; |
| //Init the net_device structure |
| strlcpy(pWlanDev->name, name, IFNAMSIZ); |
| |
| vos_mem_copy(pWlanDev->dev_addr, (void *)macAddr, sizeof(tSirMacAddr)); |
| vos_mem_copy( pAdapter->macAddressCurrent.bytes, macAddr, sizeof(tSirMacAddr)); |
| pWlanDev->watchdog_timeo = HDD_TX_TIMEOUT; |
| /* |
| * kernel will consume ethernet header length buffer for hard_header, |
| * so just reserve it |
| */ |
| hdd_set_needed_headroom(pWlanDev, pWlanDev->hard_header_len); |
| |
| if (pHddCtx->cfg_ini->enableIPChecksumOffload) |
| pWlanDev->features |= NETIF_F_HW_CSUM; |
| else if (pHddCtx->cfg_ini->enableTCPChkSumOffld) |
| pWlanDev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; |
| pWlanDev->features |= NETIF_F_RXCSUM; |
| hdd_set_station_ops( pAdapter->dev ); |
| |
| pWlanDev->destructor = free_netdev; |
| pWlanDev->ieee80211_ptr = &pAdapter->wdev ; |
| pWlanDev->tx_queue_len = HDD_NETDEV_TX_QUEUE_LEN; |
| pAdapter->wdev.wiphy = pHddCtx->wiphy; |
| pAdapter->wdev.netdev = pWlanDev; |
| /* set pWlanDev's parent to underlying device */ |
| SET_NETDEV_DEV(pWlanDev, pHddCtx->parent_dev); |
| hdd_wmm_init( pAdapter ); |
| hdd_adapter_runtime_suspend_init(pAdapter); |
| spin_lock_init(&pAdapter->pause_map_lock); |
| pAdapter->last_tx_jiffies = jiffies; |
| pAdapter->bug_report_count = 0; |
| pAdapter->start_time = pAdapter->last_time = vos_system_ticks(); |
| } |
| |
| return pAdapter; |
| } |
| |
| static hdd_adapter_t *hdd_alloc_monitor_adapter(hdd_context_t *pHddCtx, |
| tSirMacAddr macAddr, |
| unsigned char name_assign_type, |
| const char *name) |
| { |
| struct net_device *pwlan_dev = NULL; |
| hdd_adapter_t *pAdapter = NULL; |
| /* |
| * cfg80211 initialization and registration.... |
| */ |
| pwlan_dev = alloc_netdev_mq(sizeof(hdd_adapter_t), |
| name, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) || defined(WITH_BACKPORTS) |
| name_assign_type, |
| #endif |
| mon_mode_ether_setup, |
| NUM_TX_QUEUES); |
| |
| if (pwlan_dev != NULL) { |
| /* Save the pointer to the net_device in the HDD adapter */ |
| pAdapter = (hdd_adapter_t *)netdev_priv(pwlan_dev); |
| |
| vos_mem_zero(pAdapter, sizeof(hdd_adapter_t)); |
| |
| pAdapter->dev = pwlan_dev; |
| pAdapter->pHddCtx = pHddCtx; |
| pAdapter->magic = WLAN_HDD_ADAPTER_MAGIC; |
| |
| vos_event_init(&pAdapter->scan_info.scan_finished_event); |
| pAdapter->scan_info.scan_pending_option = WEXT_SCAN_PENDING_GIVEUP; |
| |
| pAdapter->offloads_configured = FALSE; |
| pAdapter->isLinkUpSvcNeeded = FALSE; |
| pAdapter->higherDtimTransition = eANI_BOOLEAN_TRUE; |
| /* Init the net_device structure */ |
| strlcpy(pwlan_dev->name, name, IFNAMSIZ); |
| |
| vos_mem_copy(pwlan_dev->dev_addr, |
| (void *)macAddr, sizeof(tSirMacAddr)); |
| vos_mem_copy(pAdapter->macAddressCurrent.bytes, |
| macAddr, sizeof(tSirMacAddr)); |
| pwlan_dev->watchdog_timeo = HDD_TX_TIMEOUT; |
| /* |
| * kernel will consume ethernet header length buffer for hard_header, |
| * so just reserve it |
| */ |
| hdd_set_needed_headroom(pwlan_dev, pwlan_dev->hard_header_len); |
| |
| if (pHddCtx->cfg_ini->enableIPChecksumOffload) |
| pwlan_dev->features |= NETIF_F_HW_CSUM; |
| else if (pHddCtx->cfg_ini->enableTCPChkSumOffld) |
| pwlan_dev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; |
| pwlan_dev->features |= NETIF_F_RXCSUM; |
| hdd_set_monitor_ops(pAdapter->dev); |
| |
| pwlan_dev->destructor = free_netdev; |
| pwlan_dev->ieee80211_ptr = &pAdapter->wdev; |
| pwlan_dev->tx_queue_len = HDD_NETDEV_TX_QUEUE_LEN; |
| pAdapter->wdev.wiphy = pHddCtx->wiphy; |
| pAdapter->wdev.netdev = pwlan_dev; |
| /* set pwlan_dev's parent to underlying device */ |
| SET_NETDEV_DEV(pwlan_dev, pHddCtx->parent_dev); |
| hdd_wmm_init(pAdapter); |
| hdd_adapter_runtime_suspend_init(pAdapter); |
| spin_lock_init(&pAdapter->pause_map_lock); |
| pAdapter->last_tx_jiffies = jiffies; |
| pAdapter->bug_report_count = 0; |
| pAdapter->start_time = pAdapter->last_time = vos_system_ticks(); |
| } |
| |
| return pAdapter; |
| } |
| |
| VOS_STATUS hdd_register_interface( hdd_adapter_t *pAdapter, tANI_U8 rtnl_lock_held ) |
| { |
| struct net_device *pWlanDev = pAdapter->dev; |
| |
| if( rtnl_lock_held ) |
| { |
| if (strnchr(pWlanDev->name, strlen(pWlanDev->name), '%')) { |
| if( dev_alloc_name(pWlanDev, pWlanDev->name) < 0 ) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s:Failed:dev_alloc_name",__func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| } |
| if (register_netdevice(pWlanDev)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s:Failed:register_netdev",__func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| } |
| else |
| { |
| if(register_netdev(pWlanDev)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Failed:register_netdev",__func__); |
| return VOS_STATUS_E_FAILURE; |
| } |
| } |
| set_bit(NET_DEVICE_REGISTERED, &pAdapter->event_flags); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| /** |
| * hdd_smeCloseSessionCallback() - HDD callback function |
| * @pContext: adapter context |
| * |
| * This is a callback function HDD registers with SME. |
| * |
| * Returns: eHalstatus |
| */ |
| eHalStatus hdd_smeCloseSessionCallback(void *pContext) |
| { |
| hdd_adapter_t *pAdapter = pContext; |
| |
| if (NULL == pAdapter) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: NULL pAdapter", __func__); |
| return eHAL_STATUS_INVALID_PARAMETER; |
| } |
| |
| if (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Invalid magic", __func__); |
| return eHAL_STATUS_NOT_INITIALIZED; |
| } |
| |
| /* |
| * For NAN Data interface, the close session results in the final |
| * indication to the userspace |
| */ |
| hdd_ndp_session_end_handler(pAdapter); |
| |
| clear_bit(SME_SESSION_OPENED, &pAdapter->event_flags); |
| |
| #if !defined (CONFIG_CNSS) && \ |
| !defined (WLAN_OPEN_SOURCE) |
| /* need to make sure all of our scheduled work has completed. |
| * This callback is called from MC thread context, so it is safe to |
| * to call below flush work queue API from here. |
| * |
| * Even though this is called from MC thread context, if there is a faulty |
| * work item in the system, that can hang this call forever. So flushing |
| * this global work queue is not safe; and now we make sure that |
| * individual work queues are stopped correctly. But the cancel work queue |
| * is a GPL only API, so the proprietary version of the driver would still |
| * rely on the global work queue flush. |
| */ |
| flush_scheduled_work(); |
| #endif |
| |
| /* We can be blocked while waiting for scheduled work to be |
| * flushed, and the adapter structure can potentially be freed, in |
| * which case the magic will have been reset. So make sure the |
| * magic is still good, and hence the adapter structure is still |
| * valid, before signalling completion */ |
| if (WLAN_HDD_ADAPTER_MAGIC == pAdapter->magic) |
| { |
| complete(&pAdapter->session_close_comp_var); |
| } |
| |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| /** |
| * hdd_close_tx_queues() - close tx queues |
| * @hdd_ctx: hdd global context |
| * |
| * Return: None |
| */ |
| static void hdd_close_tx_queues(hdd_context_t *hdd_ctx) |
| { |
| VOS_STATUS status; |
| hdd_adapter_t *adapter; |
| hdd_adapter_list_node_t *adapter_node = NULL, *next_adapter = NULL; |
| /* Not validating hdd_ctx as it's already done by the caller */ |
| ENTER(); |
| status = hdd_get_front_adapter(hdd_ctx, &adapter_node); |
| while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) { |
| adapter = adapter_node->pAdapter; |
| if (adapter && adapter->dev) { |
| wlan_hdd_netif_queue_control(adapter, |
| WLAN_NETIF_TX_DISABLE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| } |
| status = hdd_get_next_adapter(hdd_ctx, adapter_node, |
| &next_adapter); |
| adapter_node = next_adapter; |
| } |
| EXIT(); |
| } |
| |
| /** |
| * hdd_check_and_init_tdls() - check and init TDLS operation for desired mode |
| * @adapter: pointer to device adapter |
| * @type: type of interface |
| * |
| * This routine will check the mode of adapter and if it is required then it |
| * will initialize the TDLS operations |
| * |
| * Return: VOS_STATUS |
| */ |
| #ifdef FEATURE_WLAN_TDLS |
| static VOS_STATUS hdd_check_and_init_tdls(hdd_adapter_t *adapter, uint32_t type) |
| { |
| if (VOS_IBSS_MODE != type) { |
| if (0 != wlan_hdd_tdls_init(adapter)) { |
| hddLog(LOGE, FL("wlan_hdd_tdls_init failed")); |
| return VOS_STATUS_E_FAILURE; |
| } |
| set_bit(TDLS_INIT_DONE, &adapter->event_flags); |
| } |
| return VOS_STATUS_SUCCESS; |
| } |
| #else |
| static VOS_STATUS hdd_check_and_init_tdls(hdd_adapter_t *adapter, uint32_t type) |
| { |
| return VOS_STATUS_SUCCESS; |
| } |
| #endif |
| |
| VOS_STATUS hdd_init_station_mode( hdd_adapter_t *pAdapter ) |
| { |
| struct net_device *pWlanDev = pAdapter->dev; |
| hdd_station_ctx_t *pHddStaCtx = &pAdapter->sessionCtx.station; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter ); |
| eHalStatus halStatus = eHAL_STATUS_SUCCESS; |
| VOS_STATUS status = VOS_STATUS_E_FAILURE; |
| tANI_U32 type, subType; |
| unsigned long rc; |
| int ret_val; |
| |
| INIT_COMPLETION(pAdapter->session_open_comp_var); |
| sme_SetCurrDeviceMode(pHddCtx->hHal, pAdapter->device_mode); |
| sme_set_pdev_ht_vht_ies(pHddCtx->hHal, pHddCtx->cfg_ini->enable2x2); |
| status = vos_get_vdev_types(pAdapter->device_mode, &type, &subType); |
| if (VOS_STATUS_SUCCESS != status) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "failed to get vdev type"); |
| goto error_sme_open; |
| } |
| |
| ret_val = process_wma_set_command((int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_BURST_ENABLE, |
| (int)pHddCtx->cfg_ini->enableSifsBurst, |
| PDEV_CMD); |
| |
| if (0 != ret_val) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: WMI_PDEV_PARAM_BURST_ENABLE set failed %d", |
| __func__, ret_val); |
| } |
| |
| //Open a SME session for future operation |
| halStatus = sme_OpenSession( pHddCtx->hHal, hdd_smeRoamCallback, pAdapter, |
| (tANI_U8 *)&pAdapter->macAddressCurrent, &pAdapter->sessionId, |
| type, subType); |
| if ( !HAL_STATUS_SUCCESS( halStatus ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "sme_OpenSession() failed with status code %08d [x%08x]", |
| halStatus, halStatus ); |
| status = VOS_STATUS_E_FAILURE; |
| goto error_sme_open; |
| } |
| |
| //Block on a completion variable. Can't wait forever though. |
| rc = wait_for_completion_timeout( |
| &pAdapter->session_open_comp_var, |
| msecs_to_jiffies(WLAN_WAIT_TIME_SESSIONOPENCLOSE)); |
| if (!rc) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| FL("Session is not opened within timeout period code %ld"), |
| rc ); |
| status = VOS_STATUS_E_FAILURE; |
| goto error_sme_open; |
| } |
| |
| // Register wireless extensions |
| if( eHAL_STATUS_SUCCESS != (halStatus = hdd_register_wext(pWlanDev))) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "hdd_register_wext() failed with status code %08d [x%08x]", |
| halStatus, halStatus ); |
| status = VOS_STATUS_E_FAILURE; |
| goto error_register_wext; |
| } |
| |
| //Set the Connection State to Not Connected |
| hdd_connSetConnectionState(pAdapter, eConnectionState_NotConnected); |
| |
| //Set the default operation channel |
| pHddStaCtx->conn_info.operationChannel = pHddCtx->cfg_ini->OperatingChannel; |
| |
| /* Make the default Auth Type as OPEN*/ |
| pHddStaCtx->conn_info.authType = eCSR_AUTH_TYPE_OPEN_SYSTEM; |
| |
| if( VOS_STATUS_SUCCESS != ( status = hdd_init_tx_rx( pAdapter ) ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "hdd_init_tx_rx() failed with status code %08d [x%08x]", |
| status, status ); |
| goto error_init_txrx; |
| } |
| |
| set_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags); |
| |
| if( VOS_STATUS_SUCCESS != ( status = hdd_wmm_adapter_init( pAdapter ) ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "hdd_wmm_adapter_init() failed with status code %08d [x%08x]", |
| status, status ); |
| goto error_wmm_init; |
| } |
| |
| set_bit(WMM_INIT_DONE, &pAdapter->event_flags); |
| |
| status = hdd_check_and_init_tdls(pAdapter, type); |
| if (status != VOS_STATUS_SUCCESS) |
| goto error_tdls_init; |
| |
| return VOS_STATUS_SUCCESS; |
| |
| error_tdls_init: |
| clear_bit(WMM_INIT_DONE, &pAdapter->event_flags); |
| hdd_wmm_adapter_close(pAdapter); |
| error_wmm_init: |
| clear_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags); |
| hdd_deinit_tx_rx(pAdapter); |
| error_init_txrx: |
| hdd_UnregisterWext(pWlanDev); |
| error_register_wext: |
| if (test_bit(SME_SESSION_OPENED, &pAdapter->event_flags)) |
| { |
| INIT_COMPLETION(pAdapter->session_close_comp_var); |
| if (eHAL_STATUS_SUCCESS == sme_CloseSession(pHddCtx->hHal, |
| pAdapter->sessionId, |
| hdd_smeCloseSessionCallback, pAdapter)) |
| { |
| unsigned long rc; |
| |
| //Block on a completion variable. Can't wait forever though. |
| rc = wait_for_completion_timeout( |
| &pAdapter->session_close_comp_var, |
| msecs_to_jiffies(WLAN_WAIT_TIME_SESSIONOPENCLOSE)); |
| if (rc <= 0) |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Session is not opened within timeout period code %ld"), |
| rc); |
| } |
| } |
| error_sme_open: |
| return status; |
| } |
| |
| void hdd_cleanup_actionframe( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter ) |
| { |
| hdd_cfg80211_state_t *cfgState; |
| |
| cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter ); |
| |
| if( NULL != cfgState->buf ) |
| { |
| unsigned long rc; |
| INIT_COMPLETION(pAdapter->tx_action_cnf_event); |
| rc = wait_for_completion_timeout( |
| &pAdapter->tx_action_cnf_event, |
| msecs_to_jiffies(ACTION_FRAME_TX_TIMEOUT)); |
| if (!rc) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s HDD Wait for Action Confirmation Failed!!", |
| __func__); |
| /* Inform tx status as FAILURE to upper layer and free |
| * cfgState->buf */ |
| hdd_sendActionCnf( pAdapter, FALSE ); |
| } |
| } |
| return; |
| } |
| |
| void hdd_deinit_adapter(hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter, |
| bool rtnl_held) |
| { |
| ENTER(); |
| switch ( pAdapter->device_mode ) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| case WLAN_HDD_P2P_CLIENT: |
| case WLAN_HDD_P2P_DEVICE: |
| { |
| if (test_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags)) |
| { |
| hdd_deinit_tx_rx( pAdapter ); |
| clear_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags); |
| } |
| |
| if (test_bit(WMM_INIT_DONE, &pAdapter->event_flags)) |
| { |
| hdd_wmm_adapter_close( pAdapter ); |
| clear_bit(WMM_INIT_DONE, &pAdapter->event_flags); |
| } |
| |
| hdd_cleanup_actionframe(pHddCtx, pAdapter); |
| wlan_hdd_tdls_exit(pAdapter); |
| break; |
| } |
| |
| case WLAN_HDD_SOFTAP: |
| case WLAN_HDD_P2P_GO: |
| { |
| if (test_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags)) |
| { |
| hdd_softap_deinit_tx_rx(pAdapter); |
| clear_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags); |
| } |
| |
| if (test_bit(WMM_INIT_DONE, &pAdapter->event_flags)) |
| { |
| hdd_wmm_adapter_close( pAdapter ); |
| clear_bit(WMM_INIT_DONE, &pAdapter->event_flags); |
| } |
| wlan_hdd_undo_acs(pAdapter); |
| |
| hdd_cleanup_actionframe(pHddCtx, pAdapter); |
| |
| hdd_unregister_hostapd(pAdapter, rtnl_held); |
| |
| // set con_mode to STA only when no SAP concurrency mode |
| if (!(hdd_get_concurrency_mode() & (VOS_SAP | VOS_P2P_GO))) |
| hdd_set_conparam( 0 ); |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| EXIT(); |
| } |
| |
| void hdd_cleanup_adapter(hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter, |
| tANI_U8 rtnl_held) |
| { |
| struct net_device *pWlanDev = NULL; |
| |
| if (pAdapter) |
| pWlanDev = pAdapter->dev; |
| else { |
| hddLog(LOGE, FL("pAdapter is Null")); |
| return; |
| } |
| |
| hdd_adapter_runtime_suspend_denit(pAdapter); |
| /* The adapter is marked as closed. When hdd_wlan_exit() call returns, |
| * the driver is almost closed and cannot handle either control |
| * messages or data. However, unregister_netdevice() call above will |
| * eventually invoke hdd_stop (ndo_close) driver callback, which attempts |
| * to close the active connections (basically excites control path) which |
| * is not right. Setting this flag helps hdd_stop() to recognize that |
| * the interface is closed and restricts any operations on that |
| */ |
| clear_bit(DEVICE_IFACE_OPENED, &pAdapter->event_flags); |
| |
| if (test_bit(NET_DEVICE_REGISTERED, &pAdapter->event_flags)) { |
| if (rtnl_held) { |
| unregister_netdevice(pWlanDev); |
| } else { |
| unregister_netdev(pWlanDev); |
| } |
| /* Note that the pAdapter is no longer valid at this point |
| since the memory has been reclaimed */ |
| } |
| } |
| |
| void hdd_set_pwrparams(hdd_context_t *pHddCtx) |
| { |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter = NULL; |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| /*loop through all adapters.*/ |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| if ( (WLAN_HDD_INFRA_STATION != pAdapter->device_mode) |
| && (WLAN_HDD_P2P_CLIENT != pAdapter->device_mode) ) |
| |
| { // we skip this registration for modes other than STA and P2P client modes. |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| continue; |
| } |
| |
| //Apply Dynamic DTIM For P2P |
| //Only if ignoreDynamicDtimInP2pMode is not set in ini |
| if ((pHddCtx->cfg_ini->enableDynamicDTIM || |
| pHddCtx->cfg_ini->enableModulatedDTIM) && |
| ((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) || |
| ((WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) && |
| !(pHddCtx->cfg_ini->ignoreDynamicDtimInP2pMode))) && |
| (eANI_BOOLEAN_TRUE == pAdapter->higherDtimTransition) && |
| (eConnectionState_Associated == |
| (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState) && |
| (pHddCtx->cfg_ini->fIsBmpsEnabled)) |
| { |
| tSirSetPowerParamsReq powerRequest = { 0 }; |
| |
| powerRequest.uIgnoreDTIM = 1; |
| powerRequest.uMaxLIModulatedDTIM = pHddCtx->cfg_ini->fMaxLIModulatedDTIM; |
| |
| if (pHddCtx->cfg_ini->enableModulatedDTIM) |
| { |
| powerRequest.uDTIMPeriod = pHddCtx->cfg_ini->enableModulatedDTIM; |
| powerRequest.uListenInterval = pHddCtx->hdd_actual_LI_value; |
| } |
| else |
| { |
| powerRequest.uListenInterval = pHddCtx->cfg_ini->enableDynamicDTIM; |
| } |
| |
| /* Update ignoreDTIM and ListedInterval in CFG to remain at the DTIM |
| * specified during Enter/Exit BMPS when LCD off*/ |
| ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_IGNORE_DTIM, powerRequest.uIgnoreDTIM, |
| NULL, eANI_BOOLEAN_FALSE); |
| ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_LISTEN_INTERVAL, powerRequest.uListenInterval, |
| NULL, eANI_BOOLEAN_FALSE); |
| |
| /* switch to the DTIM specified in cfg.ini */ |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "Switch to DTIM %d", powerRequest.uListenInterval); |
| sme_SetPowerParams( pHddCtx->hHal, &powerRequest, TRUE); |
| break; |
| |
| } |
| |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| } |
| |
| void hdd_reset_pwrparams(hdd_context_t *pHddCtx) |
| { |
| /*Switch back to DTIM 1*/ |
| tSirSetPowerParamsReq powerRequest = { 0 }; |
| |
| powerRequest.uIgnoreDTIM = pHddCtx->hdd_actual_ignore_DTIM_value; |
| powerRequest.uListenInterval = pHddCtx->hdd_actual_LI_value; |
| powerRequest.uMaxLIModulatedDTIM = pHddCtx->cfg_ini->fMaxLIModulatedDTIM; |
| |
| /* Update ignoreDTIM and ListedInterval in CFG with default values */ |
| ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_IGNORE_DTIM, powerRequest.uIgnoreDTIM, |
| NULL, eANI_BOOLEAN_FALSE); |
| ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_LISTEN_INTERVAL, powerRequest.uListenInterval, |
| NULL, eANI_BOOLEAN_FALSE); |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "Switch to DTIM%d",powerRequest.uListenInterval); |
| sme_SetPowerParams( pHddCtx->hHal, &powerRequest, TRUE); |
| |
| } |
| |
| VOS_STATUS hdd_enable_bmps_imps(hdd_context_t *pHddCtx) |
| { |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| |
| if (0 != wlan_hdd_validate_context(pHddCtx)) |
| return VOS_STATUS_E_PERM; |
| |
| if(pHddCtx->cfg_ini->fIsBmpsEnabled) |
| { |
| sme_EnablePowerSave(pHddCtx->hHal, ePMC_BEACON_MODE_POWER_SAVE); |
| } |
| |
| if(pHddCtx->cfg_ini->fIsAutoBmpsTimerEnabled) |
| { |
| sme_StartAutoBmpsTimer(pHddCtx->hHal); |
| } |
| |
| if (pHddCtx->cfg_ini->fIsImpsEnabled) |
| { |
| sme_EnablePowerSave (pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE); |
| } |
| |
| return status; |
| } |
| |
| VOS_STATUS hdd_disable_bmps_imps(hdd_context_t *pHddCtx, tANI_U8 session_type) |
| { |
| hdd_adapter_t *pAdapter = NULL; |
| eHalStatus halStatus; |
| VOS_STATUS status = VOS_STATUS_E_INVAL; |
| v_BOOL_t disableBmps = FALSE; |
| v_BOOL_t disableImps = FALSE; |
| |
| switch(session_type) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| case WLAN_HDD_SOFTAP: |
| case WLAN_HDD_P2P_CLIENT: |
| case WLAN_HDD_P2P_GO: |
| //Exit BMPS -> Is Sta/P2P Client is already connected |
| pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION); |
| if((NULL != pAdapter)&& |
| hdd_connIsConnected( WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))) |
| { |
| disableBmps = TRUE; |
| } |
| |
| pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_P2P_CLIENT); |
| if((NULL != pAdapter)&& |
| hdd_connIsConnected( WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))) |
| { |
| disableBmps = TRUE; |
| } |
| |
| //Exit both Bmps and Imps incase of Go/SAP Mode |
| if((WLAN_HDD_SOFTAP == session_type) || |
| (WLAN_HDD_P2P_GO == session_type)) |
| { |
| disableBmps = TRUE; |
| disableImps = TRUE; |
| } |
| |
| if(TRUE == disableImps) |
| { |
| if (pHddCtx->cfg_ini->fIsImpsEnabled) |
| { |
| sme_DisablePowerSave (pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE); |
| } |
| } |
| |
| if(TRUE == disableBmps) |
| { |
| if(pHddCtx->cfg_ini->fIsBmpsEnabled) |
| { |
| halStatus = sme_DisablePowerSave(pHddCtx->hHal, ePMC_BEACON_MODE_POWER_SAVE); |
| |
| if(eHAL_STATUS_SUCCESS != halStatus) |
| { |
| status = VOS_STATUS_E_FAILURE; |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Fail to Disable Power Save", __func__); |
| VOS_ASSERT(0); |
| return status; |
| } |
| } |
| |
| if(pHddCtx->cfg_ini->fIsAutoBmpsTimerEnabled) |
| { |
| halStatus = sme_StopAutoBmpsTimer(pHddCtx->hHal); |
| |
| if(eHAL_STATUS_SUCCESS != halStatus) |
| { |
| status = VOS_STATUS_E_FAILURE; |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Fail to Stop Auto Bmps Timer", __func__); |
| VOS_ASSERT(0); |
| return status; |
| } |
| } |
| } |
| |
| if((TRUE == disableBmps) || |
| (TRUE == disableImps)) |
| { |
| /* Now, get the chip into Full Power now */ |
| INIT_COMPLETION(pHddCtx->full_pwr_comp_var); |
| halStatus = sme_RequestFullPower(pHddCtx->hHal, hdd_full_pwr_cbk, |
| pHddCtx, eSME_FULL_PWR_NEEDED_BY_HDD); |
| |
| if(halStatus != eHAL_STATUS_SUCCESS) |
| { |
| if(halStatus == eHAL_STATUS_PMC_PENDING) |
| { |
| unsigned long rc; |
| //Block on a completion variable. Can't wait forever though |
| rc = wait_for_completion_timeout( |
| &pHddCtx->full_pwr_comp_var, |
| msecs_to_jiffies(1000)); |
| if (!rc) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: wait on full_pwr_comp_var failed", |
| __func__); |
| } |
| } |
| else |
| { |
| status = VOS_STATUS_E_FAILURE; |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Request for Full Power failed", __func__); |
| VOS_ASSERT(0); |
| return status; |
| } |
| } |
| |
| status = VOS_STATUS_SUCCESS; |
| } |
| |
| break; |
| } |
| return status; |
| } |
| |
| VOS_STATUS hdd_check_for_existing_macaddr( hdd_context_t *pHddCtx, |
| tSirMacAddr macAddr ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| hdd_adapter_t *pAdapter; |
| VOS_STATUS status; |
| |
| status = hdd_get_front_adapter(pHddCtx, &pAdapterNode); |
| |
| while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| if (pAdapter && vos_mem_compare(pAdapter->macAddressCurrent.bytes, |
| macAddr, sizeof(tSirMacAddr))) { |
| return VOS_STATUS_E_FAILURE; |
| } |
| status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext); |
| pAdapterNode = pNext; |
| } |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| uint32_t hdd_get_current_vdev_sta_count(hdd_context_t *hdd_ctx) |
| { |
| hdd_adapter_t *adapter; |
| hdd_adapter_list_node_t *hdd_adapter_node, *next; |
| VOS_STATUS status; |
| uint32_t vdev_sta_cnt = 0; |
| |
| status = hdd_get_front_adapter(hdd_ctx, &hdd_adapter_node); |
| |
| while (NULL != hdd_adapter_node && VOS_STATUS_SUCCESS == status) { |
| adapter = hdd_adapter_node->pAdapter; |
| if ((NULL != adapter) && |
| ((WLAN_HDD_INFRA_STATION == adapter->device_mode) || |
| (WLAN_HDD_P2P_CLIENT == adapter->device_mode) || |
| (WLAN_HDD_IBSS == adapter->device_mode))) |
| vdev_sta_cnt++; |
| |
| status = hdd_get_next_adapter(hdd_ctx, hdd_adapter_node, &next); |
| hdd_adapter_node = next; |
| } |
| |
| return vdev_sta_cnt; |
| } |
| |
| hdd_adapter_t *hdd_open_adapter(hdd_context_t *hdd_ctx, |
| uint8_t session_type, |
| const char *iface_name, |
| tSirMacAddr mac_addr, |
| uint8_t name_assign_type, |
| uint8_t rtnl_held) |
| { |
| VOS_STATUS status, exit_bmps_status = VOS_STATUS_E_FAILURE; |
| hdd_adapter_t *adapter; |
| hdd_adapter_list_node_t *hdd_adapter_node; |
| hdd_cfg80211_state_t *cfg_state; |
| int ret; |
| |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: iface =%s type = %d\n", |
| __func__, iface_name, session_type); |
| |
| if (hdd_ctx->current_intf_count >= hdd_ctx->max_intf_count) { |
| /* |
| * Max limit reached on the number of vdevs |
| * configured by the host. |
| */ |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Unable to add virtual intf: " |
| "current_vdev_cnt=%d,host_configured_vdev_cnt=%d", |
| __func__, |
| hdd_ctx->current_intf_count, |
| hdd_ctx->max_intf_count); |
| return NULL; |
| } |
| |
| if (((WLAN_HDD_INFRA_STATION == session_type) || |
| (WLAN_HDD_P2P_CLIENT == session_type) || |
| (WLAN_HDD_IBSS == session_type)) && |
| (WLAN_HDD_VDEV_STA_MAX == |
| hdd_get_current_vdev_sta_count(hdd_ctx))) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Unable to add sta interface: max sta cnt is %d", |
| __func__, WLAN_HDD_VDEV_STA_MAX); |
| return NULL; |
| } |
| |
| if (hdd_cfg_is_sub20_channel_width_enabled(hdd_ctx) && |
| (hdd_ctx->current_intf_count >= 1)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Unable add another virtual intf when sub20 enable", |
| __func__); |
| return NULL; |
| } |
| |
| if (NULL == mac_addr) { |
| /* Not received valid macAddr */ |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s:Unable to add virtual intf: Not able to get" |
| "valid mac address", __func__); |
| return NULL; |
| } |
| |
| status = hdd_check_for_existing_macaddr(hdd_ctx, mac_addr); |
| if (VOS_STATUS_E_FAILURE == status) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Duplicate MAC addr: "MAC_ADDRESS_STR |
| " already exists", |
| __func__, MAC_ADDR_ARRAY(mac_addr)); |
| return NULL; |
| } |
| |
| /* |
| * If Powersave Offload is enabled |
| * Fw will take care incase of concurrency |
| */ |
| if (!hdd_ctx->cfg_ini->enablePowersaveOffload) { |
| /* Disable BMPS incase of Concurrency */ |
| exit_bmps_status = hdd_disable_bmps_imps(hdd_ctx, session_type); |
| |
| if (VOS_STATUS_E_FAILURE == exit_bmps_status) { |
| /* Fail to Exit BMPS */ |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Fail to Exit BMPS", |
| __func__); |
| VOS_ASSERT(0); |
| return NULL; |
| } |
| } |
| |
| switch (session_type) { |
| |
| case WLAN_HDD_INFRA_STATION: |
| /* Reset locally administered bit if the device mode is STA */ |
| WLAN_HDD_RESET_LOCALLY_ADMINISTERED_BIT(mac_addr); |
| /* fall through */ |
| case WLAN_HDD_P2P_CLIENT: |
| case WLAN_HDD_P2P_DEVICE: |
| case WLAN_HDD_OCB: |
| case WLAN_HDD_NDI: |
| { |
| adapter = hdd_alloc_station_adapter(hdd_ctx, |
| mac_addr, |
| name_assign_type, |
| iface_name); |
| |
| if (NULL == adapter) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| FL("%s: Failed to allocate adapter for session %d"), |
| __func__, session_type); |
| return NULL; |
| } |
| |
| if (0 != hdd_init_packet_filtering(hdd_ctx, adapter)) |
| goto err_init_packet_filtering; |
| |
| if (session_type == WLAN_HDD_P2P_CLIENT) |
| adapter->wdev.iftype = NL80211_IFTYPE_P2P_CLIENT; |
| else if (VOS_MONITOR_MODE == vos_get_conparam()) |
| adapter->wdev.iftype = NL80211_IFTYPE_MONITOR; |
| else |
| adapter->wdev.iftype = NL80211_IFTYPE_STATION; |
| |
| adapter->device_mode = session_type; |
| |
| hdd_initialize_adapter_common(adapter); |
| if (WLAN_HDD_NDI == session_type) |
| status = hdd_init_nan_data_mode(adapter); |
| else |
| status = hdd_init_station_mode(adapter); |
| |
| if (VOS_STATUS_SUCCESS != status) |
| goto err_init_adapter_mode; |
| |
| /* initialize action frame random mac info */ |
| hdd_adapter_init_action_frame_random_mac(adapter); |
| |
| /* Workqueue which gets scheduled in IPv4 |
| * notification callback |
| */ |
| vos_init_work(&adapter->ipv4NotifierWorkQueue, |
| hdd_ipv4_notifier_work_queue); |
| |
| #ifdef WLAN_NS_OFFLOAD |
| /* Workqueue which gets scheduled in IPv6 |
| * notification callback |
| */ |
| vos_init_work(&adapter->ipv6NotifierWorkQueue, |
| hdd_ipv6_notifier_work_queue); |
| #endif |
| status = hdd_register_interface(adapter, rtnl_held); |
| if (VOS_STATUS_SUCCESS != status) |
| goto err_register_interface; |
| |
| /* do not disable tx in monitor mode */ |
| if (VOS_MONITOR_MODE != vos_get_conparam()) { |
| /* Stop the Interface TX queue */ |
| hddLog(LOG1, FL("Disabling queues")); |
| wlan_hdd_netif_queue_control(adapter, |
| WLAN_NETIF_TX_DISABLE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| } |
| |
| #ifdef QCA_LL_TX_FLOW_CT |
| /* SAT mode default TX Flow control instance |
| * This instance will be used for |
| * STA mode, IBSS mode and TDLS mode */ |
| if (adapter->tx_flow_timer_initialized == VOS_FALSE) { |
| vos_timer_init(&adapter->tx_flow_control_timer, |
| VOS_TIMER_TYPE_SW, |
| hdd_tx_resume_timer_expired_handler, |
| adapter); |
| adapter->tx_flow_timer_initialized = VOS_TRUE; |
| } |
| WLANTL_RegisterTXFlowControl(hdd_ctx->pvosContext, |
| hdd_tx_resume_cb, |
| adapter->sessionId, |
| (void *)adapter); |
| #endif /* QCA_LL_TX_FLOW_CT */ |
| |
| break; |
| } |
| |
| case WLAN_HDD_P2P_GO: |
| case WLAN_HDD_SOFTAP: |
| { |
| adapter = hdd_wlan_create_ap_dev(hdd_ctx, |
| mac_addr, |
| name_assign_type, |
| (tANI_U8 *)iface_name); |
| if (NULL == adapter) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| FL("failed to allocate adapter for session %d"), |
| session_type); |
| return NULL; |
| } |
| |
| if (0 != hdd_init_packet_filtering(hdd_ctx, adapter)) |
| goto err_init_packet_filtering; |
| |
| adapter->wdev.iftype = (session_type == WLAN_HDD_SOFTAP) ? |
| NL80211_IFTYPE_AP : |
| NL80211_IFTYPE_P2P_GO; |
| adapter->device_mode = session_type; |
| |
| hdd_initialize_adapter_common(adapter); |
| status = hdd_init_ap_mode(adapter, false); |
| if (VOS_STATUS_SUCCESS != status) |
| goto err_init_adapter_mode; |
| |
| status = hdd_register_hostapd(adapter, rtnl_held); |
| if (VOS_STATUS_SUCCESS != status) |
| goto err_register_interface; |
| |
| hddLog(LOG1, FL("Disabling queues")); |
| wlan_hdd_netif_queue_control(adapter, |
| WLAN_NETIF_TX_DISABLE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| hdd_set_conparam(1); |
| |
| /* Workqueue which gets scheduled in IPv4 |
| * notification callback |
| */ |
| vos_init_work(&adapter->ipv4NotifierWorkQueue, |
| hdd_ipv4_notifier_work_queue); |
| |
| #ifdef WLAN_NS_OFFLOAD |
| /* Workqueue which gets scheduled in IPv6 |
| * notification callback |
| */ |
| vos_init_work(&adapter->ipv6NotifierWorkQueue, |
| hdd_ipv6_notifier_work_queue); |
| #endif |
| |
| break; |
| } |
| |
| case WLAN_HDD_FTM: |
| { |
| adapter = hdd_alloc_station_adapter(hdd_ctx, |
| mac_addr, |
| name_assign_type, |
| iface_name); |
| |
| if (NULL == adapter) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| FL("failed to allocate adapter for session %d"), |
| session_type); |
| return NULL; |
| } |
| |
| /* Assign NL80211_IFTYPE_STATION as interface type to resolve |
| * Kernel Warning message while loading driver in FTM mode. |
| */ |
| adapter->wdev.iftype = NL80211_IFTYPE_STATION; |
| adapter->device_mode = session_type; |
| status = hdd_register_interface(adapter, rtnl_held); |
| if (VOS_STATUS_SUCCESS != status) |
| goto err_register_interface; |
| |
| hdd_initialize_adapter_common(adapter); |
| hdd_init_tx_rx(adapter); |
| |
| /* Stop the Interface TX queue */ |
| hddLog(LOG1, FL("Disabling queues")); |
| wlan_hdd_netif_queue_control(adapter, |
| WLAN_NETIF_TX_DISABLE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| |
| break; |
| } |
| |
| case WLAN_HDD_MONITOR: |
| { |
| adapter = hdd_alloc_monitor_adapter(hdd_ctx, |
| mac_addr, |
| name_assign_type, |
| iface_name); |
| |
| if (NULL == adapter) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| FL("failed to allocate adapter for session %d"), |
| session_type); |
| return NULL; |
| } |
| |
| adapter->wdev.iftype = NL80211_IFTYPE_MONITOR; |
| adapter->device_mode = session_type; |
| status = hdd_register_interface(adapter, rtnl_held); |
| if (VOS_STATUS_SUCCESS != status) |
| goto err_register_interface; |
| |
| hdd_initialize_adapter_common(adapter); |
| |
| /* Stop the Interface TX queue */ |
| hddLog(LOG1, FL("Disabling queues")); |
| wlan_hdd_netif_queue_control(adapter, |
| WLAN_NETIF_TX_DISABLE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| |
| break; |
| } |
| |
| default: |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s Invalid session type %d", |
| __func__, session_type); |
| VOS_ASSERT(0); |
| return NULL; |
| } |
| |
| } |
| |
| vos_init_work(&adapter->scan_block_work, |
| wlan_hdd_cfg80211_scan_block_cb); |
| |
| cfg_state = WLAN_HDD_GET_CFG_STATE_PTR(adapter); |
| mutex_init(&cfg_state->remain_on_chan_ctx_lock); |
| |
| #ifdef WLAN_FEATURE_MBSSID |
| hdd_mbssid_apply_def_cfg_ini(adapter); |
| #endif |
| /* Add it to the hdd's session list */ |
| hdd_adapter_node = vos_mem_malloc(sizeof(hdd_adapter_list_node_t)); |
| if (NULL == hdd_adapter_node) |
| goto err_malloc_adapter_node; |
| |
| hdd_adapter_node->pAdapter = adapter; |
| status = hdd_add_adapter_back(hdd_ctx, |
| hdd_adapter_node); |
| |
| if (VOS_STATUS_SUCCESS != status) |
| goto err_add_adapter_back; |
| |
| wlan_hdd_set_concurrency_mode(hdd_ctx, session_type); |
| |
| /* Initialize the WoWL service */ |
| if (!hdd_init_wowl(adapter)) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: hdd_init_wowl failed", __func__); |
| goto err_post_add_adapter; |
| } |
| |
| /* Adapter successfully added. Increment the vdev count */ |
| hdd_ctx->current_intf_count++; |
| |
| hddLog(VOS_TRACE_LEVEL_DEBUG, |
| "%s: current_intf_count=%d", __func__, |
| hdd_ctx->current_intf_count); |
| #ifdef FEATURE_WLAN_STA_AP_MODE_DFS_DISABLE |
| if (vos_get_concurrency_mode() == VOS_STA_SAP) { |
| hdd_adapter_t *ap_adapter; |
| |
| ap_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_SOFTAP); |
| if (ap_adapter != NULL && |
| test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags) && |
| VOS_IS_DFS_CH(ap_adapter->sessionCtx.ap.operatingChannel)) { |
| |
| hddLog(VOS_TRACE_LEVEL_WARN, |
| "STA-AP Mode DFS not supported. " |
| "Restart SAP with Non DFS ACS"); |
| ap_adapter->sessionCtx.ap.sapConfig.channel = |
| AUTO_CHANNEL_SELECT; |
| ap_adapter->sessionCtx.ap.sapConfig.acs_cfg.acs_mode = |
| true; |
| wlan_hdd_restart_sap(ap_adapter); |
| } |
| } |
| #endif |
| |
| if ((vos_get_conparam() != VOS_FTM_MODE) && |
| (!hdd_ctx->cfg_ini->enable2x2)) { |
| #define HDD_DTIM_1CHAIN_RX_ID 0x5 |
| #define HDD_SMPS_PARAM_VALUE_S 29 |
| |
| /* Disable DTIM 1 chain Rx when in 1x1, |
| * we are passing two values as |
| * param_id << 29 | param_value. |
| * Below param_value = 0(disable) |
| */ |
| ret = process_wma_set_command((int)adapter->sessionId, |
| (int)WMI_STA_SMPS_PARAM_CMDID, |
| HDD_DTIM_1CHAIN_RX_ID << HDD_SMPS_PARAM_VALUE_S, |
| VDEV_CMD); |
| |
| if (ret != 0) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: DTIM 1 chain set failed %d", |
| __func__, ret); |
| goto err_post_add_adapter; |
| } |
| |
| if (!hdd_ctx->per_band_chainmask_supp) { |
| ret = process_wma_set_command((int)adapter->sessionId, |
| (int)WMI_PDEV_PARAM_TX_CHAIN_MASK, |
| (int)hdd_ctx->cfg_ini->txchainmask1x1, |
| PDEV_CMD); |
| if (ret != 0) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: PDEV_PARAM_TX_CHAIN_MASK set failed %d", |
| __func__, ret); |
| goto err_post_add_adapter; |
| } |
| ret = process_wma_set_command((int)adapter->sessionId, |
| (int)WMI_PDEV_PARAM_RX_CHAIN_MASK, |
| (int)hdd_ctx->cfg_ini->rxchainmask1x1, |
| PDEV_CMD); |
| if (ret != 0) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: WMI_PDEV_PARAM_RX_CHAIN_MASK set failed %d", |
| __func__, ret); |
| goto err_post_add_adapter; |
| } |
| } |
| #undef HDD_DTIM_1CHAIN_RX_ID |
| #undef HDD_SMPS_PARAM_VALUE_S |
| } |
| |
| if (VOS_FTM_MODE != vos_get_conparam()) { |
| uint32_t cca_threshold; |
| |
| cca_threshold = hdd_ctx->cfg_ini->cca_threshold_2g | |
| hdd_ctx->cfg_ini->cca_threshold_5g << 8; |
| |
| if (hdd_ctx->cfg_ini->cca_threshold_enable) { |
| hddLog(VOS_TRACE_LEVEL_DEBUG, |
| "%s: CCA Threshold is enabled.", __func__); |
| ret = process_wma_set_command((int)adapter->sessionId, |
| WMI_PDEV_PARAM_CCA_THRESHOLD, |
| cca_threshold, |
| PDEV_CMD); |
| } else { |
| ret = 0; |
| } |
| |
| if (ret != 0) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: WMI_PDEV_PARAM_CCA_THRESHOLD set failed %d", |
| __func__, ret); |
| goto err_post_add_adapter; |
| } |
| |
| ret = process_wma_set_command((int)adapter->sessionId, |
| (int)WMI_PDEV_PARAM_HYST_EN, |
| (int)hdd_ctx->cfg_ini->enableMemDeepSleep, |
| PDEV_CMD); |
| if (ret != 0) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: WMI_PDEV_PARAM_HYST_EN set failed %d", |
| __func__, ret); |
| goto err_post_add_adapter; |
| } |
| |
| hddLog(LOG1, FL("SET AMSDU num %d"), |
| hdd_ctx->cfg_ini->max_amsdu_num); |
| |
| ret = process_wma_set_command((int)adapter->sessionId, |
| (int)GEN_VDEV_PARAM_AMSDU, |
| (int)hdd_ctx->cfg_ini->max_amsdu_num, |
| GEN_CMD); |
| if (ret != 0) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("GEN_VDEV_PARAM_AMSDU set failed %d"), ret); |
| goto err_post_add_adapter; |
| } |
| } |
| |
| #ifdef CONFIG_FW_LOGS_BASED_ON_INI |
| |
| /* Enable FW logs based on INI configuration */ |
| if ((VOS_FTM_MODE != vos_get_conparam()) && |
| (hdd_ctx->cfg_ini->enablefwlog) && |
| (hdd_ctx->current_intf_count == 1)) { |
| uint8_t count = 0; |
| uint32_t value = 0; |
| uint8_t num_entries = 0; |
| uint8_t module_loglevel[FW_MODULE_LOG_LEVEL_STRING_LENGTH]; |
| |
| hdd_ctx->fw_log_settings.dl_type = |
| hdd_ctx->cfg_ini->enableFwLogType; |
| ret = process_wma_set_command((int)adapter->sessionId, |
| (int)WMI_DBGLOG_TYPE, |
| hdd_ctx->cfg_ini->enableFwLogType, |
| DBG_CMD); |
| if (ret != 0) |
| hddLog(LOGE, |
| FL("Failed to enable FW log type ret %d"), ret); |
| |
| hdd_ctx->fw_log_settings.dl_loglevel = |
| hdd_ctx->cfg_ini->enableFwLogLevel; |
| ret = process_wma_set_command((int)adapter->sessionId, |
| (int)WMI_DBGLOG_LOG_LEVEL, |
| hdd_ctx->cfg_ini->enableFwLogLevel, |
| DBG_CMD); |
| if (ret != 0) |
| hddLog(LOGE, |
| FL("Failed to enable FW log level ret %d"), |
| ret); |
| |
| hdd_string_to_u8_array(hdd_ctx->cfg_ini->enableFwModuleLogLevel, |
| module_loglevel, |
| &num_entries, |
| FW_MODULE_LOG_LEVEL_STRING_LENGTH); |
| |
| while (count < num_entries) { |
| /* FW module log level input string looks like below: |
| * gFwDebugModuleLoglevel= |
| * <FW Module ID>, <Log Level>, so on.... |
| * For example: |
| * gFwDebugModuleLoglevel= |
| * 1,0,2,1,3,2,4,3,5,4,6,5,7,6,8,7 |
| * Above input string means : |
| * For FW module ID 1 enable log level 0 |
| * For FW module ID 2 enable log level 1 |
| * For FW module ID 3 enable log level 2 |
| * For FW module ID 4 enable log level 3 |
| * For FW module ID 5 enable log level 4 |
| * For FW module ID 6 enable log level 5 |
| * For FW module ID 7 enable log level 6 |
| * For FW module ID 8 enable log level 7 |
| * |
| * FW expects WMI command value = |
| * Module ID * 10 + Module Log level |
| */ |
| value = ((module_loglevel[count] * 10) + |
| module_loglevel[count + 1]); |
| ret = process_wma_set_command((int)adapter->sessionId, |
| (int)WMI_DBGLOG_MOD_LOG_LEVEL, |
| value, DBG_CMD); |
| if (ret != 0) |
| hddLog(LOGE, FL("Failed to enable FW module log level %d ret %d"), |
| value, ret); |
| |
| count += 2; |
| } |
| } |
| |
| #endif |
| |
| ret = process_wma_set_command((int)adapter->sessionId, |
| (int)WMI_VDEV_PARAM_ENABLE_RTSCTS, |
| hdd_ctx->cfg_ini->rts_profile, VDEV_CMD); |
| if (ret != 0) |
| hddLog(LOGE, "FAILED TO SET RTSCTS Profile ret:%d", ret); |
| |
| return adapter; |
| |
| err_post_add_adapter: |
| hdd_remove_adapter(hdd_ctx, hdd_adapter_node); |
| |
| err_add_adapter_back: |
| vos_mem_free(hdd_adapter_node); |
| |
| err_malloc_adapter_node: |
| if (rtnl_held) |
| unregister_netdevice(adapter->dev); |
| else |
| unregister_netdev(adapter->dev); |
| |
| err_register_interface: |
| /* close sme session to detach vdev */ |
| hdd_stop_adapter(hdd_ctx, adapter, VOS_TRUE); |
| hdd_deinit_adapter(hdd_ctx, adapter, rtnl_held); |
| |
| err_init_adapter_mode: |
| hdd_deinit_packet_filtering(adapter); |
| |
| err_init_packet_filtering: |
| hdd_adapter_runtime_suspend_denit(adapter); |
| |
| free_netdev(adapter->dev); |
| wlan_hdd_release_intf_addr(hdd_ctx, |
| adapter->macAddressCurrent.bytes); |
| |
| /* If bmps disabled enable it */ |
| if (!hdd_ctx->cfg_ini->enablePowersaveOffload) { |
| if (VOS_STATUS_SUCCESS == exit_bmps_status) { |
| if (hdd_ctx->hdd_wlan_suspended) |
| hdd_set_pwrparams(hdd_ctx); |
| hdd_enable_bmps_imps(hdd_ctx); |
| } |
| } |
| |
| return NULL; |
| } |
| |
| VOS_STATUS hdd_close_adapter( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter, |
| tANI_U8 rtnl_held ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode, *pCurrent, *pNext; |
| VOS_STATUS status; |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pCurrent ); |
| if( VOS_STATUS_SUCCESS != status ) |
| { |
| hddLog(VOS_TRACE_LEVEL_WARN,"%s: adapter list empty %d", |
| __func__, status); |
| return status; |
| } |
| |
| while ( pCurrent->pAdapter != pAdapter ) |
| { |
| status = hdd_get_next_adapter ( pHddCtx, pCurrent, &pNext ); |
| if( VOS_STATUS_SUCCESS != status ) |
| break; |
| |
| pCurrent = pNext; |
| } |
| pAdapterNode = pCurrent; |
| if( VOS_STATUS_SUCCESS == status ) |
| { |
| wlan_hdd_clear_concurrency_mode(pHddCtx, pAdapter->device_mode); |
| hdd_deinit_packet_filtering(pAdapterNode->pAdapter); |
| hdd_cleanup_adapter( pHddCtx, pAdapterNode->pAdapter, rtnl_held ); |
| hdd_remove_adapter( pHddCtx, pAdapterNode ); |
| vos_mem_free( pAdapterNode ); |
| pAdapterNode = NULL; |
| |
| /* Adapter removed. Decrement vdev count */ |
| if (pHddCtx->current_intf_count != 0) |
| pHddCtx->current_intf_count--; |
| |
| /* |
| * If Powersave Offload is enabled, |
| * Fw will take care incase of concurrency |
| */ |
| if(pHddCtx->cfg_ini->enablePowersaveOffload) |
| return VOS_STATUS_SUCCESS; |
| |
| /* If there is a single session of STA/P2P client, re-enable BMPS */ |
| if ((!vos_concurrent_open_sessions_running()) && |
| ((pHddCtx->no_of_open_sessions[VOS_STA_MODE] >= 1) || |
| (pHddCtx->no_of_open_sessions[VOS_P2P_CLIENT_MODE] >= 1))) |
| { |
| if (pHddCtx->hdd_wlan_suspended) |
| { |
| hdd_set_pwrparams(pHddCtx); |
| } |
| hdd_enable_bmps_imps(pHddCtx); |
| } |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| VOS_STATUS hdd_close_all_adapters( hdd_context_t *pHddCtx ) |
| { |
| hdd_adapter_list_node_t *pHddAdapterNode; |
| VOS_STATUS status; |
| |
| ENTER(); |
| |
| do |
| { |
| status = hdd_remove_front_adapter( pHddCtx, &pHddAdapterNode ); |
| if( pHddAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| hdd_deinit_packet_filtering(pHddAdapterNode->pAdapter); |
| hdd_cleanup_adapter( pHddCtx, pHddAdapterNode->pAdapter, FALSE ); |
| vos_mem_free( pHddAdapterNode ); |
| } |
| }while( NULL != pHddAdapterNode && VOS_STATUS_E_EMPTY != status ); |
| |
| EXIT(); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| void wlan_hdd_reset_prob_rspies(hdd_adapter_t* pHostapdAdapter) |
| { |
| tANI_U8 *bssid = NULL; |
| tSirUpdateIE updateIE; |
| switch (pHostapdAdapter->device_mode) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| case WLAN_HDD_P2P_CLIENT: |
| { |
| hdd_station_ctx_t * pHddStaCtx = |
| WLAN_HDD_GET_STATION_CTX_PTR(pHostapdAdapter); |
| bssid = (tANI_U8*)&pHddStaCtx->conn_info.bssId; |
| break; |
| } |
| case WLAN_HDD_SOFTAP: |
| case WLAN_HDD_P2P_GO: |
| case WLAN_HDD_IBSS: |
| { |
| bssid = pHostapdAdapter->macAddressCurrent.bytes; |
| break; |
| } |
| case WLAN_HDD_MONITOR: |
| case WLAN_HDD_FTM: |
| case WLAN_HDD_P2P_DEVICE: |
| default: |
| /* |
| * wlan_hdd_reset_prob_rspies should not have been called |
| * for these kind of devices |
| */ |
| hddLog(LOGE, FL("Unexpected request for the current device type %d"), |
| pHostapdAdapter->device_mode); |
| return; |
| } |
| |
| vos_mem_copy(updateIE.bssid, bssid, sizeof(tSirMacAddr)); |
| updateIE.smeSessionId = pHostapdAdapter->sessionId; |
| updateIE.ieBufferlength = 0; |
| updateIE.pAdditionIEBuffer = NULL; |
| updateIE.append = VOS_TRUE; |
| updateIE.notify = VOS_FALSE; |
| if (sme_UpdateAddIE(WLAN_HDD_GET_HAL_CTX(pHostapdAdapter), |
| &updateIE, eUPDATE_IE_PROBE_RESP) == eHAL_STATUS_FAILURE) { |
| hddLog(LOGE, FL("Could not pass on PROBE_RSP_BCN data to PE")); |
| } |
| } |
| |
| /** |
| * hdd_wait_for_sme_close_sesion() - Close and wait for SME session close |
| * @hdd_ctx: HDD context which is already NULL validated |
| * @adapter: HDD adapter which is already NULL validated |
| * |
| * Close the SME session and wait for its completion, if needed. |
| * |
| * Return: None |
| */ |
| static void hdd_wait_for_sme_close_sesion(hdd_context_t *hdd_ctx, |
| hdd_adapter_t *adapter) |
| { |
| unsigned long rc; |
| |
| if (!test_bit(SME_SESSION_OPENED, &adapter->event_flags)) { |
| hddLog(LOGE, FL("session is not opened:%d"), adapter->sessionId); |
| return; |
| } |
| |
| INIT_COMPLETION(adapter->session_close_comp_var); |
| if (eHAL_STATUS_SUCCESS == |
| sme_CloseSession(hdd_ctx->hHal, adapter->sessionId, |
| hdd_smeCloseSessionCallback, |
| adapter)) { |
| /* |
| * Block on a completion variable. Can't wait |
| * forever though. |
| */ |
| rc = wait_for_completion_timeout( |
| &adapter->session_close_comp_var, |
| msecs_to_jiffies |
| (WLAN_WAIT_TIME_SESSIONOPENCLOSE)); |
| if (!rc) |
| hddLog(LOGE, FL("failure waiting for session_close_comp_var")); |
| } |
| } |
| |
| VOS_STATUS hdd_stop_adapter( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter, |
| const v_BOOL_t bCloseSession) |
| { |
| eHalStatus halStatus = eHAL_STATUS_SUCCESS; |
| hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter); |
| hdd_scaninfo_t *pScanInfo = NULL; |
| union iwreq_data wrqu; |
| tSirUpdateIE updateIE ; |
| unsigned long rc; |
| |
| ENTER(); |
| |
| pScanInfo = &pAdapter->scan_info; |
| |
| hddLog(LOG1, FL("Disabling queues")); |
| wlan_hdd_netif_queue_control(pAdapter, WLAN_NETIF_TX_DISABLE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| switch(pAdapter->device_mode) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| case WLAN_HDD_IBSS: |
| case WLAN_HDD_P2P_CLIENT: |
| case WLAN_HDD_P2P_DEVICE: |
| case WLAN_HDD_NDI: |
| if ((WLAN_HDD_NDI == pAdapter->device_mode) || |
| hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)) || |
| hdd_is_connecting(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))) { |
| INIT_COMPLETION(pAdapter->disconnect_comp_var); |
| /* |
| * For NDI do not use pWextState from sta_ctx, if needed |
| * extract from ndi_ctx. |
| */ |
| if (WLAN_HDD_NDI == pAdapter->device_mode) |
| halStatus = sme_RoamDisconnect(pHddCtx->hHal, |
| pAdapter->sessionId, |
| eCSR_DISCONNECT_REASON_NDI_DELETE); |
| else if (pWextState->roamProfile.BSSType == eCSR_BSS_TYPE_START_IBSS) |
| halStatus = sme_RoamDisconnect(pHddCtx->hHal, |
| pAdapter->sessionId, |
| eCSR_DISCONNECT_REASON_IBSS_LEAVE); |
| else |
| halStatus = sme_RoamDisconnect(pHddCtx->hHal, |
| pAdapter->sessionId, |
| eCSR_DISCONNECT_REASON_UNSPECIFIED); |
| //success implies disconnect command got queued up successfully |
| if(halStatus == eHAL_STATUS_SUCCESS) |
| { |
| rc = wait_for_completion_timeout( |
| &pAdapter->disconnect_comp_var, |
| msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT)); |
| if (!rc) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: wait on disconnect_comp_var failed", |
| __func__); |
| } |
| } |
| else |
| { |
| hddLog(LOGE, "%s: failed to post disconnect event to SME", |
| __func__); |
| } |
| memset(&wrqu, '\0', sizeof(wrqu)); |
| wrqu.ap_addr.sa_family = ARPHRD_ETHER; |
| memset(wrqu.ap_addr.sa_data,'\0',ETH_ALEN); |
| wireless_send_event(pAdapter->dev, SIOCGIWAP, &wrqu, NULL); |
| } |
| |
| if (pScanInfo != NULL && pScanInfo->mScanPending) |
| { |
| wlan_hdd_scan_abort(pAdapter); |
| } |
| if ((pAdapter->device_mode == WLAN_HDD_P2P_CLIENT) || |
| (pAdapter->device_mode == WLAN_HDD_P2P_DEVICE)) { |
| wlan_hdd_cleanup_remain_on_channel_ctx(pAdapter); |
| } |
| |
| #ifdef WLAN_OPEN_SOURCE |
| cancel_work_sync(&pAdapter->ipv4NotifierWorkQueue); |
| #endif |
| |
| wlan_hdd_clean_tx_flow_control_timer(pHddCtx, pAdapter); |
| |
| #ifdef WLAN_NS_OFFLOAD |
| #ifdef WLAN_OPEN_SOURCE |
| cancel_work_sync(&pAdapter->ipv6NotifierWorkQueue); |
| #endif |
| #endif |
| |
| /* It is possible that the caller of this function does not |
| * wish to close the session |
| */ |
| if (bCloseSession) |
| hdd_wait_for_sme_close_sesion(pHddCtx, pAdapter); |
| break; |
| |
| case WLAN_HDD_SOFTAP: |
| case WLAN_HDD_P2P_GO: |
| if (pHddCtx->cfg_ini->conc_custom_rule1 && |
| (WLAN_HDD_SOFTAP == pAdapter->device_mode)) { |
| /* |
| * Before stopping the sap adapter, lets make sure there is |
| * no sap restart work pending. |
| */ |
| vos_flush_work(&pHddCtx->sap_start_work); |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, |
| FL("Canceled the pending SAP restart work")); |
| hdd_change_ch_avoidance_status(pHddCtx, false); |
| hdd_change_sap_restart_required_status(pHddCtx, false); |
| } |
| //Any softap specific cleanup here... |
| if (pAdapter->device_mode == WLAN_HDD_P2P_GO) { |
| wlan_hdd_cleanup_remain_on_channel_ctx(pAdapter); |
| } |
| |
| hdd_set_sap_auth_offload(pAdapter, FALSE); |
| |
| wlan_hdd_clean_tx_flow_control_timer(pHddCtx, pAdapter); |
| |
| mutex_lock(&pHddCtx->sap_lock); |
| if (test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags)) |
| { |
| VOS_STATUS status; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| hdd_hostapd_state_t *pHostapdState = |
| WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter); |
| vos_event_reset(&pHostapdState->stop_bss_event); |
| |
| //Stop Bss. |
| #ifdef WLAN_FEATURE_MBSSID |
| status = WLANSAP_StopBss(WLAN_HDD_GET_SAP_CTX_PTR(pAdapter)); |
| #else |
| status = WLANSAP_StopBss(pHddCtx->pvosContext); |
| #endif |
| |
| if (VOS_IS_STATUS_SUCCESS(status)) |
| { |
| status = vos_wait_single_event(&pHostapdState->stop_bss_event, |
| 10000); |
| if (!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hddLog(LOGE, "%s: failure waiting for WLANSAP_StopBss %d", |
| __func__, status); |
| } |
| } |
| else |
| { |
| hddLog(LOGE, "%s: failure in WLANSAP_StopBss", __func__); |
| } |
| clear_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags); |
| wlan_hdd_decr_active_session(pHddCtx, pAdapter->device_mode); |
| |
| vos_mem_copy(updateIE.bssid, pAdapter->macAddressCurrent.bytes, |
| sizeof(tSirMacAddr)); |
| updateIE.smeSessionId = pAdapter->sessionId; |
| updateIE.ieBufferlength = 0; |
| updateIE.pAdditionIEBuffer = NULL; |
| updateIE.append = VOS_FALSE; |
| updateIE.notify = VOS_FALSE; |
| if (pHddCtx->cfg_ini->apOBSSProtEnabled) |
| vos_runtime_pm_allow_suspend(pHddCtx->runtime_context.obss); |
| /* Probe bcn reset */ |
| if (sme_UpdateAddIE(WLAN_HDD_GET_HAL_CTX(pAdapter), |
| &updateIE, eUPDATE_IE_PROBE_BCN) |
| == eHAL_STATUS_FAILURE) { |
| hddLog(LOGE, FL("Could not pass on PROBE_RSP_BCN data to PE")); |
| } |
| /* Assoc resp reset */ |
| if (sme_UpdateAddIE(WLAN_HDD_GET_HAL_CTX(pAdapter), |
| &updateIE, eUPDATE_IE_ASSOC_RESP) == eHAL_STATUS_FAILURE) { |
| hddLog(LOGE, FL("Could not pass on ASSOC_RSP data to PE")); |
| } |
| |
| // Reset WNI_CFG_PROBE_RSP Flags |
| wlan_hdd_reset_prob_rspies(pAdapter); |
| kfree(pAdapter->sessionCtx.ap.beacon); |
| pAdapter->sessionCtx.ap.beacon = NULL; |
| #ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| if (pAdapter->device_mode == WLAN_HDD_SOFTAP) |
| { |
| if(pHddCtx->ch_switch_ctx.chan_sw_timer_initialized == VOS_TRUE) |
| { |
| //Stop the channel switch timer |
| if (VOS_TIMER_STATE_RUNNING == |
| vos_timer_getCurrentState(&pHddCtx->ch_switch_ctx.hdd_ap_chan_switch_timer)) |
| { |
| vos_timer_stop(&pHddCtx->ch_switch_ctx.hdd_ap_chan_switch_timer); |
| } |
| //Destroy the channel switch timer |
| if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy( |
| &pHddCtx->ch_switch_ctx.hdd_ap_chan_switch_timer))) |
| { |
| hddLog(LOGE, FL("Failed to destroy AP channel switch timer!!")); |
| } |
| pHddCtx->ch_switch_ctx.chan_sw_timer_initialized = VOS_FALSE; |
| } |
| } |
| #endif //WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| } |
| mutex_unlock(&pHddCtx->sap_lock); |
| |
| cancel_work_sync(&pAdapter->ipv4NotifierWorkQueue); |
| |
| #ifdef WLAN_NS_OFFLOAD |
| cancel_work_sync(&pAdapter->ipv6NotifierWorkQueue); |
| #endif |
| if (bCloseSession) |
| hdd_wait_for_sme_close_sesion(pHddCtx, pAdapter); |
| break; |
| |
| case WLAN_HDD_OCB: |
| wlan_hdd_dsrc_deinit_chan_stats(pAdapter); |
| hdd_disconnect_tx_rx(pAdapter); |
| WLANTL_ClearSTAClient(WLAN_HDD_GET_CTX(pAdapter)->pvosContext, |
| WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)->conn_info.staId[0]); |
| break; |
| |
| case WLAN_HDD_MONITOR: |
| hdd_vir_mon_stop(pAdapter->dev); |
| break; |
| |
| default: |
| break; |
| } |
| |
| EXIT(); |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| VOS_STATUS hdd_stop_all_adapters( hdd_context_t *pHddCtx ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter; |
| |
| ENTER(); |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| hdd_stop_adapter( pHddCtx, pAdapter, VOS_TRUE ); |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| EXIT(); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| #ifdef QCA_LL_TX_FLOW_CT |
| /** |
| * hdd_adapter_abort_tx_flow() - Abort the tx flow control |
| * @pAdapter: pointer to hdd_adapter_t |
| * |
| * Resume tx and stop the tx flow control timer if the tx is paused and the flow |
| * control timer is running. This function is called by SSR to avoid the |
| * inconsistency of tx status before and after SSR. |
| * |
| * Return: void |
| */ |
| static void hdd_adapter_abort_tx_flow(hdd_adapter_t *pAdapter) |
| { |
| if ((pAdapter->hdd_stats.hddTxRxStats.is_txflow_paused == TRUE) && |
| (VOS_TIMER_STATE_RUNNING == |
| vos_timer_getCurrentState(&pAdapter->tx_flow_control_timer))) { |
| hdd_tx_resume_timer_expired_handler(pAdapter); |
| vos_timer_stop(&pAdapter->tx_flow_control_timer); |
| } |
| } |
| #else |
| static void hdd_adapter_abort_tx_flow(hdd_adapter_t *pAdapter) |
| { |
| return; |
| } |
| #endif |
| |
| VOS_STATUS hdd_reset_all_adapters( hdd_context_t *pHddCtx ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter; |
| |
| ENTER(); |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| hddLog(LOG1, FL("Disabling queues")); |
| |
| hdd_adapter_abort_tx_flow(pAdapter); |
| |
| if (pHddCtx->cfg_ini->sap_internal_restart && |
| pAdapter->device_mode == WLAN_HDD_SOFTAP) { |
| hddLog(LOG1, FL("driver supports sap restart")); |
| vos_flush_work(&pHddCtx->sap_start_work); |
| wlan_hdd_netif_queue_control(pAdapter, |
| WLAN_NETIF_TX_DISABLE, |
| WLAN_CONTROL_PATH); |
| hdd_sap_indicate_disconnect_for_sta(pAdapter); |
| hdd_cleanup_actionframe(pHddCtx, pAdapter); |
| hdd_sap_destroy_events(pAdapter); |
| |
| } else |
| wlan_hdd_netif_queue_control(pAdapter, |
| WLAN_NETIF_TX_DISABLE_N_CARRIER, |
| WLAN_CONTROL_PATH); |
| |
| pAdapter->sessionCtx.station.hdd_ReassocScenario = VOS_FALSE; |
| |
| hdd_deinit_tx_rx(pAdapter); |
| wlan_hdd_decr_active_session(pHddCtx, pAdapter->device_mode); |
| if (test_bit(WMM_INIT_DONE, &pAdapter->event_flags)) |
| { |
| hdd_wmm_adapter_close( pAdapter ); |
| clear_bit(WMM_INIT_DONE, &pAdapter->event_flags); |
| } |
| |
| /* |
| * If adapter is SAP, set session ID to invalid since SAP |
| * session will be cleanup during SSR. |
| */ |
| if (pAdapter->device_mode == WLAN_HDD_SOFTAP) |
| wlansap_set_invalid_session( |
| #ifdef WLAN_FEATURE_MBSSID |
| WLAN_HDD_GET_SAP_CTX_PTR(pAdapter)); |
| #else |
| (WLAN_HDD_GET_CTX(pAdapter))->pvosContext); |
| #endif |
| |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| EXIT(); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| /** |
| * hdd_get_bss_entry() - Get the bss entry matching the chan, bssid and ssid |
| * @wiphy: wiphy |
| * @channel: channel of the BSS to find |
| * @bssid: bssid of the BSS to find |
| * @ssid: ssid of the BSS to find |
| * @ssid_len: ssid len of of the BSS to find |
| * |
| * The API is a wrapper to get bss from kernel matching the chan, |
| * bssid and ssid |
| * |
| * Return: bss structure if found else NULL |
| */ |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)) \ |
| && !defined(WITH_BACKPORTS) && !defined(IEEE80211_PRIVACY) |
| struct cfg80211_bss *hdd_cfg80211_get_bss(struct wiphy *wiphy, |
| struct ieee80211_channel *channel, |
| const u8 *bssid, const u8 *ssid, |
| size_t ssid_len) |
| { |
| return cfg80211_get_bss(wiphy, channel, bssid, |
| ssid, ssid_len, |
| WLAN_CAPABILITY_ESS, |
| WLAN_CAPABILITY_ESS); |
| } |
| #else |
| struct cfg80211_bss *hdd_cfg80211_get_bss(struct wiphy *wiphy, |
| struct ieee80211_channel *channel, |
| const u8 *bssid, const u8 *ssid, |
| size_t ssid_len) |
| { |
| return cfg80211_get_bss(wiphy, channel, bssid, |
| ssid, ssid_len, |
| IEEE80211_BSS_TYPE_ESS, |
| IEEE80211_PRIVACY_ANY); |
| } |
| #endif |
| |
| #if defined CFG80211_CONNECT_BSS |
| #if defined CFG80211_CONNECT_TIMEOUT_REASON_CODE || \ |
| (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) |
| /** |
| * hdd_convert_timeout_reason() - Convert to kernel specific enum |
| * @timeout_reason: reason for connect timeout |
| * |
| * This function is used to convert host timeout |
| * reason enum to kernel specific enum. |
| * |
| * Return: nl timeout enum |
| */ |
| static enum nl80211_timeout_reason hdd_convert_timeout_reason( |
| tSirResultCodes timeout_reason) |
| { |
| switch (timeout_reason) { |
| case eSIR_SME_JOIN_TIMEOUT_RESULT_CODE: |
| return NL80211_TIMEOUT_SCAN; |
| case eSIR_SME_AUTH_TIMEOUT_RESULT_CODE: |
| return NL80211_TIMEOUT_AUTH; |
| case eSIR_SME_ASSOC_TIMEOUT_RESULT_CODE: |
| return NL80211_TIMEOUT_ASSOC; |
| default: |
| return NL80211_TIMEOUT_UNSPECIFIED; |
| } |
| } |
| |
| /** |
| * hdd_cfg80211_connect_timeout() - API to send connection timeout reason |
| * @dev: network device |
| * @bssid: bssid to which we want to associate |
| * @timeout_reason: reason for connect timeout |
| * |
| * This API is used to send connection timeout reason to supplicant |
| * |
| * Return: void |
| */ |
| static void hdd_cfg80211_connect_timeout(struct net_device *dev, |
| const u8 *bssid, |
| tSirResultCodes timeout_reason) |
| { |
| enum nl80211_timeout_reason nl_timeout_reason; |
| nl_timeout_reason = hdd_convert_timeout_reason(timeout_reason); |
| |
| cfg80211_connect_timeout(dev, bssid, NULL, 0, GFP_KERNEL, |
| nl_timeout_reason); |
| } |
| |
| /** |
| * __hdd_connect_bss() - API to send connection status to supplicant |
| * @dev: network device |
| * @bssid: bssid to which we want to associate |
| * @req_ie: Request Information Element |
| * @req_ie_len: len of the req IE |
| * @resp_ie: Response IE |
| * @resp_ie_len: len of ht response IE |
| * @status: status |
| * @gfp: Kernel Flag |
| * @timeout_reason: reason for connect timeout |
| * |
| * Return: void |
| */ |
| static void __hdd_connect_bss(struct net_device *dev, const u8 *bssid, |
| struct cfg80211_bss *bss, const u8 *req_ie, |
| size_t req_ie_len, const u8 *resp_ie, |
| size_t resp_ie_len, int status, gfp_t gfp, |
| tSirResultCodes timeout_reason) |
| { |
| enum nl80211_timeout_reason nl_timeout_reason; |
| nl_timeout_reason = hdd_convert_timeout_reason(timeout_reason); |
| |
| cfg80211_connect_bss(dev, bssid, bss, req_ie, req_ie_len, |
| resp_ie, resp_ie_len, status, gfp, |
| nl_timeout_reason); |
| } |
| #else /* CFG80211_CONNECT_TIMEOUT_REASON_CODE */ |
| #if defined CFG80211_CONNECT_TIMEOUT |
| static void hdd_cfg80211_connect_timeout(struct net_device *dev, |
| const u8 *bssid, |
| tSirResultCodes timeout_reason) |
| { |
| cfg80211_connect_timeout(dev, bssid, NULL, 0, GFP_KERNEL); |
| } |
| #endif /* CFG80211_CONNECT_TIMEOUT */ |
| static void __hdd_connect_bss(struct net_device *dev, const u8 *bssid, |
| struct cfg80211_bss *bss, const u8 *req_ie, |
| size_t req_ie_len, const u8 *resp_ie, |
| size_t resp_ie_len, int status, gfp_t gfp, |
| tSirResultCodes timeout_reason) |
| { |
| cfg80211_connect_bss(dev, bssid, bss, req_ie, req_ie_len, |
| resp_ie, resp_ie_len, status, gfp); |
| } |
| #endif /* CFG80211_CONNECT_TIMEOUT_REASON_CODE */ |
| /** |
| * hdd_connect_bss() - API to send connection status to supplicant |
| * @dev: network device |
| * @bssid: bssid to which we want to associate |
| * @req_ie: Request Information Element |
| * @req_ie_len: len of the req IE |
| * @resp_ie: Response IE |
| * @resp_ie_len: len of ht response IE |
| * @status: status |
| * @gfp: Kernel Flag |
| * @connect_timeout: If timed out waiting for Auth/Assoc/Probe resp |
| * @timeout_reason: reason for connect timeout |
| * |
| * The API is a wrapper to send connection status to supplicant |
| * |
| * Return: Void |
| */ |
| #if defined CFG80211_CONNECT_TIMEOUT |
| static void hdd_connect_bss(struct net_device *dev, const u8 *bssid, |
| struct cfg80211_bss *bss, const u8 *req_ie, |
| size_t req_ie_len, const u8 *resp_ie, |
| size_t resp_ie_len, int status, gfp_t gfp, |
| bool connect_timeout, tSirResultCodes timeout_reason) |
| { |
| if (connect_timeout) |
| hdd_cfg80211_connect_timeout(dev, bssid, timeout_reason); |
| else |
| __hdd_connect_bss(dev, bssid, bss, req_ie, req_ie_len, |
| resp_ie, resp_ie_len, status, gfp, timeout_reason); |
| } |
| #else |
| static void hdd_connect_bss(struct net_device *dev, const u8 *bssid, |
| struct cfg80211_bss *bss, const u8 *req_ie, |
| size_t req_ie_len, const u8 *resp_ie, |
| size_t resp_ie_len, int status, gfp_t gfp, |
| bool connect_timeout, tSirResultCodes timeout_reason) |
| { |
| __hdd_connect_bss(dev, bssid, bss, req_ie, req_ie_len, |
| resp_ie, resp_ie_len, status, gfp, timeout_reason); |
| } |
| #endif |
| |
| #ifdef WLAN_FEATURE_FILS_SK |
| #ifdef CFG80211_CONNECT_DONE |
| #ifdef CFG80211_FILS_SK_OFFLOAD_SUPPORT |
| /** |
| * hdd_populate_fils_params() - Populate FILS keys to connect response |
| * @fils_params: connect response to supplicant |
| * @fils_kek: FILS kek |
| * @fils_kek_len: FILS kek length |
| * @pmk: FILS PMK |
| * @pmk_len: FILS PMK length |
| * @pmkid: PMKID |
| * @fils_seq_num: FILS Seq number |
| * |
| * Return: None |
| */ |
| static void hdd_populate_fils_params(struct cfg80211_connect_resp_params |
| *fils_params, const uint8_t *fils_kek, |
| size_t fils_kek_len, const uint8_t *pmk, |
| size_t pmk_len, const uint8_t *pmkid, |
| uint16_t fils_seq_num) |
| { |
| /* Increament seq number to be used for next FILS */ |
| fils_params->fils_erp_next_seq_num = fils_seq_num + 1; |
| fils_params->update_erp_next_seq_num = true; |
| fils_params->fils_kek = fils_kek; |
| fils_params->fils_kek_len = fils_kek_len; |
| fils_params->pmk = pmk; |
| fils_params->pmk_len = pmk_len; |
| fils_params->pmkid = pmkid; |
| } |
| #else /* CFG80211_FILS_SK_OFFLOAD_SUPPORT */ |
| static inline void hdd_populate_fils_params(struct cfg80211_connect_resp_params |
| *fils_params, const uint8_t |
| *fils_kek, size_t fils_kek_len, |
| const uint8_t *pmk, size_t pmk_len, |
| const uint8_t *pmkid, |
| uint16_t fils_seq_num) |
| { } |
| #endif /* CFG80211_FILS_SK_OFFLOAD_SUPPORT */ |
| |
| /** |
| * hdd_connect_done() - Wrapper API to call cfg80211_connect_done |
| * @dev: network device |
| * @bssid: bssid to which we want to associate |
| * @bss: cfg80211 bss info |
| * @roam_info: information about connected bss |
| * @req_ie: Request Information Element |
| * @req_ie_len: len of the req IE |
| * @resp_ie: Response IE |
| * @resp_ie_len: len of ht response IE |
| * @status: status |
| * @gfp: allocation flags |
| * @connect_timeout: If timed out waiting for Auth/Assoc/Probe resp |
| * @timeout_reason: reason for connect timeout |
| * @roam_fils_params: FILS join response params |
| * |
| * This API is used as wrapper to send FILS key/sequence number |
| * params etc. to supplicant in case of FILS connection |
| * |
| * Return: None |
| */ |
| static void hdd_connect_done(struct net_device *dev, const u8 *bssid, |
| struct cfg80211_bss *bss, tCsrRoamInfo *roam_info, |
| const u8 *req_ie, size_t req_ie_len, |
| const u8 *resp_ie, size_t resp_ie_len, u16 status, |
| gfp_t gfp, bool connect_timeout, tSirResultCodes |
| timeout_reason, struct fils_join_rsp_params |
| *roam_fils_params) |
| { |
| struct cfg80211_connect_resp_params fils_params; |
| vos_mem_zero(&fils_params, sizeof(fils_params)); |
| |
| fils_params.bssid = bssid; |
| if (!roam_fils_params) { |
| fils_params.status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| hdd_populate_fils_params(&fils_params, NULL, 0, NULL, |
| 0, NULL, roam_info->fils_seq_num); |
| } else { |
| fils_params.status = status; |
| fils_params.timeout_reason = timeout_reason; |
| fils_params.req_ie = req_ie; |
| fils_params.req_ie_len = req_ie_len; |
| fils_params.resp_ie = resp_ie; |
| fils_params.resp_ie_len = resp_ie_len; |
| fils_params.bss = bss; |
| hdd_populate_fils_params(&fils_params, roam_fils_params->kek, |
| roam_fils_params->kek_len, |
| roam_fils_params->fils_pmk, |
| roam_fils_params->fils_pmk_len, |
| roam_fils_params->fils_pmkid, |
| roam_info->fils_seq_num); |
| } |
| hddLog(LOG1, "FILS indicate connect status %d seq no %d", |
| fils_params.status, |
| fils_params.fils_erp_next_seq_num); |
| |
| cfg80211_connect_done(dev, &fils_params, gfp); |
| |
| /* Clear all the FILS key info */ |
| if (roam_fils_params && roam_fils_params->fils_pmk) |
| vos_mem_free(roam_fils_params->fils_pmk); |
| if (roam_fils_params) |
| vos_mem_free(roam_fils_params); |
| roam_info->fils_join_rsp = NULL; |
| } |
| #else /* CFG80211_CONNECT_DONE */ |
| static inline void hdd_connect_done(struct net_device *dev, const u8 *bssid, |
| struct cfg80211_bss *bss, tCsrRoamInfo |
| *roam_info, const u8 *req_ie, |
| size_t req_ie_len, const u8 *resp_ie, |
| size_t resp_ie_len, u16 status, gfp_t gfp, |
| bool connect_timeout, tSirResultCodes |
| timeout_reason, struct fils_join_rsp_params |
| *roam_fils_params) |
| { } |
| #endif /* CFG80211_CONNECT_DONE */ |
| #endif /* WLAN_FEATURE_FILS_SK */ |
| |
| #if defined(CFG80211_CONNECT_DONE) && defined(WLAN_FEATURE_FILS_SK) |
| /** |
| * hdd_fils_update_connect_results() - API to send fils connection status to |
| * supplicant. |
| * @dev: network device |
| * @bssid: bssid to which we want to associate |
| * @bss: cfg80211 bss info |
| * @roam_info: information about connected bss |
| * @req_ie: Request Information Element |
| * @req_ie_len: len of the req IE |
| * @resp_ie: Response IE |
| * @resp_ie_len: len of ht response IE |
| * @status: status |
| * @gfp: allocation flags |
| * @connect_timeout: If timed out waiting for Auth/Assoc/Probe resp |
| * @timeout_reason: reason for connect timeout |
| * |
| * The API is a wrapper to send connection status to supplicant |
| * |
| * Return: 0 if success else failure |
| */ |
| static int hdd_fils_update_connect_results(struct net_device *dev, |
| const u8 *bssid, |
| struct cfg80211_bss *bss, |
| tCsrRoamInfo *roam_info, const u8 *req_ie, |
| size_t req_ie_len, const u8 *resp_ie, |
| size_t resp_ie_len, u16 status, gfp_t gfp, |
| bool connect_timeout, |
| tSirResultCodes timeout_reason) |
| { |
| ENTER(); |
| if (!roam_info || !roam_info->is_fils_connection) |
| return -EINVAL; |
| |
| hdd_connect_done(dev, bssid, bss, roam_info, req_ie, req_ie_len, |
| resp_ie, resp_ie_len, status, gfp, connect_timeout, |
| timeout_reason, roam_info->fils_join_rsp); |
| return 0; |
| } |
| #else /* CFG80211_CONNECT_DONE && WLAN_FEATURE_FILS_SK */ |
| static inline int hdd_fils_update_connect_results(struct net_device *dev, |
| const u8 *bssid, |
| struct cfg80211_bss *bss, |
| tCsrRoamInfo *roam_info, const u8 *req_ie, |
| size_t req_ie_len, const u8 *resp_ie, |
| size_t resp_ie_len, u16 status, gfp_t gfp, |
| bool connect_timeout, |
| tSirResultCodes timeout_reason) |
| { |
| return -EINVAL; |
| } |
| #endif /* CFG80211_CONNECT_DONE && WLAN_FEATURE_FILS_SK */ |
| |
| /** |
| * hdd_connect_result() - API to send connection status to supplicant |
| * @dev: network device |
| * @bssid: bssid to which we want to associate |
| * @roam_info: information about connected bss |
| * @req_ie: Request Information Element |
| * @req_ie_len: len of the req IE |
| * @resp_ie: Response IE |
| * @resp_ie_len: len of ht response IE |
| * @status: status |
| * @gfp: Kernel Flag |
| * @connect_timeout: If timed out waiting for Auth/Assoc/Probe resp |
| * @timeout_reason: reason for connect timeout |
| * |
| * The API is a wrapper to send connection status to supplicant |
| * and allow runtime suspend |
| * |
| * Return: Void |
| */ |
| void hdd_connect_result(struct net_device *dev, |
| const u8 *bssid, |
| tCsrRoamInfo *roam_info, |
| const u8 *req_ie, |
| size_t req_ie_len, |
| const u8 *resp_ie, |
| size_t resp_ie_len, |
| u16 status, |
| gfp_t gfp, |
| bool connect_timeout, tSirResultCodes timeout_reason) |
| { |
| hdd_adapter_t *padapter = (hdd_adapter_t *) netdev_priv(dev); |
| struct cfg80211_bss *bss = NULL; |
| |
| if (WLAN_STATUS_SUCCESS == status) { |
| struct ieee80211_channel *chan; |
| int freq; |
| int chan_no = roam_info->pBssDesc->channelId;; |
| |
| if (chan_no <= 14) |
| freq = ieee80211_channel_to_frequency(chan_no, |
| IEEE80211_BAND_2GHZ); |
| else |
| freq = ieee80211_channel_to_frequency(chan_no, |
| IEEE80211_BAND_5GHZ); |
| |
| chan = ieee80211_get_channel(padapter->wdev.wiphy, freq); |
| bss = hdd_cfg80211_get_bss(padapter->wdev.wiphy, chan, bssid, |
| roam_info->u.pConnectedProfile->SSID.ssId, |
| roam_info->u.pConnectedProfile->SSID.length); |
| } |
| |
| if (hdd_fils_update_connect_results(dev, bssid, bss, |
| roam_info, req_ie, req_ie_len, resp_ie, |
| resp_ie_len, status, gfp, connect_timeout, |
| timeout_reason) != 0) { |
| hdd_connect_bss(dev, bssid, bss, req_ie, |
| req_ie_len, resp_ie, resp_ie_len, |
| status, gfp, connect_timeout, timeout_reason); |
| } |
| |
| vos_runtime_pm_allow_suspend(padapter->runtime_context.connect); |
| } |
| #else /* CFG80211_CONNECT_BSS */ |
| void hdd_connect_result(struct net_device *dev, |
| const u8 *bssid, |
| tCsrRoamInfo *roam_info, |
| const u8 *req_ie, |
| size_t req_ie_len, |
| const u8 * resp_ie, |
| size_t resp_ie_len, |
| u16 status, |
| gfp_t gfp, |
| bool connect_timeout, tSirResultCodes timeout_reason) |
| { |
| hdd_adapter_t *padapter = (hdd_adapter_t *) netdev_priv(dev); |
| |
| cfg80211_connect_result(dev, bssid, req_ie, req_ie_len, |
| resp_ie, resp_ie_len, status, gfp); |
| |
| vos_runtime_pm_allow_suspend(padapter->runtime_context.connect); |
| } |
| #endif /* CFG80211_CONNECT_BSS */ |
| |
| VOS_STATUS hdd_start_all_adapters( hdd_context_t *pHddCtx ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter; |
| #if !defined(MSM_PLATFORM) || defined(WITH_BACKPORTS) |
| v_MACADDR_t bcastMac = VOS_MAC_ADDR_BROADCAST_INITIALIZER; |
| #endif |
| eConnectionState connState; |
| |
| ENTER(); |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| hdd_wmm_init( pAdapter ); |
| |
| switch(pAdapter->device_mode) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| case WLAN_HDD_P2P_CLIENT: |
| case WLAN_HDD_P2P_DEVICE: |
| |
| connState = (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState; |
| |
| hdd_init_station_mode(pAdapter); |
| /* Open the gates for HDD to receive Wext commands */ |
| pAdapter->isLinkUpSvcNeeded = FALSE; |
| pAdapter->scan_info.mScanPending = FALSE; |
| pAdapter->scan_info.waitScanResult = FALSE; |
| |
| //Indicate disconnect event to supplicant if associated previously |
| if (eConnectionState_Associated == connState || |
| eConnectionState_IbssConnected == connState || |
| eConnectionState_NotConnected == connState || |
| eConnectionState_IbssDisconnected == connState || |
| eConnectionState_Disconnecting == connState) |
| { |
| union iwreq_data wrqu; |
| memset(&wrqu, '\0', sizeof(wrqu)); |
| wrqu.ap_addr.sa_family = ARPHRD_ETHER; |
| memset(wrqu.ap_addr.sa_data,'\0',ETH_ALEN); |
| wireless_send_event(pAdapter->dev, SIOCGIWAP, &wrqu, NULL); |
| pAdapter->sessionCtx.station.hdd_ReassocScenario = VOS_FALSE; |
| |
| /* indicate disconnected event to nl80211 */ |
| wlan_hdd_cfg80211_indicate_disconnect(pAdapter->dev, false, |
| WLAN_REASON_UNSPECIFIED); |
| } |
| else if (eConnectionState_Connecting == connState) |
| { |
| /* |
| * Indicate connect failure to supplicant if we were in the |
| * process of connecting |
| */ |
| hdd_connect_result(pAdapter->dev, NULL, NULL, |
| NULL, 0, NULL, 0, |
| WLAN_STATUS_ASSOC_DENIED_UNSPEC, |
| GFP_KERNEL, false, 0); |
| } |
| |
| #ifdef QCA_LL_TX_FLOW_CT |
| if (pAdapter->tx_flow_timer_initialized == VOS_FALSE) { |
| vos_timer_init(&pAdapter->tx_flow_control_timer, |
| VOS_TIMER_TYPE_SW, |
| hdd_tx_resume_timer_expired_handler, |
| pAdapter); |
| pAdapter->tx_flow_timer_initialized = VOS_TRUE; |
| } |
| |
| WLANTL_RegisterTXFlowControl(pHddCtx->pvosContext, hdd_tx_resume_cb, |
| pAdapter->sessionId, (void *)pAdapter); |
| #endif |
| |
| break; |
| |
| case WLAN_HDD_SOFTAP: |
| if (pHddCtx->cfg_ini->sap_internal_restart) { |
| hdd_init_ap_mode(pAdapter, true); |
| |
| #ifdef QCA_LL_TX_FLOW_CT |
| if (pAdapter->tx_flow_timer_initialized == VOS_FALSE) { |
| vos_timer_init(&pAdapter->tx_flow_control_timer, |
| VOS_TIMER_TYPE_SW, |
| hdd_softap_tx_resume_timer_expired_handler, |
| pAdapter); |
| pAdapter->tx_flow_timer_initialized = VOS_TRUE; |
| } |
| |
| WLANTL_RegisterTXFlowControl(pHddCtx->pvosContext, |
| hdd_softap_tx_resume_cb, |
| pAdapter->sessionId, |
| (void *)pAdapter); |
| #endif |
| |
| } |
| break; |
| |
| case WLAN_HDD_P2P_GO: |
| #if defined(MSM_PLATFORM) && !defined(WITH_BACKPORTS) |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s [SSR] send stop ap to supplicant", |
| __func__); |
| cfg80211_ap_stopped(pAdapter->dev, GFP_KERNEL); |
| #else |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s [SSR] send restart supplicant", |
| __func__); |
| /* event supplicant to restart */ |
| cfg80211_del_sta(pAdapter->dev, |
| (const u8 *)&bcastMac.bytes[0], GFP_KERNEL); |
| #endif |
| break; |
| |
| case WLAN_HDD_MONITOR: |
| /* monitor interface start */ |
| break; |
| default: |
| break; |
| } |
| |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| EXIT(); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| VOS_STATUS hdd_reconnect_all_adapters( hdd_context_t *pHddCtx ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| hdd_adapter_t *pAdapter; |
| VOS_STATUS status; |
| v_U32_t roamId; |
| unsigned long rc; |
| |
| ENTER(); |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| if( (WLAN_HDD_INFRA_STATION == pAdapter->device_mode) || |
| (WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) ) |
| { |
| hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter); |
| |
| if (eConnectionState_Associated == pHddStaCtx->conn_info.connState) { |
| wlan_hdd_decr_active_session(pHddCtx, pAdapter->device_mode); |
| } |
| hdd_connSetConnectionState(pAdapter, eConnectionState_NotConnected); |
| init_completion(&pAdapter->disconnect_comp_var); |
| sme_RoamDisconnect(pHddCtx->hHal, pAdapter->sessionId, |
| eCSR_DISCONNECT_REASON_UNSPECIFIED); |
| |
| rc = wait_for_completion_timeout( |
| &pAdapter->disconnect_comp_var, |
| msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT)); |
| if (!rc) |
| hddLog(LOGE, "%s: failure waiting for disconnect_comp_var", |
| __func__); |
| pWextState->roamProfile.csrPersona = pAdapter->device_mode; |
| pHddCtx->isAmpAllowed = VOS_FALSE; |
| sme_RoamConnect(pHddCtx->hHal, |
| pAdapter->sessionId, &(pWextState->roamProfile), |
| &roamId); |
| } |
| |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| EXIT(); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| void hdd_dump_concurrency_info(hdd_context_t *pHddCtx) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter; |
| hdd_station_ctx_t *pHddStaCtx; |
| hdd_ap_ctx_t *pHddApCtx; |
| hdd_hostapd_state_t * pHostapdState; |
| tCsrBssid staBssid = { 0 }, p2pBssid = { 0 }; |
| tCsrBssid apBssid = { 0 }, apBssid1 = { 0 }, apBssid2 = { 0 }; |
| v_U8_t staChannel = 0, p2pChannel = 0, apChannel = 0; |
| v_U8_t apChannel1 = 0, apChannel2 = 0; |
| const char *p2pMode = "DEV"; |
| bool mcc_mode = FALSE; |
| |
| #ifdef QCA_LL_TX_FLOW_CT |
| v_U8_t targetChannel = 0; |
| v_U8_t preAdapterChannel = 0; |
| v_U8_t channel24; |
| v_U8_t channel5; |
| hdd_adapter_t *preAdapterContext = NULL; |
| hdd_adapter_t *pAdapter2_4 = NULL; |
| hdd_adapter_t *pAdapter5 = NULL; |
| #endif /* QCA_LL_TX_FLOW_CT */ |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| switch (pAdapter->device_mode) { |
| case WLAN_HDD_INFRA_STATION: |
| pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| if (eConnectionState_Associated == pHddStaCtx->conn_info.connState) { |
| staChannel = pHddStaCtx->conn_info.operationChannel; |
| memcpy(staBssid, pHddStaCtx->conn_info.bssId, sizeof(staBssid)); |
| #ifdef QCA_LL_TX_FLOW_CT |
| targetChannel = staChannel; |
| #endif /* QCA_LL_TX_FLOW_CT */ |
| } |
| break; |
| case WLAN_HDD_P2P_CLIENT: |
| pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| if (eConnectionState_Associated == pHddStaCtx->conn_info.connState) { |
| p2pChannel = pHddStaCtx->conn_info.operationChannel; |
| memcpy(p2pBssid, pHddStaCtx->conn_info.bssId, sizeof(p2pBssid)); |
| p2pMode = "CLI"; |
| #ifdef QCA_LL_TX_FLOW_CT |
| targetChannel = p2pChannel; |
| #endif /* QCA_LL_TX_FLOW_CT */ |
| } |
| break; |
| case WLAN_HDD_P2P_GO: |
| pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(pAdapter); |
| pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter); |
| if (pHostapdState->bssState == BSS_START && pHostapdState->vosStatus==VOS_STATUS_SUCCESS) { |
| p2pChannel = pHddApCtx->operatingChannel; |
| memcpy(p2pBssid, pAdapter->macAddressCurrent.bytes, sizeof(p2pBssid)); |
| #ifdef QCA_LL_TX_FLOW_CT |
| targetChannel = p2pChannel; |
| #endif /* QCA_LL_TX_FLOW_CT */ |
| } |
| p2pMode = "GO"; |
| break; |
| case WLAN_HDD_SOFTAP: |
| pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(pAdapter); |
| pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter); |
| if (pHostapdState->bssState == BSS_START && pHostapdState->vosStatus==VOS_STATUS_SUCCESS) { |
| apChannel = pHddApCtx->operatingChannel; |
| memcpy(apBssid, pAdapter->macAddressCurrent.bytes, sizeof(apBssid)); |
| if (!pHddApCtx->uBCStaId) { |
| apChannel1 = apChannel; |
| memcpy(apBssid1, apBssid, sizeof(apBssid)); |
| } else { |
| apChannel2 = apChannel; |
| memcpy(apBssid2, apBssid, sizeof(apBssid)); |
| } |
| #ifdef QCA_LL_TX_FLOW_CT |
| targetChannel = apChannel; |
| #endif /* QCA_LL_TX_FLOW_CT */ |
| } |
| break; |
| case WLAN_HDD_IBSS: |
| #ifdef QCA_LL_TX_FLOW_CT |
| pAdapter->tx_flow_low_watermark = |
| pHddCtx->cfg_ini->TxFlowLowWaterMark; |
| #endif |
| return; /* skip printing station message below */ |
| default: |
| break; |
| } |
| #ifdef QCA_LL_TX_FLOW_CT |
| if (targetChannel) |
| { |
| /* This is first adapter detected as active |
| * set as default for none concurrency case */ |
| if (!preAdapterChannel) |
| { |
| #ifdef IPA_UC_OFFLOAD |
| /* If IPA UC data path is enabled, |
| * target should reserve extra tx descriptors |
| * for IPA WDI data path. |
| * Then host data path should allow less TX packet pumping in case |
| * IPA WDI data path enabled */ |
| if ((pHddCtx->cfg_ini->IpaUcOffloadEnabled) && |
| (WLAN_HDD_SOFTAP == pAdapter->device_mode)) { |
| pAdapter->tx_flow_low_watermark = |
| pHddCtx->cfg_ini->TxFlowLowWaterMark + |
| WLAN_TFC_IPAUC_TX_DESC_RESERVE; |
| } else |
| #endif /* IPA_UC_OFFLOAD */ |
| { |
| pAdapter->tx_flow_low_watermark = |
| pHddCtx->cfg_ini->TxFlowLowWaterMark; |
| } |
| pAdapter->tx_flow_high_watermark_offset = |
| pHddCtx->cfg_ini->TxFlowHighWaterMarkOffset; |
| WLANTL_SetAdapterMaxQDepth(pHddCtx->pvosContext, |
| pAdapter->sessionId, |
| pHddCtx->cfg_ini->TxFlowMaxQueueDepth); |
| /* Temporary set log level as error |
| * TX Flow control feature settled down, will lower log level */ |
| hddLog(LOG1, |
| "MODE %s(%d), CH %d, LWM %d, HWM %d, TXQDEP %d", |
| hdd_device_mode_to_string(pAdapter->device_mode), |
| pAdapter->device_mode, |
| targetChannel, |
| pAdapter->tx_flow_low_watermark, |
| pAdapter->tx_flow_low_watermark + |
| pAdapter->tx_flow_high_watermark_offset, |
| pHddCtx->cfg_ini->TxFlowMaxQueueDepth); |
| preAdapterChannel = targetChannel; |
| preAdapterContext = pAdapter; |
| } |
| else |
| { |
| /* SCC, disable TX flow control for both |
| * SCC each adapter cannot reserve dedicated channel resource |
| * as a result, if any adapter blocked OS Q by flow control, |
| * blocked adapter will lost chance to recover */ |
| if (preAdapterChannel == targetChannel) |
| { |
| /* Current adapter */ |
| #ifdef CONFIG_PER_VDEV_TX_DESC_POOL |
| pAdapter->tx_flow_low_watermark = |
| pHddCtx->cfg_ini->TxFlowLowWaterMark; |
| pAdapter->tx_flow_high_watermark_offset = |
| pHddCtx->cfg_ini->TxFlowHighWaterMarkOffset; |
| #else |
| pAdapter->tx_flow_low_watermark = 0; |
| pAdapter->tx_flow_high_watermark_offset = 0; |
| #endif |
| WLANTL_SetAdapterMaxQDepth(pHddCtx->pvosContext, |
| pAdapter->sessionId, |
| pHddCtx->cfg_ini->TxHbwFlowMaxQueueDepth); |
| hddLog(LOG1, |
| "SCC: MODE %s(%d), CH %d, LWM %d, HWM %d, TXQDEP %d", |
| hdd_device_mode_to_string(pAdapter->device_mode), |
| pAdapter->device_mode, |
| targetChannel, |
| pAdapter->tx_flow_low_watermark, |
| pAdapter->tx_flow_low_watermark + |
| pAdapter->tx_flow_high_watermark_offset, |
| pHddCtx->cfg_ini->TxHbwFlowMaxQueueDepth); |
| |
| if (!preAdapterContext) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "SCC: Previous adapter context NULL"); |
| continue; |
| } |
| |
| /* Previous adapter */ |
| #ifdef CONFIG_PER_VDEV_TX_DESC_POOL |
| preAdapterContext->tx_flow_low_watermark = |
| pHddCtx->cfg_ini->TxFlowLowWaterMark; |
| preAdapterContext->tx_flow_high_watermark_offset = |
| pHddCtx->cfg_ini->TxFlowHighWaterMarkOffset; |
| #else |
| preAdapterContext->tx_flow_low_watermark = 0; |
| preAdapterContext->tx_flow_high_watermark_offset = 0; |
| #endif |
| WLANTL_SetAdapterMaxQDepth(pHddCtx->pvosContext, |
| preAdapterContext->sessionId, |
| pHddCtx->cfg_ini->TxHbwFlowMaxQueueDepth); |
| /* Temporary set log level as error |
| * TX Flow control feature settled down, will lower log level */ |
| hddLog(LOG1, |
| "SCC: MODE %s(%d), CH %d, LWM %d, HWM %d, TXQDEP %d", |
| hdd_device_mode_to_string(preAdapterContext->device_mode), |
| preAdapterContext->device_mode, |
| targetChannel, |
| preAdapterContext->tx_flow_low_watermark, |
| preAdapterContext->tx_flow_low_watermark + |
| preAdapterContext->tx_flow_high_watermark_offset, |
| pHddCtx->cfg_ini->TxHbwFlowMaxQueueDepth); |
| } |
| /* MCC, each adapter will have dedicated resource */ |
| else |
| { |
| /* current channel is 2.4 */ |
| if (targetChannel <= WLAN_HDD_TX_FLOW_CONTROL_MAX_24BAND_CH) |
| { |
| channel24 = targetChannel; |
| channel5 = preAdapterChannel; |
| pAdapter2_4 = pAdapter; |
| pAdapter5 = preAdapterContext; |
| } |
| /* Current channel is 5 */ |
| else |
| { |
| channel24 = preAdapterChannel; |
| channel5 = targetChannel; |
| pAdapter2_4 = preAdapterContext; |
| pAdapter5 = pAdapter; |
| } |
| |
| if (!pAdapter5) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "MCC: 5GHz adapter context NULL"); |
| continue; |
| } |
| pAdapter5->tx_flow_low_watermark = |
| pHddCtx->cfg_ini->TxHbwFlowLowWaterMark; |
| pAdapter5->tx_flow_high_watermark_offset = |
| pHddCtx->cfg_ini->TxHbwFlowHighWaterMarkOffset; |
| WLANTL_SetAdapterMaxQDepth(pHddCtx->pvosContext, |
| pAdapter5->sessionId, |
| pHddCtx->cfg_ini->TxHbwFlowMaxQueueDepth); |
| /* Temporary set log level as error |
| * TX Flow control feature settled down, will lower log level */ |
| hddLog(LOG1, |
| "MCC: MODE %s(%d), CH %d, LWM %d, HWM %d, TXQDEP %d", |
| hdd_device_mode_to_string(pAdapter5->device_mode), |
| pAdapter5->device_mode, |
| channel5, |
| pAdapter5->tx_flow_low_watermark, |
| pAdapter5->tx_flow_low_watermark + |
| pAdapter5->tx_flow_high_watermark_offset, |
| pHddCtx->cfg_ini->TxHbwFlowMaxQueueDepth); |
| |
| if (!pAdapter2_4) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "MCC: 2.4GHz adapter context NULL"); |
| continue; |
| } |
| pAdapter2_4->tx_flow_low_watermark = |
| pHddCtx->cfg_ini->TxLbwFlowLowWaterMark; |
| pAdapter2_4->tx_flow_high_watermark_offset = |
| pHddCtx->cfg_ini->TxLbwFlowHighWaterMarkOffset; |
| WLANTL_SetAdapterMaxQDepth(pHddCtx->pvosContext, |
| pAdapter2_4->sessionId, |
| pHddCtx->cfg_ini->TxLbwFlowMaxQueueDepth); |
| /* Temporary set log level as error |
| * TX Flow control feature settled down, will lower log level */ |
| hddLog(LOG1, |
| "MCC: MODE %s(%d), CH %d, LWM %d, HWM %d, TXQDEP %d", |
| hdd_device_mode_to_string(pAdapter2_4->device_mode), |
| pAdapter2_4->device_mode, |
| channel24, |
| pAdapter2_4->tx_flow_low_watermark, |
| pAdapter2_4->tx_flow_low_watermark + |
| pAdapter2_4->tx_flow_high_watermark_offset, |
| pHddCtx->cfg_ini->TxLbwFlowMaxQueueDepth); |
| } |
| } |
| } |
| targetChannel = 0; |
| #endif /* QCA_LL_TX_FLOW_CT */ |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| /* |
| * Determine SCC/MSS |
| * Remind that this only considered STA+AP and AP+AP concurrency |
| * Need to expand for futher concurreny in the future |
| */ |
| if (apChannel1 > 0 && apChannel2 > 0) { |
| mcc_mode = apChannel1 != apChannel2; |
| } else if (staChannel > 0 && (apChannel1 > 0 || p2pChannel > 0)) { |
| mcc_mode = !(p2pChannel==staChannel || apChannel1==staChannel); |
| } |
| if (pHddCtx->mcc_mode != mcc_mode) { |
| #ifdef IPA_UC_STA_OFFLOAD |
| /* Send SCC/MCC Switching event to IPA */ |
| hdd_ipa_send_mcc_scc_msg(pHddCtx, mcc_mode); |
| #endif |
| pHddCtx->mcc_mode = mcc_mode; |
| } |
| hddLog(VOS_TRACE_LEVEL_ERROR, "wlan(%d) " MAC_ADDRESS_STR " %s", |
| staChannel, MAC_ADDR_ARRAY(staBssid), mcc_mode ? "MCC" : "SCC"); |
| if (p2pChannel > 0) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "p2p-%s(%d) " MAC_ADDRESS_STR, |
| p2pMode, p2pChannel, MAC_ADDR_ARRAY(p2pBssid)); |
| } |
| if (apChannel1 > 0) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "AP(%d) " MAC_ADDRESS_STR, |
| apChannel1, MAC_ADDR_ARRAY(apBssid1)); |
| } |
| if (apChannel2 > 0) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "AP(%d) " MAC_ADDRESS_STR, |
| apChannel2, MAC_ADDR_ARRAY(apBssid2)); |
| } |
| |
| if (p2pChannel > 0 && apChannel1 > 0) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Error concurrent SAP %d and P2P %d which is not support"), |
| apChannel1, p2pChannel); |
| } |
| } |
| |
| bool hdd_is_ssr_required( void) |
| { |
| return (isSsrRequired == HDD_SSR_REQUIRED); |
| } |
| |
| /* Once SSR is disabled then it cannot be set. */ |
| void hdd_set_ssr_required( e_hdd_ssr_required value) |
| { |
| if (HDD_SSR_DISABLED == isSsrRequired) |
| return; |
| |
| isSsrRequired = value; |
| } |
| |
| VOS_STATUS hdd_get_front_adapter( hdd_context_t *pHddCtx, |
| hdd_adapter_list_node_t** ppAdapterNode) |
| { |
| VOS_STATUS status; |
| bool in_irq_context = (in_irq() || irqs_disabled()); |
| if (!in_irq_context) |
| spin_lock_bh(&pHddCtx->hddAdapters.lock); |
| status = hdd_list_peek_front ( &pHddCtx->hddAdapters, |
| (hdd_list_node_t**) ppAdapterNode ); |
| if (!in_irq_context) |
| spin_unlock_bh(&pHddCtx->hddAdapters.lock); |
| return status; |
| } |
| |
| VOS_STATUS hdd_get_next_adapter( hdd_context_t *pHddCtx, |
| hdd_adapter_list_node_t* pAdapterNode, |
| hdd_adapter_list_node_t** pNextAdapterNode) |
| { |
| VOS_STATUS status; |
| spin_lock_bh(&pHddCtx->hddAdapters.lock); |
| status = hdd_list_peek_next ( &pHddCtx->hddAdapters, |
| (hdd_list_node_t*) pAdapterNode, |
| (hdd_list_node_t**)pNextAdapterNode ); |
| |
| spin_unlock_bh(&pHddCtx->hddAdapters.lock); |
| return status; |
| } |
| |
| VOS_STATUS hdd_remove_adapter( hdd_context_t *pHddCtx, |
| hdd_adapter_list_node_t* pAdapterNode) |
| { |
| VOS_STATUS status; |
| spin_lock_bh(&pHddCtx->hddAdapters.lock); |
| status = hdd_list_remove_node ( &pHddCtx->hddAdapters, |
| &pAdapterNode->node ); |
| spin_unlock_bh(&pHddCtx->hddAdapters.lock); |
| return status; |
| } |
| |
| VOS_STATUS hdd_remove_front_adapter( hdd_context_t *pHddCtx, |
| hdd_adapter_list_node_t** ppAdapterNode) |
| { |
| VOS_STATUS status; |
| spin_lock_bh(&pHddCtx->hddAdapters.lock); |
| status = hdd_list_remove_front( &pHddCtx->hddAdapters, |
| (hdd_list_node_t**) ppAdapterNode ); |
| spin_unlock_bh(&pHddCtx->hddAdapters.lock); |
| return status; |
| } |
| |
| VOS_STATUS hdd_add_adapter_back( hdd_context_t *pHddCtx, |
| hdd_adapter_list_node_t* pAdapterNode) |
| { |
| VOS_STATUS status; |
| spin_lock_bh(&pHddCtx->hddAdapters.lock); |
| status = hdd_list_insert_back ( &pHddCtx->hddAdapters, |
| (hdd_list_node_t*) pAdapterNode ); |
| spin_unlock_bh(&pHddCtx->hddAdapters.lock); |
| return status; |
| } |
| |
| VOS_STATUS hdd_add_adapter_front( hdd_context_t *pHddCtx, |
| hdd_adapter_list_node_t* pAdapterNode) |
| { |
| VOS_STATUS status; |
| spin_lock_bh(&pHddCtx->hddAdapters.lock); |
| status = hdd_list_insert_front ( &pHddCtx->hddAdapters, |
| (hdd_list_node_t*) pAdapterNode ); |
| spin_unlock_bh(&pHddCtx->hddAdapters.lock); |
| return status; |
| } |
| |
| hdd_adapter_t * hdd_get_adapter_by_macaddr( hdd_context_t *pHddCtx, |
| tSirMacAddr macAddr ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| hdd_adapter_t *pAdapter; |
| VOS_STATUS status; |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| if( pAdapter && vos_mem_compare( pAdapter->macAddressCurrent.bytes, |
| macAddr, sizeof(tSirMacAddr) ) ) |
| { |
| return pAdapter; |
| } |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| return NULL; |
| |
| } |
| |
| hdd_adapter_t * hdd_get_adapter_by_rand_macaddr(hdd_context_t *hdd_ctx, |
| tSirMacAddr mac_addr) |
| { |
| hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL; |
| hdd_adapter_t *adapter; |
| VOS_STATUS status; |
| |
| status = hdd_get_front_adapter(hdd_ctx, &adapter_node); |
| while (adapter_node && status == VOS_STATUS_SUCCESS) { |
| adapter = adapter_node->pAdapter; |
| if(adapter && hdd_check_random_mac(adapter, mac_addr)) |
| return adapter; |
| status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next); |
| adapter_node = next; |
| } |
| |
| return NULL; |
| } |
| |
| hdd_adapter_t * hdd_get_adapter_by_name( hdd_context_t *pHddCtx, tANI_U8 *name ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| hdd_adapter_t *pAdapter; |
| VOS_STATUS status; |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| if( pAdapter && !strncmp( pAdapter->dev->name, (const char *)name, |
| IFNAMSIZ ) ) |
| { |
| return pAdapter; |
| } |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| return NULL; |
| |
| } |
| |
| hdd_adapter_t *hdd_get_adapter_by_vdev( hdd_context_t *pHddCtx, |
| tANI_U32 vdev_id ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| hdd_adapter_t *pAdapter; |
| VOS_STATUS vos_status; |
| |
| |
| vos_status = hdd_get_front_adapter( pHddCtx, &pAdapterNode); |
| |
| while ((NULL != pAdapterNode) && (VOS_STATUS_SUCCESS == vos_status)) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| if (pAdapter->sessionId == vdev_id) |
| return pAdapter; |
| |
| vos_status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext); |
| pAdapterNode = pNext; |
| } |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: vdev_id %d does not exist with host", |
| __func__, vdev_id); |
| |
| return NULL; |
| } |
| |
| /** |
| * hdd_get_adapter_by_sme_session_id() - Return adapter with |
| * the sessionid |
| * @hdd_ctx: hdd cntx. |
| * @sme_session_id: sme session is for the adapter to get. |
| * |
| * This function is used to get the adapter with provided session id |
| * |
| * Return: adapter pointer if found |
| * |
| */ |
| hdd_adapter_t *hdd_get_adapter_by_sme_session_id(hdd_context_t *hdd_ctx, |
| uint32_t sme_session_id) |
| { |
| hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL; |
| hdd_adapter_t *adapter; |
| VOS_STATUS vos_status; |
| |
| |
| vos_status = hdd_get_front_adapter(hdd_ctx, &adapter_node); |
| |
| while ((NULL != adapter_node) && |
| (VOS_STATUS_SUCCESS == vos_status)) { |
| adapter = adapter_node->pAdapter; |
| |
| if (adapter && |
| adapter->sessionId == sme_session_id) |
| return adapter; |
| |
| vos_status = |
| hdd_get_next_adapter(hdd_ctx, |
| adapter_node, &next); |
| adapter_node = next; |
| } |
| return NULL; |
| } |
| |
| hdd_adapter_t * hdd_get_adapter( hdd_context_t *pHddCtx, device_mode_t mode ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| hdd_adapter_t *pAdapter; |
| VOS_STATUS status; |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| if( pAdapter && (mode == pAdapter->device_mode) ) |
| { |
| return pAdapter; |
| } |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| return NULL; |
| |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_get_operating_channel() - |
| |
| This API returns the operating channel of the requested device mode |
| |
| \param - pHddCtx - Pointer to the HDD context. |
| - mode - Device mode for which operating channel is required |
| supported modes - WLAN_HDD_INFRA_STATION, WLAN_HDD_P2P_CLIENT |
| WLAN_HDD_SOFTAP, WLAN_HDD_P2P_GO. |
| \return - channel number. "0" id the requested device is not found OR it is not connected. |
| --------------------------------------------------------------------------*/ |
| v_U8_t hdd_get_operating_channel( hdd_context_t *pHddCtx, device_mode_t mode ) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter; |
| v_U8_t operatingChannel = 0; |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| |
| if( mode == pAdapter->device_mode ) |
| { |
| switch(pAdapter->device_mode) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| case WLAN_HDD_P2P_CLIENT: |
| if( hdd_connIsConnected( WLAN_HDD_GET_STATION_CTX_PTR( pAdapter )) ) |
| operatingChannel = (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.operationChannel; |
| break; |
| case WLAN_HDD_SOFTAP: |
| case WLAN_HDD_P2P_GO: |
| /*softap connection info */ |
| if(test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags)) |
| operatingChannel = (WLAN_HDD_GET_AP_CTX_PTR(pAdapter))->operatingChannel; |
| break; |
| default: |
| break; |
| } |
| |
| break; //Found the device of interest. break the loop |
| } |
| |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| return operatingChannel; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_wlan_initial_scan() - |
| |
| This function triggers the initial scan |
| |
| \param - pAdapter - Pointer to the HDD adapter. |
| |
| --------------------------------------------------------------------------*/ |
| void hdd_wlan_initial_scan(hdd_adapter_t *pAdapter) |
| { |
| tCsrScanRequest scanReq; |
| tCsrChannelInfo channelInfo; |
| eHalStatus halStatus; |
| tANI_U32 scanId; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| |
| vos_mem_zero(&scanReq, sizeof(tCsrScanRequest)); |
| vos_mem_set(&scanReq.bssid, sizeof(tCsrBssid), 0xff); |
| scanReq.BSSType = eCSR_BSS_TYPE_ANY; |
| |
| if(sme_Is11dSupported(pHddCtx->hHal)) |
| { |
| halStatus = sme_ScanGetBaseChannels( pHddCtx->hHal, &channelInfo ); |
| if ( HAL_STATUS_SUCCESS( halStatus ) ) |
| { |
| scanReq.ChannelInfo.ChannelList = vos_mem_malloc(channelInfo.numOfChannels); |
| if( !scanReq.ChannelInfo.ChannelList ) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s kmalloc failed", __func__); |
| vos_mem_free(channelInfo.ChannelList); |
| channelInfo.ChannelList = NULL; |
| return; |
| } |
| vos_mem_copy(scanReq.ChannelInfo.ChannelList, channelInfo.ChannelList, |
| channelInfo.numOfChannels); |
| scanReq.ChannelInfo.numOfChannels = channelInfo.numOfChannels; |
| vos_mem_free(channelInfo.ChannelList); |
| channelInfo.ChannelList = NULL; |
| } |
| |
| scanReq.scanType = eSIR_PASSIVE_SCAN; |
| scanReq.requestType = eCSR_SCAN_REQUEST_11D_SCAN; |
| scanReq.maxChnTime = pHddCtx->cfg_ini->nPassiveMaxChnTime; |
| scanReq.minChnTime = pHddCtx->cfg_ini->nPassiveMinChnTime; |
| } |
| else |
| { |
| scanReq.scanType = eSIR_ACTIVE_SCAN; |
| scanReq.requestType = eCSR_SCAN_REQUEST_FULL_SCAN; |
| scanReq.maxChnTime = pHddCtx->cfg_ini->nActiveMaxChnTime; |
| scanReq.minChnTime = pHddCtx->cfg_ini->nActiveMinChnTime; |
| } |
| |
| halStatus = sme_ScanRequest(pHddCtx->hHal, pAdapter->sessionId, &scanReq, &scanId, NULL, NULL); |
| if ( !HAL_STATUS_SUCCESS( halStatus ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: sme_ScanRequest failed status code %d", |
| __func__, halStatus ); |
| } |
| |
| if(sme_Is11dSupported(pHddCtx->hHal)) |
| vos_mem_free(scanReq.ChannelInfo.ChannelList); |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_full_power_callback() - HDD full power callback function |
| |
| This is the function invoked by SME to inform the result of a full power |
| request issued by HDD |
| |
| \param - callback context - Pointer to cookie |
| \param - status - result of request |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| static void hdd_full_power_callback(void *callbackContext, eHalStatus status) |
| { |
| struct statsContext *pContext = callbackContext; |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: context = %pK, status = %d", __func__, pContext, status); |
| |
| if (NULL == callbackContext) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Bad param, context [%pK]", |
| __func__, callbackContext); |
| return; |
| } |
| |
| /* there is a race condition that exists between this callback |
| function and the caller since the caller could time out either |
| before or while this code is executing. we use a spinlock to |
| serialize these actions */ |
| spin_lock(&hdd_context_lock); |
| |
| if (POWER_CONTEXT_MAGIC != pContext->magic) |
| { |
| /* the caller presumably timed out so there is nothing we can do */ |
| spin_unlock(&hdd_context_lock); |
| hddLog(VOS_TRACE_LEVEL_WARN, |
| "%s: Invalid context, magic [%08x]", |
| __func__, pContext->magic); |
| return; |
| } |
| |
| /* context is valid so caller is still waiting */ |
| |
| /* paranoia: invalidate the magic */ |
| pContext->magic = 0; |
| |
| /* notify the caller */ |
| complete(&pContext->completion); |
| |
| /* serialization is complete */ |
| spin_unlock(&hdd_context_lock); |
| } |
| |
| static inline VOS_STATUS hdd_UnregisterWext_all_adapters(hdd_context_t *pHddCtx) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter; |
| |
| ENTER(); |
| |
| status = hdd_get_front_adapter(pHddCtx, &pAdapterNode); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| if ((pAdapter->device_mode == WLAN_HDD_INFRA_STATION) || |
| (pAdapter->device_mode == WLAN_HDD_P2P_CLIENT) || |
| (pAdapter->device_mode == WLAN_HDD_IBSS) || |
| (pAdapter->device_mode == WLAN_HDD_P2P_DEVICE) || |
| (pAdapter->device_mode == WLAN_HDD_SOFTAP) || |
| (pAdapter->device_mode == WLAN_HDD_P2P_GO)) { |
| wlan_hdd_cfg80211_deregister_frames(pAdapter); |
| hdd_UnregisterWext(pAdapter->dev); |
| } |
| status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext); |
| pAdapterNode = pNext; |
| } |
| |
| EXIT(); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| VOS_STATUS hdd_abort_mac_scan_all_adapters(hdd_context_t *pHddCtx) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter; |
| |
| ENTER(); |
| |
| status = hdd_get_front_adapter(pHddCtx, &pAdapterNode); |
| |
| while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) |
| { |
| pAdapter = pAdapterNode->pAdapter; |
| if ((pAdapter->device_mode == WLAN_HDD_INFRA_STATION) || |
| (pAdapter->device_mode == WLAN_HDD_P2P_CLIENT) || |
| (pAdapter->device_mode == WLAN_HDD_IBSS) || |
| (pAdapter->device_mode == WLAN_HDD_P2P_DEVICE) || |
| (pAdapter->device_mode == WLAN_HDD_SOFTAP) || |
| (pAdapter->device_mode == WLAN_HDD_P2P_GO)) { |
| hdd_abort_mac_scan(pHddCtx, pAdapter->sessionId, |
| eCSR_SCAN_ABORT_DEFAULT); |
| } |
| status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext); |
| pAdapterNode = pNext; |
| } |
| |
| EXIT(); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| /** |
| * wlan_hdd_restart_init() - restart init |
| * @pHddCtx: Pointer to hdd context |
| * |
| * This function initializes restart timer/flag. An internal function. |
| * |
| * Return: None |
| */ |
| static void wlan_hdd_restart_init(hdd_context_t *pHddCtx) |
| { |
| /* Initialize */ |
| pHddCtx->hdd_restart_retries = 0; |
| atomic_set(&pHddCtx->isRestartInProgress, 0); |
| vos_timer_init(&pHddCtx->hdd_restart_timer, |
| VOS_TIMER_TYPE_SW, |
| wlan_hdd_restart_timer_cb, |
| pHddCtx); |
| } |
| |
| /** |
| * wlan_hdd_restart_deinit() - restart deinit |
| * @pHddCtx: Pointer to hdd context |
| * |
| * This function cleans up the resources used. An internal function. |
| * |
| * Return: None |
| */ |
| static void wlan_hdd_restart_deinit(hdd_context_t* pHddCtx) |
| { |
| VOS_STATUS vos_status; |
| /* Block any further calls */ |
| atomic_set(&pHddCtx->isRestartInProgress, 1); |
| /* Cleanup */ |
| vos_status = vos_timer_stop( &pHddCtx->hdd_restart_timer ); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| hddLog(LOGE, FL("Failed to stop HDD restart timer")); |
| vos_status = vos_timer_destroy(&pHddCtx->hdd_restart_timer); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) |
| hddLog(LOGE, FL("Failed to destroy HDD restart timer")); |
| } |
| |
| #ifdef WLAN_NS_OFFLOAD |
| /** |
| * hdd_wlan_unregister_ip6_notifier() - unregister IP6 change notifier |
| * @hdd_ctx: Pointer to hdd context |
| * |
| * Return: None |
| */ |
| static void hdd_wlan_unregister_ip6_notifier(hdd_context_t *hdd_ctx) |
| { |
| unregister_inet6addr_notifier(&hdd_ctx->ipv6_notifier); |
| |
| return; |
| } |
| |
| /** |
| * hdd_wlan_register_ip6_notifier() - register IP6 change notifier |
| * @hdd_ctx: Pointer to hdd context |
| * |
| * Return: None |
| */ |
| static void hdd_wlan_register_ip6_notifier(hdd_context_t *hdd_ctx) |
| { |
| int ret; |
| |
| hdd_ctx->ipv6_notifier.notifier_call = wlan_hdd_ipv6_changed; |
| ret = register_inet6addr_notifier(&hdd_ctx->ipv6_notifier); |
| if (ret) |
| hddLog(LOGE, FL("Failed to register IPv6 notifier")); |
| else |
| hddLog(LOG1, FL("Registered IPv6 notifier")); |
| |
| return; |
| } |
| #else |
| /** |
| * hdd_wlan_unregister_ip6_notifier() - unregister IP6 change notifier |
| * @hdd_ctx: Pointer to hdd context |
| * |
| * Return: None |
| */ |
| static void hdd_wlan_unregister_ip6_notifier(hdd_context_t *hdd_ctx) |
| { |
| } |
| /** |
| * hdd_wlan_register_ip6_notifier() - register IP6 change notifier |
| * @hdd_ctx: Pointer to hdd context |
| * |
| * Return: None |
| */ |
| static void hdd_wlan_register_ip6_notifier(hdd_context_t *hdd_ctx) |
| { |
| } |
| #endif |
| |
| #ifdef WLAN_LOGGING_SOCK_SVC_ENABLE |
| /** |
| * wlan_hdd_logging_sock_activate_svc() - Activate logging |
| * @hdd_ctx: HDD context |
| * |
| * Activates the logging service |
| * |
| * Return: Zero in case of success, negative value otherwise |
| */ |
| static int wlan_hdd_logging_sock_activate_svc(hdd_context_t *hdd_ctx) |
| { |
| if (hdd_ctx->cfg_ini->wlanLoggingEnable) { |
| if (wlan_logging_sock_activate_svc( |
| hdd_ctx->cfg_ini->wlanLoggingFEToConsole, |
| hdd_ctx->cfg_ini->wlanLoggingNumBuf)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: wlan_logging_sock_activate_svc failed", |
| __func__); |
| return -EINVAL; |
| } |
| } |
| return 0; |
| } |
| /** |
| * wlan_hdd_logging_sock_deactivate_svc() - Deactivate logging |
| * @hdd_ctx: HDD context |
| * |
| * Deactivates the logging service |
| * |
| * Return: 0 on deactivating the logging service |
| */ |
| static int wlan_hdd_logging_sock_deactivate_svc(hdd_context_t *hdd_ctx) |
| { |
| if (hdd_ctx && hdd_ctx->cfg_ini->wlanLoggingEnable) |
| return wlan_logging_sock_deactivate_svc(); |
| |
| return 0; |
| } |
| #else |
| static inline int wlan_hdd_logging_sock_activate_svc(hdd_context_t *hdd_ctx) |
| { |
| return 0; |
| } |
| |
| static inline int wlan_hdd_logging_sock_deactivate_svc(hdd_context_t *hdd_ctx) |
| { |
| return 0; |
| } |
| #endif |
| |
| /** |
| * enum driver_unload_state - Various driver unload states |
| * @unload_start: Driver starting unload module. |
| * @unload_unregister_ip_notifier: Driver unregistering with ip notifiers. |
| * @unload_unregister_wext_adpater: Driver unregistering wext adapter. |
| * @unload_hdd_ftm_stop: Driver stopping ftm. |
| * @unload_hdd_ftm_close: Driver closing ftm. |
| * @unload_deregister_pm_ops: Driver deregistering pm ops. |
| * @unload_unregister_thermal_notify_cb: Driver unregistering thermal callback. |
| * @unload_aborting_all_scan: Driver aborting all scan. |
| * @unload_disable_pwr_save: Driver disable power save in firmware. |
| * @unload_req_full_power: Driver requesting full power mode. |
| * @unload_set_idle_ps_config: Driver setting imps false. |
| * @unload_debugfs_exit: Driver exiting debugfs. |
| * @unload_netdev_notifier: Driver unregistering with netdev notifiers. |
| * @unload_stop_all_adapter: Driver stopping all adapters. |
| * @unload_vos_stop: Driver stopping vos. |
| * @unload_vos_sched_close: Driver closing scheduler. |
| * @unload_vos_nv_close: Driver closing nv module. |
| * @unload_vos_close: Driver closing vos module. |
| * @unload_deinit_greep_ap: Driver deinit greep ap. |
| * @unload_logging_sock_deactivate_svc: Driver deactivating logging socket. |
| * @unload_hdd_send_status_pkg: Driver sending status packet to LPSS. |
| * @unload_close_cesium_nl_sock: Driver closing cesium nl socket. |
| * @unload_runtime_suspend_deinit: Driver deinit runtime suspend. |
| * @unload_close_all_adapters: Driver closing all adapter. |
| * @unload_ipa_cleanup: Driver performing ipa cleanup. |
| * @unload_flush_roc_work: Driver flush roc work. |
| * @unload_nl_srv_exit: Driver exit netlink service. |
| * @unload_wiphy_unregister: Driver unregister wiphy. |
| * @unload_wiphy_free: Driver free wiphy. |
| * @unload_subsystem_restart: Driver do subsystem restart. |
| * @unload_finish: Driver completed unload. |
| */ |
| enum driver_unload_state{ |
| unload_start = 1, |
| unload_unregister_ip_notifier, |
| unload_unregister_wext_adpater, |
| unload_hdd_ftm_stop, |
| unload_hdd_ftm_close =5 , |
| unload_deregister_pm_ops, |
| unload_unregister_thermal_notify_cb, |
| unload_aborting_all_scan, |
| unload_disable_pwr_save, |
| unload_req_full_power = 10, |
| unload_set_idle_ps_config, |
| unload_debugfs_exit, |
| unload_netdev_notifier, |
| unload_stop_all_adapter, |
| unload_vos_stop = 15, |
| unload_vos_sched_close, |
| unload_vos_nv_close, |
| unload_vos_close, |
| unload_deinit_greep_ap, |
| unload_logging_sock_deactivate_svc = 20, |
| unload_hdd_send_status_pkg, |
| unload_close_cesium_nl_sock, |
| unload_runtime_suspend_deinit, |
| unload_close_all_adapters, |
| unload_ipa_cleanup = 25, |
| unload_flush_roc_work, |
| unload_nl_srv_exit, |
| unload_wiphy_unregister, |
| unload_wiphy_free, |
| unload_subsystem_restart = 30, |
| |
| /* keep it last*/ |
| unload_finish = 0xff |
| }; |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_wlan_exit() - HDD WLAN exit function |
| |
| This is the driver exit point (invoked during rmmod) |
| |
| \param - pHddCtx - Pointer to the HDD Context |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| void hdd_wlan_exit(hdd_context_t *pHddCtx) |
| { |
| eHalStatus halStatus; |
| v_CONTEXT_t pVosContext = pHddCtx->pvosContext; |
| VOS_STATUS vosStatus; |
| struct wiphy *wiphy = pHddCtx->wiphy; |
| struct statsContext powerContext; |
| unsigned long rc; |
| hdd_config_t *pConfig = pHddCtx->cfg_ini; |
| |
| ENTER(); |
| TRACK_UNLOAD_STATUS(unload_start); |
| |
| hddLog(LOGE, FL("Unregister IPv6 notifier")); |
| TRACK_UNLOAD_STATUS(unload_unregister_ip_notifier); |
| hdd_wlan_unregister_ip6_notifier(pHddCtx); |
| hddLog(LOGE, FL("Unregister IPv4 notifier")); |
| unregister_inetaddr_notifier(&pHddCtx->ipv4_notifier); |
| wlan_hdd_tsf_deinit(pHddCtx); |
| |
| if (VOS_FTM_MODE != hdd_get_conparam()) |
| { |
| // Unloading, restart logic is no more required. |
| wlan_hdd_restart_deinit(pHddCtx); |
| } |
| TRACK_UNLOAD_STATUS(unload_unregister_wext_adpater); |
| hdd_UnregisterWext_all_adapters(pHddCtx); |
| if (VOS_FTM_MODE == hdd_get_conparam()) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: FTM MODE", __func__); |
| TRACK_UNLOAD_STATUS(unload_hdd_ftm_stop); |
| #if defined(QCA_WIFI_FTM) |
| if (hdd_ftm_stop(pHddCtx)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hdd_ftm_stop Failed",__func__); |
| VOS_ASSERT(0); |
| } |
| pHddCtx->ftm.ftm_state = WLAN_FTM_STOPPED; |
| #endif |
| TRACK_UNLOAD_STATUS(unload_hdd_ftm_close); |
| wlan_hdd_ftm_close(pHddCtx); |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: FTM driver unloaded", __func__); |
| goto free_hdd_ctx; |
| } |
| |
| TRACK_UNLOAD_STATUS(unload_deregister_pm_ops); |
| /* DeRegister with platform driver as client for Suspend/Resume */ |
| vosStatus = hddDeregisterPmOps(pHddCtx); |
| if ( !VOS_IS_STATUS_SUCCESS( vosStatus ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hddDeregisterPmOps failed",__func__); |
| VOS_ASSERT(0); |
| } |
| |
| TRACK_UNLOAD_STATUS(unload_unregister_thermal_notify_cb); |
| vosStatus = hddDevTmUnregisterNotifyCallback(pHddCtx); |
| if ( !VOS_IS_STATUS_SUCCESS( vosStatus ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hddDevTmUnregisterNotifyCallback failed",__func__); |
| } |
| |
| if (VOS_TIMER_STATE_RUNNING == |
| vos_timer_getCurrentState(&pHddCtx->tdls_source_timer)) |
| vos_timer_stop(&pHddCtx->tdls_source_timer); |
| vos_timer_destroy(&pHddCtx->tdls_source_timer); |
| |
| /* |
| * Cancel any outstanding scan requests. We are about to close all |
| * of our adapters, but an adapter structure is what SME passes back |
| * to our callback function. Hence if there are any outstanding scan |
| * requests then there is a race condition between when the adapter |
| * is closed and when the callback is invoked. We try to resolve that |
| * race condition here by cancelling any outstanding scans before we |
| * close the adapters. |
| * Note that the scans may be cancelled in an asynchronous manner, so |
| * ideally there needs to be some kind of synchronization. Rather than |
| * introduce a new synchronization here, we will utilize the fact that |
| * we are about to Request Full Power, and since that is synchronized, |
| * the expectation is that by the time Request Full Power has completed, |
| * all scans will be cancelled. |
| */ |
| TRACK_UNLOAD_STATUS(unload_aborting_all_scan); |
| hdd_abort_mac_scan_all_adapters(pHddCtx); |
| #ifdef FEATURE_BUS_BANDWIDTH |
| if (VOS_TIMER_STATE_RUNNING == |
| vos_timer_getCurrentState(&pHddCtx->bus_bw_timer)) |
| { |
| vos_timer_stop(&pHddCtx->bus_bw_timer); |
| hdd_rst_tcp_delack(pHddCtx); |
| |
| if (pHddCtx->hbw_requested) { |
| vos_remove_pm_qos(); |
| pHddCtx->hbw_requested = false; |
| } |
| } |
| |
| if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy( |
| &pHddCtx->bus_bw_timer))) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Cannot deallocate Bus bandwidth timer", __func__); |
| } |
| #endif |
| |
| #ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE |
| if (VOS_TIMER_STATE_RUNNING == |
| vos_timer_getCurrentState(&pHddCtx->skip_acs_scan_timer)) { |
| vos_timer_stop(&pHddCtx->skip_acs_scan_timer); |
| } |
| |
| if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy( |
| &pHddCtx->skip_acs_scan_timer))) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Cannot deallocate ACS Skip timer", __func__); |
| } |
| spin_lock(&pHddCtx->acs_skip_lock); |
| vos_mem_free(pHddCtx->last_acs_channel_list); |
| pHddCtx->last_acs_channel_list = NULL; |
| pHddCtx->num_of_channels = 0; |
| spin_unlock(&pHddCtx->acs_skip_lock); |
| #endif |
| |
| |
| if (pConfig && !pConfig->enablePowersaveOffload) |
| { |
| TRACK_UNLOAD_STATUS(unload_disable_pwr_save); |
| //Disable IMPS/BMPS as we do not want the device to enter any power |
| //save mode during shutdown |
| sme_DisablePowerSave(pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE); |
| sme_DisablePowerSave(pHddCtx->hHal, ePMC_BEACON_MODE_POWER_SAVE); |
| sme_DisablePowerSave(pHddCtx->hHal, ePMC_UAPSD_MODE_POWER_SAVE); |
| |
| //Ensure that device is in full power as we will touch H/W during vos_Stop |
| init_completion(&powerContext.completion); |
| powerContext.magic = POWER_CONTEXT_MAGIC; |
| |
| TRACK_UNLOAD_STATUS(unload_req_full_power); |
| halStatus = sme_RequestFullPower(pHddCtx->hHal, hdd_full_power_callback, |
| &powerContext, eSME_FULL_PWR_NEEDED_BY_HDD); |
| |
| if (eHAL_STATUS_SUCCESS != halStatus) |
| { |
| if (eHAL_STATUS_PMC_PENDING == halStatus) |
| { |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout( |
| &powerContext.completion, |
| msecs_to_jiffies(WLAN_WAIT_TIME_POWER)); |
| if (!rc) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: timed out while requesting full power", |
| __func__); |
| } |
| } |
| else |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Request for Full Power failed, status %d", |
| __func__, halStatus); |
| /* continue -- need to clean up as much as possible */ |
| } |
| } |
| /* either we never sent a request, we sent a request and received a |
| response or we sent a request and timed out. if we never sent a |
| request or if we sent a request and got a response, we want to |
| clear the magic out of paranoia. if we timed out there is a |
| race condition such that the callback function could be |
| executing at the same time we are. of primary concern is if the |
| callback function had already verified the "magic" but had not |
| yet set the completion variable when a timeout occurred. we |
| serialize these activities by invalidating the magic while |
| holding a shared spinlock which will cause us to block if the |
| callback is currently executing */ |
| spin_lock(&hdd_context_lock); |
| powerContext.magic = 0; |
| spin_unlock(&hdd_context_lock); |
| } |
| else |
| { |
| /* |
| * Powersave Offload Case |
| * Disable Idle Power Save Mode |
| */ |
| TRACK_UNLOAD_STATUS(unload_set_idle_ps_config); |
| hdd_set_idle_ps_config(pHddCtx, FALSE); |
| } |
| |
| TRACK_UNLOAD_STATUS(unload_debugfs_exit); |
| hdd_debugfs_exit(pHddCtx); |
| |
| // Unregister the Net Device Notifier |
| TRACK_UNLOAD_STATUS(unload_netdev_notifier); |
| unregister_netdevice_notifier(&hdd_netdev_notifier); |
| |
| /* Stop all adapters, this will ensure the termination of active |
| * connections on the interface. Make sure the vos_scheduler is |
| * still available to handle those control messages |
| */ |
| TRACK_UNLOAD_STATUS(unload_stop_all_adapter); |
| hdd_stop_all_adapters( pHddCtx ); |
| |
| #ifdef QCA_PKT_PROTO_TRACE |
| if (VOS_FTM_MODE != hdd_get_conparam()) |
| vos_pkt_proto_trace_close(); |
| #endif |
| |
| TRACK_UNLOAD_STATUS(unload_vos_stop); |
| //Stop all the modules |
| vosStatus = vos_stop( pVosContext ); |
| if (!VOS_IS_STATUS_SUCCESS(vosStatus)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to stop VOSS",__func__); |
| VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) ); |
| } |
| |
| //This requires pMac access, Call this before vos_close(). |
| hdd_unregister_mcast_bcast_filter(pHddCtx); |
| |
| TRACK_UNLOAD_STATUS(unload_vos_sched_close); |
| //Close the scheduler before calling vos_close to make sure no thread is |
| // scheduled after the each module close is called i.e after all the data |
| // structures are freed. |
| vosStatus = vos_sched_close( pVosContext ); |
| if (!VOS_IS_STATUS_SUCCESS(vosStatus)) { |
| VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to close VOSS Scheduler",__func__); |
| VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) ); |
| } |
| /* Destroy the wake lock */ |
| vos_wake_lock_destroy(&pHddCtx->rx_wake_lock); |
| /* Destroy the wake lock */ |
| vos_wake_lock_destroy(&pHddCtx->sap_wake_lock); |
| |
| hdd_hostapd_channel_wakelock_deinit(pHddCtx); |
| |
| TRACK_UNLOAD_STATUS(unload_vos_nv_close); |
| vosStatus = vos_nv_close(); |
| if (!VOS_IS_STATUS_SUCCESS(vosStatus)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to close NV", __func__); |
| VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) ); |
| } |
| |
| TRACK_UNLOAD_STATUS(unload_vos_close); |
| //Close VOSS |
| //This frees pMac(HAL) context. There should not be any call that requires pMac access after this. |
| vos_close(pVosContext); |
| |
| TRACK_UNLOAD_STATUS(unload_deinit_greep_ap); |
| hdd_wlan_green_ap_deinit(pHddCtx); |
| |
| //Close Watchdog |
| if (pConfig && pConfig->fIsLogpEnabled) |
| vos_watchdog_close(pVosContext); |
| |
| if (VOS_FTM_MODE != hdd_get_conparam()) { |
| TRACK_UNLOAD_STATUS(unload_logging_sock_deactivate_svc); |
| wlan_hdd_logging_sock_deactivate_svc(pHddCtx); |
| } |
| |
| #ifdef WLAN_FEATURE_LPSS |
| TRACK_UNLOAD_STATUS(unload_hdd_send_status_pkg); |
| wlan_hdd_send_status_pkg(NULL, NULL, 0, 0); |
| #endif |
| TRACK_UNLOAD_STATUS(unload_close_cesium_nl_sock); |
| hdd_close_cesium_nl_sock(); |
| |
| TRACK_UNLOAD_STATUS(unload_runtime_suspend_deinit); |
| hdd_runtime_suspend_deinit(pHddCtx); |
| TRACK_UNLOAD_STATUS(unload_close_all_adapters); |
| hdd_close_all_adapters( pHddCtx ); |
| |
| #ifdef IPA_OFFLOAD |
| TRACK_UNLOAD_STATUS(unload_ipa_cleanup); |
| hdd_ipa_cleanup(pHddCtx); |
| #endif |
| |
| /* free the power on lock from platform driver */ |
| if (free_riva_power_on_lock("wlan")) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: failed to free power on lock", |
| __func__); |
| } |
| |
| TRACK_UNLOAD_STATUS(unload_flush_roc_work); |
| /* Free up RoC request queue and flush workqueue */ |
| vos_flush_work(&pHddCtx->rocReqWork); |
| hdd_list_destroy(&pHddCtx->hdd_roc_req_q); |
| |
| free_hdd_ctx: |
| TRACK_UNLOAD_STATUS(unload_nl_srv_exit); |
| nl_srv_exit(); |
| |
| /* Free up dynamically allocated members inside HDD Adapter */ |
| if (pHddCtx->cfg_ini) { |
| vos_mem_free(pHddCtx->cfg_ini); |
| pHddCtx->cfg_ini= NULL; |
| } |
| |
| wlan_hdd_deinit_chan_info(pHddCtx); |
| wlan_hdd_deinit_tx_rx_histogram(pHddCtx); |
| hdd_free_probe_req_ouis(pHddCtx); |
| TRACK_UNLOAD_STATUS(unload_wiphy_unregister); |
| wiphy_unregister(wiphy) ; |
| wlan_hdd_cfg80211_deinit(wiphy); |
| TRACK_UNLOAD_STATUS(unload_wiphy_free); |
| wiphy_free(wiphy) ; |
| if (hdd_is_ssr_required()) |
| { |
| #ifdef MSM_PLATFORM |
| #ifdef CONFIG_CNSS |
| /* WDI timeout had happened during unload, so SSR is needed here */ |
| TRACK_UNLOAD_STATUS(unload_subsystem_restart); |
| subsystem_restart("wcnss"); |
| #endif |
| #endif |
| msleep(5000); |
| } |
| hdd_set_ssr_required (VOS_FALSE); |
| TRACK_UNLOAD_STATUS(unload_finish); |
| } |
| |
| void __hdd_wlan_exit(void) |
| { |
| hdd_context_t *pHddCtx = NULL; |
| v_CONTEXT_t pVosContext = NULL; |
| |
| ENTER(); |
| |
| //Get the global vos context |
| pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL); |
| if (!pVosContext) |
| return; |
| if (WLAN_IS_EPPING_ENABLED(con_mode)) { |
| epping_exit(pVosContext); |
| return; |
| } |
| |
| if(NULL == pVosContext) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s:Invalid global VOSS context", __func__); |
| EXIT(); |
| return; |
| } |
| |
| //Get the HDD context. |
| pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, |
| pVosContext); |
| |
| if(NULL == pHddCtx) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s:Invalid HDD Context", __func__); |
| EXIT(); |
| return; |
| } |
| |
| /* module exit should never proceed if SSR is not completed */ |
| while(pHddCtx->isLogpInProgress){ |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s:SSR in Progress; block rmmod for 1 second!!!", |
| __func__); |
| msleep(1000); |
| } |
| |
| pHddCtx->isUnloadInProgress = TRUE; |
| pHddCtx->driver_being_stopped = false; |
| |
| vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, TRUE); |
| vos_set_unload_in_progress(TRUE); |
| |
| hdd_close_tx_queues(pHddCtx); |
| //Do all the cleanup before deregistering the driver |
| hdd_driver_memdump_deinit(); |
| hdd_wlan_exit(pHddCtx); |
| EXIT(); |
| } |
| |
| #ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE |
| /** |
| * hdd_skip_acs_scan_timer_handler() - skip ACS scan timer timeout handler |
| * @data: pointer to hdd_context_t |
| * |
| * This function will reset acs_scan_status to eSAP_DO_NEW_ACS_SCAN. |
| * Then new ACS request will do a fresh scan without reusing the cached |
| * scan information. |
| * |
| * Return: void |
| */ |
| void hdd_skip_acs_scan_timer_handler(void * data) |
| { |
| hdd_context_t *hdd_ctx = (hdd_context_t *) data; |
| hdd_adapter_t *ap_adapter; |
| |
| hddLog(LOG1, FL("ACS Scan result expired. Reset ACS scan skip")); |
| hdd_ctx->skip_acs_scan_status = eSAP_DO_NEW_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; |
| spin_unlock(&hdd_ctx->acs_skip_lock); |
| |
| /* Get first SAP adapter to clear results */ |
| ap_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_SOFTAP); |
| if (!hdd_ctx->hHal || !ap_adapter) |
| return; |
| sme_ScanFlushResult(hdd_ctx->hHal, ap_adapter->sessionId); |
| } |
| #endif |
| |
| #ifdef QCA_HT_2040_COEX |
| /**-------------------------------------------------------------------------- |
| |
| \brief notify FW with HT20/HT40 mode |
| |
| -------------------------------------------------------------------------*/ |
| int hdd_wlan_set_ht2040_mode(hdd_adapter_t *pAdapter, v_U16_t staId, |
| v_MACADDR_t macAddrSTA, int channel_type) |
| { |
| int status; |
| VOS_STATUS vosStatus; |
| hdd_context_t *pHddCtx = NULL; |
| |
| pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return -1; |
| |
| if (!pHddCtx->hHal) |
| return -1; |
| |
| vosStatus = sme_notify_ht2040_mode(pHddCtx->hHal, staId, macAddrSTA, |
| pAdapter->sessionId, channel_type); |
| if (VOS_STATUS_SUCCESS != vosStatus) { |
| hddLog(LOGE, "Fail to send notification with ht2040 mode\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| |
| /**-------------------------------------------------------------------------- |
| |
| \brief notify FW with modem power status |
| |
| -------------------------------------------------------------------------*/ |
| int hdd_wlan_notify_modem_power_state(int state) |
| { |
| int status; |
| VOS_STATUS vosStatus; |
| v_CONTEXT_t pVosContext = NULL; |
| hdd_context_t *pHddCtx = NULL; |
| |
| pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL); |
| if (!pVosContext) |
| return -1; |
| |
| pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext); |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return -1; |
| |
| if (!pHddCtx->hHal) |
| return -1; |
| |
| vosStatus = sme_notify_modem_power_state(pHddCtx->hHal, state); |
| if (VOS_STATUS_SUCCESS != vosStatus) { |
| hddLog(LOGE, "Fail to send notification with modem power state %d", |
| state); |
| return -1; |
| } |
| return 0; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_post_voss_start_config() - HDD post voss start config helper |
| |
| \param - pAdapter - Pointer to the HDD |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| VOS_STATUS hdd_post_voss_start_config(hdd_context_t* pHddCtx) |
| { |
| eHalStatus halStatus; |
| v_U32_t listenInterval; |
| tANI_U32 ignoreDtim; |
| |
| |
| // Send ready indication to the HDD. This will kick off the MAC |
| // into a 'running' state and should kick off an initial scan. |
| halStatus = sme_HDDReadyInd( pHddCtx->hHal ); |
| if ( !HAL_STATUS_SUCCESS( halStatus ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: sme_HDDReadyInd() failed with status " |
| "code %08d [x%08x]",__func__, halStatus, halStatus ); |
| return VOS_STATUS_E_FAILURE; |
| } |
| |
| // Set default LI and ignoreDtim into HDD context, |
| // otherwise under some race condition, HDD will set 0 LI value into RIVA, |
| // And RIVA will crash |
| wlan_cfgGetInt(pHddCtx->hHal, WNI_CFG_LISTEN_INTERVAL, &listenInterval); |
| pHddCtx->hdd_actual_LI_value = listenInterval; |
| wlan_cfgGetInt(pHddCtx->hHal, WNI_CFG_IGNORE_DTIM, &ignoreDtim); |
| pHddCtx->hdd_actual_ignore_DTIM_value = ignoreDtim; |
| |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| /* wake lock APIs for HDD */ |
| void hdd_prevent_suspend(uint32_t reason) |
| { |
| vos_wake_lock_acquire(&wlan_wake_lock, reason); |
| } |
| |
| void hdd_allow_suspend(uint32_t reason) |
| { |
| vos_wake_lock_release(&wlan_wake_lock, reason); |
| } |
| |
| void hdd_prevent_suspend_timeout(v_U32_t timeout, uint32_t reason) |
| { |
| vos_wake_lock_timeout_acquire(&wlan_wake_lock, timeout, |
| reason); |
| } |
| |
| /** |
| * hdd_wlan_wakelock_create() -Create wakelock named as wlan |
| * |
| * Return: none |
| */ |
| void hdd_wlan_wakelock_create(void) |
| { |
| vos_wake_lock_init(&wlan_wake_lock, "wlan"); |
| } |
| |
| /** |
| * hdd_wlan_wakelock_destroy() -Destroy wakelock named as wlan |
| * |
| * Return: none |
| */ |
| void hdd_wlan_wakelock_destroy(void) |
| { |
| vos_wake_lock_destroy(&wlan_wake_lock); |
| } |
| |
| /** |
| * wlan_hdd_wakelocks_destroy() -Destroy all the wakelocks |
| * @hdd_ctx: hdd context |
| * |
| * Return: none |
| */ |
| void wlan_hdd_wakelocks_destroy(hdd_context_t *hdd_ctx) |
| { |
| if (hdd_ctx) { |
| vos_wake_lock_destroy(&hdd_ctx->rx_wake_lock); |
| vos_wake_lock_destroy(&hdd_ctx->sap_wake_lock); |
| hdd_hostapd_channel_wakelock_deinit(hdd_ctx); |
| } |
| } |
| |
| /** |
| * wlan_hdd_netdev_notifiers_cleanup() -unregister notifiers with kernel |
| * @hdd_ctx: hdd context |
| * |
| * Return: none |
| */ |
| void wlan_hdd_netdev_notifiers_cleanup(hdd_context_t * hdd_ctx) |
| { |
| if (hdd_ctx) { |
| hddLog(LOGE, FL("Unregister IPv6 notifier")); |
| hdd_wlan_unregister_ip6_notifier(hdd_ctx); |
| hddLog(LOGE, FL("Unregister IPv4 notifier")); |
| unregister_inetaddr_notifier(&hdd_ctx->ipv4_notifier); |
| } |
| unregister_netdevice_notifier(&hdd_netdev_notifier); |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_exchange_version_and_caps() - HDD function to exchange version and capability |
| information between Host and Riva |
| |
| This function gets reported version of FW |
| It also finds the version of Riva headers used to compile the host |
| It compares the above two and prints a warning if they are different |
| It gets the SW and HW version string |
| Finally, it exchanges capabilities between host and Riva i.e. host and riva exchange a msg |
| indicating the features they support through a bitmap |
| |
| \param - pHddCtx - Pointer to HDD context |
| |
| \return - void |
| |
| --------------------------------------------------------------------------*/ |
| |
| void hdd_exchange_version_and_caps(hdd_context_t *pHddCtx) |
| { |
| |
| tSirVersionType versionCompiled; |
| tSirVersionType versionReported; |
| tSirVersionString versionString; |
| tANI_U8 fwFeatCapsMsgSupported = 0; |
| VOS_STATUS vstatus; |
| |
| memset(&versionCompiled, 0, sizeof(versionCompiled)); |
| memset(&versionReported, 0, sizeof(versionReported)); |
| |
| /* retrieve and display WCNSS version information */ |
| do { |
| |
| vstatus = sme_GetWcnssWlanCompiledVersion(pHddCtx->hHal, |
| &versionCompiled); |
| if (!VOS_IS_STATUS_SUCCESS(vstatus)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: unable to retrieve WCNSS WLAN compiled version", |
| __func__); |
| break; |
| } |
| |
| vstatus = sme_GetWcnssWlanReportedVersion(pHddCtx->hHal, |
| &versionReported); |
| if (!VOS_IS_STATUS_SUCCESS(vstatus)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: unable to retrieve WCNSS WLAN reported version", |
| __func__); |
| break; |
| } |
| |
| if ((versionCompiled.major != versionReported.major) || |
| (versionCompiled.minor != versionReported.minor) || |
| (versionCompiled.version != versionReported.version) || |
| (versionCompiled.revision != versionReported.revision)) |
| { |
| pr_err("%s: WCNSS WLAN Version %u.%u.%u.%u, " |
| "Host expected %u.%u.%u.%u\n", |
| WLAN_MODULE_NAME, |
| (int)versionReported.major, |
| (int)versionReported.minor, |
| (int)versionReported.version, |
| (int)versionReported.revision, |
| (int)versionCompiled.major, |
| (int)versionCompiled.minor, |
| (int)versionCompiled.version, |
| (int)versionCompiled.revision); |
| } |
| else |
| { |
| pr_info("%s: WCNSS WLAN version %u.%u.%u.%u\n", |
| WLAN_MODULE_NAME, |
| (int)versionReported.major, |
| (int)versionReported.minor, |
| (int)versionReported.version, |
| (int)versionReported.revision); |
| } |
| |
| vstatus = sme_GetWcnssSoftwareVersion(pHddCtx->hHal, |
| versionString, |
| sizeof(versionString)); |
| if (!VOS_IS_STATUS_SUCCESS(vstatus)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: unable to retrieve WCNSS software version string", |
| __func__); |
| break; |
| } |
| |
| pr_info("%s: WCNSS software version %s\n", |
| WLAN_MODULE_NAME, versionString); |
| |
| vstatus = sme_GetWcnssHardwareVersion(pHddCtx->hHal, |
| versionString, |
| sizeof(versionString)); |
| if (!VOS_IS_STATUS_SUCCESS(vstatus)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: unable to retrieve WCNSS hardware version string", |
| __func__); |
| break; |
| } |
| |
| pr_info("%s: WCNSS hardware version %s\n", |
| WLAN_MODULE_NAME, versionString); |
| |
| /* 1.Check if FW version is greater than 0.1.1.0. Only then send host-FW capability exchange message |
| 2.Host-FW capability exchange message is only present on riva 1.1 so |
| send the message only if it the riva is 1.1 |
| minor numbers for different riva branches: |
| 0 -> (1.0)Mainline Build |
| 1 -> (1.1)Mainline Build |
| 2->(1.04) Stability Build |
| */ |
| if (((versionReported.major>0) || (versionReported.minor>1) || |
| ((versionReported.minor>=1) && (versionReported.version>=1))) |
| && ((versionReported.major == 1) && (versionReported.minor >= 1))) |
| fwFeatCapsMsgSupported = 1; |
| |
| if (fwFeatCapsMsgSupported) |
| { |
| #ifdef WLAN_ACTIVEMODE_OFFLOAD_FEATURE |
| if(!pHddCtx->cfg_ini->fEnableActiveModeOffload) |
| sme_disableFeatureCapablity(WLANACTIVE_OFFLOAD); |
| #endif |
| /* Indicate if IBSS heartbeat monitoring needs to be offloaded */ |
| if (!pHddCtx->cfg_ini->enableIbssHeartBeatOffload) |
| { |
| sme_disableFeatureCapablity(IBSS_HEARTBEAT_OFFLOAD); |
| } |
| |
| sme_featureCapsExchange(pHddCtx->hHal); |
| } |
| |
| } while (0); |
| |
| } |
| |
| /* Initialize channel list in sme based on the country code */ |
| VOS_STATUS hdd_set_sme_chan_list(hdd_context_t *hdd_ctx) |
| { |
| return sme_init_chan_list(hdd_ctx->hHal, hdd_ctx->reg.alpha2, |
| hdd_ctx->reg.cc_src); |
| } |
| |
| void hdd_set_dfs_regdomain(hdd_context_t *phddctx, bool restore) |
| { |
| if(!restore) { |
| if (vos_nv_get_dfs_region(&phddctx->hdd_dfs_regdomain)) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: unable to retrieve dfs region from hdd", |
| __func__); |
| } |
| } |
| else { |
| if (vos_nv_set_dfs_region(phddctx->hdd_dfs_regdomain)) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: unable to set dfs region", |
| __func__); |
| } |
| } |
| } |
| |
| /** |
| * hdd_is_5g_supported() - to know if ini configuration supports 5GHz |
| * @pHddCtx: Pointer to the hdd context |
| * |
| * Return: true if ini configuration supports 5GHz |
| */ |
| boolean hdd_is_5g_supported(hdd_context_t * pHddCtx) |
| { |
| /** |
| * If wcnss_wlan_iris_xo_mode() returns WCNSS_XO_48MHZ(1); |
| * then hardware support 5Ghz. |
| */ |
| if(!pHddCtx || !pHddCtx->cfg_ini) |
| return true; |
| |
| if (pHddCtx->cfg_ini->nBandCapability != eCSR_BAND_24) |
| return true; |
| else |
| return false; |
| } |
| |
| static VOS_STATUS wlan_hdd_reg_init(hdd_context_t *hdd_ctx) |
| { |
| struct wiphy *wiphy; |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| |
| wiphy = hdd_ctx->wiphy; |
| |
| /* initialize the NV module. This is required so that |
| we can initialize the channel information in wiphy |
| from the NV.bin data. The channel information in |
| wiphy needs to be initialized before wiphy registration */ |
| |
| status = vos_init_wiphy_from_eeprom(); |
| if (!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| /* NV module cannot be initialized */ |
| hddLog( VOS_TRACE_LEVEL_FATAL, |
| "%s: vos_init_wiphy failed", __func__); |
| return status; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)) || defined(WITH_BACKPORTS) |
| wiphy->wowlan = &wowlan_support_reg_init; |
| #else |
| wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | |
| WIPHY_WOWLAN_MAGIC_PKT | |
| WIPHY_WOWLAN_DISCONNECT | |
| WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | |
| WIPHY_WOWLAN_GTK_REKEY_FAILURE | |
| WIPHY_WOWLAN_EAP_IDENTITY_REQ | |
| WIPHY_WOWLAN_4WAY_HANDSHAKE | |
| WIPHY_WOWLAN_RFKILL_RELEASE; |
| |
| wiphy->wowlan.n_patterns = (WOW_MAX_FILTER_LISTS * |
| WOW_MAX_FILTERS_PER_LIST); |
| wiphy->wowlan.pattern_min_len = WOW_MIN_PATTERN_SIZE; |
| wiphy->wowlan.pattern_max_len = WOW_MAX_PATTERN_SIZE; |
| #endif |
| |
| return status; |
| } |
| |
| #ifdef FEATURE_BUS_BANDWIDTH |
| #ifdef QCA_SUPPORT_TXRX_HL_BUNDLE |
| static void hdd_set_bundle_require(uint16_t session_id, hdd_context_t *hdd_ctx, |
| uint64_t tx_bytes) |
| { |
| tlshim_set_bundle_require(session_id, tx_bytes, |
| hdd_ctx->cfg_ini->busBandwidthComputeInterval, |
| hdd_ctx->cfg_ini->pkt_bundle_threshold_high, |
| hdd_ctx->cfg_ini->pkt_bundle_threshold_low); |
| |
| } |
| #else |
| static void hdd_set_bundle_require(uint16_t session_id, hdd_context_t *hdd_ctx, |
| uint64_t tx_bytes) |
| { |
| return; |
| } |
| #endif |
| |
| |
| void hdd_cnss_request_bus_bandwidth(hdd_context_t *pHddCtx, |
| const uint64_t tx_packets, const uint64_t rx_packets) |
| { |
| uint64_t total = tx_packets + rx_packets; |
| uint64_t temp_rx = 0; |
| uint64_t temp_tx = 0; |
| enum cnss_bus_width_type next_vote_level = CNSS_BUS_WIDTH_NONE; |
| enum wlan_tp_level next_rx_level = WLAN_SVC_TP_NONE; |
| enum wlan_tp_level next_tx_level = WLAN_SVC_TP_NONE; |
| struct device *dev = pHddCtx->parent_dev; |
| |
| if (total > pHddCtx->cfg_ini->busBandwidthHighThreshold) |
| next_vote_level = CNSS_BUS_WIDTH_HIGH; |
| else if (total > pHddCtx->cfg_ini->busBandwidthMediumThreshold) |
| next_vote_level = CNSS_BUS_WIDTH_MEDIUM; |
| else if (total > pHddCtx->cfg_ini->busBandwidthLowThreshold) |
| next_vote_level = CNSS_BUS_WIDTH_LOW; |
| else |
| next_vote_level = CNSS_BUS_WIDTH_NONE; |
| |
| pHddCtx->hdd_txrx_hist[pHddCtx->hdd_txrx_hist_idx].next_vote_level |
| = next_vote_level; |
| |
| if (pHddCtx->cur_vote_level != next_vote_level) { |
| hddLog(VOS_TRACE_LEVEL_DEBUG, |
| "%s: trigger level %d, tx_packets: %lld, rx_packets: %lld", |
| __func__, next_vote_level, tx_packets, rx_packets); |
| pHddCtx->cur_vote_level = next_vote_level; |
| vos_request_bus_bandwidth(dev, next_vote_level); |
| |
| if (next_vote_level <= CNSS_BUS_WIDTH_LOW) { |
| if (pHddCtx->hbw_requested) { |
| vos_remove_pm_qos(); |
| pHddCtx->hbw_requested = false; |
| } |
| if (vos_sched_handle_throughput_req(false)) |
| hddLog(LOGE, FL("low bandwidth set rx affinity fail")); |
| } else { |
| if (!pHddCtx->hbw_requested) { |
| vos_request_pm_qos_type(PM_QOS_CPU_DMA_LATENCY, |
| DISABLE_KRAIT_IDLE_PS_VAL); |
| pHddCtx->hbw_requested = true; |
| } |
| if (vos_sched_handle_throughput_req(true)) |
| hddLog(LOGE, FL("high bandwidth set rx affinity fail")); |
| } |
| } |
| |
| /* fine-tuning parameters for RX Flows */ |
| temp_rx = (rx_packets + pHddCtx->prev_rx) / 2; |
| pHddCtx->prev_rx = rx_packets; |
| if (temp_rx > pHddCtx->cfg_ini->tcpDelackThresholdHigh) { |
| if ((pHddCtx->cur_rx_level != WLAN_SVC_TP_HIGH) && |
| (++pHddCtx->rx_high_ind_cnt == pHddCtx->cfg_ini->tcpDelackTimerCount)) { |
| next_rx_level = WLAN_SVC_TP_HIGH; |
| } |
| } else { |
| next_rx_level = WLAN_SVC_TP_LOW; |
| pHddCtx->rx_high_ind_cnt = 0; |
| } |
| |
| pHddCtx->hdd_txrx_hist[pHddCtx->hdd_txrx_hist_idx].next_rx_level |
| = next_rx_level; |
| |
| if (pHddCtx->cur_rx_level != next_rx_level) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG, |
| "%s: TCP DELACK trigger level %d, average_rx: %llu", |
| __func__, next_rx_level, temp_rx); |
| pHddCtx->cur_rx_level = next_rx_level; |
| #ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK |
| if (pHddCtx->cfg_ini->del_ack_enable) |
| next_rx_level = WLAN_SVC_TP_LOW; |
| #endif |
| wlan_hdd_send_svc_nlink_msg(pHddCtx->radio_index, |
| WLAN_SVC_WLAN_TP_IND, |
| &next_rx_level, |
| sizeof(next_rx_level)); |
| } |
| |
| /* fine-tuning parameters for TX Flows */ |
| temp_tx = (tx_packets + pHddCtx->prev_tx) / 2; |
| pHddCtx->prev_tx = tx_packets; |
| if (temp_tx > pHddCtx->cfg_ini->tcp_tx_high_tput_thres) |
| next_tx_level = WLAN_SVC_TP_HIGH; |
| else |
| next_tx_level = WLAN_SVC_TP_LOW; |
| |
| if (pHddCtx->cur_tx_level != next_tx_level) { |
| hddLog(VOS_TRACE_LEVEL_DEBUG, |
| "%s: change TCP TX trigger level %d, average_tx: %llu ", |
| __func__, next_tx_level, temp_tx); |
| pHddCtx->cur_tx_level = next_tx_level; |
| wlan_hdd_send_svc_nlink_msg(pHddCtx->radio_index, |
| WLAN_SVC_WLAN_TP_TX_IND, |
| &next_tx_level, |
| sizeof(next_tx_level)); |
| } |
| |
| pHddCtx->hdd_txrx_hist[pHddCtx->hdd_txrx_hist_idx].next_tx_level |
| = next_tx_level; |
| |
| pHddCtx->hdd_txrx_hist_idx++; |
| pHddCtx->hdd_txrx_hist_idx &= NUM_TX_RX_HISTOGRAM_MASK; |
| } |
| |
| static void hdd_bus_bw_compute_cbk(void *priv) |
| { |
| hdd_context_t *pHddCtx = (hdd_context_t *)priv; |
| hdd_adapter_t *pAdapter = NULL; |
| uint64_t tx_packets = 0, rx_packets = 0, tx_bytes = 0; |
| unsigned long fwd_tx_packets = 0, fwd_rx_packets = 0; |
| unsigned long fwd_tx_packets_diff = 0, fwd_rx_packets_diff = 0; |
| uint64_t total_tx = 0, total_rx = 0; |
| hdd_adapter_list_node_t *pAdapterNode = NULL; |
| VOS_STATUS status = 0; |
| A_STATUS ret; |
| v_BOOL_t connected = FALSE; |
| #ifdef IPA_UC_OFFLOAD |
| uint32_t ipa_tx_packets = 0, ipa_rx_packets = 0; |
| hdd_adapter_t *pValidAdapter = NULL; |
| #endif /* IPA_UC_OFFLOAD */ |
| |
| if (wlan_hdd_validate_context(pHddCtx)) |
| return; |
| |
| for (status = hdd_get_front_adapter(pHddCtx, &pAdapterNode); |
| NULL != pAdapterNode && VOS_STATUS_SUCCESS == status; |
| status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pAdapterNode)) |
| { |
| |
| if ((pAdapter = pAdapterNode->pAdapter) == NULL) |
| continue; |
| /* Validate magic so we don't end up accessing a freed adapter.*/ |
| if (pAdapter->magic != WLAN_HDD_ADAPTER_MAGIC) |
| continue; |
| |
| #ifdef IPA_UC_OFFLOAD |
| if (NULL == pValidAdapter) |
| pValidAdapter = pAdapter; |
| #endif /* IPA_UC_OFFLOAD */ |
| |
| if ((pAdapter->device_mode == WLAN_HDD_INFRA_STATION || |
| pAdapter->device_mode == WLAN_HDD_P2P_CLIENT) && |
| WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)->conn_info.connState |
| != eConnectionState_Associated) { |
| |
| continue; |
| } |
| |
| if ((pAdapter->device_mode == WLAN_HDD_SOFTAP || |
| pAdapter->device_mode == WLAN_HDD_P2P_GO) && |
| WLAN_HDD_GET_AP_CTX_PTR(pAdapter)->bApActive == VOS_FALSE) { |
| |
| continue; |
| } |
| |
| tx_packets += HDD_BW_GET_DIFF(pAdapter->stats.tx_packets, |
| pAdapter->prev_tx_packets); |
| tx_bytes += HDD_BW_GET_DIFF(pAdapter->stats.tx_bytes, |
| pAdapter->prev_tx_bytes); |
| rx_packets += HDD_BW_GET_DIFF(pAdapter->stats.rx_packets, |
| pAdapter->prev_rx_packets); |
| |
| if (pAdapter->device_mode == WLAN_HDD_SOFTAP || |
| pAdapter->device_mode == WLAN_HDD_P2P_GO || |
| pAdapter->device_mode == WLAN_HDD_IBSS) { |
| |
| ret = tlshim_get_intra_bss_fwd_pkts_count(pAdapter->sessionId, |
| &fwd_tx_packets, &fwd_rx_packets); |
| if (ret == A_OK) { |
| fwd_tx_packets_diff += HDD_BW_GET_DIFF(fwd_tx_packets, |
| pAdapter->prev_fwd_tx_packets); |
| fwd_rx_packets_diff += HDD_BW_GET_DIFF(fwd_tx_packets, |
| pAdapter->prev_fwd_rx_packets); |
| } |
| } |
| |
| hdd_set_bundle_require(pAdapter->sessionId, pHddCtx, tx_bytes); |
| hdd_set_driver_del_ack_enable(pAdapter->sessionId, pHddCtx, rx_packets); |
| |
| total_rx += pAdapter->stats.rx_packets; |
| total_tx += pAdapter->stats.tx_packets; |
| |
| |
| spin_lock_bh(&pHddCtx->bus_bw_lock); |
| pAdapter->prev_tx_packets = pAdapter->stats.tx_packets; |
| pAdapter->prev_tx_bytes = pAdapter->stats.tx_bytes; |
| pAdapter->prev_rx_packets = pAdapter->stats.rx_packets; |
| pAdapter->prev_fwd_tx_packets = fwd_tx_packets; |
| pAdapter->prev_fwd_rx_packets = fwd_rx_packets; |
| spin_unlock_bh(&pHddCtx->bus_bw_lock); |
| connected = TRUE; |
| } |
| |
| pHddCtx->hdd_txrx_hist[pHddCtx->hdd_txrx_hist_idx].total_rx = total_rx; |
| pHddCtx->hdd_txrx_hist[pHddCtx->hdd_txrx_hist_idx].total_tx = total_tx; |
| pHddCtx->hdd_txrx_hist[pHddCtx->hdd_txrx_hist_idx].interval_rx = rx_packets; |
| pHddCtx->hdd_txrx_hist[pHddCtx->hdd_txrx_hist_idx].interval_tx = tx_packets; |
| |
| /* add intra bss forwarded tx and rx packets */ |
| tx_packets += fwd_tx_packets_diff; |
| rx_packets += fwd_rx_packets_diff; |
| |
| #ifdef IPA_UC_OFFLOAD |
| hdd_ipa_uc_stat_query(pHddCtx, &ipa_tx_packets, &ipa_rx_packets); |
| tx_packets += (uint64_t)ipa_tx_packets; |
| rx_packets += (uint64_t)ipa_rx_packets; |
| |
| if (pValidAdapter) { |
| pValidAdapter->stats.tx_packets += ipa_tx_packets; |
| pValidAdapter->stats.rx_packets += ipa_rx_packets; |
| } |
| #endif /* IPA_UC_OFFLOAD */ |
| if (!connected) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "bus bandwidth timer running in disconnected state"); |
| return; |
| } |
| |
| hdd_cnss_request_bus_bandwidth(pHddCtx, tx_packets, rx_packets); |
| |
| #ifdef IPA_OFFLOAD |
| hdd_ipa_set_perf_level(pHddCtx, tx_packets, rx_packets); |
| #ifdef IPA_UC_OFFLOAD |
| hdd_ipa_uc_stat_request(pValidAdapter, 2); |
| #endif /* IPA_UC_OFFLOAD */ |
| #endif |
| |
| vos_timer_start(&pHddCtx->bus_bw_timer, |
| pHddCtx->cfg_ini->busBandwidthComputeInterval); |
| } |
| #endif |
| |
| /** |
| * wlan_hdd_init_tx_rx_histogram() - init tx/rx histogram stats |
| * @pHddCtx: hdd context |
| * |
| * Return: 0 for success |
| */ |
| int wlan_hdd_init_tx_rx_histogram(hdd_context_t *pHddCtx) |
| { |
| pHddCtx->hdd_txrx_hist = vos_mem_malloc( |
| (sizeof(struct hdd_tx_rx_histogram) * NUM_TX_RX_HISTOGRAM)); |
| if (pHddCtx->hdd_txrx_hist == NULL) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed malloc for hdd_txrx_hist",__func__); |
| return -ENOMEM; |
| } |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_deinit_tx_rx_histogram() - deinit tx/rx histogram stats |
| * @pHddCtx: hdd context |
| * |
| * Return: none |
| */ |
| void wlan_hdd_deinit_tx_rx_histogram(hdd_context_t *pHddCtx) |
| { |
| if (pHddCtx->hdd_txrx_hist) { |
| vos_mem_free(pHddCtx->hdd_txrx_hist); |
| pHddCtx->hdd_txrx_hist = NULL; |
| } |
| } |
| |
| void wlan_hdd_display_tx_rx_histogram(hdd_context_t *pHddCtx) |
| { |
| int i; |
| |
| #ifdef FEATURE_BUS_BANDWIDTH |
| hddLog(VOS_TRACE_LEVEL_ERROR, "BW Interval: %d curr_index %d", |
| pHddCtx->cfg_ini->busBandwidthComputeInterval, |
| pHddCtx->hdd_txrx_hist_idx); |
| hddLog(VOS_TRACE_LEVEL_ERROR, "BW High TH: %d BW Med TH: %d BW Low TH: %d", |
| pHddCtx->cfg_ini->busBandwidthHighThreshold, |
| pHddCtx->cfg_ini->busBandwidthMediumThreshold, |
| pHddCtx->cfg_ini->busBandwidthLowThreshold); |
| hddLog(VOS_TRACE_LEVEL_ERROR, "TCP DEL High TH: %d TCP DEL Low TH: %d", |
| pHddCtx->cfg_ini->tcpDelackThresholdHigh, |
| pHddCtx->cfg_ini->tcpDelackThresholdLow); |
| #endif |
| |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "index, total_rx, interval_rx, total_tx, interval_tx, next_vote_level, next_rx_level, next_tx_level"); |
| for (i=0; i < NUM_TX_RX_HISTOGRAM; i++){ |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%d: %llu, %llu, %llu, %llu, %d, %d, %d", |
| i, pHddCtx->hdd_txrx_hist[i].total_rx, |
| pHddCtx->hdd_txrx_hist[i].interval_rx, |
| pHddCtx->hdd_txrx_hist[i].total_tx, |
| pHddCtx->hdd_txrx_hist[i].interval_tx, |
| pHddCtx->hdd_txrx_hist[i].next_vote_level, |
| pHddCtx->hdd_txrx_hist[i].next_rx_level, |
| pHddCtx->hdd_txrx_hist[i].next_tx_level); |
| } |
| return; |
| } |
| |
| void wlan_hdd_clear_tx_rx_histogram(hdd_context_t *pHddCtx) |
| { |
| pHddCtx->hdd_txrx_hist_idx = 0; |
| vos_mem_zero(pHddCtx->hdd_txrx_hist, |
| (sizeof(struct hdd_tx_rx_histogram) * NUM_TX_RX_HISTOGRAM)); |
| } |
| |
| /** |
| * wlan_hdd_display_adapter_netif_queue_history() - display adapter's netif |
| * queue operation history |
| * @adapter: hdd adapter |
| * |
| * Return: none |
| */ |
| static inline void |
| wlan_hdd_display_adapter_netif_queue_history(hdd_adapter_t *adapter) |
| { |
| int i; |
| adf_os_time_t total, pause, unpause, curr_time; |
| |
| if (!adapter) |
| return; |
| hddLog(LOGE, FL("Session_id %d device mode %d"), |
| adapter->sessionId, adapter->device_mode); |
| |
| hddLog(LOGE, "Netif queue operation statistics:"); |
| hddLog(LOGE, "Current pause_map value %x", adapter->pause_map); |
| curr_time = vos_system_ticks(); |
| total = curr_time - adapter->start_time; |
| if (adapter->pause_map) { |
| pause = adapter->total_pause_time + |
| curr_time - adapter->last_time; |
| unpause = adapter->total_unpause_time; |
| } else { |
| unpause = adapter->total_unpause_time + |
| curr_time - adapter->last_time; |
| pause = adapter->total_pause_time; |
| } |
| hddLog(LOGE, "Total: %ums Pause: %ums Unpause: %ums", |
| vos_system_ticks_to_msecs(total), |
| vos_system_ticks_to_msecs(pause), |
| vos_system_ticks_to_msecs(unpause)); |
| hddLog(LOGE, " reason_type: pause_cnt: unpause_cnt"); |
| |
| for (i = 0; i < WLAN_REASON_TYPE_MAX; i++) { |
| hddLog(LOGE, "%s: %d: %d", |
| hdd_reason_type_to_string(i), |
| adapter->queue_oper_stats[i].pause_count, |
| adapter->queue_oper_stats[i].unpause_count); |
| } |
| |
| hddLog(LOGE, "Netif queue operation history: current index %d", |
| adapter->history_index); |
| hddLog(LOGE, "index: time: action_type: reason_type: pause_map"); |
| |
| for (i = 0; i < WLAN_HDD_MAX_HISTORY_ENTRY; i++) { |
| hddLog(LOGE, "%d: %u: %s: %s: %x", |
| i, vos_system_ticks_to_msecs( |
| adapter->queue_oper_history[i].time), |
| hdd_action_type_to_string( |
| adapter->queue_oper_history[i].netif_action), |
| hdd_reason_type_to_string( |
| adapter->queue_oper_history[i].netif_reason), |
| adapter->queue_oper_history[i].pause_map); |
| } |
| } |
| |
| /** |
| * wlan_hdd_display_netif_queue_history() - display netif queue |
| * operation history |
| * @hdd_ctx: hdd context |
| * |
| * Return: none |
| */ |
| void wlan_hdd_display_netif_queue_history(hdd_context_t *hdd_ctx) |
| { |
| hdd_adapter_t *adapter; |
| hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL; |
| VOS_STATUS status; |
| |
| status = hdd_get_front_adapter(hdd_ctx, &adapter_node); |
| while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) { |
| adapter = adapter_node->pAdapter; |
| |
| wlan_hdd_display_adapter_netif_queue_history(adapter); |
| |
| status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next); |
| adapter_node = next; |
| } |
| } |
| |
| /** |
| * wlan_hdd_clear_netif_queue_history() - clear netif queue operation history |
| * @hdd_ctx: hdd context |
| * |
| * Return: none |
| */ |
| void wlan_hdd_clear_netif_queue_history(hdd_context_t *hdd_ctx) |
| { |
| hdd_adapter_t *adapter = NULL; |
| hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL; |
| VOS_STATUS status; |
| |
| status = hdd_get_front_adapter(hdd_ctx, &adapter_node); |
| while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) { |
| adapter = adapter_node->pAdapter; |
| |
| vos_mem_zero(adapter->queue_oper_stats, |
| sizeof(adapter->queue_oper_stats)); |
| vos_mem_zero(adapter->queue_oper_history, |
| sizeof(adapter->queue_oper_history)); |
| |
| adapter->history_index = 0; |
| adapter->start_time = adapter->last_time = vos_system_ticks(); |
| adapter->total_pause_time = 0; |
| adapter->total_unpause_time = 0; |
| status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next); |
| adapter_node = next; |
| } |
| } |
| |
| /**--------------------------------------------------------------------------- |
| \brief hdd_11d_scan_done - callback to be executed when 11d scan is |
| completed to flush out the scan results |
| |
| 11d scan is done during driver load and is a passive scan on all |
| channels supported by the device, 11d scans may find some APs on |
| frequencies which are forbidden to be used in the regulatory domain |
| the device is operating in. If these APs are notified to the supplicant |
| it may try to connect to these APs, thus flush out all the scan results |
| which are present in SME after 11d scan is done. |
| |
| \return - eHalStatus |
| |
| --------------------------------------------------------------------------*/ |
| static eHalStatus hdd_11d_scan_done(tHalHandle halHandle, void *pContext, |
| tANI_U8 sessionId, tANI_U32 scanId, |
| eCsrScanStatus status) |
| { |
| ENTER(); |
| |
| sme_ScanFlushResult(halHandle, 0); |
| |
| EXIT(); |
| |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| #ifdef QCA_ARP_SPOOFING_WAR |
| int wlan_check_xxx(struct net_device *dev, int if_idex, void *data) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, "Checking for arp spoof packtes\n"); |
| return 0; |
| } |
| |
| int hdd_filter_cb(tANI_U32 vdev_id, adf_nbuf_t skb, tANI_U32 type) |
| { |
| hdd_adapter_t *pAdapter = NULL; |
| hdd_context_t *pHddCtx = NULL; |
| v_CONTEXT_t pVosContext = NULL; |
| int ret = 0; |
| |
| switch (type) { |
| case RX_INTRA_BSS_FWD: |
| pVosContext = vos_get_global_context(VOS_MODULE_ID_HDD, NULL); |
| if(!pVosContext) { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Global VOS context is Null", __func__); |
| goto out; |
| } |
| |
| pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext); |
| if(!pHddCtx) { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: HDD context is Null",__func__); |
| goto out; |
| } |
| |
| pAdapter = hdd_get_adapter_by_vdev(pHddCtx, vdev_id); |
| if (NULL == pAdapter) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: vdev_id %d does not exist with host", |
| __func__, vdev_id); |
| goto out; |
| } |
| |
| if (*((unsigned short *)(skb->data + HDD_ARP_PACKET_TYPE_OFFSET)) |
| == htons(ETH_P_ARP)) { |
| |
| ret = wlan_check_xxx(pAdapter->dev, pAdapter->dev->ifindex, |
| skb->data); |
| } |
| break; |
| default: |
| hddLog(VOS_TRACE_LEVEL_WARN, "Invalid filter type"); |
| goto out; |
| } |
| out: |
| return ret; |
| } |
| #endif |
| #if defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) |
| /** |
| * wlan_hdd_bad_peer_txctl() - HDD API to initialize the bad peer |
| * tx flow control parameters |
| * @pHddCtx: HDD context which contains INI setting. |
| * |
| * Read configuation from INI setting, and then update the setting |
| * of SME module. |
| * |
| * Return: NULL |
| */ |
| static void wlan_hdd_bad_peer_txctl(hdd_context_t *p_hdd_ctx) |
| { |
| struct sme_bad_peer_txctl_param bad_peer_txctl; |
| enum sme_max_bad_peer_thresh_levels level = IEEE80211_B_LEVEL; |
| |
| bad_peer_txctl.enabled = |
| p_hdd_ctx->cfg_ini->bad_peer_txctl_enable; |
| bad_peer_txctl.period = |
| p_hdd_ctx->cfg_ini->bad_peer_txctl_prd; |
| bad_peer_txctl.txq_limit = |
| p_hdd_ctx->cfg_ini->bad_peer_txctl_txq_lmt; |
| bad_peer_txctl.tgt_backoff = |
| p_hdd_ctx->cfg_ini->bad_peer_tgt_backoff; |
| bad_peer_txctl.tgt_report_prd = |
| p_hdd_ctx->cfg_ini->bad_peer_tgt_report_prd; |
| |
| bad_peer_txctl.thresh[level].cond = |
| p_hdd_ctx->cfg_ini->bad_peer_cond_ieee80211b; |
| bad_peer_txctl.thresh[level].delta = |
| p_hdd_ctx->cfg_ini->bad_peer_delta_ieee80211b; |
| bad_peer_txctl.thresh[level].percentage = |
| p_hdd_ctx->cfg_ini->bad_peer_pct_ieee80211b; |
| bad_peer_txctl.thresh[level].thresh = |
| p_hdd_ctx->cfg_ini->bad_peer_tput_ieee80211b; |
| bad_peer_txctl.thresh[level].limit = |
| p_hdd_ctx->cfg_ini->bad_peer_limit_ieee80211b; |
| |
| level++; |
| bad_peer_txctl.thresh[level].cond = |
| p_hdd_ctx->cfg_ini->bad_peer_cond_ieee80211ag; |
| bad_peer_txctl.thresh[level].delta = |
| p_hdd_ctx->cfg_ini->bad_peer_delta_ieee80211ag; |
| bad_peer_txctl.thresh[level].percentage = |
| p_hdd_ctx->cfg_ini->bad_peer_pct_ieee80211ag; |
| bad_peer_txctl.thresh[level].thresh = |
| p_hdd_ctx->cfg_ini->bad_peer_tput_ieee80211ag; |
| bad_peer_txctl.thresh[level].limit = |
| p_hdd_ctx->cfg_ini->bad_peer_limit_ieee80211ag; |
| |
| level++; |
| bad_peer_txctl.thresh[level].cond = |
| p_hdd_ctx->cfg_ini->bad_peer_cond_ieee80211n; |
| bad_peer_txctl.thresh[level].delta = |
| p_hdd_ctx->cfg_ini->bad_peer_delta_ieee80211n; |
| bad_peer_txctl.thresh[level].percentage = |
| p_hdd_ctx->cfg_ini->bad_peer_pct_ieee80211n; |
| bad_peer_txctl.thresh[level].thresh = |
| p_hdd_ctx->cfg_ini->bad_peer_tput_ieee80211n; |
| bad_peer_txctl.thresh[level].limit = |
| p_hdd_ctx->cfg_ini->bad_peer_limit_ieee80211n; |
| |
| level++; |
| bad_peer_txctl.thresh[level].cond = |
| p_hdd_ctx->cfg_ini->bad_peer_cond_ieee80211ag; |
| bad_peer_txctl.thresh[level].delta = |
| p_hdd_ctx->cfg_ini->bad_peer_delta_ieee80211ac; |
| bad_peer_txctl.thresh[level].percentage = |
| p_hdd_ctx->cfg_ini->bad_peer_pct_ieee80211ac; |
| bad_peer_txctl.thresh[level].thresh = |
| p_hdd_ctx->cfg_ini->bad_peer_tput_ieee80211ac; |
| bad_peer_txctl.thresh[level].limit = |
| p_hdd_ctx->cfg_ini->bad_peer_limit_ieee80211ac; |
| |
| if (eHAL_STATUS_SUCCESS != |
| sme_init_bad_peer_txctl_info(p_hdd_ctx->hHal, bad_peer_txctl)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Error while initializing bad peer Txctl infor", |
| __func__); |
| } |
| } |
| #else |
| static inline void wlan_hdd_bad_peer_txctl(hdd_context_t *p_hdd_ctx) |
| { |
| /* no-op */ |
| } |
| #endif /* defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) */ |
| |
| #ifdef WLAN_FEATURE_OFFLOAD_PACKETS |
| /** |
| * hdd_init_offloaded_packets_ctx() - Initialize offload packets context |
| * @hdd_ctx: hdd global context |
| * |
| * Return: none |
| */ |
| static void hdd_init_offloaded_packets_ctx(hdd_context_t *hdd_ctx) |
| { |
| uint8_t i; |
| |
| mutex_init(&hdd_ctx->op_ctx.op_lock); |
| for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++) { |
| hdd_ctx->op_ctx.op_table[i].request_id = MAX_REQUEST_ID; |
| hdd_ctx->op_ctx.op_table[i].pattern_id = i; |
| } |
| } |
| #else |
| static void hdd_init_offloaded_packets_ctx(hdd_context_t *hdd_ctx) |
| { |
| } |
| #endif |
| |
| #ifdef WLAN_FEATURE_WOW_PULSE |
| /** |
| * wlan_hdd_set_wow_pulse() - call SME to send wmi cmd of wow pulse |
| * @phddctx: hdd_context_t structure pointer |
| * @enable: enable or disable this behaviour |
| * |
| * Return: int |
| */ |
| static int wlan_hdd_set_wow_pulse(hdd_context_t *phddctx, bool enable) |
| { |
| hdd_config_t *pcfg_ini = phddctx->cfg_ini; |
| struct wow_pulse_mode wow_pulse_set_info; |
| VOS_STATUS status; |
| |
| hddLog(LOG1, FL("wow pulse enable flag is %d"), enable); |
| |
| if (false == phddctx->cfg_ini->wow_pulse_support) |
| return 0; |
| |
| /* prepare the request to send to SME */ |
| if (enable == true) { |
| wow_pulse_set_info.wow_pulse_enable = true; |
| wow_pulse_set_info.wow_pulse_pin = |
| pcfg_ini->wow_pulse_pin; |
| wow_pulse_set_info.wow_pulse_interval_low = |
| pcfg_ini->wow_pulse_interval_low; |
| wow_pulse_set_info.wow_pulse_interval_high= |
| pcfg_ini->wow_pulse_interval_high; |
| } else { |
| wow_pulse_set_info.wow_pulse_enable = false; |
| wow_pulse_set_info.wow_pulse_pin = 0; |
| wow_pulse_set_info.wow_pulse_interval_low = 0; |
| wow_pulse_set_info.wow_pulse_interval_high= 0; |
| } |
| hddLog(LOG1,"%s: enable %d pin %d low %d high %d", |
| __func__, wow_pulse_set_info.wow_pulse_enable, |
| wow_pulse_set_info.wow_pulse_pin, |
| wow_pulse_set_info.wow_pulse_interval_low, |
| wow_pulse_set_info.wow_pulse_interval_high); |
| |
| status = sme_set_wow_pulse(&wow_pulse_set_info); |
| if (VOS_STATUS_E_FAILURE == status) { |
| hddLog(LOGE, |
| "%s: sme_set_wow_pulse failure!", __func__); |
| return -EIO; |
| } |
| hddLog(LOG2, |
| "%s: sme_set_wow_pulse success!", __func__); |
| return 0; |
| } |
| #else |
| static int inline wlan_hdd_set_wow_pulse(hdd_context_t *phddctx, bool enable) |
| { |
| return 0; |
| } |
| #endif |
| |
| /** |
| * wlan_hdd_set_wakeup_gpio() - call SME to send wmi cmd of wakeup gpio |
| * @phddctx: hdd_context_t structure pointer |
| * |
| * Return: int |
| */ |
| static int wlan_hdd_set_wakeup_gpio(hdd_context_t *hddctx) |
| { |
| hdd_config_t *cfg_ini = hddctx->cfg_ini; |
| struct wakeup_gpio_mode wakeup_gpio_info; |
| VOS_STATUS status; |
| |
| wakeup_gpio_info.host_wakeup_gpio = cfg_ini->host_wakeup_gpio; |
| wakeup_gpio_info.host_wakeup_type = cfg_ini->host_wakeup_type; |
| wakeup_gpio_info.target_wakeup_gpio = cfg_ini->target_wakeup_gpio; |
| wakeup_gpio_info.target_wakeup_type = cfg_ini->target_wakeup_type; |
| |
| hddLog(LOG1, "%s:host_gpio %d host_type %d tar_gpio %d tar_type %d", |
| __func__, wakeup_gpio_info.host_wakeup_gpio, |
| wakeup_gpio_info.host_wakeup_type, |
| wakeup_gpio_info.target_wakeup_gpio, |
| wakeup_gpio_info.target_wakeup_type); |
| |
| status = sme_set_wakeup_gpio(&wakeup_gpio_info); |
| if (VOS_STATUS_E_FAILURE == status) { |
| hddLog(LOGE, |
| "%s: sme_set_wakeup_gpio failure!", __func__); |
| return -EIO; |
| } |
| hddLog(LOG1, |
| "%s: sme_set_wakeup_gpio success!", __func__); |
| return 0; |
| } |
| |
| /** |
| * hdd_state_info_dump() - prints state information of hdd layer |
| * @buf: buffer pointer |
| * @size: size of buffer to be filled |
| * |
| * This function is used to dump state information of hdd layer |
| * |
| * Return: None |
| */ |
| static void hdd_state_info_dump(char **buf_ptr, uint16_t *size) |
| { |
| v_CONTEXT_t vos_ctx_ptr; |
| hdd_context_t *hdd_ctx_ptr; |
| hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL; |
| VOS_STATUS status; |
| hdd_station_ctx_t *hdd_sta_ctx; |
| hdd_adapter_t *adapter; |
| uint16_t len = 0; |
| char *buf = *buf_ptr; |
| |
| /* get the global voss context */ |
| vos_ctx_ptr = vos_get_global_context(VOS_MODULE_ID_VOSS, NULL); |
| |
| if (NULL == vos_ctx_ptr) { |
| VOS_ASSERT(0); |
| return; |
| } |
| |
| hdd_ctx_ptr = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx_ptr); |
| if (!hdd_ctx_ptr) { |
| hddLog(LOGE, FL("Failed to get hdd context ")); |
| return; |
| } |
| |
| hddLog(LOG1, FL("size of buffer: %d"), *size); |
| |
| len += scnprintf(buf + len, *size - len, |
| "\n isWlanSuspended %d", hdd_ctx_ptr->isWlanSuspended); |
| len += scnprintf(buf + len, *size - len, |
| "\n isMcThreadSuspended %d", |
| hdd_ctx_ptr->isMcThreadSuspended); |
| |
| status = hdd_get_front_adapter(hdd_ctx_ptr, &adapter_node); |
| |
| while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) { |
| adapter = adapter_node->pAdapter; |
| if (adapter->dev) |
| len += scnprintf(buf + len, *size - len, |
| "\n device name: %s", adapter->dev->name); |
| len += scnprintf(buf + len, *size - len, |
| "\n device_mode: %d", adapter->device_mode); |
| switch (adapter->device_mode) { |
| case WLAN_HDD_INFRA_STATION: |
| case WLAN_HDD_P2P_CLIENT: |
| hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| len += scnprintf(buf + len, *size - len, |
| "\n connState: %d", |
| hdd_sta_ctx->conn_info.connState); |
| break; |
| |
| default: |
| break; |
| } |
| status = hdd_get_next_adapter(hdd_ctx_ptr, adapter_node, &next); |
| adapter_node = next; |
| } |
| |
| *size -= len; |
| *buf_ptr += len; |
| } |
| |
| /** |
| * hdd_register_debug_callback() - registration function for hdd layer |
| * to print hdd state information |
| * |
| * Return: None |
| */ |
| static void hdd_register_debug_callback(void) |
| { |
| vos_register_debug_callback(VOS_MODULE_ID_HDD, &hdd_state_info_dump); |
| } |
| |
| /** |
| * hdd_populate_random_mac_addr() - API to populate random mac addresses |
| * @hdd_ctx: HDD Context |
| * @num: Number of random mac addresses needed |
| * |
| * Generate random addresses using bit manipulation on the base mac address |
| * |
| * Return: None |
| */ |
| static void hdd_populate_random_mac_addr(hdd_context_t *hdd_ctx, uint32_t num) |
| { |
| uint32_t start_idx = VOS_MAX_CONCURRENCY_PERSONA - num; |
| uint32_t iter; |
| hdd_config_t *ini = hdd_ctx->cfg_ini; |
| u8 *buf = NULL; |
| u8 macaddr_b3, tmp_br3; |
| u8 *src = ini->intfMacAddr[0].bytes; |
| |
| for (iter = start_idx; iter < VOS_MAX_CONCURRENCY_PERSONA; ++iter) { |
| buf = ini->intfMacAddr[iter].bytes; |
| vos_mem_copy(buf, src, VOS_MAC_ADDR_SIZE); |
| macaddr_b3 = buf[3]; |
| tmp_br3 = ((macaddr_b3 >> 4 & INTF_MACADDR_MASK) + iter) & |
| INTF_MACADDR_MASK; |
| macaddr_b3 += tmp_br3; |
| macaddr_b3 ^= (1 << INTF_MACADDR_MASK); |
| buf[0] |= 0x02; |
| buf[3] = macaddr_b3; |
| hddLog(LOG1, FL(MAC_ADDRESS_STR), MAC_ADDR_ARRAY(buf)); |
| } |
| } |
| |
| /** |
| * hdd_cnss_wlan_mac() - API to get mac addresses from cnss platform driver |
| * @hdd_ctx: HDD Context |
| * |
| * API to get mac addresses from platform driver and update the driver |
| * structures and configure FW with the base mac address. |
| * |
| * Return: int |
| */ |
| static int hdd_cnss_wlan_mac(hdd_context_t *hdd_ctx) |
| { |
| uint32_t no_of_mac_addr, iter; |
| uint32_t max_mac_addr = VOS_MAX_CONCURRENCY_PERSONA; |
| uint32_t mac_addr_size = VOS_MAC_ADDR_SIZE; |
| u8 *addr, *buf; |
| struct device *dev = hdd_ctx->parent_dev; |
| hdd_config_t *ini = hdd_ctx->cfg_ini; |
| tSirMacAddr customMacAddr; |
| VOS_STATUS status; |
| |
| addr = vos_get_cnss_wlan_mac_buff(dev, &no_of_mac_addr); |
| |
| if (no_of_mac_addr == 0 || !addr) { |
| hddLog(LOG1, |
| FL("Platform Driver Doesn't have wlan mac addresses")); |
| return -EINVAL; |
| } |
| |
| if (no_of_mac_addr > max_mac_addr) |
| no_of_mac_addr = max_mac_addr; |
| |
| vos_mem_copy(&customMacAddr, addr, mac_addr_size); |
| |
| for (iter = 0; iter < no_of_mac_addr; ++iter, addr += mac_addr_size) { |
| buf = ini->intfMacAddr[iter].bytes; |
| vos_mem_copy(buf, addr, VOS_MAC_ADDR_SIZE); |
| hddLog(LOG1, FL(MAC_ADDRESS_STR), MAC_ADDR_ARRAY(buf)); |
| } |
| |
| status = sme_SetCustomMacAddr(customMacAddr); |
| |
| if (status != VOS_STATUS_SUCCESS) |
| return -EAGAIN; |
| |
| if (no_of_mac_addr < max_mac_addr) |
| hdd_populate_random_mac_addr(hdd_ctx, max_mac_addr - |
| no_of_mac_addr); |
| return 0; |
| } |
| |
| /** |
| * hdd_initialize_mac_address() - API to get wlan mac addresses |
| * @hdd_ctx: HDD Context |
| * |
| * Get MAC addresses from platform driver or wlan_mac.bin. If platform driver is |
| * provisioned with mac addresses, driver uses it, else it will use wlan_mac.bin |
| * to update HW MAC addresses. |
| * |
| * Return: 0 if success, errno otherwise |
| */ |
| static int hdd_initialize_mac_address(hdd_context_t *hdd_ctx) |
| { |
| VOS_STATUS status; |
| int ret; |
| |
| ret = hdd_cnss_wlan_mac(hdd_ctx); |
| |
| if (ret == 0) |
| return ret; |
| |
| hddLog(LOGW, FL("Can't update MAC via platform driver ret: %d"), ret); |
| |
| if (!hdd_ctx->cfg_ini->skip_mac_config) { |
| status = hdd_update_mac_config(hdd_ctx); |
| if (status == VOS_STATUS_SUCCESS) |
| return 0; |
| |
| hddLog(LOGW, FL("Can't update MAC from %s status: %d"), |
| WLAN_MAC_FILE, status); |
| } |
| |
| if (!vos_is_macaddr_zero(&hdd_ctx->hw_macaddr)) { |
| hdd_update_macaddr(hdd_ctx->cfg_ini, hdd_ctx->hw_macaddr); |
| } else { |
| tSirMacAddr customMacAddr; |
| |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Invalid MAC passed from target, using MAC from ini" |
| MAC_ADDRESS_STR, __func__, |
| MAC_ADDR_ARRAY(hdd_ctx->cfg_ini->intfMacAddr[0].bytes)); |
| vos_mem_copy(&customMacAddr, |
| &hdd_ctx->cfg_ini->intfMacAddr[0].bytes, |
| VOS_MAC_ADDR_SIZE); |
| sme_SetCustomMacAddr(customMacAddr); |
| } |
| return 0; |
| } |
| |
| static void hdd_get_DPD_Recaliberation_ini_param(tSmeDPDRecalParams *pDPDParam, |
| hdd_context_t *pHddCtx) |
| { |
| pDPDParam->enable = |
| pHddCtx->cfg_ini->dpd_recalib_enabled; |
| pDPDParam->delta_degreeHigh = |
| pHddCtx->cfg_ini->dpd_recalib_delta_degreehigh; |
| pDPDParam->delta_degreeLow = |
| pHddCtx->cfg_ini->dpd_recalib_delta_degreelow; |
| pDPDParam->cooling_time = |
| pHddCtx->cfg_ini->dpd_recalib_cooling_time; |
| pDPDParam->dpd_dur_max = |
| pHddCtx->cfg_ini->dpd_recalib_duration_max; |
| } |
| |
| #ifdef FEATURE_WLAN_THERMAL_SHUTDOWN |
| static VOS_STATUS hdd_init_thermal_ctx(hdd_context_t *pHddCtx) |
| { |
| pHddCtx->system_suspended = false; |
| pHddCtx->thermal_suspend_state = HDD_WLAN_THERMAL_ACTIVE; |
| spin_lock_init(&pHddCtx->thermal_suspend_lock); |
| INIT_DELAYED_WORK(&pHddCtx->thermal_suspend_work, hdd_thermal_suspend_work); |
| pHddCtx->thermal_suspend_wq = create_singlethread_workqueue("thermal_wq"); |
| if (!pHddCtx->thermal_suspend_wq) |
| return VOS_STATUS_E_INVAL; |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| static void hdd_get_thermal_shutdown_ini_param(tSmeThermalParams *pthermalParam, |
| hdd_context_t *pHddCtx) |
| { |
| pthermalParam->thermal_shutdown_enabled = |
| pHddCtx->cfg_ini->thermal_shutdown_enabled; |
| pthermalParam->thermal_shutdown_auto_enabled = |
| pHddCtx->cfg_ini->thermal_shutdown_auto_enabled; |
| pthermalParam->thermal_resume_threshold = |
| pHddCtx->cfg_ini->thermal_resume_threshold; |
| pthermalParam->thermal_warning_threshold = |
| pHddCtx->cfg_ini->thermal_warning_threshold; |
| pthermalParam->thermal_suspend_threshold = |
| pHddCtx->cfg_ini->thermal_suspend_threshold; |
| pthermalParam->thermal_sample_rate = |
| pHddCtx->cfg_ini->thermal_sample_rate; |
| } |
| #else |
| static inline VOS_STATUS hdd_init_thermal_ctx(hdd_context_t *pHddCtx) |
| { |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| static inline void hdd_get_thermal_shutdown_ini_param(tSmeThermalParams *pthermalParam, |
| hdd_context_t *pHddCtx) |
| { |
| return; |
| } |
| |
| #endif |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_wlan_startup() - HDD init function |
| |
| This is the driver startup code executed once a WLAN device has been detected |
| |
| \param - dev - Pointer to the underlying device |
| |
| \return - 0 for success, < 0 for failure |
| |
| --------------------------------------------------------------------------*/ |
| |
| int hdd_wlan_startup(struct device *dev, v_VOID_t *hif_sc) |
| { |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter = NULL; |
| #ifdef WLAN_OPEN_P2P_INTERFACE |
| hdd_adapter_t *pP2pAdapter = NULL; |
| #endif |
| hdd_context_t *pHddCtx = NULL; |
| v_CONTEXT_t pVosContext= NULL; |
| eHalStatus hal_status; |
| int ret; |
| int i; |
| struct wiphy *wiphy; |
| unsigned long rc; |
| tSmeThermalParams thermalParam; |
| tSmeDPDRecalParams DPDParam; |
| tSirTxPowerLimit *hddtxlimit; |
| #ifdef FEATURE_WLAN_CH_AVOID |
| #ifdef CONFIG_CNSS |
| uint16_t unsafe_channel_count; |
| int unsafeChannelIndex; |
| #endif |
| #endif |
| tANI_U8 rtnl_lock_enable; |
| tANI_U8 reg_netdev_notifier_done = FALSE; |
| hdd_adapter_t *dot11_adapter = NULL; |
| #ifdef QCA_ARP_SPOOFING_WAR |
| adf_os_device_t adf_ctx; |
| #endif |
| int set_value; |
| struct sme_5g_band_pref_params band_pref_params; |
| |
| ENTER(); |
| |
| if (WLAN_IS_EPPING_ENABLED(con_mode)) { |
| /* if epping enabled redirect start to epping module */ |
| ret = epping_wlan_startup(dev, hif_sc); |
| EXIT(); |
| return ret; |
| } |
| /* |
| * cfg80211: wiphy allocation |
| */ |
| wiphy = wlan_hdd_cfg80211_wiphy_alloc(sizeof(hdd_context_t)) ; |
| |
| if(wiphy == NULL) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: cfg80211 init failed", __func__); |
| return -EIO; |
| } |
| |
| pHddCtx = wiphy_priv(wiphy); |
| |
| //Initialize the adapter context to zeros. |
| vos_mem_zero(pHddCtx, sizeof( hdd_context_t )); |
| |
| pHddCtx->wiphy = wiphy; |
| pHddCtx->isLoadInProgress = TRUE; |
| |
| status = hdd_init_thermal_ctx(pHddCtx); |
| if (VOS_STATUS_SUCCESS != status) |
| goto err_free_hdd_context; |
| |
| pHddCtx->ioctl_scan_mode = eSIR_ACTIVE_SCAN; |
| vos_set_wakelock_logging(false); |
| |
| vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, TRUE); |
| vos_set_load_in_progress(VOS_MODULE_ID_VOSS, TRUE); |
| |
| /*Get vos context here bcoz vos_open requires it*/ |
| pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL); |
| |
| if(pVosContext == NULL) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Failed vos_get_global_context",__func__); |
| goto err_free_hdd_context; |
| } |
| #ifdef QCA_ARP_SPOOFING_WAR |
| adf_ctx = vos_get_context(VOS_MODULE_ID_ADF, pVosContext); |
| if (adf_ctx == NULL) { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Failed vos_get_global_context",__func__); |
| goto err_free_hdd_context; |
| } |
| adf_ctx->filter_cb = (void *)hdd_filter_cb; |
| #endif |
| |
| //Save the Global VOSS context in adapter context for future. |
| pHddCtx->pvosContext = pVosContext; |
| |
| //Save the adapter context in global context for future. |
| ((VosContextType*)(pVosContext))->pHDDContext = (v_VOID_t*)pHddCtx; |
| |
| pHddCtx->parent_dev = dev; |
| pHddCtx->last_scan_reject_session_id = 0xFF; |
| pHddCtx->last_scan_reject_reason = 0; |
| pHddCtx->last_scan_reject_timestamp = 0; |
| pHddCtx->scan_reject_cnt = 0; |
| |
| init_completion(&pHddCtx->full_pwr_comp_var); |
| init_completion(&pHddCtx->standby_comp_var); |
| init_completion(&pHddCtx->req_bmps_comp_var); |
| init_completion(&pHddCtx->mc_sus_event_var); |
| init_completion(&pHddCtx->tx_sus_event_var); |
| init_completion(&pHddCtx->rx_sus_event_var); |
| init_completion(&pHddCtx->ready_to_suspend); |
| #ifdef WLAN_FEATURE_EXTWOW_SUPPORT |
| init_completion(&pHddCtx->ready_to_extwow); |
| #endif |
| hdd_init_bpf_completion(); |
| #ifdef FEATURE_WLAN_EXTSCAN |
| init_completion(&pHddCtx->ext_scan_context.response_event); |
| #endif /* FEATURE_WLAN_EXTSCAN */ |
| |
| hdd_init_ll_stats_ctx(pHddCtx); |
| |
| init_completion(&pHddCtx->chain_rssi_context.response_event); |
| |
| spin_lock_init(&pHddCtx->schedScan_lock); |
| |
| hdd_list_init( &pHddCtx->hddAdapters, MAX_NUMBER_OF_ADAPTERS ); |
| |
| #ifdef FEATURE_WLAN_TDLS |
| /* tdls_lock is initialized before an hdd_open_adapter ( which is |
| * invoked by other instances also) to protect the concurrent |
| * access for the Adapters by TDLS module. |
| */ |
| mutex_init(&pHddCtx->tdls_lock); |
| #endif |
| |
| status = wlan_hdd_init_tx_rx_histogram(pHddCtx); |
| if (status != 0) { |
| goto err_free_hdd_context; |
| } |
| |
| spin_lock_init(&pHddCtx->dfs_lock); |
| spin_lock_init(&pHddCtx->sap_update_info_lock); |
| spin_lock_init(&pHddCtx->sta_update_info_lock); |
| hdd_init_offloaded_packets_ctx(pHddCtx); |
| // Load all config first as TL config is needed during vos_open |
| pHddCtx->cfg_ini = (hdd_config_t*) vos_mem_malloc(sizeof(hdd_config_t)); |
| if(pHddCtx->cfg_ini == NULL) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Failed kmalloc hdd_config_t",__func__); |
| goto err_histogram; |
| } |
| |
| vos_mem_zero(pHddCtx->cfg_ini, sizeof( hdd_config_t )); |
| |
| // Read and parse the qcom_cfg.ini file |
| status = hdd_parse_config_ini( pHddCtx ); |
| if ( VOS_STATUS_SUCCESS != status ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: error parsing %s", |
| __func__, WLAN_INI_FILE); |
| goto err_config; |
| } |
| |
| /* If IPA HW is not existing, disable offload from INI */ |
| if (!hdd_ipa_is_present(pHddCtx)) |
| hdd_ipa_reset_ipaconfig(pHddCtx, 0); |
| |
| if (pHddCtx->cfg_ini->probe_req_ie_whitelist) |
| { |
| if (hdd_validate_prb_req_ie_bitmap(pHddCtx)) |
| { |
| /* parse ini string probe req oui */ |
| status = hdd_parse_probe_req_ouis(pHddCtx); |
| if (VOS_STATUS_SUCCESS != status) |
| { |
| hddLog(LOGE, FL("Error parsing probe req ouis - Ignoring them" |
| " disabling white list")); |
| pHddCtx->cfg_ini->probe_req_ie_whitelist = false; |
| } |
| } |
| else |
| { |
| hddLog(LOGE, FL("invalid probe req ie bitmap and ouis," |
| " disabling white list")); |
| pHddCtx->cfg_ini->probe_req_ie_whitelist = false; |
| } |
| } |
| |
| if (0 == pHddCtx->cfg_ini->max_go_peers) |
| pHddCtx->cfg_ini->max_go_peers = pHddCtx->cfg_ini->max_sap_peers; |
| |
| pHddCtx->max_peers = MAX(pHddCtx->cfg_ini->max_sap_peers, |
| pHddCtx->cfg_ini->max_go_peers); |
| |
| ((VosContextType*)pVosContext)->pHIFContext = hif_sc; |
| |
| /* store target type and target version info in hdd ctx */ |
| pHddCtx->target_type = ((struct ol_softc *)hif_sc)->target_type; |
| |
| pHddCtx->current_intf_count=0; |
| pHddCtx->max_intf_count = CSR_ROAM_SESSION_MAX; |
| |
| /* INI has been read, initialise the configuredMcastBcastFilter with |
| * INI value as this will serve as the default value |
| */ |
| pHddCtx->configuredMcastBcastFilter = pHddCtx->cfg_ini->mcastBcastFilterSetting; |
| hddLog(VOS_TRACE_LEVEL_INFO, "Setting configuredMcastBcastFilter: %d", |
| pHddCtx->cfg_ini->mcastBcastFilterSetting); |
| |
| vos_set_fatal_event(pHddCtx->cfg_ini->enable_fatal_event); |
| if (false == hdd_is_5g_supported(pHddCtx)) |
| { |
| //5Ghz is not supported. |
| if (1 != pHddCtx->cfg_ini->nBandCapability) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: Setting pHddCtx->cfg_ini->nBandCapability = 1", __func__); |
| pHddCtx->cfg_ini->nBandCapability = 1; |
| } |
| } |
| |
| if (pHddCtx->cfg_ini->fhostNSOffload) |
| pHddCtx->ns_offload_enable = true; |
| |
| /* |
| * If SNR Monitoring is enabled, FW has to parse all beacons |
| * for calculating and storing the average SNR, so set Nth beacon |
| * filter to 1 to enable FW to parse all the beacons |
| */ |
| if (1 == pHddCtx->cfg_ini->fEnableSNRMonitoring) |
| { |
| /* The log level is deliberately set to WARN as overriding |
| * nthBeaconFilter to 1 will increase power consumption and this |
| * might just prove helpful to detect the power issue. |
| */ |
| hddLog(VOS_TRACE_LEVEL_WARN, |
| "%s: Setting pHddCtx->cfg_ini->nthBeaconFilter = 1", __func__); |
| pHddCtx->cfg_ini->nthBeaconFilter = 1; |
| } |
| /* |
| * cfg80211: Initialization ... |
| */ |
| if (0 < wlan_hdd_cfg80211_init(dev, wiphy, pHddCtx->cfg_ini)) { |
| hddLog(LOGE, |
| "%s: wlan_hdd_cfg80211_init return failure", __func__); |
| goto err_config; |
| } |
| |
| /* Initialize struct for saving f/w log setting will be used |
| after ssr */ |
| pHddCtx->fw_log_settings.enable = pHddCtx->cfg_ini->enablefwlog; |
| pHddCtx->fw_log_settings.dl_type = 0; |
| pHddCtx->fw_log_settings.dl_report = 0; |
| pHddCtx->fw_log_settings.dl_loglevel = 0; |
| pHddCtx->fw_log_settings.index = 0; |
| for (i = 0; i < MAX_MOD_LOGLEVEL; i++) { |
| pHddCtx->fw_log_settings.dl_mod_loglevel[i] = 0; |
| } |
| |
| if (VOS_FTM_MODE != hdd_get_conparam()) { |
| vos_set_multicast_logging(pHddCtx->cfg_ini->multicast_host_fw_msgs); |
| |
| if (wlan_hdd_logging_sock_activate_svc(pHddCtx) < 0) |
| goto err_sock_activate; |
| |
| /* |
| * Update VOS trace levels based upon the code. The multicast log |
| * log levels of the code need not be set when the logger thread |
| * is not enabled. |
| */ |
| if (vos_is_multicast_logging()) |
| wlan_logging_set_log_level(); |
| } |
| |
| /* |
| * Update VOS trace levels based upon the cfg.ini |
| */ |
| hdd_vos_trace_enable(VOS_MODULE_ID_TL, |
| pHddCtx->cfg_ini->vosTraceEnableTL); |
| hdd_vos_trace_enable(VOS_MODULE_ID_WDI, |
| pHddCtx->cfg_ini->vosTraceEnableWDI); |
| hdd_vos_trace_enable(VOS_MODULE_ID_HDD, |
| pHddCtx->cfg_ini->vosTraceEnableHDD); |
| hdd_vos_trace_enable(VOS_MODULE_ID_SME, |
| pHddCtx->cfg_ini->vosTraceEnableSME); |
| hdd_vos_trace_enable(VOS_MODULE_ID_PE, |
| pHddCtx->cfg_ini->vosTraceEnablePE); |
| hdd_vos_trace_enable(VOS_MODULE_ID_PMC, |
| pHddCtx->cfg_ini->vosTraceEnablePMC); |
| hdd_vos_trace_enable(VOS_MODULE_ID_WDA, |
| pHddCtx->cfg_ini->vosTraceEnableWDA); |
| hdd_vos_trace_enable(VOS_MODULE_ID_SYS, |
| pHddCtx->cfg_ini->vosTraceEnableSYS); |
| hdd_vos_trace_enable(VOS_MODULE_ID_VOSS, |
| pHddCtx->cfg_ini->vosTraceEnableVOSS); |
| hdd_vos_trace_enable(VOS_MODULE_ID_SAP, |
| pHddCtx->cfg_ini->vosTraceEnableSAP); |
| hdd_vos_trace_enable(VOS_MODULE_ID_HDD_SOFTAP, |
| pHddCtx->cfg_ini->vosTraceEnableHDDSAP); |
| hdd_vos_trace_enable(VOS_MODULE_ID_HDD_DATA, |
| pHddCtx->cfg_ini->vosTraceEnableHDDDATA); |
| hdd_vos_trace_enable(VOS_MODULE_ID_HDD_SAP_DATA, |
| pHddCtx->cfg_ini->vosTraceEnableHDDSAPDATA); |
| hdd_vos_trace_enable(VOS_MODULE_ID_HIF, |
| pHddCtx->cfg_ini->vosTraceEnableHIF); |
| hdd_vos_trace_enable(VOS_MODULE_ID_TXRX, |
| pHddCtx->cfg_ini->vosTraceEnableTXRX); |
| hdd_vos_trace_enable(VOS_MODULE_ID_HTC, |
| pHddCtx->cfg_ini->vosTraceEnableHTC); |
| hdd_vos_trace_enable(VOS_MODULE_ID_ADF, |
| pHddCtx->cfg_ini->vosTraceEnableADF); |
| hdd_vos_trace_enable(VOS_MODULE_ID_CFG, |
| pHddCtx->cfg_ini->vosTraceEnableCFG); |
| |
| print_hdd_cfg(pHddCtx); |
| |
| /* Initialize the nlink service */ |
| if (wlan_hdd_nl_init(pHddCtx) != 0) { |
| hddLog(LOGP, FL("nl_srv_init failed")); |
| goto err_logging_sock; |
| } |
| vos_set_radio_index(pHddCtx->radio_index); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| if ( VOS_STATUS_SUCCESS != wlan_hdd_ftm_open(pHddCtx) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: wlan_hdd_ftm_open Failed",__func__); |
| goto err_nl_srv; |
| } |
| if (VOS_STATUS_SUCCESS != hdd_ftm_start(pHddCtx)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hdd_ftm_start Failed",__func__); |
| goto err_free_ftm_open; |
| } |
| } else { |
| |
| //Open watchdog module |
| if(pHddCtx->cfg_ini->fIsLogpEnabled) |
| { |
| status = vos_watchdog_open(pVosContext, |
| &((VosContextType*)pVosContext)->vosWatchdog, |
| sizeof(VosWatchdogContext)); |
| |
| if(!VOS_IS_STATUS_SUCCESS( status )) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: vos_watchdog_open failed",__func__); |
| goto err_nl_srv; |
| } |
| } |
| |
| pHddCtx->isLogpInProgress = FALSE; |
| vos_set_logp_in_progress(VOS_MODULE_ID_VOSS, FALSE); |
| |
| status = vos_nv_open(); |
| if (!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| /* NV module cannot be initialized */ |
| hddLog( VOS_TRACE_LEVEL_FATAL, |
| "%s: vos_nv_open failed", __func__); |
| goto err_wdclose; |
| } |
| |
| hdd_wlan_green_ap_init(pHddCtx); |
| |
| status = vos_open( &pVosContext, 0); |
| if ( !VOS_IS_STATUS_SUCCESS( status )) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: vos_open failed", __func__); |
| goto err_vos_nv_close; |
| } |
| |
| #if !defined(REMOVE_PKT_LOG) |
| hif_init_pdev_txrx_handle(hif_sc, |
| vos_get_context(VOS_MODULE_ID_TXRX, pVosContext)); |
| #endif |
| |
| pHddCtx->hHal = (tHalHandle)vos_get_context(VOS_MODULE_ID_SME, |
| pVosContext ); |
| |
| if ( NULL == pHddCtx->hHal ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: HAL context is null", __func__); |
| goto err_vosclose; |
| } |
| |
| status = vos_preStart( pHddCtx->pvosContext ); |
| if ( !VOS_IS_STATUS_SUCCESS( status ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: vos_preStart failed", __func__); |
| goto err_vosclose; |
| } |
| |
| wlan_hdd_update_wiphy(wiphy, pHddCtx); |
| |
| if (sme_IsFeatureSupportedByFW(DOT11AC)) { |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: support 11ac", __func__); |
| } else { |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: not support 11ac", __func__); |
| if ((pHddCtx->cfg_ini->dot11Mode == eHDD_DOT11_MODE_11ac_ONLY)|| |
| (pHddCtx->cfg_ini->dot11Mode == eHDD_DOT11_MODE_11ac)) { |
| |
| pHddCtx->cfg_ini->dot11Mode = eHDD_DOT11_MODE_11n; |
| pHddCtx->cfg_ini->sap_p2p_11ac_override = 0; |
| } |
| } |
| |
| if (0 != wlan_hdd_set_wow_pulse(pHddCtx, true)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to set wow pulse", __func__); |
| } |
| |
| if ((pHddCtx->cfg_ini->host_wakeup_gpio != |
| CFG_HOST_WAKEUP_GPIO_DEFAULT) || |
| (pHddCtx->cfg_ini->target_wakeup_gpio != |
| CFG_HOST_WAKEUP_GPIO_DEFAULT)) { |
| if (0 != wlan_hdd_set_wakeup_gpio(pHddCtx)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to set wakeup gpio", __func__); |
| } |
| } |
| |
| /* Set 802.11p config |
| * TODO-OCB: This has been temporarily added here to ensure this paramter |
| * is set in CSR when we init the channel list. This should be removed |
| * once the 5.9 GHz channels are added to the regulatory domain. |
| */ |
| hdd_set_dot11p_config(pHddCtx); |
| |
| if (0 == enable_dfs_chan_scan || 1 == enable_dfs_chan_scan) |
| { |
| pHddCtx->cfg_ini->enableDFSChnlScan = enable_dfs_chan_scan; |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: module enable_dfs_chan_scan set to %d", |
| __func__, enable_dfs_chan_scan); |
| } |
| if (0 == enable_11d || 1 == enable_11d) |
| { |
| pHddCtx->cfg_ini->Is11dSupportEnabled = enable_11d; |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: module enable_11d set to %d", |
| __func__, enable_11d); |
| } |
| |
| /* Note that the vos_preStart() sequence triggers the cfg download. |
| The cfg download must occur before we update the SME config |
| since the SME config operation must access the cfg database */ |
| status = hdd_set_sme_config( pHddCtx ); |
| |
| if ( VOS_STATUS_SUCCESS != status ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Failed hdd_set_sme_config", |
| __func__); |
| goto err_vosclose; |
| } |
| |
| status = wlan_hdd_reg_init(pHddCtx); |
| if (status != VOS_STATUS_SUCCESS) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to init channel list", __func__); |
| goto err_vosclose; |
| } |
| } |
| |
| /* registration of wiphy dev with cfg80211 */ |
| if (0 > wlan_hdd_cfg80211_register(pHddCtx->wiphy)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: wiphy register failed", __func__); |
| status = VOS_STATUS_E_FAILURE; |
| if (VOS_FTM_MODE == hdd_get_conparam()) |
| goto err_free_ftm_open; |
| else |
| goto err_vosclose; |
| } |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, FALSE); |
| vos_set_load_in_progress(VOS_MODULE_ID_VOSS, FALSE); |
| pHddCtx->isLoadInProgress = FALSE; |
| |
| hdd_driver_memdump_init(); |
| hddLog(LOGE, FL("FTM driver loaded")); |
| wlan_comp.status = 0; |
| complete(&wlan_comp.wlan_start_comp); |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| ret = process_wma_set_command(0, WMI_PDEV_PARAM_TX_CHAIN_MASK_1SS, |
| pHddCtx->cfg_ini->tx_chain_mask_1ss, |
| PDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: WMI_PDEV_PARAM_TX_CHAIN_MASK_1SS failed %d", |
| __func__, ret); |
| } |
| |
| ret = process_wma_set_command(0, WMI_PDEV_PARAM_TX_SCH_DELAY, |
| pHddCtx->cfg_ini->tx_sch_delay, |
| PDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: WMI_PDEV_PARAM_TX_SCH_DELAY failed %d", |
| __func__, ret); |
| } |
| |
| if (pHddCtx->cfg_ini->sap_get_peer_info) { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s Send WMI_PDEV_PARAM_PEER_STATS_INFO_ENABLE", |
| __func__); |
| ret = process_wma_set_command(0, |
| WMI_PDEV_PARAM_PEER_STATS_INFO_ENABLE, |
| pHddCtx->cfg_ini->sap_get_peer_info, |
| PDEV_CMD); |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: WMI_PDEV_PARAM_PEER_STATS_INFO_ENABLE failed %d", |
| __func__, ret); |
| } |
| } |
| ret = process_wma_set_command(0, WMI_PDEV_PARAM_ARP_AC_OVERRIDE, |
| pHddCtx->cfg_ini->arp_ac_category, PDEV_CMD); |
| if (0 != ret) { |
| hddLog(LOGE, |
| "%s: WMI_PDEV_PARAM_ARP_AC_OVERRIDE failed AC: %d ret: %d", |
| __func__, pHddCtx->cfg_ini->arp_ac_category, ret); |
| } |
| |
| status = hdd_set_sme_chan_list(pHddCtx); |
| if (status != VOS_STATUS_SUCCESS) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to init channel list", __func__); |
| goto err_wiphy_unregister; |
| } |
| |
| /* In the integrated architecture we update the configuration from |
| the INI file and from NV before vOSS has been started so that |
| the final contents are available to send down to the cCPU */ |
| |
| // Apply the cfg.ini to cfg.dat |
| if (FALSE == hdd_update_config_dat(pHddCtx)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: config update failed",__func__ ); |
| goto err_wiphy_unregister; |
| } |
| |
| ret = hdd_initialize_mac_address(pHddCtx); |
| if (!pHddCtx->cfg_ini->g_use_otpmac && ret) { |
| hddLog(LOGE, |
| FL("Failed to read MAC from platform driver or %s (driver load failed)"), |
| WLAN_MAC_FILE); |
| goto err_wiphy_unregister; |
| } |
| |
| { |
| eHalStatus halStatus; |
| /* Set the MAC Address Currently this is used by HAL to |
| * add self sta. Remove this once self sta is added as |
| * part of session open. |
| */ |
| halStatus = cfgSetStr( pHddCtx->hHal, WNI_CFG_STA_ID, |
| (v_U8_t *)&pHddCtx->cfg_ini->intfMacAddr[0], |
| sizeof( pHddCtx->cfg_ini->intfMacAddr[0]) ); |
| |
| if (!HAL_STATUS_SUCCESS( halStatus )) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Failed to set MAC Address. " |
| "HALStatus is %08d [x%08x]",__func__, halStatus, halStatus ); |
| goto err_wiphy_unregister; |
| } |
| } |
| |
| #ifdef IPA_OFFLOAD |
| #ifdef SYNC_IPA_READY |
| /* Check if IPA is ready before calling any IPA API */ |
| if ((ret = ipa_register_ipa_ready_cb((void *)hdd_ipa_ready_cb, |
| (void *)pHddCtx)) == -EEXIST) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, FL("IPA is ready")); |
| } else if (ret >= 0) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| FL("IPA is not ready - wait until it is ready")); |
| init_completion(&pHddCtx->ipa_ready); |
| wait_for_completion(&pHddCtx->ipa_ready); |
| } else { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| FL("IPA is not ready - Fail to register IPA ready callback")); |
| goto err_wiphy_unregister; |
| } |
| #endif |
| if (hdd_ipa_init(pHddCtx) == VOS_STATUS_E_FAILURE) |
| goto err_wiphy_unregister; |
| #endif |
| |
| /*Start VOSS which starts up the SME/MAC/HAL modules and everything else */ |
| status = vos_start( pHddCtx->pvosContext ); |
| if ( !VOS_IS_STATUS_SUCCESS( status ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: vos_start failed",__func__); |
| #ifdef IPA_OFFLOAD |
| goto err_ipa_cleanup; |
| #else |
| goto err_wiphy_unregister; |
| #endif |
| } |
| |
| #ifdef FEATURE_WLAN_CH_AVOID |
| #ifdef CONFIG_CNSS |
| vos_get_wlan_unsafe_channel(pHddCtx->unsafe_channel_list, |
| &(pHddCtx->unsafe_channel_count), |
| sizeof(v_U16_t) * NUM_20MHZ_RF_CHANNELS); |
| hddLog(LOG1,"%s: num of unsafe channels is %d. ", |
| __func__, pHddCtx->unsafe_channel_count); |
| |
| unsafe_channel_count = VOS_MIN((uint16_t)pHddCtx->unsafe_channel_count, |
| (uint16_t)NUM_20MHZ_RF_CHANNELS); |
| |
| for (unsafeChannelIndex = 0; |
| unsafeChannelIndex < unsafe_channel_count; |
| unsafeChannelIndex++) { |
| hddLog(LOG1,"%s: channel %d is not safe. ", |
| __func__, pHddCtx->unsafe_channel_list[unsafeChannelIndex]); |
| } |
| |
| /* Plug in avoid channel notification callback */ |
| sme_AddChAvoidCallback(pHddCtx->hHal, |
| hdd_ch_avoid_cb); |
| #endif |
| #endif /* FEATURE_WLAN_CH_AVOID */ |
| |
| #ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| /* Initialize the lock*/ |
| mutex_init(&pHddCtx->ch_switch_ctx.sap_ch_sw_lock); |
| /*Register the CSA Notification callback*/ |
| sme_AddCSAIndCallback(pHddCtx->hHal, hdd_csa_notify_cb); |
| #endif//#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| |
| status = hdd_post_voss_start_config( pHddCtx ); |
| if ( !VOS_IS_STATUS_SUCCESS( status ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hdd_post_voss_start_config failed", |
| __func__); |
| goto err_vosstop; |
| } |
| |
| #ifdef QCA_PKT_PROTO_TRACE |
| /* Ensure pkt tracing happen only in Non FTM mode */ |
| vos_pkt_proto_trace_init(); |
| #endif /* QCA_PKT_PROTO_TRACE */ |
| |
| #if defined(CONFIG_HDD_INIT_WITH_RTNL_LOCK) |
| rtnl_lock(); |
| rtnl_lock_enable = TRUE; |
| #else |
| rtnl_lock_enable = FALSE; |
| #endif |
| |
| /* Initialize the RoC Request queue and work. */ |
| hdd_list_init((&pHddCtx->hdd_roc_req_q), MAX_ROC_REQ_QUEUE_ENTRY); |
| vos_init_delayed_work(&pHddCtx->rocReqWork, wlan_hdd_roc_request_dequeue); |
| vos_init_work(&pHddCtx->sap_start_work, hdd_sap_restart_handle); |
| |
| if (pHddCtx->cfg_ini->dot11p_mode == WLAN_HDD_11P_STANDALONE) { |
| /* Create only 802.11p interface */ |
| pAdapter = hdd_open_adapter(pHddCtx, WLAN_HDD_OCB,"wlanocb%d", |
| wlan_hdd_get_intf_addr(pHddCtx), |
| NET_NAME_UNKNOWN, |
| rtnl_lock_enable); |
| } else { |
| pAdapter = hdd_open_adapter(pHddCtx, WLAN_HDD_INFRA_STATION, "wlan%d", |
| wlan_hdd_get_intf_addr(pHddCtx), |
| NET_NAME_UNKNOWN, |
| rtnl_lock_enable); |
| |
| #ifdef WLAN_OPEN_P2P_INTERFACE |
| if(VOS_MONITOR_MODE != vos_get_conparam()){ |
| /* Open P2P device interface */ |
| if (pAdapter != NULL && |
| !hdd_cfg_is_sub20_channel_width_enabled(pHddCtx)) { |
| if (pHddCtx->cfg_ini->isP2pDeviceAddrAdministrated && |
| !(pHddCtx->cfg_ini->intfMacAddr[0].bytes[0] & 0x02)) { |
| vos_mem_copy(pHddCtx->p2pDeviceAddress.bytes, |
| pHddCtx->cfg_ini->intfMacAddr[0].bytes, |
| sizeof(tSirMacAddr)); |
| |
| /* Generate the P2P Device Address. This consists of the device's |
| * primary MAC address with the locally administered bit set. |
| */ |
| pHddCtx->p2pDeviceAddress.bytes[0] |= 0x02; |
| } else { |
| uint8_t* p2p_dev_addr = wlan_hdd_get_intf_addr(pHddCtx); |
| if (p2p_dev_addr != NULL) { |
| vos_mem_copy(&pHddCtx->p2pDeviceAddress.bytes[0], |
| p2p_dev_addr, VOS_MAC_ADDR_SIZE); |
| } else { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| FL("Failed to allocate mac_address for p2p_device")); |
| goto err_close_adapter; |
| } |
| } |
| |
| pP2pAdapter = hdd_open_adapter(pHddCtx, WLAN_HDD_P2P_DEVICE, "p2p%d", |
| &pHddCtx->p2pDeviceAddress.bytes[0], |
| NET_NAME_UNKNOWN, |
| rtnl_lock_enable); |
| |
| if (NULL == pP2pAdapter) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| FL("Failed to do hdd_open_adapter for P2P Device Interface")); |
| goto err_close_adapter; |
| } |
| } |
| } |
| #endif /* WLAN_OPEN_P2P_INTERFACE */ |
| |
| /* Open 802.11p Interface */ |
| if (pAdapter != NULL) { |
| if (pHddCtx->cfg_ini->dot11p_mode == WLAN_HDD_11P_CONCURRENT) { |
| dot11_adapter = hdd_open_adapter(pHddCtx, WLAN_HDD_OCB, "wlanocb%d", |
| wlan_hdd_get_intf_addr(pHddCtx), |
| NET_NAME_UNKNOWN, |
| rtnl_lock_enable); |
| |
| if (dot11_adapter == NULL) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| FL("hdd_open_adapter() failed for 802.11p Interface")); |
| goto err_close_adapter; |
| } |
| } |
| } |
| } |
| |
| if( pAdapter == NULL ) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: hdd_open_adapter failed", __func__); |
| goto err_close_adapter; |
| } |
| |
| |
| /* Target hw version/revision would only be retrieved after |
| firmware download */ |
| hif_get_hw_info(hif_sc, &pHddCtx->target_hw_version, |
| &pHddCtx->target_hw_revision); |
| |
| /* Get the wlan hw/fw version */ |
| hdd_wlan_get_version(pAdapter, NULL, NULL); |
| |
| /* pass target_fw_version to HIF layer */ |
| hif_set_fw_info(hif_sc, pHddCtx->target_fw_version); |
| |
| if (country_code) |
| { |
| eHalStatus ret; |
| |
| INIT_COMPLETION(pAdapter->change_country_code); |
| hdd_checkandupdate_dfssetting(pAdapter, country_code); |
| |
| ret = sme_ChangeCountryCode(pHddCtx->hHal, |
| (void *)(tSmeChangeCountryCallback) |
| wlan_hdd_change_country_code_callback, |
| country_code, pAdapter, pHddCtx->pvosContext, eSIR_TRUE, eSIR_TRUE); |
| if (eHAL_STATUS_SUCCESS == ret) |
| { |
| rc = wait_for_completion_timeout( |
| &pAdapter->change_country_code, |
| msecs_to_jiffies(WLAN_WAIT_TIME_COUNTRY)); |
| if (!rc) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: SME while setting country code timed out", __func__); |
| } |
| } |
| else |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: SME Change Country code from module param fail ret=%d", __func__, ret); |
| ret = -EINVAL; |
| } |
| } |
| |
| |
| #ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD |
| if(!(IS_ROAM_SCAN_OFFLOAD_FEATURE_ENABLE)) |
| { |
| hddLog(VOS_TRACE_LEVEL_DEBUG,"%s: ROAM_SCAN_OFFLOAD Feature not supported",__func__); |
| pHddCtx->cfg_ini->isRoamOffloadScanEnabled = 0; |
| sme_UpdateRoamScanOffloadEnabled((tHalHandle)(pHddCtx->hHal), |
| pHddCtx->cfg_ini->isRoamOffloadScanEnabled); |
| } |
| #endif |
| #ifdef FEATURE_WLAN_SCAN_PNO |
| /*SME must send channel update configuration to FW*/ |
| sme_UpdateChannelConfig(pHddCtx->hHal); |
| #endif |
| sme_Register11dScanDoneCallback(pHddCtx->hHal, hdd_11d_scan_done); |
| |
| /* Fwr capabilities received, Set the Dot11 mode */ |
| sme_SetDefDot11Mode(pHddCtx->hHal); |
| |
| /* Register with platform driver as client for Suspend/Resume */ |
| status = hddRegisterPmOps(pHddCtx); |
| if ( !VOS_IS_STATUS_SUCCESS( status ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hddRegisterPmOps failed",__func__); |
| goto err_close_adapter; |
| } |
| |
| /* Open debugfs interface */ |
| if (VOS_STATUS_SUCCESS != hdd_debugfs_init(pAdapter)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR, |
| "%s: hdd_debugfs_init failed!", __func__); |
| } |
| |
| /* Register TM level change handler function to the platform */ |
| status = hddDevTmRegisterNotifyCallback(pHddCtx); |
| if ( !VOS_IS_STATUS_SUCCESS( status ) ) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hddDevTmRegisterNotifyCallback failed",__func__); |
| goto err_unregister_pmops; |
| } |
| |
| /* register for riva power on lock to platform driver */ |
| if (req_riva_power_on_lock("wlan")) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: req riva power on lock failed", |
| __func__); |
| goto err_unregister_pmops; |
| } |
| #if !defined(CONFIG_HDD_INIT_WITH_RTNL_LOCK) |
| // register net device notifier for device change notification |
| ret = register_netdevice_notifier(&hdd_netdev_notifier); |
| |
| if(ret < 0) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: register_netdevice_notifier failed",__func__); |
| goto err_free_power_on_lock; |
| } |
| reg_netdev_notifier_done = TRUE; |
| #endif |
| |
| #ifdef WLAN_KD_READY_NOTIFIER |
| pHddCtx->kd_nl_init = 1; |
| #endif /* WLAN_KD_READY_NOTIFIER */ |
| |
| #ifdef FEATURE_OEM_DATA_SUPPORT |
| //Initialize the OEM service |
| if (oem_activate_service(pHddCtx) != 0) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: oem_activate_service failed", __func__); |
| goto err_reg_netdev; |
| } |
| #endif |
| |
| #ifdef PTT_SOCK_SVC_ENABLE |
| //Initialize the PTT service |
| if(ptt_sock_activate_svc(pHddCtx) != 0) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: ptt_sock_activate_svc failed",__func__); |
| goto err_reg_netdev; |
| } |
| #endif |
| |
| if (hdd_open_cesium_nl_sock() < 0) |
| hddLog(VOS_TRACE_LEVEL_WARN, FL("hdd_open_cesium_nl_sock failed")); |
| |
| //Initialize the CNSS-DIAG service |
| if (cnss_diag_activate_service() < 0) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: cnss_diag_activate_service failed", __func__); |
| goto err_close_cesium; |
| } |
| |
| hdd_register_mcast_bcast_filter(pHddCtx); |
| if (VOS_STA_SAP_MODE != hdd_get_conparam()) |
| { |
| /* Action frame registered in one adapter which will |
| * applicable to all interfaces |
| */ |
| wlan_hdd_cfg80211_register_frames(pAdapter); |
| } |
| |
| mutex_init(&pHddCtx->sap_lock); |
| |
| #if defined(CONFIG_HDD_INIT_WITH_RTNL_LOCK) |
| if (rtnl_lock_enable == TRUE) { |
| rtnl_lock_enable = FALSE; |
| rtnl_unlock(); |
| } |
| /* register net device notifier for device change notification */ |
| ret = register_netdevice_notifier(&hdd_netdev_notifier); |
| if (ret < 0) { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: register_netdevice_notifier failed", |
| __func__); |
| goto err_close_cesium; |
| } |
| reg_netdev_notifier_done = TRUE; |
| #endif |
| |
| /* Initialize the wake lcok */ |
| vos_wake_lock_init(&pHddCtx->rx_wake_lock, |
| "qcom_rx_wakelock"); |
| /* Initialize the wake lcok */ |
| vos_wake_lock_init(&pHddCtx->sap_wake_lock, |
| "qcom_sap_wakelock"); |
| |
| hdd_hostapd_channel_wakelock_init(pHddCtx); |
| |
| // Initialize the restart logic |
| wlan_hdd_restart_init(pHddCtx); |
| |
| if(pHddCtx->cfg_ini->enablePowersaveOffload) |
| { |
| hdd_set_idle_ps_config(pHddCtx, TRUE); |
| } |
| |
| if ((pHddCtx->cfg_ini->enable_ac_txq_optimize >> 4) & 0x01) |
| sme_set_ac_txq_optimize(pHddCtx->hHal, |
| &pHddCtx->cfg_ini->enable_ac_txq_optimize); |
| |
| if (pHddCtx->cfg_ini->enable_go_cts2self_for_sta) |
| sme_set_cts2self_for_p2p_go(pHddCtx->hHal); |
| |
| /* Reset previous stats before turning on/off */ |
| vos_mem_set(&pAdapter->mib_stats, |
| sizeof(pAdapter->mib_stats), 0); |
| |
| hal_status = sme_set_mib_stats_enable(pHddCtx->hHal, |
| pHddCtx->cfg_ini->mib_stats_enabled); |
| |
| if (eHAL_STATUS_SUCCESS != hal_status) |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("set mib stats failed")); |
| |
| #ifdef FEATURE_WLAN_AUTO_SHUTDOWN |
| if (pHddCtx->cfg_ini->WlanAutoShutdown != 0) |
| if (sme_set_auto_shutdown_cb(pHddCtx->hHal, wlan_hdd_auto_shutdown_cb) |
| != eHAL_STATUS_SUCCESS) |
| hddLog(LOGE, FL("Auto shutdown feature could not be enabled")); |
| #endif |
| |
| #ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE |
| status = vos_timer_init(&pHddCtx->skip_acs_scan_timer, VOS_TIMER_TYPE_SW, |
| hdd_skip_acs_scan_timer_handler, (void *)pHddCtx); |
| if (!VOS_IS_STATUS_SUCCESS(status)) |
| hddLog(LOGE, FL("Failed to init ACS Skip timer\n")); |
| spin_lock_init(&pHddCtx->acs_skip_lock); |
| #endif |
| |
| #ifdef WLAN_FEATURE_NAN |
| wlan_hdd_cfg80211_nan_init(pHddCtx); |
| #endif |
| |
| /* Thermal Mitigation */ |
| thermalParam.smeThermalMgmtEnabled = |
| pHddCtx->cfg_ini->thermalMitigationEnable; |
| thermalParam.smeThrottlePeriod = pHddCtx->cfg_ini->throttlePeriod; |
| |
| thermalParam.sme_throttle_duty_cycle_tbl[0]= |
| pHddCtx->cfg_ini->throttle_dutycycle_level0; |
| thermalParam.sme_throttle_duty_cycle_tbl[1]= |
| pHddCtx->cfg_ini->throttle_dutycycle_level1; |
| thermalParam.sme_throttle_duty_cycle_tbl[2]= |
| pHddCtx->cfg_ini->throttle_dutycycle_level2; |
| thermalParam.sme_throttle_duty_cycle_tbl[3]= |
| pHddCtx->cfg_ini->throttle_dutycycle_level3; |
| |
| thermalParam.smeThermalLevels[0].smeMinTempThreshold = |
| pHddCtx->cfg_ini->thermalTempMinLevel0; |
| thermalParam.smeThermalLevels[0].smeMaxTempThreshold = |
| pHddCtx->cfg_ini->thermalTempMaxLevel0; |
| thermalParam.smeThermalLevels[1].smeMinTempThreshold = |
| pHddCtx->cfg_ini->thermalTempMinLevel1; |
| thermalParam.smeThermalLevels[1].smeMaxTempThreshold = |
| pHddCtx->cfg_ini->thermalTempMaxLevel1; |
| thermalParam.smeThermalLevels[2].smeMinTempThreshold = |
| pHddCtx->cfg_ini->thermalTempMinLevel2; |
| thermalParam.smeThermalLevels[2].smeMaxTempThreshold = |
| pHddCtx->cfg_ini->thermalTempMaxLevel2; |
| thermalParam.smeThermalLevels[3].smeMinTempThreshold = |
| pHddCtx->cfg_ini->thermalTempMinLevel3; |
| thermalParam.smeThermalLevels[3].smeMaxTempThreshold = |
| pHddCtx->cfg_ini->thermalTempMaxLevel3; |
| |
| hdd_get_thermal_shutdown_ini_param(&thermalParam, pHddCtx); |
| |
| if (eHAL_STATUS_SUCCESS != sme_InitThermalInfo(pHddCtx->hHal,thermalParam)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Error while initializing thermal information", __func__); |
| } |
| |
| /* Runtime DPD Recaliberation config*/ |
| hdd_get_DPD_Recaliberation_ini_param(&DPDParam, pHddCtx); |
| |
| if (eHAL_STATUS_SUCCESS != sme_InitDPDRecalInfo(pHddCtx->hHal, DPDParam)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Error while initializing Runtime DPD Recaliberation information", __func__); |
| } |
| |
| /* Plug in set thermal level callback */ |
| sme_add_set_thermal_level_callback(pHddCtx->hHal, |
| (tSmeSetThermalLevelCallback)hdd_set_thermal_level_cb); |
| |
| sme_add_thermal_temperature_ind_callback(pHddCtx->hHal, |
| (tSmeThermalTempIndCb)hdd_thermal_temp_ind_event_cb); |
| |
| /* Bad peer tx flow control */ |
| wlan_hdd_bad_peer_txctl(pHddCtx); |
| |
| /* SAR power limit */ |
| hddtxlimit = vos_mem_malloc(sizeof(tSirTxPowerLimit)); |
| if (!hddtxlimit) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Memory allocation for TxPowerLimit " |
| "failed!", __func__); |
| goto err_close_cesium; |
| } |
| hddtxlimit->txPower2g = pHddCtx->cfg_ini->TxPower2g; |
| hddtxlimit->txPower5g = pHddCtx->cfg_ini->TxPower5g; |
| |
| if (eHAL_STATUS_SUCCESS != sme_TxpowerLimit(pHddCtx->hHal,hddtxlimit)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Error setting txlimit in sme", __func__); |
| } |
| |
| vos_timer_init(&pHddCtx->tdls_source_timer, VOS_TIMER_TYPE_SW, |
| wlan_hdd_change_tdls_mode, (void *)pHddCtx); |
| |
| #ifdef FEATURE_BUS_BANDWIDTH |
| spin_lock_init(&pHddCtx->bus_bw_lock); |
| vos_timer_init(&pHddCtx->bus_bw_timer, |
| VOS_TIMER_TYPE_SW, |
| hdd_bus_bw_compute_cbk, |
| (void *)pHddCtx); |
| #endif |
| |
| #ifdef WLAN_FEATURE_STATS_EXT |
| wlan_hdd_cfg80211_stats_ext_init(pHddCtx); |
| #endif |
| #ifdef FEATURE_WLAN_EXTSCAN |
| sme_ExtScanRegisterCallback(pHddCtx->hHal, |
| wlan_hdd_cfg80211_extscan_callback); |
| #endif /* FEATURE_WLAN_EXTSCAN */ |
| sme_chain_rssi_register_callback(pHddCtx->hHal, |
| wlan_hdd_cfg80211_chainrssi_callback); |
| sme_set_rssi_threshold_breached_cb(pHddCtx->hHal, hdd_rssi_threshold_breached); |
| wlan_hdd_cfg80211_link_layer_stats_init(pHddCtx); |
| wlan_hdd_tsf_init(pHddCtx); |
| |
| #ifdef WLAN_FEATURE_LPSS |
| wlan_hdd_send_all_scan_intf_info(pHddCtx); |
| wlan_hdd_send_version_pkg(pHddCtx->target_fw_version, |
| pHddCtx->target_hw_version, |
| pHddCtx->target_hw_name); |
| #endif |
| |
| if (WLAN_HDD_RX_HANDLE_RPS == pHddCtx->cfg_ini->rxhandle) |
| hdd_set_rps_cpu_mask(pHddCtx); |
| |
| hal_status = sme_set_lost_link_info_cb(pHddCtx->hHal, |
| hdd_lost_link_info_cb); |
| /* print error and not block the startup process */ |
| if (eHAL_STATUS_SUCCESS != hal_status) |
| hddLog(LOGE, "%s: set lost link info callback failed", __func__); |
| |
| hal_status = sme_set_smps_force_mode_cb(pHddCtx->hHal, |
| hdd_smps_force_mode_cb); |
| if (eHAL_STATUS_SUCCESS != hal_status) |
| hddLog(LOGE, FL("set smps force mode callback failed")); |
| |
| hal_status = sme_bpf_offload_register_callback(pHddCtx->hHal, |
| hdd_get_bpf_offload_cb); |
| if (eHAL_STATUS_SUCCESS != hal_status) |
| hddLog(LOGE, FL("set bpf offload callback failed")); |
| |
| wlan_hdd_dcc_register_for_dcc_stats_event(pHddCtx); |
| |
| wlan_hdd_init_chan_info(pHddCtx); |
| |
| /* |
| * Register IPv6 notifier to notify if any change in IP |
| * So that we can reconfigure the offload parameters |
| */ |
| hdd_wlan_register_ip6_notifier(pHddCtx); |
| |
| /* |
| * Register IPv4 notifier to notify if any change in IP |
| * So that we can reconfigure the offload parameters |
| */ |
| pHddCtx->ipv4_notifier.notifier_call = wlan_hdd_ipv4_changed; |
| ret = register_inetaddr_notifier(&pHddCtx->ipv4_notifier); |
| if (ret) |
| hddLog(LOGE, FL("Failed to register IPv4 notifier")); |
| else |
| hddLog(LOG1, FL("Registered IPv4 notifier")); |
| |
| ol_pktlog_init(hif_sc); |
| |
| /* |
| * Send btc page and wlan (p2p/sta/sap) interval to firmware if |
| * relevant parameters set in ini file. |
| */ |
| hdd_set_btc_bt_wlan_interval(pHddCtx); |
| |
| hdd_runtime_suspend_init(pHddCtx); |
| pHddCtx->isLoadInProgress = FALSE; |
| vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, FALSE); |
| vos_set_load_in_progress(VOS_MODULE_ID_VOSS, FALSE); |
| |
| if (pHddCtx->cfg_ini->fIsLogpEnabled) { |
| vos_wdthread_init_timer_work(vos_process_wd_timer); |
| /* Initialize the timer to detect thread stuck issues */ |
| vos_thread_stuck_timer_init( |
| &((VosContextType*)pVosContext)->vosWatchdog); |
| } |
| |
| if (pHddCtx->cfg_ini->enable_dynamic_sta_chainmask) |
| hdd_decide_dynamic_chain_mask(pHddCtx, |
| HDD_ANTENNA_MODE_1X1); |
| |
| hdd_driver_memdump_init(); |
| |
| if (pHddCtx->cfg_ini->goptimize_chan_avoid_event) { |
| hal_status = sme_enable_disable_chanavoidind_event(pHddCtx->hHal, 0); |
| if (eHAL_STATUS_SUCCESS != hal_status) |
| hddLog(LOGE, FL("Failed to disable Chan Avoidance Indcation")); |
| } |
| if (pHddCtx->cfg_ini->enable_5g_band_pref) { |
| band_pref_params.rssi_boost_threshold_5g = |
| pHddCtx->cfg_ini->rssi_boost_threshold_5g; |
| band_pref_params.rssi_boost_factor_5g = |
| pHddCtx->cfg_ini->rssi_boost_factor_5g; |
| band_pref_params.max_rssi_boost_5g = |
| pHddCtx->cfg_ini->max_rssi_boost_5g; |
| band_pref_params.rssi_penalize_threshold_5g = |
| pHddCtx->cfg_ini->rssi_penalize_threshold_5g; |
| band_pref_params.rssi_penalize_factor_5g = |
| pHddCtx->cfg_ini->rssi_penalize_factor_5g; |
| band_pref_params.max_rssi_penalize_5g = |
| pHddCtx->cfg_ini->max_rssi_penalize_5g; |
| sme_set_5g_band_pref(pHddCtx->hHal, &band_pref_params); |
| } |
| |
| |
| if (pHddCtx->cfg_ini->sifs_burst_duration) { |
| set_value = (SIFS_BURST_DUR_MULTIPLIER) * |
| pHddCtx->cfg_ini->sifs_burst_duration; |
| |
| if ((set_value > 0) && (set_value <= SIFS_BURST_DUR_MAX)) |
| process_wma_set_command(0, (int)WMI_PDEV_PARAM_BURST_DUR, |
| set_value, PDEV_CMD); |
| } |
| |
| if (pHddCtx->cfg_ini->max_mpdus_inampdu) { |
| set_value = pHddCtx->cfg_ini->max_mpdus_inampdu; |
| process_wma_set_command(0, (int)WMI_PDEV_PARAM_MAX_MPDUS_IN_AMPDU, |
| set_value, PDEV_CMD); |
| } |
| |
| if (pHddCtx->cfg_ini->enable_rts_sifsbursting) { |
| set_value = pHddCtx->cfg_ini->enable_rts_sifsbursting; |
| process_wma_set_command(0, (int)WMI_PDEV_PARAM_ENABLE_RTS_SIFS_BURSTING, |
| set_value, PDEV_CMD); |
| |
| } |
| |
| if (hdd_wlan_enable_egap(pHddCtx)) |
| hddLog(LOGE, FL("enhance green ap is not enabled")); |
| |
| /* set chip power save failure detected callback */ |
| sme_set_chip_pwr_save_fail_cb(pHddCtx->hHal, |
| hdd_chip_pwr_save_fail_detected_cb); |
| |
| wlan_comp.status = 0; |
| complete(&wlan_comp.wlan_start_comp); |
| goto success; |
| |
| err_close_cesium: |
| hdd_close_cesium_nl_sock(); |
| |
| err_reg_netdev: |
| if (rtnl_lock_enable == TRUE) { |
| rtnl_lock_enable = FALSE; |
| rtnl_unlock(); |
| } |
| if (reg_netdev_notifier_done == TRUE) { |
| unregister_netdevice_notifier(&hdd_netdev_notifier); |
| reg_netdev_notifier_done = FALSE; |
| } |
| |
| #if !defined(CONFIG_HDD_INIT_WITH_RTNL_LOCK) |
| err_free_power_on_lock: |
| #endif |
| free_riva_power_on_lock("wlan"); |
| |
| err_unregister_pmops: |
| hddDevTmUnregisterNotifyCallback(pHddCtx); |
| hddDeregisterPmOps(pHddCtx); |
| |
| hdd_debugfs_exit(pHddCtx); |
| |
| err_close_adapter: |
| #if defined(CONFIG_HDD_INIT_WITH_RTNL_LOCK) |
| if (rtnl_lock_enable == TRUE) { |
| rtnl_lock_enable = FALSE; |
| rtnl_unlock(); |
| } |
| #endif |
| hdd_close_all_adapters( pHddCtx ); |
| |
| #ifdef QCA_PKT_PROTO_TRACE |
| if (VOS_FTM_MODE != hdd_get_conparam()) |
| vos_pkt_proto_trace_close(); |
| #endif /* QCA_PKT_PROTO_TRACE */ |
| |
| err_vosstop: |
| vos_stop(pVosContext); |
| |
| #ifdef IPA_OFFLOAD |
| err_ipa_cleanup: |
| hdd_ipa_cleanup(pHddCtx); |
| #endif |
| |
| err_wiphy_unregister: |
| wiphy_unregister(wiphy); |
| |
| err_vosclose: |
| status = vos_sched_close( pVosContext ); |
| if (!VOS_IS_STATUS_SUCCESS(status)) { |
| VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL, |
| "%s: Failed to close VOSS Scheduler", __func__); |
| VOS_ASSERT( VOS_IS_STATUS_SUCCESS( status ) ); |
| } |
| vos_close(pVosContext ); |
| |
| err_vos_nv_close: |
| |
| vos_nv_close(); |
| |
| hdd_wlan_green_ap_deinit(pHddCtx); |
| |
| err_wdclose: |
| if(pHddCtx->cfg_ini->fIsLogpEnabled) |
| vos_watchdog_close(pVosContext); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) |
| { |
| #if defined(QCA_WIFI_FTM) |
| err_free_ftm_open: |
| wlan_hdd_ftm_close(pHddCtx); |
| #endif |
| } |
| |
| err_nl_srv: |
| nl_srv_exit(); |
| |
| err_logging_sock: |
| if (VOS_FTM_MODE != hdd_get_conparam()) |
| wlan_hdd_logging_sock_deactivate_svc(pHddCtx); |
| |
| err_sock_activate: |
| wlan_hdd_cfg80211_deinit(wiphy); |
| |
| err_config: |
| vos_mem_free(pHddCtx->cfg_ini); |
| pHddCtx->cfg_ini= NULL; |
| |
| err_histogram: |
| wlan_hdd_deinit_tx_rx_histogram(pHddCtx); |
| |
| err_free_hdd_context: |
| /* wiphy_free() will free the HDD context so remove global reference */ |
| if (pVosContext) { |
| hdd_free_probe_req_ouis(pHddCtx); |
| ((VosContextType*)(pVosContext))->pHDDContext = NULL; |
| } |
| |
| wiphy_free(wiphy) ; |
| //kfree(wdev) ; |
| VOS_BUG(1); |
| |
| if (hdd_is_ssr_required()) |
| { |
| #ifdef MSM_PLATFORM |
| #ifdef CONFIG_CNSS |
| /* WDI timeout had happened during load, so SSR is needed here */ |
| subsystem_restart("wcnss"); |
| #endif |
| #endif |
| msleep(5000); |
| } |
| hdd_set_ssr_required (VOS_FALSE); |
| |
| wlan_comp.status = -EAGAIN; |
| complete(&wlan_comp.wlan_start_comp); |
| return -EIO; |
| |
| success: |
| EXIT(); |
| return 0; |
| } |
| |
| /* accommodate the request firmware bin time out 2 min */ |
| #define REQUEST_FWR_TIMEOUT 120000 |
| #define HDD_WLAN_START_WAIT_TIME (VOS_WDA_TIMEOUT + 5000 + REQUEST_FWR_TIMEOUT) |
| /** |
| * hdd_hif_register_driver() - API for HDD to register with HIF |
| * |
| * API for HDD to register with HIF layer |
| * |
| * Return: success/failure |
| */ |
| int hdd_hif_register_driver(void) |
| { |
| int ret; |
| unsigned long rc, timeout; |
| |
| init_completion(&wlan_comp.wlan_start_comp); |
| wlan_comp.status = 0; |
| |
| ret = hif_register_driver(); |
| |
| if (ret) { |
| hddLog(LOGE, FL("HIF registration failed")); |
| return ret; |
| } |
| |
| timeout = msecs_to_jiffies(HDD_WLAN_START_WAIT_TIME); |
| |
| rc = wait_for_completion_timeout(&wlan_comp.wlan_start_comp, timeout); |
| |
| if (!rc) { |
| hddLog(LOGE, FL("hif registration timedout")); |
| return -EAGAIN; |
| } |
| |
| if (wlan_comp.status) |
| hddLog(LOGE, |
| FL("hdd_wlan_startup failed status:%d jiffies_left:%lu"), |
| wlan_comp.status, rc); |
| |
| return wlan_comp.status; |
| } |
| |
| #ifdef TIMER_MANAGER |
| static inline void hdd_timer_exit(void) |
| { |
| vos_timer_exit(); |
| } |
| #else |
| static inline void hdd_timer_exit(void) |
| { |
| } |
| #endif |
| |
| #ifdef MEMORY_DEBUG |
| static inline void hdd_mem_exit(void) |
| { |
| adf_net_buf_debug_exit(); |
| vos_mem_exit(); |
| } |
| #else |
| static inline void hdd_mem_exit(void) |
| { |
| } |
| #endif |
| |
| #ifdef WLAN_LOGGING_SOCK_SVC_ENABLE |
| static inline void hdd_logging_sock_deinit_svc(void) |
| { |
| wlan_logging_sock_deinit_svc(); |
| } |
| #else |
| static inline void hdd_logging_sock_deinit_svc(void) |
| { |
| } |
| #endif |
| |
| static int hdd_register_fail_clean_up(v_CONTEXT_t vos_context) |
| { |
| hif_unregister_driver(); |
| vos_preClose(&vos_context); |
| hdd_timer_exit(); |
| hdd_mem_exit(); |
| hdd_logging_sock_deinit_svc(); |
| |
| return -ENODEV; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_driver_init() - Core Driver Init Function |
| |
| This is the driver entry point - called in different time line depending |
| on whether the driver is statically or dynamically linked |
| |
| \param - None |
| |
| \return - 0 for success, non zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static int hdd_driver_init( void) |
| { |
| VOS_STATUS status; |
| v_CONTEXT_t pVosContext = NULL; |
| int ret_status = 0; |
| u_int64_t start; |
| |
| start = adf_get_boottime(); |
| |
| #ifdef WLAN_LOGGING_SOCK_SVC_ENABLE |
| wlan_logging_sock_init_svc(); |
| #endif |
| |
| ENTER(); |
| |
| #ifdef TIMER_MANAGER |
| vos_timer_manager_init(); |
| #endif |
| |
| #ifdef MEMORY_DEBUG |
| vos_mem_init(); |
| adf_net_buf_debug_init(); |
| #endif |
| |
| hdd_wlan_wakelock_create(); |
| hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT); |
| |
| /* |
| * The Krait is going to Idle/Stand Alone Power Save |
| * more aggressively which is resulting in the longer driver load time. |
| * The Fix is to not allow Krait to enter Idle Power Save during driver load. |
| * at a time, and wait for the completion interrupt to start the next |
| * transfer. During this phase, the KRAIT is entering IDLE/StandAlone(SA) |
| * Power Save(PS). The delay incurred for resuming from IDLE/SA PS is |
| * huge during driver load. So prevent APPS IDLE/SA PS during driver |
| * load for reducing interrupt latency. |
| */ |
| |
| vos_request_pm_qos_type(PM_QOS_CPU_DMA_LATENCY, DISABLE_KRAIT_IDLE_PS_VAL); |
| |
| vos_ssr_protect_init(); |
| |
| pr_info("%s: loading driver v%s\n", WLAN_MODULE_NAME, |
| QWLAN_VERSIONSTR TIMER_MANAGER_STR MEMORY_DEBUG_STR); |
| |
| do { |
| |
| #ifndef MODULE |
| /* For statically linked driver, call hdd_set_conparam to update curr_con_mode |
| */ |
| hdd_set_conparam((v_UINT_t)con_mode); |
| if (WLAN_IS_EPPING_ENABLED(con_mode)) { |
| ret_status = epping_driver_init(con_mode, &wlan_wake_lock, |
| WLAN_MODULE_NAME); |
| if (ret_status < 0) { |
| vos_remove_pm_qos(); |
| hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT); |
| hdd_wlan_wakelock_destroy(); |
| } |
| return ret_status; |
| } |
| #else |
| if (WLAN_IS_EPPING_ENABLED(hdd_get_conparam())) { |
| ret_status = epping_driver_init(hdd_get_conparam(), |
| &wlan_wake_lock, WLAN_MODULE_NAME); |
| if (ret_status < 0) { |
| vos_remove_pm_qos(); |
| hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT); |
| hdd_wlan_wakelock_destroy(); |
| } |
| return ret_status; |
| } |
| #endif |
| |
| /* Preopen VOSS so that it is ready to start at least SAL */ |
| status = vos_preOpen(&pVosContext); |
| |
| if (!VOS_IS_STATUS_SUCCESS(status)) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Failed to preOpen VOSS", __func__); |
| vos_remove_pm_qos(); |
| ret_status = -1; |
| break; |
| } |
| |
| #ifdef HDD_TRACE_RECORD |
| MTRACE(hddTraceInit()); |
| #endif |
| hdd_register_debug_callback(); |
| |
| ret_status = hdd_hif_register_driver(); |
| vos_remove_pm_qos(); |
| |
| if (ret_status == 0) { |
| pr_info("%s: driver loaded in %lld\n", WLAN_MODULE_NAME, |
| adf_get_boottime() - start); |
| hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT); |
| return 0; |
| } |
| |
| hddLog(VOS_TRACE_LEVEL_FATAL, "%s: WLAN Driver Initialization failed", |
| __func__); |
| |
| ret_status = hdd_register_fail_clean_up(pVosContext); |
| hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT); |
| hdd_wlan_wakelock_destroy(); |
| |
| } while (0); |
| |
| EXIT(); |
| |
| return ret_status; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_module_init() - Init Function |
| |
| This is the driver entry point (invoked when module is loaded using insmod) |
| |
| \param - None |
| |
| \return - 0 for success, non zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| #ifdef MODULE |
| static int __init hdd_module_init ( void) |
| { |
| return hdd_driver_init(); |
| } |
| #else /* #ifdef MODULE */ |
| static int __init hdd_module_init ( void) |
| { |
| /* Driver initialization is delayed to fwpath_changed_handler */ |
| return 0; |
| } |
| #endif /* #ifdef MODULE */ |
| |
| static struct timer_list unload_timer; |
| static bool unload_timer_started; |
| |
| #ifdef CONFIG_SLUB_DEBUG_ON |
| #define HDD_UNLOAD_WAIT_TIME 35000 |
| #else |
| #define HDD_UNLOAD_WAIT_TIME 30000 |
| #endif |
| |
| /** |
| * hdd_unload_timer_init() - API to initialize unload timer |
| * |
| * initialize unload timer |
| * |
| * Return: None |
| */ |
| static void hdd_unload_timer_init(void) |
| { |
| init_timer(&unload_timer); |
| } |
| |
| /** |
| * hdd_unload_timer_del() - API to Delete unload timer |
| * |
| * Delete unload timer |
| * |
| * Return: None |
| */ |
| static void hdd_unload_timer_del(void) |
| { |
| del_timer(&unload_timer); |
| unload_timer_started = false; |
| } |
| |
| /** |
| * hdd_unload_timer_cb() - Unload timer callback function |
| * |
| * Unload timer callback function |
| * |
| * Return: None |
| */ |
| static void hdd_unload_timer_cb(unsigned long data) |
| { |
| v_CONTEXT_t vos_context = NULL; |
| hdd_context_t *hdd_ctx = NULL; |
| |
| pr_err("HDD unload timer expired!,current unload status: %d", |
| g_current_unload_state); |
| /* Get the global VOSS context. */ |
| vos_context = vos_get_global_context(VOS_MODULE_ID_SYS, NULL); |
| if(vos_context) { |
| hdd_ctx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, |
| vos_context ); |
| if(hdd_ctx) |
| pr_err("Driver loading: %d unloading:%d logp_in_progress: %d", |
| hdd_ctx->isLoadInProgress, |
| hdd_ctx->isUnloadInProgress, |
| hdd_ctx->isLogpInProgress); |
| } else |
| pr_err("%s: Global VOS context is Null", __func__); |
| |
| #ifdef CONFIG_SLUB_DEBUG_ON |
| VOS_BUG(0); |
| #endif |
| } |
| |
| /** |
| * hdd_unload_timer_start() - API to start unload timer |
| * @msec: timer interval in msec units |
| * |
| * API to start unload timer |
| * |
| * Return: None |
| */ |
| static void hdd_unload_timer_start(int msec) |
| { |
| if(unload_timer_started) |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: Starting unload timer when it's running!", |
| __func__); |
| unload_timer.expires = jiffies + msecs_to_jiffies(msec); |
| unload_timer.function = hdd_unload_timer_cb; |
| add_timer(&unload_timer); |
| unload_timer_started = true; |
| } |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_driver_exit() - Exit function |
| |
| This is the driver exit point (invoked when module is unloaded using rmmod |
| or con_mode was changed by user space) |
| |
| \param - None |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| static void hdd_driver_exit(void) |
| { |
| hdd_context_t *pHddCtx = NULL; |
| int retry = 0; |
| v_CONTEXT_t pVosContext = NULL; |
| |
| pr_info("%s: unloading driver v%s\n", WLAN_MODULE_NAME, QWLAN_VERSIONSTR); |
| |
| //Get the global vos context |
| pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL); |
| |
| if(!pVosContext) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Global VOS context is Null", __func__); |
| goto done; |
| } |
| |
| if (WLAN_IS_EPPING_ENABLED(con_mode)) { |
| epping_driver_exit(pVosContext); |
| goto done; |
| } |
| |
| //Get the HDD context. |
| pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext ); |
| |
| if(!pHddCtx) |
| { |
| hddLog(VOS_TRACE_LEVEL_FATAL,"%s: module exit called before probe",__func__); |
| } |
| else |
| { |
| hdd_thermal_suspend_cleanup(pHddCtx); |
| |
| /* |
| * Check IPA HW pipe shutdown properly or not |
| * If not, force shut down HW pipe |
| */ |
| hdd_ipa_uc_force_pipe_shutdown(pHddCtx); |
| |
| pHddCtx->driver_being_stopped = false; |
| |
| rtnl_lock(); |
| pHddCtx->isUnloadInProgress = TRUE; |
| vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, TRUE); |
| vos_set_unload_in_progress(TRUE); |
| rtnl_unlock(); |
| |
| while(pHddCtx->isLogpInProgress || |
| vos_is_logp_in_progress(VOS_MODULE_ID_VOSS, NULL)) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s:SSR in Progress; block rmmod for 1 second!!!", __func__); |
| msleep(1000); |
| |
| if (retry++ == HDD_MOD_EXIT_SSR_MAX_RETRIES) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s:SSR never completed, fatal error", __func__); |
| VOS_BUG(0); |
| } |
| } |
| } |
| |
| vos_wait_for_work_thread_completion(__func__); |
| /* If unload never completes, then do kernel panic. */ |
| hdd_unload_timer_init(); |
| hdd_unload_timer_start(HDD_UNLOAD_WAIT_TIME); |
| hif_unregister_driver(); |
| hdd_unload_timer_del(); |
| |
| vos_preClose( &pVosContext ); |
| |
| #ifdef TIMER_MANAGER |
| vos_timer_exit(); |
| #endif |
| #ifdef MEMORY_DEBUG |
| adf_net_buf_debug_exit(); |
| vos_mem_exit(); |
| #endif |
| |
| #ifdef WLAN_LOGGING_SOCK_SVC_ENABLE |
| wlan_logging_sock_deinit_svc(); |
| #endif |
| |
| done: |
| hdd_wlan_wakelock_destroy(); |
| pr_info("%s: driver unloaded\n", WLAN_MODULE_NAME); |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_module_exit() - Exit function |
| |
| This is the driver exit point (invoked when module is unloaded using rmmod) |
| |
| \param - None |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| static void __exit hdd_module_exit(void) |
| { |
| hdd_driver_exit(); |
| } |
| |
| #ifdef MODULE |
| static int fwpath_changed_handler(const char *kmessage, |
| struct kernel_param *kp) |
| { |
| return param_set_copystring(kmessage, kp); |
| } |
| |
| #if ! defined(QCA_WIFI_FTM) |
| static int con_mode_handler(const char *kmessage, |
| struct kernel_param *kp) |
| { |
| return param_set_int(kmessage, kp); |
| } |
| #endif |
| #else /* #ifdef MODULE */ |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief fwpath_changed_handler() - Handler Function |
| |
| Handle changes to the fwpath parameter |
| |
| \return - 0 for success, non zero for failure |
| |
| --------------------------------------------------------------------------*/ |
| static int fwpath_changed_handler(const char *kmessage, |
| struct kernel_param *kp) |
| { |
| int ret; |
| bool mode_change; |
| |
| ret = param_set_copystring(kmessage, kp); |
| |
| if (!ret) { |
| bool ready; |
| |
| ret = strncmp(fwpath_mode_local, kmessage , 3); |
| mode_change = ret ? true : false; |
| |
| |
| pr_info("%s : new_mode : %s, present_mode : %s\n", __func__, |
| kmessage, fwpath_mode_local); |
| |
| strlcpy(fwpath_mode_local, kmessage, |
| sizeof(fwpath_mode_local)); |
| |
| ready = vos_is_load_unload_ready(__func__); |
| |
| if (!ready) { |
| VOS_ASSERT(0); |
| return -EINVAL; |
| } |
| |
| vos_load_unload_protect(__func__); |
| ret = kickstart_driver(true, mode_change); |
| vos_load_unload_unprotect(__func__); |
| } |
| |
| return ret; |
| } |
| |
| #if ! defined(QCA_WIFI_FTM) |
| /**--------------------------------------------------------------------------- |
| |
| \brief con_mode_handler() - |
| |
| Handler function for module param con_mode when it is changed by user space |
| Dynamically linked - do nothing |
| Statically linked - exit and init driver, as in rmmod and insmod |
| |
| \param - |
| |
| \return - |
| |
| --------------------------------------------------------------------------*/ |
| static int con_mode_handler(const char *kmessage, struct kernel_param *kp) |
| { |
| int ret; |
| |
| ret = param_set_int(kmessage, kp); |
| if (0 == ret) |
| ret = kickstart_driver(true, false); |
| return ret; |
| } |
| #endif |
| #endif /* #ifdef MODULE */ |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_get_conparam() - |
| |
| This is the driver exit point (invoked when module is unloaded using rmmod) |
| |
| \param - None |
| |
| \return - tVOS_CON_MODE |
| |
| --------------------------------------------------------------------------*/ |
| tVOS_CON_MODE hdd_get_conparam ( void ) |
| { |
| #ifdef MODULE |
| return (tVOS_CON_MODE)con_mode; |
| #else |
| return (tVOS_CON_MODE)curr_con_mode; |
| #endif |
| } |
| void hdd_set_conparam ( v_UINT_t newParam ) |
| { |
| con_mode = newParam; |
| #ifndef MODULE |
| curr_con_mode = con_mode; |
| #endif |
| } |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_softap_sta_deauth() - function |
| |
| This to take counter measure to handle deauth req from HDD |
| |
| \param - pAdapter - Pointer to the HDD |
| |
| \param - enable - boolean value |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| |
| VOS_STATUS hdd_softap_sta_deauth(hdd_adapter_t *pAdapter, |
| struct tagCsrDelStaParams *pDelStaParams) |
| { |
| #ifndef WLAN_FEATURE_MBSSID |
| v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext; |
| #endif |
| VOS_STATUS vosStatus = VOS_STATUS_E_FAULT; |
| |
| ENTER(); |
| |
| hddLog(LOG1, "hdd_softap_sta_deauth:(%pK, false)", |
| (WLAN_HDD_GET_CTX(pAdapter))->pvosContext); |
| |
| //Ignore request to deauth bcmc station |
| if (pDelStaParams->peerMacAddr[0] & 0x1) |
| return vosStatus; |
| |
| #ifdef WLAN_FEATURE_MBSSID |
| vosStatus = WLANSAP_DeauthSta(WLAN_HDD_GET_SAP_CTX_PTR(pAdapter), |
| pDelStaParams); |
| #else |
| vosStatus = WLANSAP_DeauthSta(pVosContext, pDelStaParams); |
| #endif |
| |
| EXIT(); |
| return vosStatus; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_softap_sta_disassoc() - function |
| |
| This to take counter measure to handle deauth req from HDD |
| |
| \param - pAdapter - Pointer to the HDD |
| |
| \param - enable - boolean value |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| |
| void hdd_softap_sta_disassoc(hdd_adapter_t *pAdapter, |
| struct tagCsrDelStaParams *pDelStaParams) |
| { |
| #ifndef WLAN_FEATURE_MBSSID |
| v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext; |
| #endif |
| |
| ENTER(); |
| |
| hddLog( LOGE, "hdd_softap_sta_disassoc:(%pK, false)", (WLAN_HDD_GET_CTX(pAdapter))->pvosContext); |
| |
| //Ignore request to disassoc bcmc station |
| if( pDelStaParams->peerMacAddr[0] & 0x1 ) |
| return; |
| |
| #ifdef WLAN_FEATURE_MBSSID |
| WLANSAP_DisassocSta(WLAN_HDD_GET_SAP_CTX_PTR(pAdapter), pDelStaParams); |
| #else |
| WLANSAP_DisassocSta(pVosContext, pDelStaParams); |
| #endif |
| } |
| |
| void hdd_softap_tkip_mic_fail_counter_measure(hdd_adapter_t *pAdapter,v_BOOL_t enable) |
| { |
| #ifndef WLAN_FEATURE_MBSSID |
| v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext; |
| #endif |
| |
| ENTER(); |
| |
| hddLog( LOGE, "hdd_softap_tkip_mic_fail_counter_measure:(%pK, false)", (WLAN_HDD_GET_CTX(pAdapter))->pvosContext); |
| |
| #ifdef WLAN_FEATURE_MBSSID |
| WLANSAP_SetCounterMeasure(WLAN_HDD_GET_SAP_CTX_PTR(pAdapter), (v_BOOL_t)enable); |
| #else |
| WLANSAP_SetCounterMeasure(pVosContext, (v_BOOL_t)enable); |
| #endif |
| } |
| |
| /**--------------------------------------------------------------------------- |
| * |
| * \brief hdd_get__concurrency_mode() - |
| * |
| * |
| * \param - None |
| * |
| * \return - CONCURRENCY MODE |
| * |
| * --------------------------------------------------------------------------*/ |
| tVOS_CONCURRENCY_MODE hdd_get_concurrency_mode ( void ) |
| { |
| v_CONTEXT_t pVosContext = vos_get_global_context( VOS_MODULE_ID_HDD, NULL ); |
| hdd_context_t *pHddCtx; |
| |
| if (NULL != pVosContext) |
| { |
| pHddCtx = vos_get_context( VOS_MODULE_ID_HDD, pVosContext); |
| if (NULL != pHddCtx) |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s: concurrency_mode = 0x%x", __func__, |
| pHddCtx->concurrency_mode); |
| return (tVOS_CONCURRENCY_MODE)pHddCtx->concurrency_mode; |
| } |
| } |
| |
| /* we are in an invalid state :( */ |
| hddLog(LOGE, "%s: Invalid context", __func__); |
| return VOS_STA; |
| } |
| |
| /* Decides whether to send suspend notification to Riva |
| * if any adapter is in BMPS; then it is required */ |
| v_BOOL_t hdd_is_suspend_notify_allowed(hdd_context_t* pHddCtx) |
| { |
| tPmcState pmcState = pmcGetPmcState(pHddCtx->hHal); |
| hdd_config_t *pConfig = pHddCtx->cfg_ini; |
| |
| if (pConfig->fIsBmpsEnabled && (pmcState == BMPS)) |
| { |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| void wlan_hdd_set_concurrency_mode(hdd_context_t *pHddCtx, tVOS_CON_MODE mode) |
| { |
| switch (mode) { |
| case VOS_STA_MODE: |
| case VOS_P2P_CLIENT_MODE: |
| case VOS_P2P_GO_MODE: |
| case VOS_STA_SAP_MODE: |
| pHddCtx->concurrency_mode |= (1 << mode); |
| pHddCtx->no_of_open_sessions[mode]++; |
| break; |
| default: |
| break; |
| } |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("concurrency_mode = 0x%x, Number of open sessions for mode %d = %d"), |
| pHddCtx->concurrency_mode, mode, |
| pHddCtx->no_of_open_sessions[mode]); |
| |
| hdd_wlan_green_ap_start_bss(pHddCtx); |
| } |
| |
| |
| void wlan_hdd_clear_concurrency_mode(hdd_context_t *pHddCtx, tVOS_CON_MODE mode) |
| { |
| switch (mode) { |
| case VOS_STA_MODE: |
| case VOS_P2P_CLIENT_MODE: |
| case VOS_P2P_GO_MODE: |
| case VOS_STA_SAP_MODE: |
| pHddCtx->no_of_open_sessions[mode]--; |
| if (!(pHddCtx->no_of_open_sessions[mode])) |
| pHddCtx->concurrency_mode &= (~(1 << mode)); |
| break; |
| default: |
| break; |
| } |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("concurrency_mode = 0x%x, Number of open sessions for mode %d = %d"), |
| pHddCtx->concurrency_mode, mode, |
| pHddCtx->no_of_open_sessions[mode]); |
| |
| hdd_wlan_green_ap_start_bss(pHddCtx); |
| } |
| |
| /**--------------------------------------------------------------------------- |
| * |
| * \brief wlan_hdd_incr_active_session() |
| * |
| * This function increments the number of active sessions |
| * maintained per device mode |
| * Incase of STA/P2P CLI/IBSS upon connection indication it is incremented |
| * Incase of SAP/P2P GO upon bss start it is incremented |
| * |
| * \param pHddCtx - HDD Context |
| * \param mode - device mode |
| * |
| * \return - None |
| * |
| * --------------------------------------------------------------------------*/ |
| void wlan_hdd_incr_active_session(hdd_context_t *pHddCtx, tVOS_CON_MODE mode) |
| { |
| switch (mode) { |
| case VOS_STA_MODE: |
| case VOS_P2P_CLIENT_MODE: |
| case VOS_P2P_GO_MODE: |
| case VOS_STA_SAP_MODE: |
| pHddCtx->no_of_active_sessions[mode]++; |
| break; |
| default: |
| break; |
| } |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("No.# of active sessions for mode %d = %d"), |
| mode, |
| pHddCtx->no_of_active_sessions[mode]); |
| } |
| |
| /**--------------------------------------------------------------------------- |
| * |
| * \brief wlan_hdd_decr_active_session() |
| * |
| * This function decrements the number of active sessions |
| * maintained per device mode |
| * Incase of STA/P2P CLI/IBSS upon disconnection it is decremented |
| * Incase of SAP/P2P GO upon bss stop it is decremented |
| * |
| * \param pHddCtx - HDD Context |
| * \param mode - device mode |
| * |
| * \return - None |
| * |
| * --------------------------------------------------------------------------*/ |
| void wlan_hdd_decr_active_session(hdd_context_t *pHddCtx, tVOS_CON_MODE mode) |
| { |
| switch (mode) { |
| case VOS_STA_MODE: |
| case VOS_P2P_CLIENT_MODE: |
| case VOS_P2P_GO_MODE: |
| case VOS_STA_SAP_MODE: |
| if (pHddCtx->no_of_active_sessions[mode]) |
| pHddCtx->no_of_active_sessions[mode]--; |
| break; |
| default: |
| break; |
| } |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("No.# of active sessions for mode %d = %d"), |
| mode, |
| pHddCtx->no_of_active_sessions[mode]); |
| } |
| |
| /** |
| * wlan_hdd_get_active_session_count() - get active session |
| * connection count |
| * @hdd_ctx: Pointer to hdd context |
| * |
| * Return: count of active connections |
| */ |
| uint8_t wlan_hdd_get_active_session_count(hdd_context_t *hdd_ctx) |
| { |
| uint8_t i = 0; |
| uint8_t count = 0; |
| |
| for (i = 0; i < VOS_MAX_NO_OF_MODE; i++) { |
| count += hdd_ctx->no_of_active_sessions[i]; |
| } |
| return count; |
| } |
| |
| /** |
| * wlan_hdd_update_txrx_chain_mask() - updates the TX/RX chain |
| * mask to FW |
| * @hdd_ctx: Pointer to hdd context |
| * @chain_mask : Vlaue of the chain_mask to be updated |
| * |
| * Return: 0 for success non-zero for failure |
| */ |
| int wlan_hdd_update_txrx_chain_mask(hdd_context_t *hdd_ctx, |
| uint8_t chain_mask) |
| { |
| int ret; |
| |
| if (hdd_ctx->per_band_chainmask_supp == 1) { |
| ret = process_wma_set_command(0, |
| WMI_PDEV_PARAM_RX_CHAIN_MASK_2G, |
| chain_mask, PDEV_CMD); |
| if (0 != ret) { |
| hddLog(LOGE, FL("Failed to set 2G RX chain mask: %d"), |
| chain_mask); |
| return -EFAULT; |
| } |
| ret = process_wma_set_command(0, |
| WMI_PDEV_PARAM_TX_CHAIN_MASK_2G, |
| chain_mask, PDEV_CMD); |
| if (0 != ret) { |
| hddLog(LOGE, FL("Failed to set 2G TX chain mask: %d"), |
| chain_mask); |
| return -EFAULT; |
| } |
| |
| ret = process_wma_set_command(0, |
| WMI_PDEV_PARAM_RX_CHAIN_MASK_5G, |
| chain_mask, PDEV_CMD); |
| if (0 != ret) { |
| hddLog(LOGE, FL("Failed to set 5G RX chain mask: %d"), |
| chain_mask); |
| return -EFAULT; |
| } |
| ret = process_wma_set_command(0, |
| WMI_PDEV_PARAM_TX_CHAIN_MASK_5G, |
| chain_mask, PDEV_CMD); |
| if (0 != ret) { |
| hddLog(LOGE, FL("Failed to set 5G TX chain mask: %d"), |
| chain_mask); |
| return -EFAULT; |
| } |
| } else { |
| ret = process_wma_set_command(0, |
| WMI_PDEV_PARAM_RX_CHAIN_MASK, |
| chain_mask, PDEV_CMD); |
| if (0 != ret) { |
| hddLog(LOGE, FL("Failed to set RX chain mask: %d"), |
| chain_mask); |
| return -EFAULT; |
| } |
| |
| ret = process_wma_set_command(0, |
| WMI_PDEV_PARAM_TX_CHAIN_MASK, |
| chain_mask, PDEV_CMD); |
| if (0 != ret) { |
| hddLog(LOGE, FL("Failed to set TX chain mask: %d"), |
| chain_mask); |
| return -EFAULT; |
| } |
| } |
| |
| hddLog(LOG1, FL("Sucessfully updated the TX/RX chain mask to: %d"), |
| chain_mask); |
| return 0; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| * |
| * \brief wlan_hdd_framework_restart |
| * |
| * This function uses a cfg80211 API to start a framework initiated WLAN |
| * driver module unload/load. |
| * |
| * Also this API keep retrying (WLAN_HDD_RESTART_RETRY_MAX_CNT). |
| * |
| * |
| * \param - pHddCtx |
| * |
| * \return - VOS_STATUS_SUCCESS: Success |
| * VOS_STATUS_E_EMPTY: Adapter is Empty |
| * VOS_STATUS_E_NOMEM: No memory |
| |
| * --------------------------------------------------------------------------*/ |
| |
| static VOS_STATUS wlan_hdd_framework_restart(hdd_context_t *pHddCtx) |
| { |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| int len = (sizeof (struct ieee80211_mgmt)); |
| struct ieee80211_mgmt *mgmt = NULL; |
| |
| /* Prepare the DEAUTH management frame with reason code */ |
| mgmt = kzalloc(len, GFP_KERNEL); |
| if(mgmt == NULL) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, |
| "%s: memory allocation failed (%d bytes)", __func__, len); |
| return VOS_STATUS_E_NOMEM; |
| } |
| mgmt->u.deauth.reason_code = WLAN_REASON_DISASSOC_LOW_ACK; |
| |
| /* Iterate over all adapters/devices */ |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| if ((NULL == pAdapterNode) || (VOS_STATUS_SUCCESS != status)) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| FL("fail to get adapter: %pK %d"), pAdapterNode, status); |
| goto end; |
| } |
| do |
| { |
| if(pAdapterNode->pAdapter && |
| WLAN_HDD_ADAPTER_MAGIC == pAdapterNode->pAdapter->magic) { |
| hddLog(LOGP, |
| "restarting the driver(intf:\'%s\' mode:%s(%d) :try %d)", |
| pAdapterNode->pAdapter->dev->name, |
| hdd_device_mode_to_string(pAdapterNode->pAdapter->device_mode), |
| pAdapterNode->pAdapter->device_mode, |
| pHddCtx->hdd_restart_retries + 1); |
| /* |
| * CFG80211 event to restart the driver |
| * |
| * 'cfg80211_send_unprot_deauth' sends a |
| * NL80211_CMD_UNPROT_DEAUTHENTICATE event to supplicant at any state |
| * of SME(Linux Kernel) state machine. |
| * |
| * Reason code WLAN_REASON_DISASSOC_LOW_ACK is currently used to restart |
| * the driver. |
| * |
| */ |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)) || defined(WITH_BACKPORTS) |
| cfg80211_rx_unprot_mlme_mgmt(pAdapterNode->pAdapter->dev, |
| (u_int8_t*)mgmt, len); |
| #else |
| cfg80211_send_unprot_deauth(pAdapterNode->pAdapter->dev, |
| (u_int8_t*)mgmt, len); |
| #endif |
| } |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } while((NULL != pAdapterNode) && (VOS_STATUS_SUCCESS == status)); |
| |
| end: |
| /* Free the allocated management frame */ |
| kfree(mgmt); |
| |
| /* Retry until we unload or reach max count */ |
| if(++pHddCtx->hdd_restart_retries < WLAN_HDD_RESTART_RETRY_MAX_CNT) |
| vos_timer_start(&pHddCtx->hdd_restart_timer, WLAN_HDD_RESTART_RETRY_DELAY_MS); |
| |
| return status; |
| |
| } |
| /**--------------------------------------------------------------------------- |
| * |
| * \brief wlan_hdd_restart_timer_cb |
| * |
| * Restart timer callback. An internal function. |
| * |
| * \param - User data: |
| * |
| * \return - None |
| * |
| * --------------------------------------------------------------------------*/ |
| |
| void wlan_hdd_restart_timer_cb(v_PVOID_t usrDataForCallback) |
| { |
| hdd_context_t *pHddCtx = usrDataForCallback; |
| wlan_hdd_framework_restart(pHddCtx); |
| return; |
| |
| } |
| |
| |
| /**--------------------------------------------------------------------------- |
| * |
| * \brief wlan_hdd_restart_driver |
| * |
| * This function sends an event to supplicant to restart the WLAN driver. |
| * |
| * This function is called from vos_wlanRestart. |
| * |
| * \param - pHddCtx |
| * |
| * \return - VOS_STATUS_SUCCESS: Success |
| * VOS_STATUS_E_EMPTY: Adapter is Empty |
| * VOS_STATUS_E_ALREADY: Request already in progress |
| |
| * --------------------------------------------------------------------------*/ |
| VOS_STATUS wlan_hdd_restart_driver(hdd_context_t *pHddCtx) |
| { |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| |
| /* A tight check to make sure reentrancy */ |
| if(atomic_xchg(&pHddCtx->isRestartInProgress, 1)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "%s: WLAN restart is already in progress", __func__); |
| |
| return VOS_STATUS_E_ALREADY; |
| } |
| /* Send reset FIQ to WCNSS to invoke SSR. */ |
| #ifdef HAVE_WCNSS_RESET_INTR |
| wcnss_reset_intr(); |
| #endif |
| |
| return status; |
| } |
| |
| /* |
| * API to find if there is any STA or P2P-Client is connected |
| */ |
| VOS_STATUS hdd_issta_p2p_clientconnected(hdd_context_t *pHddCtx) |
| { |
| return sme_isSta_p2p_clientConnected(pHddCtx->hHal); |
| } |
| |
| #ifdef FEATURE_WLAN_CH_AVOID |
| /** |
| * hdd_find_prefd_safe_chnl - Finds safe channel within preferred channel |
| * @hdd_ctxt: hdd context pointer |
| * @ap_adapter: hdd hostapd adapter pointer |
| * |
| * If auto channel selection enabled: |
| * Preferred and safe channel should be used |
| * If no overlapping, preferred channel should be used |
| * |
| * Return: |
| * 1: found preferred safe channel |
| * 0: could not found preferred safe channel |
| */ |
| |
| uint8_t hdd_find_prefd_safe_chnl(hdd_context_t *hdd_ctxt, |
| hdd_adapter_t *ap_adapter) |
| { |
| uint16_t safe_channels[NUM_20MHZ_RF_CHANNELS]; |
| uint16_t safe_channel_count; |
| uint16_t unsafe_channel_count; |
| uint8_t is_unsafe = 1; |
| uint16_t i; |
| uint16_t channel_loop; |
| |
| if (!hdd_ctxt || !ap_adapter) { |
| hddLog(LOGE, "%s : Invalid arguments: hdd_ctxt=%pK, ap_adapter=%pK", |
| __func__, hdd_ctxt, ap_adapter); |
| return 0; |
| } |
| |
| safe_channel_count = 0; |
| unsafe_channel_count = VOS_MIN((uint16_t)hdd_ctxt->unsafe_channel_count, |
| (uint16_t)NUM_20MHZ_RF_CHANNELS); |
| |
| for (i = 0; i < NUM_20MHZ_RF_CHANNELS; i++) { |
| is_unsafe = 0; |
| for (channel_loop = 0; |
| channel_loop < unsafe_channel_count; channel_loop++) { |
| if (rfChannels[i].channelNum == |
| hdd_ctxt->unsafe_channel_list[channel_loop]) { |
| is_unsafe = 1; |
| break; |
| } |
| } |
| if (!is_unsafe) { |
| safe_channels[safe_channel_count] = rfChannels[i].channelNum; |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, |
| "safe channel %d", safe_channels[safe_channel_count]); |
| safe_channel_count++; |
| } |
| } |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, |
| "perferred range %d - %d", |
| ap_adapter->sessionCtx.ap.sapConfig.acs_cfg.start_ch, |
| ap_adapter->sessionCtx.ap.sapConfig.acs_cfg.end_ch); |
| for (i = 0; i < safe_channel_count; i++) { |
| if ((safe_channels[i] >= |
| ap_adapter->sessionCtx.ap.sapConfig.acs_cfg.start_ch) && |
| (safe_channels[i] <= |
| ap_adapter->sessionCtx.ap.sapConfig.acs_cfg.end_ch)) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, |
| "safe channel %d is in perferred range", safe_channels[i]); |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_unsafe_channel_restart_sap - restart sap if sap is on unsafe channel |
| * @hdd_ctx: hdd context pointer |
| * |
| * hdd_unsafe_channel_restart_sap check all unsafe channel list |
| * and if ACS is enabled, driver will ask userspace to restart the |
| * sap. User space on LTE coex indication restart driver. |
| * |
| * Return - none |
| */ |
| void hdd_unsafe_channel_restart_sap(hdd_context_t *hdd_ctx) |
| { |
| hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL; |
| eRfChannels channel_loop; |
| hdd_adapter_t *adapter; |
| VOS_STATUS status; |
| |
| status = hdd_get_front_adapter(hdd_ctx, &adapter_node); |
| while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) { |
| adapter = adapter_node->pAdapter; |
| |
| if (!(adapter && (WLAN_HDD_SOFTAP == adapter->device_mode))) { |
| status = hdd_get_next_adapter(hdd_ctx, adapter_node, |
| &next); |
| adapter_node = next; |
| continue; |
| } |
| /* |
| * If auto channel select is enabled |
| * preferred channel is in safe channel, |
| * re-start softap interface with safe channel. |
| * no overlap with preferred channel and safe channel |
| * do not re-start softap interface |
| * stay current operating channel. |
| */ |
| if ((adapter->sessionCtx.ap.sapConfig.acs_cfg.acs_mode) && |
| (!hdd_find_prefd_safe_chnl(hdd_ctx, adapter))) |
| return; |
| |
| hddLog(LOG1, FL("Current operation channel %d"), |
| adapter->sessionCtx.ap.operatingChannel); |
| |
| for (channel_loop = 0; |
| channel_loop < hdd_ctx->unsafe_channel_count; |
| channel_loop++) { |
| if (((hdd_ctx->unsafe_channel_list[channel_loop] == |
| adapter->sessionCtx.ap.operatingChannel)) && |
| (false == hdd_ctx->is_ch_avoid_in_progress) && |
| (adapter->sessionCtx.ap.sapConfig.acs_cfg. |
| acs_mode == true)) { |
| hdd_change_ch_avoidance_status(hdd_ctx, true); |
| |
| vos_flush_work( |
| &hdd_ctx->sap_start_work); |
| |
| /* |
| * current operating channel |
| * is un-safe channel, restart driver |
| */ |
| hddLog(LOGE, |
| FL("Restarting SAP due to unsafe channel")); |
| |
| /* SAP restart due to unsafe channel. While |
| * restarting the SAP, makee sure to clear |
| * acs_channel, channel to reset to 0. |
| * Otherwise these settings will override the |
| * ACS while restart. |
| */ |
| hdd_ctx->acs_policy.acs_channel = |
| AUTO_CHANNEL_SELECT; |
| adapter->sessionCtx.ap.sapConfig.channel = |
| AUTO_CHANNEL_SELECT; |
| |
| hddLog(LOG1, FL("set sapConfig.channel to %d"), |
| AUTO_CHANNEL_SELECT); |
| |
| wlan_hdd_send_svc_nlink_msg( |
| hdd_ctx->radio_index, |
| WLAN_SVC_LTE_COEX_IND, |
| NULL, |
| 0); |
| |
| hddLog(LOG1, FL("driver to start sap: %d"), |
| hdd_ctx->cfg_ini->sap_internal_restart); |
| if (hdd_ctx->cfg_ini->sap_internal_restart) { |
| wlan_hdd_netif_queue_control(adapter, |
| WLAN_NETIF_TX_DISABLE, |
| WLAN_CONTROL_PATH); |
| schedule_work( |
| &hdd_ctx->sap_start_work); |
| } |
| else |
| hdd_hostapd_stop(adapter->dev); |
| |
| return; |
| } |
| } |
| status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next); |
| adapter_node = next; |
| } |
| return; |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief hdd_ch_avoid_cb() - |
| |
| Avoid channel notification from FW handler. |
| FW will send un-safe channel list to avoid over wrapping. |
| hostapd should not use notified channel |
| |
| \param - pAdapter HDD adapter pointer |
| indParam channel avoid notification parameter |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| void hdd_ch_avoid_cb |
| ( |
| void *hdd_context, |
| void *indi_param |
| ) |
| { |
| hdd_context_t *hdd_ctxt; |
| tSirChAvoidIndType *ch_avoid_indi; |
| v_U8_t range_loop; |
| eRfChannels channel_loop, start_channel_idx = INVALID_RF_CHANNEL, |
| end_channel_idx = INVALID_RF_CHANNEL; |
| v_U16_t start_channel; |
| v_U16_t end_channel; |
| v_CONTEXT_t vos_context; |
| tHddAvoidFreqList hdd_avoid_freq_list; |
| tANI_U32 i; |
| |
| /* Basic sanity */ |
| if (!hdd_context || !indi_param) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s : Invalid arguments", __func__); |
| return; |
| } |
| |
| hdd_ctxt = (hdd_context_t *)hdd_context; |
| ch_avoid_indi = (tSirChAvoidIndType *)indi_param; |
| vos_context = hdd_ctxt->pvosContext; |
| |
| /* Make unsafe channel list */ |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s : band count %d", |
| __func__, ch_avoid_indi->avoid_range_count); |
| |
| /* generate vendor specific event */ |
| vos_mem_zero((void *)&hdd_avoid_freq_list, sizeof(tHddAvoidFreqList)); |
| for (i = 0; i < ch_avoid_indi->avoid_range_count; i++) |
| { |
| hdd_avoid_freq_list.avoidFreqRange[i].startFreq = |
| ch_avoid_indi->avoid_freq_range[i].start_freq; |
| hdd_avoid_freq_list.avoidFreqRange[i].endFreq = |
| ch_avoid_indi->avoid_freq_range[i].end_freq; |
| } |
| hdd_avoid_freq_list.avoidFreqRangeCount = ch_avoid_indi->avoid_range_count; |
| |
| |
| /* clear existing unsafe channel cache */ |
| hdd_ctxt->unsafe_channel_count = 0; |
| vos_mem_zero(hdd_ctxt->unsafe_channel_list, |
| sizeof(hdd_ctxt->unsafe_channel_list)); |
| |
| for (range_loop = 0; range_loop < ch_avoid_indi->avoid_range_count; |
| range_loop++) { |
| if (hdd_ctxt->unsafe_channel_count >= NUM_20MHZ_RF_CHANNELS) { |
| hddLog(LOGW, FL("LTE Coex unsafe channel list full")); |
| break; |
| } |
| |
| start_channel = ieee80211_frequency_to_channel( |
| ch_avoid_indi->avoid_freq_range[range_loop].start_freq); |
| end_channel = ieee80211_frequency_to_channel( |
| ch_avoid_indi->avoid_freq_range[range_loop].end_freq); |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s : start %d : %d, end %d : %d", __func__, |
| ch_avoid_indi->avoid_freq_range[range_loop].start_freq, |
| start_channel, |
| ch_avoid_indi->avoid_freq_range[range_loop].end_freq, |
| end_channel); |
| |
| /* do not process frequency bands that are not mapped to predefined |
| * channels |
| */ |
| if (start_channel == 0 || end_channel == 0) |
| continue; |
| |
| for (channel_loop = MIN_20MHZ_RF_CHANNEL; channel_loop <= |
| MAX_20MHZ_RF_CHANNEL; channel_loop++) { |
| if (rfChannels[channel_loop].targetFreq >= |
| ch_avoid_indi->avoid_freq_range[range_loop].start_freq) { |
| start_channel_idx = channel_loop; |
| break; |
| } |
| } |
| for (channel_loop = MIN_20MHZ_RF_CHANNEL; channel_loop <= |
| MAX_20MHZ_RF_CHANNEL; channel_loop++) { |
| if (rfChannels[channel_loop].targetFreq >= |
| ch_avoid_indi->avoid_freq_range[range_loop].end_freq) { |
| end_channel_idx = channel_loop; |
| if (rfChannels[channel_loop].targetFreq > |
| ch_avoid_indi->avoid_freq_range[range_loop].end_freq) |
| end_channel_idx--; |
| break; |
| } |
| } |
| |
| if (start_channel_idx == INVALID_RF_CHANNEL || |
| end_channel_idx == INVALID_RF_CHANNEL) |
| continue; |
| |
| for (channel_loop = start_channel_idx; channel_loop <= |
| end_channel_idx; channel_loop++) { |
| hdd_ctxt->unsafe_channel_list[hdd_ctxt->unsafe_channel_count++] |
| = rfChannels[channel_loop].channelNum; |
| if (hdd_ctxt->unsafe_channel_count >= NUM_20MHZ_RF_CHANNELS) { |
| hddLog(LOGW, FL("LTE Coex unsafe channel list full")); |
| break; |
| } |
| } |
| } |
| |
| #ifdef CONFIG_CNSS |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s : number of unsafe channels is %d ", |
| __func__, hdd_ctxt->unsafe_channel_count); |
| |
| if (vos_set_wlan_unsafe_channel(hdd_ctxt->unsafe_channel_list, |
| hdd_ctxt->unsafe_channel_count)) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Failed to set unsafe channel", |
| __func__); |
| |
| /* clear existing unsafe channel cache */ |
| hdd_ctxt->unsafe_channel_count = 0; |
| vos_mem_zero(hdd_ctxt->unsafe_channel_list, |
| sizeof(v_U16_t) * NUM_20MHZ_RF_CHANNELS); |
| |
| return; |
| } |
| |
| for (channel_loop = 0; |
| channel_loop < hdd_ctxt->unsafe_channel_count; |
| channel_loop++) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: channel %d is not safe ", __func__, |
| hdd_ctxt->unsafe_channel_list[channel_loop]); |
| } |
| #endif |
| |
| /* |
| * first update the unsafe channel list to the platform driver and |
| * send the avoid freq event to the application |
| */ |
| wlan_hdd_send_avoid_freq_event(hdd_ctxt, &hdd_avoid_freq_list); |
| |
| if (0 == hdd_ctxt->unsafe_channel_count) |
| return; |
| hdd_unsafe_channel_restart_sap(hdd_ctxt); |
| return; |
| } |
| #endif /* FEATURE_WLAN_CH_AVOID */ |
| |
| #ifdef WLAN_FEATURE_LPSS |
| int wlan_hdd_gen_wlan_status_pack(struct wlan_status_data *data, |
| hdd_adapter_t *pAdapter, |
| hdd_station_ctx_t *pHddStaCtx, |
| v_U8_t is_on, |
| v_U8_t is_connected) |
| { |
| hdd_context_t *pHddCtx = NULL; |
| tANI_U8 buflen = WLAN_SVC_COUNTRY_CODE_LEN; |
| |
| if (!data) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: invalid data pointer", __func__); |
| return (-1); |
| } |
| if (!pAdapter) { |
| if (is_on) { |
| /* no active interface */ |
| data->lpss_support = 0; |
| data->is_on = is_on; |
| return 0; |
| } |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: invalid adapter pointer", __func__); |
| return (-1); |
| } |
| |
| pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| if (pHddCtx->lpss_support && pHddCtx->cfg_ini->enablelpasssupport) |
| data->lpss_support = 1; |
| else |
| data->lpss_support = 0; |
| data->numChannels = WLAN_SVC_MAX_NUM_CHAN; |
| sme_GetCfgValidChannels(pHddCtx->hHal, data->channel_list, |
| &data->numChannels); |
| sme_GetCountryCode(pHddCtx->hHal, data->country_code, &buflen); |
| data->is_on = is_on; |
| data->vdev_id = pAdapter->sessionId; |
| data->vdev_mode = pAdapter->device_mode; |
| if (pHddStaCtx) { |
| data->is_connected = is_connected; |
| data->rssi = pAdapter->rssi; |
| data->freq = vos_chan_to_freq(pHddStaCtx->conn_info.operationChannel); |
| if (WLAN_SVC_MAX_SSID_LEN >= pHddStaCtx->conn_info.SSID.SSID.length) { |
| data->ssid_len = pHddStaCtx->conn_info.SSID.SSID.length; |
| memcpy(data->ssid, |
| pHddStaCtx->conn_info.SSID.SSID.ssId, |
| pHddStaCtx->conn_info.SSID.SSID.length); |
| } |
| if (WLAN_SVC_MAX_BSSID_LEN >= sizeof(pHddStaCtx->conn_info.bssId)) |
| memcpy(data->bssid, |
| pHddStaCtx->conn_info.bssId, |
| sizeof(pHddStaCtx->conn_info.bssId)); |
| } |
| return 0; |
| } |
| |
| int wlan_hdd_gen_wlan_version_pack(struct wlan_version_data *data, |
| v_U32_t fw_version, |
| v_U32_t chip_id, |
| const char *chip_name) |
| { |
| if (!data) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: invalid data pointer", __func__); |
| return (-1); |
| } |
| |
| data->chip_id = chip_id; |
| strlcpy(data->chip_name, chip_name, WLAN_SVC_MAX_STR_LEN); |
| if (strncmp(chip_name, "Unknown", 7)) |
| strlcpy(data->chip_from, "Qualcomm", WLAN_SVC_MAX_STR_LEN); |
| else |
| strlcpy(data->chip_from, "Unknown", WLAN_SVC_MAX_STR_LEN); |
| strlcpy(data->host_version, QWLAN_VERSIONSTR, WLAN_SVC_MAX_STR_LEN); |
| scnprintf(data->fw_version, WLAN_SVC_MAX_STR_LEN, "%d.%d.%d.%d", |
| (fw_version & 0xf0000000) >> 28, |
| (fw_version & 0xf000000) >> 24, |
| (fw_version & 0xf00000) >> 20, |
| (fw_version & 0x7fff)); |
| return 0; |
| } |
| #endif |
| |
| #if defined(FEATURE_WLAN_LFR) && defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD) |
| /**--------------------------------------------------------------------------- |
| |
| \brief wlan_hdd_disable_roaming() |
| |
| This function loop through each adapter and disable roaming on each STA |
| device mode except the input adapter. |
| Note: On the input adapter roaming is not enabled yet hence no need to |
| disable. |
| |
| \param - pAdapter HDD adapter pointer |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| void wlan_hdd_disable_roaming(hdd_adapter_t *pAdapter) |
| { |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| hdd_adapter_t *pAdapterIdx = NULL; |
| hdd_adapter_list_node_t *pAdapterNode = NULL; |
| hdd_adapter_list_node_t *pNext = NULL; |
| VOS_STATUS status; |
| |
| if (pHddCtx->cfg_ini->isFastRoamIniFeatureEnabled && |
| pHddCtx->cfg_ini->isRoamOffloadScanEnabled && |
| WLAN_HDD_INFRA_STATION == pAdapter->device_mode && |
| vos_is_sta_active_connection_exists()) { |
| hddLog(LOG1, FL("Connect received on STA sessionId(%d)"), |
| pAdapter->sessionId); |
| /* Loop through adapter and disable roaming for each STA device mode |
| except the input adapter. */ |
| |
| status = hdd_get_front_adapter (pHddCtx, &pAdapterNode); |
| |
| while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) { |
| pAdapterIdx = pAdapterNode->pAdapter; |
| |
| if (WLAN_HDD_INFRA_STATION == pAdapterIdx->device_mode && |
| pAdapter->sessionId != pAdapterIdx->sessionId) { |
| hddLog(LOG1, FL("Disable Roaming on sessionId(%d)"), |
| pAdapterIdx->sessionId); |
| sme_stopRoaming(WLAN_HDD_GET_HAL_CTX(pAdapterIdx), |
| pAdapterIdx->sessionId, 0); |
| } |
| |
| status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext); |
| pAdapterNode = pNext; |
| } |
| } |
| } |
| |
| /**--------------------------------------------------------------------------- |
| |
| \brief wlan_hdd_enable_roaming() |
| |
| This function loop through each adapter and enable roaming on each STA |
| device mode except the input adapter. |
| Note: On the input adapter no need to enable roaming because link got |
| disconnected on this. |
| |
| \param - pAdapter HDD adapter pointer |
| |
| \return - None |
| |
| --------------------------------------------------------------------------*/ |
| void wlan_hdd_enable_roaming(hdd_adapter_t *pAdapter) |
| { |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| hdd_adapter_t *pAdapterIdx = NULL; |
| hdd_adapter_list_node_t *pAdapterNode = NULL; |
| hdd_adapter_list_node_t *pNext = NULL; |
| VOS_STATUS status; |
| |
| if (pHddCtx->cfg_ini->isFastRoamIniFeatureEnabled && |
| pHddCtx->cfg_ini->isRoamOffloadScanEnabled && |
| WLAN_HDD_INFRA_STATION == pAdapter->device_mode && |
| vos_is_sta_active_connection_exists()) { |
| hddLog(LOG1, FL("Disconnect received on STA sessionId(%d)"), |
| pAdapter->sessionId); |
| /* Loop through adapter and enable roaming for each STA device mode |
| except the input adapter. */ |
| |
| status = hdd_get_front_adapter (pHddCtx, &pAdapterNode); |
| |
| while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) { |
| pAdapterIdx = pAdapterNode->pAdapter; |
| |
| if (WLAN_HDD_INFRA_STATION == pAdapterIdx->device_mode && |
| pAdapter->sessionId != pAdapterIdx->sessionId) { |
| hddLog(LOG1, FL("Enabling Roaming on sessionId(%d)"), |
| pAdapterIdx->sessionId); |
| sme_startRoaming(WLAN_HDD_GET_HAL_CTX(pAdapterIdx), |
| pAdapterIdx->sessionId, |
| REASON_CONNECT); |
| } |
| |
| status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext); |
| pAdapterNode = pNext; |
| } |
| } |
| } |
| #endif |
| |
| /** |
| * nl_srv_bcast_svc() - Wrapper function to send bcast msgs to SVC mcast group |
| * @skb: sk buffer pointer |
| * |
| * Sends the bcast message to SVC multicast group with generic nl socket |
| * if CNSS_GENL is enabled. Else, use the legacy netlink socket to send. |
| * |
| * Return: None |
| */ |
| static void nl_srv_bcast_svc(struct sk_buff *skb) |
| { |
| #ifdef CNSS_GENL |
| nl_srv_bcast(skb, CLD80211_MCGRP_SVC_MSGS, WLAN_NL_MSG_SVC); |
| #else |
| nl_srv_bcast(skb); |
| #endif |
| } |
| |
| void wlan_hdd_send_svc_nlink_msg(int radio, int type, void *data, int len) |
| { |
| struct sk_buff *skb; |
| struct nlmsghdr *nlh; |
| tAniMsgHdr *ani_hdr; |
| void *nl_data = NULL; |
| int flags = GFP_KERNEL; |
| struct radio_index_tlv *radio_info; |
| int tlv_len; |
| |
| |
| if (in_interrupt() || irqs_disabled() || in_atomic()) |
| flags = GFP_ATOMIC; |
| |
| skb = alloc_skb(NLMSG_SPACE(WLAN_NL_MAX_PAYLOAD), flags); |
| |
| if(skb == NULL) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: alloc_skb failed", __func__); |
| return; |
| } |
| |
| nlh = (struct nlmsghdr *)skb->data; |
| nlh->nlmsg_pid = 0; /* from kernel */ |
| nlh->nlmsg_flags = 0; |
| nlh->nlmsg_seq = 0; |
| nlh->nlmsg_type = WLAN_NL_MSG_SVC; |
| |
| ani_hdr = NLMSG_DATA(nlh); |
| ani_hdr->type = type; |
| |
| switch(type) { |
| case WLAN_SVC_FW_CRASHED_IND: |
| case WLAN_SVC_FW_SHUTDOWN_IND: |
| case WLAN_SVC_LTE_COEX_IND: |
| case WLAN_SVC_WLAN_AUTO_SHUTDOWN_IND: |
| case WLAN_SVC_WLAN_AUTO_SHUTDOWN_CANCEL_IND: |
| ani_hdr->length = 0; |
| nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr))); |
| break; |
| case WLAN_SVC_WLAN_STATUS_IND: |
| case WLAN_SVC_WLAN_VERSION_IND: |
| case WLAN_SVC_DFS_CAC_START_IND: |
| case WLAN_SVC_DFS_CAC_END_IND: |
| case WLAN_SVC_DFS_RADAR_DETECT_IND: |
| case WLAN_SVC_DFS_ALL_CHANNEL_UNAVAIL_IND: |
| case WLAN_SVC_WLAN_TP_IND: |
| case WLAN_SVC_WLAN_TP_TX_IND: |
| case WLAN_SVC_RPS_ENABLE_IND: |
| ani_hdr->length = len; |
| nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + len)); |
| nl_data = (char *)ani_hdr + sizeof(tAniMsgHdr); |
| memcpy(nl_data, data, len); |
| break; |
| default: |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "WLAN SVC: Attempt to send unknown nlink message %d", type); |
| kfree_skb(skb); |
| return; |
| } |
| |
| /* |
| * Add radio index at the end of the svc event in TLV format to maintain |
| * the backward compatibilty with userspace applications. |
| */ |
| |
| tlv_len = 0; |
| |
| if ((sizeof(*ani_hdr) + len + sizeof(struct radio_index_tlv)) |
| < WLAN_NL_MAX_PAYLOAD) { |
| radio_info = (struct radio_index_tlv *)((char *) ani_hdr + |
| sizeof (*ani_hdr) + len); |
| radio_info->type = (unsigned short) WLAN_SVC_WLAN_RADIO_INDEX; |
| radio_info->length = (unsigned short) sizeof(radio_info->radio); |
| radio_info->radio = radio; |
| tlv_len = sizeof(*radio_info); |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "Added radio index tlv - radio index %d", radio_info->radio); |
| } |
| |
| nlh->nlmsg_len += tlv_len; |
| skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr) + len + tlv_len)); |
| |
| nl_srv_bcast_svc(skb); |
| |
| return; |
| } |
| |
| #ifdef WLAN_FEATURE_LPSS |
| void wlan_hdd_send_status_pkg(hdd_adapter_t *pAdapter, |
| hdd_station_ctx_t *pHddStaCtx, |
| v_U8_t is_on, |
| v_U8_t is_connected) |
| { |
| int ret = 0; |
| struct wlan_status_data data; |
| hdd_context_t *hdd_ctx; |
| v_PVOID_t vos_ctx; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) |
| return; |
| |
| memset(&data, 0, sizeof(struct wlan_status_data)); |
| if (is_on) |
| ret = wlan_hdd_gen_wlan_status_pack(&data, pAdapter, pHddStaCtx, |
| is_on, is_connected); |
| if (!ret) { |
| if (pAdapter) { |
| hdd_ctx = WLAN_HDD_GET_CTX(pAdapter); |
| } else { |
| vos_ctx = vos_get_global_context(VOS_MODULE_ID_HDD, NULL); |
| hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx); |
| } |
| |
| if (!hdd_ctx) |
| return; |
| |
| wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, |
| WLAN_SVC_WLAN_STATUS_IND, |
| &data, sizeof(struct wlan_status_data)); |
| } |
| } |
| |
| void wlan_hdd_send_version_pkg(v_U32_t fw_version, |
| v_U32_t chip_id, |
| const char *chip_name) |
| { |
| int ret = 0; |
| struct wlan_version_data data; |
| v_PVOID_t vos_ctx = vos_get_global_context(VOS_MODULE_ID_HDD, NULL); |
| hdd_context_t *hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) |
| return; |
| |
| if (!hdd_ctx) |
| return; |
| |
| memset(&data, 0, sizeof(struct wlan_version_data)); |
| ret = wlan_hdd_gen_wlan_version_pack(&data, fw_version, chip_id, |
| chip_name); |
| if (!ret) |
| wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, |
| WLAN_SVC_WLAN_VERSION_IND, |
| &data, sizeof(struct wlan_version_data)); |
| } |
| |
| void wlan_hdd_send_all_scan_intf_info(hdd_context_t *pHddCtx) |
| { |
| hdd_adapter_t *pDataAdapter = NULL; |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| v_BOOL_t scan_intf_found = VOS_FALSE; |
| VOS_STATUS status; |
| |
| if (!pHddCtx) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: NULL pointer for pHddCtx", |
| __func__); |
| return; |
| } |
| |
| status = hdd_get_front_adapter(pHddCtx, &pAdapterNode); |
| while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) { |
| pDataAdapter = pAdapterNode->pAdapter; |
| if (pDataAdapter) { |
| if (pDataAdapter->device_mode == WLAN_HDD_INFRA_STATION || |
| pDataAdapter->device_mode == WLAN_HDD_P2P_CLIENT || |
| pDataAdapter->device_mode == WLAN_HDD_P2P_DEVICE) { |
| scan_intf_found = VOS_TRUE; |
| wlan_hdd_send_status_pkg(pDataAdapter, NULL, 1, 0); |
| } |
| } |
| status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext); |
| pAdapterNode = pNext; |
| } |
| |
| if (!scan_intf_found) |
| wlan_hdd_send_status_pkg(pDataAdapter, NULL, 1, 0); |
| } |
| #endif |
| |
| #ifdef FEATURE_WLAN_AUTO_SHUTDOWN |
| v_VOID_t wlan_hdd_auto_shutdown_cb(v_VOID_t) |
| { |
| v_PVOID_t vos_ctx = vos_get_global_context(VOS_MODULE_ID_HDD, NULL); |
| hdd_context_t *hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx); |
| |
| if (!hdd_ctx) |
| return; |
| |
| hddLog(LOGE, FL("%s: Wlan Idle. Sending Shutdown event.."),__func__); |
| wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, |
| WLAN_SVC_WLAN_AUTO_SHUTDOWN_IND, NULL, 0); |
| } |
| |
| void wlan_hdd_auto_shutdown_enable(hdd_context_t *hdd_ctx, v_BOOL_t enable) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| hdd_adapter_t *pAdapter; |
| v_BOOL_t ap_connected = VOS_FALSE, sta_connected = VOS_FALSE; |
| tHalHandle hHal; |
| |
| hHal = hdd_ctx->hHal; |
| if (hHal == NULL) |
| return; |
| |
| if (hdd_ctx->cfg_ini->WlanAutoShutdown == 0) |
| return; |
| |
| if (enable == VOS_FALSE) { |
| if (sme_set_auto_shutdown_timer(hHal, 0) != eHAL_STATUS_SUCCESS) { |
| hddLog(LOGE, FL("Failed to stop wlan auto shutdown timer")); |
| } |
| wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index, |
| WLAN_SVC_WLAN_AUTO_SHUTDOWN_CANCEL_IND, NULL, 0); |
| return; |
| } |
| |
| /* To enable shutdown timer check concurrency */ |
| if (vos_concurrent_open_sessions_running()) { |
| status = hdd_get_front_adapter ( hdd_ctx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) { |
| pAdapter = pAdapterNode->pAdapter; |
| if (pAdapter && pAdapter->device_mode == WLAN_HDD_INFRA_STATION) { |
| if (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)->conn_info.connState |
| == eConnectionState_Associated) { |
| sta_connected = VOS_TRUE; |
| break; |
| } |
| } |
| if (pAdapter && pAdapter->device_mode == WLAN_HDD_SOFTAP) { |
| if(WLAN_HDD_GET_AP_CTX_PTR(pAdapter)->bApActive == VOS_TRUE) { |
| ap_connected = VOS_TRUE; |
| break; |
| } |
| } |
| status = hdd_get_next_adapter ( hdd_ctx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| } |
| |
| if (ap_connected == VOS_TRUE || sta_connected == VOS_TRUE) { |
| hddLog(LOG1, FL("CC Session active. Shutdown timer not enabled")); |
| return; |
| } else { |
| if (sme_set_auto_shutdown_timer(hHal, |
| hdd_ctx->cfg_ini->WlanAutoShutdown) |
| != eHAL_STATUS_SUCCESS) |
| hddLog(LOGE, FL("Failed to start wlan auto shutdown timer")); |
| else |
| hddLog(LOG1, FL("Auto Shutdown timer for %d seconds enabled"), |
| hdd_ctx->cfg_ini->WlanAutoShutdown); |
| |
| } |
| } |
| #endif |
| |
| hdd_adapter_t * hdd_get_con_sap_adapter(hdd_adapter_t *this_sap_adapter, |
| bool check_start_bss) |
| { |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(this_sap_adapter); |
| hdd_adapter_t *pAdapter, *con_sap_adapter; |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| |
| con_sap_adapter = NULL; |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) { |
| pAdapter = pAdapterNode->pAdapter; |
| if (pAdapter && ((pAdapter->device_mode == WLAN_HDD_SOFTAP) || |
| (pAdapter->device_mode == WLAN_HDD_P2P_GO)) && |
| pAdapter != this_sap_adapter) { |
| if (check_start_bss) { |
| if (test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags)) { |
| con_sap_adapter = pAdapter; |
| break; |
| } |
| } else { |
| con_sap_adapter = pAdapter; |
| break; |
| } |
| } |
| status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| return con_sap_adapter; |
| } |
| |
| #ifdef FEATURE_BUS_BANDWIDTH |
| void hdd_start_bus_bw_compute_timer(hdd_adapter_t *pAdapter) |
| { |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| |
| if (VOS_TIMER_STATE_RUNNING == |
| vos_timer_getCurrentState(&pHddCtx->bus_bw_timer)) |
| return; |
| |
| vos_timer_start(&pHddCtx->bus_bw_timer, |
| pHddCtx->cfg_ini->busBandwidthComputeInterval); |
| } |
| |
| void hdd_stop_bus_bw_compute_timer(hdd_adapter_t *pAdapter) |
| { |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| VOS_STATUS status; |
| v_BOOL_t can_stop = VOS_TRUE; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| |
| if (VOS_TIMER_STATE_RUNNING != |
| vos_timer_getCurrentState(&pHddCtx->bus_bw_timer)) { |
| /* trying to stop timer, when not running is not good */ |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "bus band width compute timer is not running"); |
| return; |
| } |
| |
| if (vos_concurrent_open_sessions_running()) { |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) { |
| pAdapter = pAdapterNode->pAdapter; |
| if (pAdapter && (pAdapter->device_mode == WLAN_HDD_INFRA_STATION || |
| pAdapter->device_mode == WLAN_HDD_P2P_CLIENT) && |
| WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)->conn_info.connState |
| == eConnectionState_Associated) { |
| can_stop = VOS_FALSE; |
| break; |
| } |
| if (pAdapter && (pAdapter->device_mode == WLAN_HDD_SOFTAP || |
| pAdapter->device_mode == WLAN_HDD_P2P_GO) && |
| WLAN_HDD_GET_AP_CTX_PTR(pAdapter)->bApActive == VOS_TRUE) { |
| can_stop = VOS_FALSE; |
| break; |
| } |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| } |
| |
| if (can_stop == VOS_TRUE) { |
| vos_timer_stop(&pHddCtx->bus_bw_timer); |
| if (pHddCtx->hbw_requested) { |
| vos_remove_pm_qos(); |
| pHddCtx->hbw_requested = false; |
| } |
| /* reset the ipa perf level */ |
| hdd_ipa_set_perf_level(pHddCtx, 0, 0); |
| hdd_rst_tcp_delack(pHddCtx); |
| tlshim_reset_bundle_require(); |
| tlshim_driver_del_ack_disable(); |
| } |
| } |
| #endif |
| |
| |
| #ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH |
| void wlan_hdd_check_sta_ap_concurrent_ch_intf(void *data) |
| { |
| hdd_adapter_t *ap_adapter = NULL, *sta_adapter = (hdd_adapter_t *)data; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(sta_adapter); |
| tHalHandle hHal; |
| hdd_ap_ctx_t *pHddApCtx; |
| uint16_t intf_ch = 0, vht_channel_width = 0; |
| eCsrBand orig_band, new_band; |
| |
| if ((pHddCtx->cfg_ini->WlanMccToSccSwitchMode == VOS_MCC_TO_SCC_SWITCH_DISABLE) |
| || !(vos_concurrent_open_sessions_running() |
| || !(vos_get_concurrency_mode() == VOS_STA_SAP))) |
| return; |
| |
| ap_adapter = hdd_get_adapter(pHddCtx, WLAN_HDD_SOFTAP); |
| if (ap_adapter == NULL) |
| return; |
| |
| if (!test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags)) |
| return; |
| |
| pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); |
| hHal = WLAN_HDD_GET_HAL_CTX(ap_adapter); |
| |
| if (hHal == NULL) |
| return; |
| |
| intf_ch = WLANSAP_CheckCCIntf(pHddApCtx->sapContext); |
| vht_channel_width = wlan_sap_get_vht_ch_width(pHddApCtx->sapContext); |
| if (intf_ch == 0) |
| return; |
| |
| if (pHddApCtx->sapConfig.band_switch_enable) { |
| if (pHddApCtx->sapConfig.channel > MAX_2_4GHZ_CHANNEL) { |
| orig_band = eCSR_BAND_5G; |
| } else { |
| orig_band = eCSR_BAND_24; |
| } |
| |
| if (intf_ch > MAX_2_4GHZ_CHANNEL) { |
| new_band = eCSR_BAND_5G; |
| } else { |
| new_band = eCSR_BAND_24; |
| } |
| |
| if (orig_band != new_band) { |
| if (new_band == eCSR_BAND_5G) { |
| pHddApCtx->sapConfig.ch_width_orig = |
| pHddApCtx->sapConfig.ch_width_5g_orig; |
| } else { |
| pHddApCtx->sapConfig.ch_width_orig = |
| pHddApCtx->sapConfig.ch_width_24g_orig; |
| } |
| } |
| } |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("SAP restarts due to MCC->SCC switch, orig chan: %d, new chan: %d"), |
| pHddApCtx->sapConfig.channel, intf_ch); |
| |
| pHddApCtx->sapConfig.channel = intf_ch; |
| #ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| if(vos_is_ch_switch_with_csa_enabled()) |
| { |
| struct wlan_sap_csa_info csa_info; |
| |
| csa_info.sta_channel = intf_ch; |
| |
| hddLog(VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: Indicate connected event to HostApd Chan=%d", |
| __func__, csa_info.sta_channel); |
| |
| /*Indicate to HostApd about Station interface state change*/ |
| hdd_sta_state_sap_notify(pHddCtx, STA_NOTIFY_CONNECTED, csa_info); |
| }else{ |
| #endif |
| pHddApCtx->bss_stop_reason = BSS_STOP_DUE_TO_MCC_SCC_SWITCH; |
| sme_SelectCBMode(hHal, |
| pHddApCtx->sapConfig.SapHw_mode, |
| pHddApCtx->sapConfig.channel, |
| pHddApCtx->sapConfig.sec_ch, |
| &vht_channel_width, pHddApCtx->sapConfig.ch_width_orig); |
| wlan_sap_set_vht_ch_width(pHddApCtx->sapContext, vht_channel_width); |
| wlan_hdd_restart_sap(ap_adapter); |
| #ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| } |
| #endif |
| } |
| #endif |
| |
| #ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| |
| void hdd_csa_notify_cb |
| ( |
| void *hdd_context, |
| void *indi_param |
| ) |
| { |
| tpSmeCsaOffloadInd csa_params = NULL; |
| hdd_context_t *hdd_ctxt = NULL; |
| struct wlan_sap_csa_info csa_info; |
| v_U32_t ret = 0; |
| /* Basic sanity */ |
| |
| if(!vos_is_ch_switch_with_csa_enabled()) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s : SAP channel switch with CSA not enabled", __func__); |
| return; |
| } |
| |
| if (!hdd_context || !indi_param) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s : Invalid arguments", __func__); |
| return; |
| } |
| |
| hdd_ctxt = (hdd_context_t *)hdd_context; |
| |
| csa_params = (tpSmeCsaOffloadInd)indi_param; |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s : tbtt count %d Channel = %d", |
| __func__,csa_params->tbtt_count, csa_params->channel); |
| hdd_ctxt->ch_switch_ctx.tbtt_count = csa_params->tbtt_count - 1; /* Will reduce the count by 1, |
| as the switch might take time. |
| Currently of No Use, as this will be |
| override by ini g_sap_chanswitch_beacon_cnt */ |
| csa_info.sta_channel = csa_params->channel; |
| ret = hdd_sta_state_sap_notify(hdd_ctxt, STA_NOTIFY_CSA, csa_info); |
| if(ret != 0) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s : Failed to trigger Channel Switch Ch:%d ret=%d", |
| __func__,csa_params->channel, ret); |
| } |
| } |
| #endif//#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| |
| /** |
| * wlan_hdd_check_custom_con_channel_rules() - This function checks the sap's |
| * and sta's operating channel. |
| * @sta_adapter: Describe the first argument to foobar. |
| * @ap_adapter: Describe the second argument to foobar. |
| * @roam_profile: Roam profile of AP to which STA wants to connect. |
| * @concurrent_chnl_same: If both SAP and STA channels are same then |
| * set this flag to true else false. |
| * |
| * This function checks the sap's operating channel and sta's operating channel. |
| * if both are same then it will return false else it will restart the sap in |
| * sta's channel and return true. |
| * |
| * |
| * Return: VOS_STATUS_SUCCESS or VOS_STATUS_E_FAILURE. |
| */ |
| VOS_STATUS wlan_hdd_check_custom_con_channel_rules(hdd_adapter_t *sta_adapter, |
| hdd_adapter_t *ap_adapter, |
| tCsrRoamProfile *roam_profile, |
| tScanResultHandle *scan_cache, |
| bool *concurrent_chnl_same) |
| { |
| hdd_ap_ctx_t *hdd_ap_ctx; |
| uint8_t channel_id; |
| VOS_STATUS status; |
| device_mode_t device_mode = ap_adapter->device_mode; |
| *concurrent_chnl_same = true; |
| |
| hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); |
| status = |
| sme_get_ap_channel_from_scan_cache(WLAN_HDD_GET_HAL_CTX(sta_adapter), |
| roam_profile, |
| scan_cache, |
| &channel_id); |
| if ((VOS_STATUS_SUCCESS == status)) { |
| if ((WLAN_HDD_SOFTAP == device_mode) && |
| (channel_id < SIR_11A_CHANNEL_BEGIN)) { |
| if (hdd_ap_ctx->operatingChannel != channel_id) { |
| *concurrent_chnl_same = false; |
| hddLog(VOS_TRACE_LEVEL_INFO_MED, |
| FL("channels are different")); |
| } |
| } else if ((WLAN_HDD_P2P_GO == device_mode) && |
| (channel_id >= SIR_11A_CHANNEL_BEGIN)) { |
| if (hdd_ap_ctx->operatingChannel != channel_id) { |
| *concurrent_chnl_same = false; |
| hddLog(VOS_TRACE_LEVEL_INFO_MED, |
| FL("channels are different")); |
| } |
| } |
| } else { |
| /* |
| * Lets handle worst case scenario here, Scan cache lookup is failed |
| * so we have to stop the SAP to avoid any channel discrepancy between |
| * SAP's channel and STA's channel. Return the status as failure so |
| * caller function could know that scan look up is failed. |
| */ |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Finding AP from scan cache failed")); |
| return VOS_STATUS_E_FAILURE; |
| } |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| #ifdef WLAN_FEATURE_MBSSID |
| /** |
| * wlan_hdd_stop_sap() - This function stops bss of SAP. |
| * @ap_adapter: SAP adapter |
| * |
| * This function will process the stopping of sap adapter. |
| * |
| * Return: void. |
| */ |
| void wlan_hdd_stop_sap(hdd_adapter_t *ap_adapter) |
| { |
| hdd_ap_ctx_t *hdd_ap_ctx; |
| hdd_hostapd_state_t *hostapd_state; |
| VOS_STATUS vos_status; |
| hdd_context_t *hdd_ctx; |
| #ifdef CFG80211_DEL_STA_V2 |
| struct station_del_parameters delStaParams; |
| #endif |
| |
| if (NULL == ap_adapter) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| FL("ap_adapter is NULL here")); |
| return; |
| } |
| |
| hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); |
| hdd_ctx = WLAN_HDD_GET_CTX(ap_adapter); |
| if (0 != wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| |
| mutex_lock(&hdd_ctx->sap_lock); |
| if (test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags)) { |
| #ifdef CFG80211_DEL_STA_V2 |
| delStaParams.mac = NULL; |
| delStaParams.subtype = SIR_MAC_MGMT_DEAUTH >> 4; |
| delStaParams.reason_code = eCsrForcedDeauthSta; |
| wlan_hdd_cfg80211_del_station(ap_adapter->wdev.wiphy, ap_adapter->dev, |
| &delStaParams); |
| #else |
| wlan_hdd_cfg80211_del_station(ap_adapter->wdev.wiphy, ap_adapter->dev, |
| NULL); |
| #endif |
| hdd_cleanup_actionframe(hdd_ctx, ap_adapter); |
| hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(ap_adapter); |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, |
| FL("Now doing SAP STOPBSS")); |
| if (VOS_STATUS_SUCCESS == WLANSAP_StopBss(hdd_ap_ctx->sapContext)) { |
| vos_status = vos_wait_single_event(&hostapd_state->stop_bss_event, |
| 10000); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) { |
| mutex_unlock(&hdd_ctx->sap_lock); |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| FL("SAP Stop Failed")); |
| return; |
| } |
| } |
| clear_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags); |
| wlan_hdd_decr_active_session(hdd_ctx, ap_adapter->device_mode); |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, |
| FL("SAP Stop Success")); |
| if (hdd_ctx->cfg_ini->apOBSSProtEnabled) |
| vos_runtime_pm_allow_suspend(hdd_ctx->runtime_context.obss); |
| } else { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| FL("Can't stop ap because its not started")); |
| } |
| mutex_unlock(&hdd_ctx->sap_lock); |
| return; |
| } |
| |
| /** |
| * wlan_hdd_start_sap() - This function starts bss of SAP. |
| * @ap_adapter: SAP adapter |
| * |
| * This function will process the starting of sap adapter. |
| * |
| * Return: void. |
| */ |
| void wlan_hdd_start_sap(hdd_adapter_t *ap_adapter, bool reinit) |
| { |
| hdd_ap_ctx_t *hdd_ap_ctx; |
| hdd_hostapd_state_t *hostapd_state; |
| VOS_STATUS vos_status; |
| hdd_context_t *hdd_ctx; |
| tsap_Config_t *pConfig; |
| |
| if (NULL == ap_adapter) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| FL("ap_adapter is NULL here")); |
| return; |
| } |
| |
| hdd_ctx = WLAN_HDD_GET_CTX(ap_adapter); |
| hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter); |
| hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(ap_adapter); |
| pConfig = &ap_adapter->sessionCtx.ap.sapConfig; |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| FL("ssr in progress %d"), reinit); |
| |
| if (!reinit) { |
| if (0 != wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| } |
| |
| mutex_lock(&hdd_ctx->sap_lock); |
| if (test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags)) |
| goto end; |
| |
| if (0 != wlan_hdd_cfg80211_update_apies(ap_adapter)) { |
| hddLog(LOGE, FL("SAP Not able to set AP IEs")); |
| WLANSAP_ResetSapConfigAddIE(pConfig, eUPDATE_IE_ALL); |
| goto end; |
| } |
| |
| vos_event_reset(&hostapd_state->vosEvent); |
| if (WLANSAP_StartBss(hdd_ap_ctx->sapContext, hdd_hostapd_SAPEventCB, |
| &hdd_ap_ctx->sapConfig, (v_PVOID_t)ap_adapter->dev) |
| != VOS_STATUS_SUCCESS) { |
| goto end; |
| } |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, |
| FL("Waiting for SAP to start")); |
| vos_status = vos_wait_single_event(&hostapd_state->vosEvent, 10000); |
| if (!VOS_IS_STATUS_SUCCESS(vos_status)) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| FL("SAP Start failed")); |
| goto end; |
| } |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, |
| FL("SAP Start Success")); |
| set_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags); |
| wlan_hdd_incr_active_session(hdd_ctx, ap_adapter->device_mode); |
| hostapd_state->bCommit = TRUE; |
| if (hdd_ctx->cfg_ini->apOBSSProtEnabled) |
| vos_runtime_pm_prevent_suspend(hdd_ctx->runtime_context.obss); |
| |
| end: |
| mutex_unlock(&hdd_ctx->sap_lock); |
| return; |
| } |
| #endif |
| |
| /** |
| * hdd_wlan_go_set_mcc_p2p_quota() - Function to set quota for P2P GO |
| * @hostapd_adapter: Pointer to HDD adapter |
| * @set_value: Qouta value for the interface |
| * |
| * This function is used to set the quota for P2P GO cases |
| * |
| * Return: Configuration message posting status, SUCCESS or Fail |
| * |
| */ |
| int32_t hdd_wlan_go_set_mcc_p2p_quota(hdd_adapter_t *hostapd_adapter, |
| uint32_t set_value) |
| { |
| uint8_t first_adapter_operating_channel = 0; |
| uint8_t second_adapter_opertaing_channel = 0; |
| tVOS_CONCURRENCY_MODE concurrent_state = 0; |
| hdd_adapter_t *staAdapter = NULL; |
| int32_t ret = 0; /* success */ |
| |
| /* |
| * Check if concurrency mode is active. |
| * Need to modify this code to support MCC modes other than |
| * STA/P2P GO |
| */ |
| |
| concurrent_state = hdd_get_concurrency_mode(); |
| if (concurrent_state == (VOS_STA | VOS_P2P_GO)) { |
| hddLog(LOG1, "%s: STA & P2P are both enabled", __func__); |
| |
| /* |
| * The channel numbers for both adapters and the time |
| * quota for the 1st adapter, i.e., one specified in cmd |
| * are formatted as a bit vector then passed on to WMA |
| * +************************************************+ |
| * |bit 31-24 |bit 23-16 | bits 15-8 |bits 7-0 | |
| * | Unused | Quota for| chan. # for |chan. # for| |
| * | | 1st chan.| 1st chan. |2nd chan. | |
| * +************************************************+ |
| */ |
| |
| /* Get the operating channel of the specified vdev */ |
| first_adapter_operating_channel = |
| hdd_get_operating_channel(hostapd_adapter->pHddCtx, |
| hostapd_adapter->device_mode); |
| |
| hddLog(LOG1, "%s: 1st channel No.:%d and quota:%dms", |
| __func__, first_adapter_operating_channel, |
| set_value); |
| |
| /* Move the time quota for first adapter to bits 15-8 */ |
| set_value = set_value << 8; |
| /* |
| * Store the operating channel number of 1st adapter at |
| * the lower 8-bits of bit vector. |
| */ |
| set_value = set_value | first_adapter_operating_channel; |
| if (hostapd_adapter->device_mode == |
| WLAN_HDD_INFRA_STATION) { |
| /* iwpriv cmd issued on wlan0; get p2p0 vdev chan */ |
| if ((concurrent_state & VOS_P2P_CLIENT) != 0) { |
| /* The 2nd MCC vdev is P2P client */ |
| staAdapter = hdd_get_adapter |
| ( |
| hostapd_adapter->pHddCtx, |
| WLAN_HDD_P2P_CLIENT |
| ); |
| } else { |
| /* The 2nd MCC vdev is P2P GO */ |
| staAdapter = hdd_get_adapter |
| ( |
| hostapd_adapter->pHddCtx, |
| WLAN_HDD_P2P_GO |
| ); |
| } |
| } else { |
| /* iwpriv cmd issued on p2p0; get channel for wlan0 */ |
| staAdapter = hdd_get_adapter |
| ( |
| hostapd_adapter->pHddCtx, |
| WLAN_HDD_INFRA_STATION |
| ); |
| } |
| if (staAdapter != NULL) { |
| second_adapter_opertaing_channel = |
| hdd_get_operating_channel |
| ( |
| staAdapter->pHddCtx, |
| staAdapter->device_mode |
| ); |
| hddLog(LOG1, "%s: 2nd vdev channel No. is:%d", |
| __func__, |
| second_adapter_opertaing_channel); |
| |
| if (second_adapter_opertaing_channel == 0 || |
| first_adapter_operating_channel == 0) { |
| hddLog(LOGE, "Invalid channel"); |
| return -EINVAL; |
| } |
| |
| /* |
| * Move the time quota and operating channel number |
| * for the first adapter to bits 23-16 & bits 15-8 |
| * of set_value vector, respectively. |
| */ |
| set_value = set_value << 8; |
| /* |
| * Store the channel number for 2nd MCC vdev at bits |
| * 7-0 of set_value vector as per the bit format above. |
| */ |
| set_value = set_value | |
| second_adapter_opertaing_channel; |
| ret = process_wma_set_command |
| ( |
| (int32_t)hostapd_adapter->sessionId, |
| (int32_t)WMA_VDEV_MCC_SET_TIME_QUOTA, |
| set_value, |
| VDEV_CMD |
| ); |
| } else { |
| hddLog(LOGE, "%s: NULL adapter handle. Exit", |
| __func__); |
| } |
| } else { |
| hddLog(LOG1, "%s: MCC is not active. " |
| "Exit w/o setting latency", __func__); |
| } |
| return ret; |
| } |
| |
| /** |
| * hdd_wlan_set_mcc_p2p_quota() - Function to set quota for P2P |
| * @hostapd_adapter: Pointer to HDD adapter |
| * @set_value: Qouta value for the interface |
| * |
| * This function is used to set the quota for P2P cases |
| * |
| * Return: Configuration message posting status, SUCCESS or Fail |
| * |
| */ |
| int32_t hdd_wlan_set_mcc_p2p_quota(hdd_adapter_t *hostapd_adapater, |
| uint32_t set_value) |
| { |
| uint8_t first_adapter_operating_channel = 0; |
| uint8_t second_adapter_opertaing_channel = 0; |
| hdd_adapter_t *staAdapter = NULL; |
| int32_t ret = 0; /* success */ |
| |
| tVOS_CONCURRENCY_MODE concurrent_state = hdd_get_concurrency_mode(); |
| hddLog(LOG1, "iwpriv cmd to set MCC quota with val %dms", |
| set_value); |
| /* |
| * Check if concurrency mode is active. |
| * Need to modify this code to support MCC modes other than STA/P2P |
| */ |
| if ((concurrent_state == (VOS_STA | VOS_P2P_CLIENT)) || |
| (concurrent_state == (VOS_STA | VOS_P2P_GO))) { |
| hddLog(LOG1, "STA & P2P are both enabled"); |
| /* |
| * The channel numbers for both adapters and the time |
| * quota for the 1st adapter, i.e., one specified in cmd |
| * are formatted as a bit vector then passed on to WMA |
| * +***********************************************************+ |
| * |bit 31-24 | bit 23-16 | bits 15-8 | bits 7-0 | |
| * | Unused | Quota for | chan. # for | chan. # for | |
| * | | 1st chan. | 1st chan. | 2nd chan. | |
| * +***********************************************************+ |
| */ |
| /* Get the operating channel of the specified vdev */ |
| first_adapter_operating_channel = |
| hdd_get_operating_channel |
| ( |
| hostapd_adapater->pHddCtx, |
| hostapd_adapater->device_mode |
| ); |
| hddLog(LOG1, "1st channel No.:%d and quota:%dms", |
| first_adapter_operating_channel, set_value); |
| /* Move the time quota for first channel to bits 15-8 */ |
| set_value = set_value << 8; |
| /* |
| * Store the channel number of 1st channel at bits 7-0 |
| * of the bit vector |
| */ |
| set_value = set_value | first_adapter_operating_channel; |
| /* Find out the 2nd MCC adapter and its operating channel */ |
| if (hostapd_adapater->device_mode == WLAN_HDD_INFRA_STATION) { |
| /* |
| * iwpriv cmd was issued on wlan0; |
| * get p2p0 vdev channel |
| */ |
| if ((concurrent_state & VOS_P2P_CLIENT) != 0) { |
| /* The 2nd MCC vdev is P2P client */ |
| staAdapter = hdd_get_adapter( |
| hostapd_adapater->pHddCtx, |
| WLAN_HDD_P2P_CLIENT); |
| } else { |
| /* The 2nd MCC vdev is P2P GO */ |
| staAdapter = hdd_get_adapter( |
| hostapd_adapater->pHddCtx, |
| WLAN_HDD_P2P_GO); |
| } |
| } else { |
| /* |
| * iwpriv cmd was issued on p2p0; |
| * get wlan0 vdev channel |
| */ |
| staAdapter = hdd_get_adapter(hostapd_adapater->pHddCtx, |
| WLAN_HDD_INFRA_STATION); |
| } |
| if (staAdapter != NULL) { |
| second_adapter_opertaing_channel = |
| hdd_get_operating_channel |
| ( |
| staAdapter->pHddCtx, |
| staAdapter->device_mode |
| ); |
| hddLog(LOG1, "2nd vdev channel No. is:%d", |
| second_adapter_opertaing_channel); |
| |
| if (second_adapter_opertaing_channel == 0 || |
| first_adapter_operating_channel == 0) { |
| hddLog(LOGE, "Invalid channel"); |
| return -EINVAL; |
| } |
| /* |
| * Now move the time quota and channel number of the |
| * 1st adapter to bits 23-16 and bits 15-8 of the bit |
| * vector, respectively. |
| */ |
| set_value = set_value << 8; |
| /* |
| * Store the channel number for 2nd MCC vdev at bits |
| * 7-0 of set_value |
| */ |
| set_value = set_value | |
| second_adapter_opertaing_channel; |
| ret = process_wma_set_command( |
| (int32_t)hostapd_adapater->sessionId, |
| (int32_t)WMA_VDEV_MCC_SET_TIME_QUOTA, |
| set_value, VDEV_CMD); |
| } else { |
| hddLog(LOGE, "NULL adapter handle. Exit"); |
| } |
| } else { |
| hddLog(LOG1, "%s: MCC is not active. Exit w/o setting latency", |
| __func__); |
| } |
| return ret; |
| } |
| |
| /** |
| * hdd_get_fw_version() - Get FW version |
| * @hdd_ctx: pointer to HDD context. |
| * @major_spid: FW version - major spid. |
| * @minor_spid: FW version - minor spid |
| * @ssid: FW version - ssid |
| * @crmid: FW version - crmid |
| * |
| * This function is called to get the firmware build version stored |
| * as part of the HDD context |
| * |
| * Return: None |
| */ |
| |
| void hdd_get_fw_version(hdd_context_t *hdd_ctx, |
| uint32_t *major_spid, uint32_t *minor_spid, |
| uint32_t *siid, uint32_t *crmid) |
| { |
| *major_spid = (hdd_ctx->target_fw_version & 0xf0000000) >> 28; |
| *minor_spid = (hdd_ctx->target_fw_version & 0xf000000) >> 24; |
| *siid = (hdd_ctx->target_fw_version & 0xf00000) >> 20; |
| *crmid = hdd_ctx->target_fw_version & 0x7fff; |
| } |
| |
| #ifdef QCA_CONFIG_SMP |
| int wlan_hdd_get_cpu() |
| { |
| int cpu_index = get_cpu(); |
| put_cpu(); |
| return cpu_index; |
| } |
| #endif |
| |
| /** |
| * hdd_get_fwpath() - get framework path |
| * |
| * This function is used to get the string written by |
| * userspace to start the wlan driver |
| * |
| * Return: string |
| */ |
| const char *hdd_get_fwpath(void) |
| { |
| return fwpath.string; |
| } |
| |
| /** |
| * hdd_enable_disable_ca_event() - enable/disable channel avoidance event |
| * @hddctx: pointer to hdd context |
| * @set_value: enable/disable |
| * |
| * When Host sends vendor command enable, FW will send *ONE* CA ind to |
| * Host(even though it is duplicate). When Host send vendor command |
| * disable,FW doesn't perform any action. Whenever any change in |
| * CA *and* WLAN is in SAP/P2P-GO mode, FW sends CA ind to host. |
| * |
| * return - 0 on success, appropriate error values on failure. |
| */ |
| int hdd_enable_disable_ca_event(hdd_context_t *hddctx, tANI_U8 set_value) |
| { |
| eHalStatus status; |
| int ret_val = 0; |
| |
| if (0 != wlan_hdd_validate_context(hddctx)) { |
| ret_val = -EAGAIN; |
| goto exit; |
| } |
| |
| if (!hddctx->cfg_ini->goptimize_chan_avoid_event) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("goptimize_chan_avoid_event ini param disabled")); |
| ret_val = -EAGAIN; |
| goto exit; |
| } |
| |
| status = sme_enable_disable_chanavoidind_event(hddctx->hHal, set_value); |
| if (status != eHAL_STATUS_SUCCESS) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Failed to send chan avoid command to SME")); |
| ret_val = -EINVAL; |
| } |
| |
| exit: |
| return ret_val; |
| } |
| |
| #ifdef WLAN_FEATURE_PACKET_FILTERING |
| /** |
| * hdd_init_packet_filtering - allocate packet filter MC address list |
| * @hdd_ctx: pointer to hdd context |
| * @adapter: pointer to hdd_adapter_t |
| * |
| * This function allocates memory for link layer MC address list which will |
| * be communicated to firmware for packet filtering in HOST suspend state. |
| * |
| * Return: 0 on success, error number otherwise. |
| */ |
| int hdd_init_packet_filtering(hdd_context_t *hdd_ctx, |
| hdd_adapter_t *adapter) |
| { |
| adapter->mc_addr_list.addr = |
| vos_mem_malloc(hdd_ctx->max_mc_addr_list * ETH_ALEN); |
| |
| if (NULL == adapter->mc_addr_list.addr) { |
| hddLog(LOGE, FL("Could not allocate Memory")); |
| return -ENOMEM; |
| } |
| |
| vos_mem_zero(adapter->mc_addr_list.addr, |
| (hdd_ctx->max_mc_addr_list * ETH_ALEN)); |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_deinit_packet_filtering - deallocate packet filter MC address list |
| * @adapter: pointer to hdd_adapter_t |
| * |
| * Return: none |
| */ |
| void hdd_deinit_packet_filtering(hdd_adapter_t *adapter) |
| { |
| vos_mem_free(adapter->mc_addr_list.addr); |
| adapter->mc_addr_list.addr = NULL; |
| } |
| #endif |
| |
| /** |
| * wlan_hdd_get_dfs_mode() - get ACS DFS mode |
| * @mode : cfg80211 DFS mode |
| * |
| * Return: return SAP ACS DFS mode else return ACS_DFS_MODE_NONE |
| */ |
| enum sap_acs_dfs_mode wlan_hdd_get_dfs_mode(enum dfs_mode mode) |
| { |
| switch (mode) { |
| case DFS_MODE_ENABLE: |
| return ACS_DFS_MODE_ENABLE; |
| break; |
| case DFS_MODE_DISABLE: |
| return ACS_DFS_MODE_DISABLE; |
| break; |
| case DFS_MODE_DEPRIORITIZE: |
| return ACS_DFS_MODE_DEPRIORITIZE; |
| break; |
| default: |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("ACS dfs mode is NONE")); |
| return ACS_DFS_MODE_NONE; |
| } |
| } |
| |
| |
| /** |
| * hdd_set_rps_cpu_mask - set RPS CPU mask for interfaces |
| * @hdd_ctx: pointer to hdd_context_t |
| * |
| * Return: none |
| */ |
| void hdd_set_rps_cpu_mask(hdd_context_t *hdd_ctx) |
| { |
| hdd_adapter_t *adapter; |
| hdd_adapter_list_node_t *adapter_node, *next; |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| |
| status = hdd_get_front_adapter (hdd_ctx, &adapter_node); |
| while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) { |
| adapter = adapter_node->pAdapter; |
| if (NULL != adapter) |
| hdd_dp_util_send_rps_ind(adapter); |
| status = hdd_get_next_adapter (hdd_ctx, adapter_node, &next); |
| adapter_node = next; |
| } |
| } |
| |
| /** |
| * hdd_initialize_adapter_common() - initialize completion variables |
| * @adapter: pointer to hdd_adapter_t |
| * |
| * Return: none |
| */ |
| void hdd_initialize_adapter_common(hdd_adapter_t *adapter) |
| { |
| if (NULL == adapter) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: adapter is NULL ", __func__); |
| return; |
| } |
| init_completion(&adapter->session_open_comp_var); |
| init_completion(&adapter->session_close_comp_var); |
| init_completion(&adapter->disconnect_comp_var); |
| init_completion(&adapter->linkup_event_var); |
| init_completion(&adapter->cancel_rem_on_chan_var); |
| init_completion(&adapter->rem_on_chan_ready_event); |
| init_completion(&adapter->offchannel_tx_event); |
| init_completion(&adapter->tx_action_cnf_event); |
| #ifdef FEATURE_WLAN_TDLS |
| init_completion(&adapter->tdls_add_station_comp); |
| init_completion(&adapter->tdls_del_station_comp); |
| init_completion(&adapter->tdls_mgmt_comp); |
| init_completion(&adapter->tdls_link_establish_req_comp); |
| #endif |
| |
| #ifdef WLAN_FEATURE_RMC |
| init_completion(&adapter->ibss_peer_info_comp); |
| #endif /* WLAN_FEATURE_RMC */ |
| init_completion(&adapter->ula_complete); |
| init_completion(&adapter->change_country_code); |
| init_completion(&adapter->smps_force_mode_comp_var); |
| init_completion(&adapter->scan_info.scan_req_completion_event); |
| init_completion(&adapter->scan_info.abortscan_event_var); |
| |
| return; |
| } |
| |
| //Register the module init/exit functions |
| module_init(hdd_module_init); |
| module_exit(hdd_module_exit); |
| |
| MODULE_LICENSE("Dual BSD/GPL"); |
| MODULE_AUTHOR("Qualcomm Atheros, Inc."); |
| MODULE_DESCRIPTION("WLAN HOST DEVICE DRIVER"); |
| |
| #if defined(QCA_WIFI_FTM) |
| module_param(con_mode, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |
| #else |
| module_param_call(con_mode, con_mode_handler, param_get_int, &con_mode, |
| S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |
| #endif |
| |
| module_param_call(fwpath, fwpath_changed_handler, param_get_string, &fwpath, |
| S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |
| |
| module_param(enable_dfs_chan_scan, int, |
| S_IRUSR | S_IRGRP | S_IROTH); |
| |
| module_param(enable_11d, int, |
| S_IRUSR | S_IRGRP | S_IROTH); |
| |
| module_param(country_code, charp, |
| S_IRUSR | S_IRGRP | S_IROTH); |