| /* |
| * 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 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")); |
| |
| 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 <=
|