| /* |
| * 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_p2p.c |
| |
| \brief WLAN Host Device Driver implementation for P2P commands interface |
| |
| ========================================================================*/ |
| |
| #include <wlan_hdd_includes.h> |
| #include <wlan_hdd_hostapd.h> |
| #include <net/cfg80211.h> |
| #include "sme_Api.h" |
| #include "sme_QosApi.h" |
| #include "wlan_hdd_p2p.h" |
| #include "sapApi.h" |
| #include "wlan_hdd_main.h" |
| #include "vos_trace.h" |
| #include <linux/netdevice.h> |
| #include <linux/skbuff.h> |
| #include <linux/etherdevice.h> |
| #include <net/ieee80211_radiotap.h> |
| #include "wlan_hdd_tdls.h" |
| #include "wlan_hdd_trace.h" |
| #include "vos_types.h" |
| #include "vos_trace.h" |
| #include "vos_sched.h" |
| |
| //Ms to Micro Sec |
| #define MS_TO_MUS(x) ((x)*1000) |
| |
| static tANI_U8* hdd_getActionString(tANI_U16 MsgType) |
| { |
| switch (MsgType) |
| { |
| CASE_RETURN_STRING(SIR_MAC_ACTION_SPECTRUM_MGMT); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_QOS_MGMT); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_DLP); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_BLKACK); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_PUBLIC_USAGE); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_RRM); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_FAST_BSS_TRNST); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_HT); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_SA_QUERY); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_PROT_DUAL_PUB); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_WNM); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_UNPROT_WNM); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_TDLS); |
| CASE_RETURN_STRING(SIR_MAC_ACITON_MESH); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_MHF); |
| CASE_RETURN_STRING(SIR_MAC_SELF_PROTECTED); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_WME); |
| CASE_RETURN_STRING(SIR_MAC_ACTION_VHT); |
| default: |
| return ("UNKNOWN"); |
| } |
| } |
| |
| #ifdef WLAN_FEATURE_P2P_DEBUG |
| #define MAX_P2P_ACTION_FRAME_TYPE 9 |
| const char *p2p_action_frame_type[]={"GO Negotiation Request", |
| "GO Negotiation Response", |
| "GO Negotiation Confirmation", |
| "P2P Invitation Request", |
| "P2P Invitation Response", |
| "Device Discoverability Request", |
| "Device Discoverability Response", |
| "Provision Discovery Request", |
| "Provision Discovery Response"}; |
| |
| /* We no need to protect this variable since |
| * there is no chance of race to condition |
| * and also not make any complicating the code |
| * just for debugging log |
| */ |
| tP2PConnectionStatus globalP2PConnectionStatus = P2P_NOT_ACTIVE; |
| |
| #endif |
| #define MAX_TDLS_ACTION_FRAME_TYPE 11 |
| const char *tdls_action_frame_type[] = {"TDLS Setup Request", |
| "TDLS Setup Response", |
| "TDLS Setup Confirm", |
| "TDLS Teardown", |
| "TDLS Peer Traffic Indication", |
| "TDLS Channel Switch Request", |
| "TDLS Channel Switch Response", |
| "TDLS Peer PSM Request", |
| "TDLS Peer PSM Response", |
| "TDLS Peer Traffic Response", |
| "TDLS Discovery Request" }; |
| |
| extern struct net_device_ops net_ops_struct; |
| |
| static bool wlan_hdd_is_type_p2p_action( const u8 *buf, uint32_t len) |
| { |
| const u8 *ouiPtr; |
| |
| if (len < WLAN_HDD_PUBLIC_ACTION_FRAME_SUB_TYPE_OFFSET + 1) |
| return FALSE; |
| |
| if ( buf[WLAN_HDD_PUBLIC_ACTION_FRAME_CATEGORY_OFFSET] != |
| WLAN_HDD_PUBLIC_ACTION_FRAME ) { |
| return FALSE; |
| } |
| |
| if ( buf[WLAN_HDD_PUBLIC_ACTION_FRAME_ACTION_OFFSET] != |
| WLAN_HDD_VENDOR_SPECIFIC_ACTION ) { |
| return FALSE; |
| } |
| |
| ouiPtr = &buf[WLAN_HDD_PUBLIC_ACTION_FRAME_OUI_OFFSET]; |
| |
| if ( WPA_GET_BE24(ouiPtr) != WLAN_HDD_WFA_OUI ) { |
| return FALSE; |
| } |
| |
| if ( buf[WLAN_HDD_PUBLIC_ACTION_FRAME_OUI_TYPE_OFFSET] != |
| WLAN_HDD_WFA_P2P_OUI_TYPE ) { |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| static bool hdd_p2p_is_action_type_rsp( const u8 *buf, uint32_t len ) |
| { |
| tActionFrmType actionFrmType; |
| |
| if ( wlan_hdd_is_type_p2p_action(buf, len) ) |
| { |
| actionFrmType = buf[WLAN_HDD_PUBLIC_ACTION_FRAME_SUB_TYPE_OFFSET]; |
| if ( actionFrmType != WLAN_HDD_INVITATION_REQ && |
| actionFrmType != WLAN_HDD_GO_NEG_REQ && |
| actionFrmType != WLAN_HDD_DEV_DIS_REQ && |
| actionFrmType != WLAN_HDD_PROV_DIS_REQ ) |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| /** |
| * hdd_random_mac_callback() - Callback invoked from wmi layer |
| * @set_random_addr: Status of random mac filter set operation |
| * @context: Context passed while registring callback |
| * |
| * This function is invoked from wmi layer to give the status of |
| * random mac filter set operation by firmware. |
| * |
| * Return: None |
| */ |
| static void hdd_random_mac_callback(bool set_random_addr, void *context) |
| { |
| struct random_mac_context *rnd_ctx; |
| hdd_adapter_t *adapter; |
| |
| if (!context) { |
| hddLog(LOGE, FL("Bad param, pContext")); |
| return; |
| } |
| |
| rnd_ctx = context; |
| adapter = rnd_ctx->adapter; |
| |
| spin_lock(&hdd_context_lock); |
| if ((!adapter) || |
| (rnd_ctx->magic != ACTION_FRAME_RANDOM_CONTEXT_MAGIC)) { |
| spin_unlock(&hdd_context_lock); |
| hddLog(VOS_TRACE_LEVEL_WARN, |
| FL("Invalid context, magic [%08x]"), rnd_ctx->magic); |
| return; |
| } |
| |
| rnd_ctx->magic = 0; |
| if (set_random_addr) |
| rnd_ctx->set_random_addr = true; |
| |
| complete(&rnd_ctx->random_mac_completion); |
| spin_unlock(&hdd_context_lock); |
| } |
| |
| /** |
| * hdd_set_random_mac() - Invoke sme api to set random mac filter |
| * @adapter: Pointer to adapter |
| * @random_mac_addr: Mac addr filter to be set |
| * |
| * Return: If set is successful return true else return false |
| */ |
| static bool hdd_set_random_mac(hdd_adapter_t *adapter, uint8_t *random_mac_addr) |
| { |
| struct random_mac_context context; |
| hdd_context_t *hdd_ctx; |
| eHalStatus sme_status; |
| unsigned long rc; |
| bool status = false; |
| |
| ENTER(); |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (wlan_hdd_validate_context(hdd_ctx)) { |
| hddLog(LOGE,FL("Invalid hdd ctx")); |
| return false; |
| } |
| |
| init_completion(&context.random_mac_completion); |
| context.adapter = adapter; |
| context.magic = ACTION_FRAME_RANDOM_CONTEXT_MAGIC; |
| context.set_random_addr = false; |
| |
| sme_status = sme_set_random_mac(hdd_ctx->hHal, hdd_random_mac_callback, |
| adapter->sessionId, random_mac_addr, |
| &context); |
| |
| if (sme_status != eHAL_STATUS_SUCCESS) { |
| hddLog(LOGE,FL("Unable to set random mac")); |
| } else { |
| rc = wait_for_completion_timeout(&context.random_mac_completion, |
| msecs_to_jiffies(WLAN_WAIT_TIME_SET_RND)); |
| if (!rc) { |
| hddLog(LOGE, |
| FL("SME timed out while setting random mac")); |
| } |
| } |
| |
| spin_lock(&hdd_context_lock); |
| context.magic = 0; |
| status = context.set_random_addr; |
| spin_unlock(&hdd_context_lock); |
| |
| EXIT(); |
| return status; |
| } |
| |
| /** |
| * hdd_clear_random_mac() - Invoke sme api to clear random mac filter |
| * @adapter: Pointer to adapter |
| * @random_mac_addr: Mac addr filter to be cleared |
| * |
| * Return: If clear is successful return true else return false |
| */ |
| static bool hdd_clear_random_mac(hdd_adapter_t *adapter, |
| uint8_t *random_mac_addr) |
| { |
| hdd_context_t *hdd_ctx; |
| eHalStatus status; |
| |
| ENTER(); |
| hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| if (wlan_hdd_validate_context(hdd_ctx)) { |
| hddLog(LOGE,FL("Invalid hdd ctx")); |
| return false; |
| } |
| |
| status = sme_clear_random_mac(hdd_ctx->hHal, adapter->sessionId, |
| random_mac_addr); |
| |
| if (status != eHAL_STATUS_SUCCESS) { |
| hddLog(LOGE,FL("Unable to clear random mac")); |
| return false; |
| } |
| |
| EXIT(); |
| return true; |
| } |
| |
| bool hdd_check_random_mac(hdd_adapter_t *adapter, uint8_t *random_mac_addr) |
| { |
| uint32_t i = 0; |
| |
| spin_lock(&adapter->random_mac_lock); |
| for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { |
| if ((adapter->random_mac[i].in_use) && |
| (!memcmp(adapter->random_mac[i].addr, random_mac_addr, |
| VOS_MAC_ADDR_SIZE))) { |
| spin_unlock(&adapter->random_mac_lock); |
| return true; |
| } |
| } |
| spin_unlock(&adapter->random_mac_lock); |
| return false; |
| } |
| |
| /** |
| * find_action_frame_cookie() - Checks for action cookie in cookie list |
| * @cookie_list: List of cookies |
| * @cookie: Cookie to be searched |
| * |
| * Return: If search is successful return pointer to action_frame_cookie |
| * object in which cookie item is encapsulated. |
| */ |
| static struct action_frame_cookie * find_action_frame_cookie( |
| struct list_head *cookie_list, |
| uint64_t cookie) |
| { |
| struct action_frame_cookie *action_cookie = NULL; |
| struct list_head *temp = NULL; |
| |
| list_for_each(temp, cookie_list) { |
| action_cookie = list_entry(temp, struct action_frame_cookie, |
| cookie_node); |
| if (action_cookie->cookie == cookie) |
| return action_cookie; |
| } |
| |
| return NULL; |
| } |
| |
| /** |
| * allocate_action_frame_cookie() - Allocate and add action cookie to given list |
| * @cookie_list: List of cookies |
| * @cookie: Cookie to be added |
| * |
| * Return: If allocation and addition is successful return pointer to |
| * action_frame_cookie object in which cookie item is encapsulated. |
| */ |
| static struct action_frame_cookie * allocate_action_frame_cookie( |
| struct list_head *cookie_list, |
| uint64_t cookie) |
| { |
| struct action_frame_cookie *action_cookie = NULL; |
| |
| action_cookie = vos_mem_malloc(sizeof(*action_cookie)); |
| if(!action_cookie) |
| return NULL; |
| |
| action_cookie->cookie = cookie; |
| list_add(&action_cookie->cookie_node, cookie_list); |
| |
| return action_cookie; |
| } |
| |
| /** |
| * delete_action_frame_cookie() - Delete the cookie from given list |
| * @cookie_list: List of cookies |
| * @cookie: Cookie to be deleted |
| * |
| * This function deletes the cookie item from given list and corresponding |
| * object in which it is encapsulated. |
| * |
| * Return: None |
| */ |
| static void delete_action_frame_cookie( |
| struct action_frame_cookie *action_cookie) |
| { |
| list_del(&action_cookie->cookie_node); |
| vos_mem_free(action_cookie); |
| } |
| |
| /** |
| * append_action_frame_cookie() - Append action cookie to given list |
| * @cookie_list: List of cookies |
| * @cookie: Cookie to be append |
| * |
| * This is a wrapper function which invokes allocate_action_frame_cookie |
| * if the cookie to be added is not duplicate |
| * |
| * Return: 0 - for successfull case |
| * -EALREADY - if cookie is duplicate |
| * -ENOMEM - if allocation is failed |
| */ |
| static int32_t append_action_frame_cookie(struct list_head *cookie_list, |
| uint64_t cookie) |
| { |
| struct action_frame_cookie *action_cookie = NULL; |
| |
| /* |
| * There should be no mac entry with empty cookie list, |
| * check and ignore if duplicate |
| */ |
| action_cookie = find_action_frame_cookie(cookie_list, cookie); |
| if (action_cookie) |
| /* random mac address is already programmed */ |
| return -EALREADY; |
| |
| /* insert new cookie in cookie list */ |
| action_cookie = allocate_action_frame_cookie(cookie_list, cookie); |
| if (!action_cookie) |
| return -ENOMEM; |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_set_action_frame_random_mac() - Store action frame cookie |
| * @adapter: Pointer to adapter |
| * @random_mac_addr: Mac addr for cookie |
| * @cookie: Cookie to be stored |
| * |
| * This function is used to create cookie list and append the cookies |
| * to same for corresponding random mac addr. If this cookie is the first |
| * item in the list then random mac filter is set. |
| * |
| * Return: 0 - for success else negative value |
| */ |
| static int32_t hdd_set_action_frame_random_mac(hdd_adapter_t *adapter, |
| uint8_t *random_mac_addr, |
| uint64_t cookie) |
| { |
| uint32_t i = 0; |
| uint32_t in_use_cnt = 0; |
| struct action_frame_cookie *action_cookie = NULL; |
| int32_t append_ret = 0; |
| |
| if (!cookie) { |
| hddLog(LOGE, FL("Invalid cookie")); |
| return -EINVAL; |
| } |
| |
| hddLog(LOG1, FL("mac_addr: " MAC_ADDRESS_STR " && cookie = %llu"), |
| MAC_ADDR_ARRAY(random_mac_addr), cookie); |
| |
| spin_lock(&adapter->random_mac_lock); |
| for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { |
| if (adapter->random_mac[i].in_use) { |
| in_use_cnt++; |
| if (!memcmp(adapter->random_mac[i].addr, |
| random_mac_addr, VOS_MAC_ADDR_SIZE)) |
| break; |
| } |
| } |
| |
| if (i != MAX_RANDOM_MAC_ADDRS) { |
| append_ret = append_action_frame_cookie( |
| &adapter->random_mac[i].cookie_list, |
| cookie); |
| spin_unlock(&adapter->random_mac_lock); |
| |
| if(append_ret == -ENOMEM) { |
| hddLog(LOGE, FL("No Sufficient memory for cookie")); |
| return append_ret; |
| } |
| |
| return 0; |
| } |
| |
| /* get the first unused buf and store new random mac */ |
| for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { |
| if (!adapter->random_mac[i].in_use) |
| break; |
| } |
| |
| if ((in_use_cnt == MAX_RANDOM_MAC_ADDRS) |
| || (i == MAX_RANDOM_MAC_ADDRS)) { |
| spin_unlock(&adapter->random_mac_lock); |
| hddLog(LOGE, FL("Reached the limit of Max random addresses")); |
| return -EBUSY; |
| } |
| |
| INIT_LIST_HEAD(&adapter->random_mac[i].cookie_list); |
| action_cookie = allocate_action_frame_cookie(&adapter->random_mac[i].cookie_list, |
| cookie); |
| if(!action_cookie) { |
| spin_unlock(&adapter->random_mac_lock); |
| hddLog(LOGE, FL("No Sufficient memory for cookie")); |
| return -ENOMEM; |
| } |
| vos_mem_copy(adapter->random_mac[i].addr, random_mac_addr, |
| VOS_MAC_ADDR_SIZE); |
| adapter->random_mac[i].in_use = true; |
| spin_unlock(&adapter->random_mac_lock); |
| /* Program random mac_addr */ |
| if (!hdd_set_random_mac(adapter, adapter->random_mac[i].addr)) { |
| spin_lock(&adapter->random_mac_lock); |
| /* clear the cookie */ |
| delete_action_frame_cookie(action_cookie); |
| adapter->random_mac[i].in_use = false; |
| spin_unlock(&adapter->random_mac_lock); |
| hddLog(LOGE, FL("random mac filter set failed for: " |
| MAC_ADDRESS_STR), |
| MAC_ADDR_ARRAY(adapter->random_mac[i].addr)); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hdd_reset_action_frame_random_mac() - Delete action frame cookie with |
| * given random mac addr |
| * @adapter: Pointer to adapter |
| * @random_mac_addr: Mac addr for cookie |
| * @cookie: Cookie to be deleted |
| * |
| * This function is used to delete the cookie from the cookie list corresponding |
| * to given random mac addr.If cookie list is empty after deleting, |
| * it will clear random mac filter. |
| * |
| * Return: 0 - for success else negative value |
| */ |
| static int32_t hdd_reset_action_frame_random_mac(hdd_adapter_t *adapter, |
| uint8_t *random_mac_addr, |
| uint64_t cookie) |
| { |
| uint32_t i = 0; |
| struct action_frame_cookie *action_cookie = NULL; |
| |
| if (!cookie) { |
| hddLog(LOGE, FL("Invalid cookie")); |
| return -EINVAL; |
| } |
| |
| hddLog(LOG1, FL("mac_addr: " MAC_ADDRESS_STR " && cookie = %llu"), |
| MAC_ADDR_ARRAY(random_mac_addr), cookie); |
| |
| spin_lock(&adapter->random_mac_lock); |
| for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { |
| if ((adapter->random_mac[i].in_use) && |
| (!memcmp(adapter->random_mac[i].addr, |
| random_mac_addr, VOS_MAC_ADDR_SIZE))) |
| break; |
| } |
| |
| if (i == MAX_RANDOM_MAC_ADDRS) { |
| spin_unlock(&adapter->random_mac_lock); |
| hddLog(LOGE, FL("trying to delete cookie of random mac-addr" |
| " for which entry is not present")); |
| return -EINVAL; |
| } |
| |
| action_cookie = find_action_frame_cookie(&adapter->random_mac[i].cookie_list, |
| cookie); |
| |
| if (!action_cookie) { |
| spin_unlock(&adapter->random_mac_lock); |
| hddLog(LOG1, FL("No cookie matches")); |
| return 0; |
| } |
| |
| delete_action_frame_cookie(action_cookie); |
| if (list_empty(&adapter->random_mac[i].cookie_list)) { |
| adapter->random_mac[i].in_use = false; |
| spin_unlock(&adapter->random_mac_lock); |
| hdd_clear_random_mac(adapter, random_mac_addr); |
| hddLog(LOG1, FL("Deleted random mac_addr:" |
| MAC_ADDRESS_STR), |
| MAC_ADDR_ARRAY(random_mac_addr)); |
| return 0; |
| } |
| |
| spin_unlock(&adapter->random_mac_lock); |
| return 0; |
| } |
| |
| /** |
| * hdd_delete_action_frame_cookie() - Delete action frame cookie |
| * @adapter: Pointer to adapter |
| * @cookie: Cookie to be deleted |
| * |
| * This function parses the cookie list of each random mac addr until the |
| * specified cookie is found and then deletes it. If cookie list is empty |
| * after deleting, it will clear random mac filter. |
| * |
| * Return: 0 - for success else negative value |
| */ |
| static int32_t hdd_delete_action_frame_cookie(hdd_adapter_t *adapter, |
| uint64_t cookie) |
| { |
| uint32_t i = 0; |
| struct action_frame_cookie *action_cookie = NULL; |
| |
| hddLog(LOG1, FL("Delete cookie = %llu"), cookie); |
| |
| spin_lock(&adapter->random_mac_lock); |
| for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { |
| if (!adapter->random_mac[i].in_use) |
| continue; |
| |
| action_cookie = find_action_frame_cookie(&adapter->random_mac[i].cookie_list, |
| cookie); |
| |
| if (!action_cookie) |
| continue; |
| |
| delete_action_frame_cookie(action_cookie); |
| |
| if (list_empty(&adapter->random_mac[i].cookie_list)) { |
| adapter->random_mac[i].in_use = false; |
| spin_unlock(&adapter->random_mac_lock); |
| hdd_clear_random_mac(adapter, |
| adapter->random_mac[i].addr); |
| hddLog(LOG1, FL("Deleted random addr "MAC_ADDRESS_STR), |
| MAC_ADDR_ARRAY(adapter->random_mac[i].addr)); |
| return 0; |
| } |
| spin_unlock(&adapter->random_mac_lock); |
| return 0; |
| } |
| |
| spin_unlock(&adapter->random_mac_lock); |
| hddLog(LOG1, FL("Invalid cookie")); |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_delete_all_action_frame_cookies() - Delete all action frame cookies |
| * @adapter: Pointer to adapter |
| * |
| * This function deletes all the cookie lists of each random mac addr and clears |
| * the corresponding random mac filters. |
| * |
| * Return: 0 - for success else negative value |
| */ |
| static void hdd_delete_all_action_frame_cookies(hdd_adapter_t *adapter) |
| { |
| uint32_t i = 0; |
| struct action_frame_cookie *action_cookie = NULL; |
| struct list_head *n; |
| struct list_head *temp; |
| |
| spin_lock(&adapter->random_mac_lock); |
| |
| for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { |
| |
| if (!adapter->random_mac[i].in_use) |
| continue; |
| |
| /* empty the list and clear random addr */ |
| list_for_each_safe(temp, n, |
| &adapter->random_mac[i].cookie_list) { |
| action_cookie = list_entry(temp, |
| struct action_frame_cookie, |
| cookie_node); |
| list_del(temp); |
| vos_mem_free(action_cookie); |
| } |
| |
| adapter->random_mac[i].in_use = false; |
| spin_unlock(&adapter->random_mac_lock); |
| hdd_clear_random_mac(adapter, adapter->random_mac[i].addr); |
| hddLog(LOG1, FL("Deleted random addr " MAC_ADDRESS_STR), |
| MAC_ADDR_ARRAY(adapter->random_mac[i].addr)); |
| spin_lock(&adapter->random_mac_lock); |
| } |
| |
| spin_unlock(&adapter->random_mac_lock); |
| } |
| |
| static eHalStatus |
| wlan_hdd_remain_on_channel_callback(tHalHandle hHal, void* pCtx, |
| eHalStatus status) |
| { |
| hdd_adapter_t *pAdapter = (hdd_adapter_t*) pCtx; |
| hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter ); |
| hdd_remain_on_chan_ctx_t *pRemainChanCtx; |
| hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(pAdapter); |
| int ret_code; |
| |
| ret_code = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret_code) { |
| /* If ssr is inprogress, do not return, resource release is necessary */ |
| if (!(-EAGAIN == ret_code && hdd_ctx->isLogpInProgress)) { |
| return eHAL_STATUS_FAILURE; |
| } |
| } |
| |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| pRemainChanCtx = cfgState->remain_on_chan_ctx; |
| |
| if( pRemainChanCtx == NULL ) |
| { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog( LOGW, |
| "%s: No Rem on channel pending for which Rsp is received", __func__); |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| hddLog( LOG1, "Received remain on channel rsp"); |
| if (!VOS_IS_STATUS_SUCCESS(vos_timer_stop( |
| &pRemainChanCtx->hdd_remain_on_chan_timer))) |
| hddLog( LOGE, FL("Failed to stop hdd_remain_on_chan_timer")); |
| if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy( |
| &pRemainChanCtx->hdd_remain_on_chan_timer))) |
| hddLog( LOGE, FL("Failed to destroy hdd_remain_on_chan_timer")); |
| cfgState->remain_on_chan_ctx = NULL; |
| /* |
| * Resetting the roc in progress early ensures that the subsequent |
| * roc requests are immediately processed without being queued |
| */ |
| pAdapter->is_roc_inprogress = false; |
| vos_runtime_pm_allow_suspend(hdd_ctx->runtime_context.roc); |
| /* |
| * If the allow suspend is done later, the scheduled roc wil prevent |
| * the system from going into suspend and immediately this logic |
| * will allow the system to go to suspend breaking the exising logic. |
| * Basically, the system must not go into suspend while roc is in progress. |
| */ |
| hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC); |
| |
| if( REMAIN_ON_CHANNEL_REQUEST == pRemainChanCtx->rem_on_chan_request) |
| { |
| if( cfgState->buf ) |
| { |
| hddLog( LOG1, |
| "%s: We need to receive yet an ack from one of tx packet", |
| __func__); |
| } |
| cfg80211_remain_on_channel_expired( |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| pRemainChanCtx->dev->ieee80211_ptr, |
| #else |
| pRemainChanCtx->dev, |
| #endif |
| pRemainChanCtx->cookie, |
| &pRemainChanCtx->chan, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS) |
| pRemainChanCtx->chan_type, |
| #endif |
| GFP_KERNEL); |
| pAdapter->lastRocTs = vos_timer_get_system_time(); |
| } |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| |
| /* Schedule any pending RoC: Any new roc request during this time |
| * would have got queued in 'wlan_hdd_request_remain_on_channel' |
| * since the queue is not empty. So, the roc at the head of the |
| * queue will only get the priority. Scheduling the work queue |
| * after sending any cancel remain on channel event will also |
| * ensure that the cancel roc is sent without any delays. |
| */ |
| /* If ssr is inprogress, do not schedule next roc req */ |
| if (!hdd_ctx->isLogpInProgress) |
| schedule_delayed_work(&hdd_ctx->rocReqWork, 0); |
| |
| if ( ( WLAN_HDD_INFRA_STATION == pAdapter->device_mode ) || |
| ( WLAN_HDD_P2P_CLIENT == pAdapter->device_mode ) || |
| ( WLAN_HDD_P2P_DEVICE == pAdapter->device_mode ) |
| ) |
| { |
| tANI_U8 sessionId = pAdapter->sessionId; |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| if( REMAIN_ON_CHANNEL_REQUEST == pRemainChanCtx->rem_on_chan_request) |
| { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| sme_DeregisterMgmtFrame( |
| hHal, sessionId, |
| (SIR_MAC_MGMT_FRAME << 2) | ( SIR_MAC_MGMT_PROBE_REQ << 4), |
| NULL, 0 ); |
| } |
| else |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| } |
| else if ( ( WLAN_HDD_SOFTAP== pAdapter->device_mode ) || |
| ( WLAN_HDD_P2P_GO == pAdapter->device_mode ) |
| ) |
| { |
| WLANSAP_DeRegisterMgmtFrame( |
| #ifdef WLAN_FEATURE_MBSSID |
| WLAN_HDD_GET_SAP_CTX_PTR(pAdapter), |
| #else |
| (WLAN_HDD_GET_CTX(pAdapter))->pvosContext, |
| #endif |
| (SIR_MAC_MGMT_FRAME << 2) | ( SIR_MAC_MGMT_PROBE_REQ << 4), |
| NULL, 0 ); |
| |
| } |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| if(pRemainChanCtx->action_pkt_buff.frame_ptr != NULL |
| && pRemainChanCtx->action_pkt_buff.frame_length != 0 ) |
| { |
| vos_mem_free(pRemainChanCtx->action_pkt_buff.frame_ptr); |
| pRemainChanCtx->action_pkt_buff.frame_ptr = NULL; |
| pRemainChanCtx->action_pkt_buff.frame_length = 0; |
| } |
| vos_mem_free( pRemainChanCtx ); |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| complete(&pAdapter->cancel_rem_on_chan_var); |
| if (eHAL_STATUS_SUCCESS != status) |
| complete(&pAdapter->rem_on_chan_ready_event); |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| void wlan_hdd_cancel_existing_remain_on_channel(hdd_adapter_t *pAdapter) |
| { |
| hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter ); |
| hdd_remain_on_chan_ctx_t *pRemainChanCtx; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| unsigned long rc; |
| |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| if(cfgState->remain_on_chan_ctx != NULL) |
| { |
| hddLog(LOGE, "Cancel Existing Remain on Channel"); |
| |
| if (VOS_TIMER_STATE_RUNNING == vos_timer_getCurrentState( |
| &cfgState->remain_on_chan_ctx->hdd_remain_on_chan_timer)) |
| { |
| if (!VOS_IS_STATUS_SUCCESS( |
| vos_timer_stop(&cfgState->remain_on_chan_ctx-> |
| hdd_remain_on_chan_timer))) |
| hddLog( LOGE, FL("Failed to stop hdd_remain_on_chan_timer")); |
| } |
| pRemainChanCtx = cfgState->remain_on_chan_ctx; |
| if (NULL == pRemainChanCtx) |
| { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog(LOGE, FL("pRemainChanCtx is NULL")); |
| return; |
| } |
| if (pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress == TRUE) |
| { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog(LOGE, |
| "ROC timer cancellation in progress," |
| " wait for completion"); |
| rc = wait_for_completion_timeout(&pAdapter->cancel_rem_on_chan_var, |
| msecs_to_jiffies(WAIT_CANCEL_REM_CHAN)); |
| if (!rc) { |
| hddLog(LOGE, |
| "%s:wait on cancel_rem_on_chan_var timed out", |
| __func__); |
| } |
| return; |
| } |
| pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress = TRUE; |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| /* Wait till remain on channel ready indication before issuing cancel |
| * remain on channel request, otherwise if remain on channel not |
| * received and if the driver issues cancel remain on channel then lim |
| * will be in unknown state. |
| */ |
| rc = wait_for_completion_timeout(&pAdapter->rem_on_chan_ready_event, |
| msecs_to_jiffies(WAIT_REM_CHAN_READY)); |
| if (!rc) { |
| hddLog( LOGE, |
| "%s: timeout waiting for remain on channel ready indication", |
| __func__); |
| vos_flush_logs(WLAN_LOG_TYPE_FATAL, |
| WLAN_LOG_INDICATOR_HOST_DRIVER, |
| WLAN_LOG_REASON_HDD_TIME_OUT, |
| DUMP_VOS_TRACE); |
| } |
| |
| INIT_COMPLETION(pAdapter->cancel_rem_on_chan_var); |
| |
| /* Issue abort remain on chan request to sme. |
| * The remain on channel callback will make sure the remain_on_chan |
| * expired event is sent. |
| */ |
| if ( ( WLAN_HDD_INFRA_STATION == pAdapter->device_mode ) || |
| ( WLAN_HDD_P2P_CLIENT == pAdapter->device_mode ) || |
| ( WLAN_HDD_P2P_DEVICE == pAdapter->device_mode ) |
| ) |
| { |
| hdd_delete_all_action_frame_cookies(pAdapter); |
| sme_CancelRemainOnChannel( WLAN_HDD_GET_HAL_CTX( pAdapter ), |
| pAdapter->sessionId ); |
| } |
| else if ( (WLAN_HDD_SOFTAP== pAdapter->device_mode) || |
| (WLAN_HDD_P2P_GO == pAdapter->device_mode) |
| ) |
| { |
| WLANSAP_CancelRemainOnChannel( |
| #ifdef WLAN_FEATURE_MBSSID |
| WLAN_HDD_GET_SAP_CTX_PTR(pAdapter)); |
| #else |
| (WLAN_HDD_GET_CTX(pAdapter))->pvosContext); |
| #endif |
| } |
| |
| rc = wait_for_completion_timeout(&pAdapter->cancel_rem_on_chan_var, |
| msecs_to_jiffies(WAIT_CANCEL_REM_CHAN)); |
| |
| if (!rc) { |
| hddLog( LOGE, |
| "%s: timeout waiting for cancel remain on channel ready" |
| " indication", |
| __func__); |
| } |
| vos_runtime_pm_allow_suspend(pHddCtx->runtime_context.roc); |
| hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC); |
| } else |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| } |
| |
| int wlan_hdd_check_remain_on_channel(hdd_adapter_t *pAdapter) |
| { |
| int status = 0; |
| hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter ); |
| |
| if(WLAN_HDD_P2P_GO != pAdapter->device_mode) |
| { |
| //Cancel Existing Remain On Channel |
| //If no action frame is pending |
| if( cfgState->remain_on_chan_ctx != NULL) |
| { |
| //Check whether Action Frame is pending or not |
| if( cfgState->buf == NULL) |
| { |
| wlan_hdd_cancel_existing_remain_on_channel(pAdapter); |
| } |
| else |
| { |
| hddLog( LOG1, "Cannot Cancel Existing Remain on Channel"); |
| status = -EBUSY; |
| } |
| } |
| } |
| return status; |
| } |
| /* Clean up RoC context at hdd_stop_adapter*/ |
| void wlan_hdd_cleanup_remain_on_channel_ctx(hdd_adapter_t *pAdapter) |
| { |
| unsigned long rc; |
| v_U8_t retry = 0; |
| hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR(pAdapter); |
| hdd_remain_on_chan_ctx_t *roc_ctx; |
| |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| while (pAdapter->is_roc_inprogress) |
| { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: ROC in progress for session %d!!!", |
| __func__, pAdapter->sessionId); |
| msleep(500); |
| if (retry++ > 3) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: ROC completion is not received.!!!", __func__); |
| |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| roc_ctx = cfgState->remain_on_chan_ctx; |
| if (roc_ctx == NULL) |
| { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog(LOG1, FL("roc_ctx is NULL!")); |
| return; |
| } |
| if (roc_ctx->hdd_remain_on_chan_cancel_in_progress == true) { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog(LOG1, FL("roc cancel already in progress")); |
| /* |
| * Since a cancel roc is already issued and is |
| * in progress, we need not send another |
| * cancel roc again. Instead we can just wait |
| * for cancel roc completion |
| */ |
| goto wait; |
| } |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| |
| if (pAdapter->device_mode == WLAN_HDD_P2P_GO) |
| { |
| WLANSAP_CancelRemainOnChannel( |
| (WLAN_HDD_GET_CTX(pAdapter))->pvosContext); |
| } else if (pAdapter->device_mode == WLAN_HDD_P2P_CLIENT || |
| pAdapter->device_mode == WLAN_HDD_P2P_DEVICE) |
| { |
| hdd_delete_all_action_frame_cookies(pAdapter); |
| sme_CancelRemainOnChannel(WLAN_HDD_GET_HAL_CTX(pAdapter), |
| pAdapter->sessionId); |
| } |
| wait: |
| rc = wait_for_completion_timeout(&pAdapter->cancel_rem_on_chan_var, |
| msecs_to_jiffies(WAIT_CANCEL_REM_CHAN)); |
| if (!rc) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: Timeout occurred while waiting for RoC Cancellation" , |
| __func__); |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| roc_ctx = cfgState->remain_on_chan_ctx; |
| if (roc_ctx != NULL) |
| { |
| cfgState->remain_on_chan_ctx = NULL; |
| if (!VOS_IS_STATUS_SUCCESS(vos_timer_stop( |
| &roc_ctx->hdd_remain_on_chan_timer))) |
| hddLog( LOGE, FL("Failed to stop hdd_remain_on_chan_timer")); |
| if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy |
| (&roc_ctx->hdd_remain_on_chan_timer))) |
| hddLog( LOGE, FL( |
| "Failed to destroy hdd_remain_on_chan_timer")); |
| if (roc_ctx->action_pkt_buff.frame_ptr != NULL |
| && roc_ctx->action_pkt_buff.frame_length != 0) |
| { |
| vos_mem_free(roc_ctx->action_pkt_buff.frame_ptr); |
| roc_ctx->action_pkt_buff.frame_ptr = NULL; |
| roc_ctx->action_pkt_buff.frame_length = 0; |
| } |
| vos_mem_free(roc_ctx); |
| pAdapter->is_roc_inprogress = FALSE; |
| } |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| } |
| /* hold the lock before break from the loop */ |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| break; |
| } |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| } /* end of while */ |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| |
| } |
| |
| void wlan_hdd_remain_on_chan_timeout(void *data) |
| { |
| hdd_adapter_t *pAdapter = (hdd_adapter_t *)data; |
| hdd_context_t *pHddCtx; |
| hdd_remain_on_chan_ctx_t *pRemainChanCtx; |
| hdd_cfg80211_state_t *cfgState; |
| |
| if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)) { |
| hddLog(LOGE, FL("pAdapter is invalid %pK !!!"), pAdapter); |
| return; |
| } |
| |
| pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| |
| cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter ); |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| pRemainChanCtx = cfgState->remain_on_chan_ctx; |
| |
| if(NULL == pRemainChanCtx) |
| { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog( LOGE,"%s: No Remain on channel is pending", __func__); |
| return; |
| } |
| |
| if ( TRUE == pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress ) |
| { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog( LOGE, FL("Cancellation already in progress")); |
| return; |
| } |
| |
| pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress = TRUE; |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog( LOG1,"%s: Cancel Remain on Channel on timeout", __func__); |
| |
| if ( ( WLAN_HDD_INFRA_STATION == pAdapter->device_mode ) || |
| ( WLAN_HDD_P2P_CLIENT == pAdapter->device_mode ) || |
| ( WLAN_HDD_P2P_DEVICE == pAdapter->device_mode ) |
| ) |
| { |
| hdd_delete_all_action_frame_cookies(pAdapter); |
| sme_CancelRemainOnChannel( WLAN_HDD_GET_HAL_CTX( pAdapter ), |
| pAdapter->sessionId ); |
| } |
| else if ( (WLAN_HDD_SOFTAP== pAdapter->device_mode) || |
| (WLAN_HDD_P2P_GO == pAdapter->device_mode) |
| ) |
| { |
| WLANSAP_CancelRemainOnChannel( |
| (WLAN_HDD_GET_CTX(pAdapter))->pvosContext); |
| } |
| |
| wlan_hdd_start_stop_tdls_source_timer(pHddCtx, eTDLS_SUPPORT_ENABLED); |
| vos_runtime_pm_allow_suspend(pHddCtx->runtime_context.roc); |
| hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC); |
| } |
| |
| static int wlan_hdd_execute_remain_on_channel(hdd_adapter_t *pAdapter, |
| hdd_remain_on_chan_ctx_t *pRemainChanCtx) |
| { |
| hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter ); |
| VOS_STATUS vos_status = VOS_STATUS_E_FAILURE; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter ); |
| hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL; |
| hdd_adapter_t *pAdapter_temp; |
| VOS_STATUS status; |
| v_BOOL_t isGoPresent = VOS_FALSE; |
| unsigned int duration; |
| |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| if (pAdapter->is_roc_inprogress == TRUE) { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("remain on channel request is in execution")); |
| return -EBUSY; |
| } |
| cfgState->remain_on_chan_ctx = pRemainChanCtx; |
| cfgState->current_freq = pRemainChanCtx->chan.center_freq; |
| pAdapter->is_roc_inprogress = TRUE; |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| |
| /* Initialize Remain on chan timer */ |
| vos_status = vos_timer_init(&pRemainChanCtx->hdd_remain_on_chan_timer, |
| VOS_TIMER_TYPE_SW, |
| wlan_hdd_remain_on_chan_timeout, |
| pAdapter); |
| if (vos_status != VOS_STATUS_SUCCESS) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Not able to initialize remain_on_chan timer")); |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| cfgState->remain_on_chan_ctx = NULL; |
| pAdapter->is_roc_inprogress = FALSE; |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| vos_mem_free(pRemainChanCtx); |
| return -EINVAL; |
| } |
| |
| status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode ); |
| while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) |
| { |
| pAdapter_temp = pAdapterNode->pAdapter; |
| if(pAdapter_temp->device_mode == WLAN_HDD_P2P_GO) |
| { |
| isGoPresent = VOS_TRUE; |
| } |
| status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext ); |
| pAdapterNode = pNext; |
| } |
| |
| //Extending duration for proactive extension logic for RoC |
| duration = pRemainChanCtx->duration; |
| if (isGoPresent == VOS_TRUE) |
| duration = P2P_ROC_DURATION_MULTIPLIER_GO_PRESENT * duration; |
| else |
| duration = P2P_ROC_DURATION_MULTIPLIER_GO_ABSENT * duration; |
| |
| hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC); |
| vos_runtime_pm_prevent_suspend(pHddCtx->runtime_context.roc); |
| INIT_COMPLETION(pAdapter->rem_on_chan_ready_event); |
| |
| //call sme API to start remain on channel. |
| if ( ( WLAN_HDD_INFRA_STATION == pAdapter->device_mode ) || |
| ( WLAN_HDD_P2P_CLIENT == pAdapter->device_mode ) || |
| ( WLAN_HDD_P2P_DEVICE == pAdapter->device_mode ) |
| ) |
| { |
| tANI_U8 sessionId = pAdapter->sessionId; |
| //call sme API to start remain on channel. |
| |
| if (eHAL_STATUS_SUCCESS != sme_RemainOnChannel( |
| WLAN_HDD_GET_HAL_CTX(pAdapter), sessionId, |
| pRemainChanCtx->chan.hw_value, duration, |
| wlan_hdd_remain_on_channel_callback, pAdapter, |
| (tANI_U8)(pRemainChanCtx->rem_on_chan_request |
| == REMAIN_ON_CHANNEL_REQUEST)? TRUE:FALSE)) { |
| hddLog(LOGE, FL("sme_RemainOnChannel returned failure")); |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| pAdapter->is_roc_inprogress = FALSE; |
| pRemainChanCtx = cfgState->remain_on_chan_ctx; |
| hddLog( LOG1, FL( |
| "Freeing ROC ctx cfgState->remain_on_chan_ctx=%pK"), |
| cfgState->remain_on_chan_ctx); |
| if (pRemainChanCtx) |
| { |
| if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy |
| (&pRemainChanCtx->hdd_remain_on_chan_timer))) |
| hddLog( LOGE, FL( |
| "Failed to destroy hdd_remain_on_chan_timer")); |
| vos_mem_free(pRemainChanCtx); |
| cfgState->remain_on_chan_ctx = NULL; |
| } |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| vos_runtime_pm_allow_suspend(pHddCtx->runtime_context.roc); |
| hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC); |
| return -EINVAL; |
| } |
| |
| if (REMAIN_ON_CHANNEL_REQUEST == pRemainChanCtx->rem_on_chan_request) { |
| if (eHAL_STATUS_SUCCESS != sme_RegisterMgmtFrame( |
| WLAN_HDD_GET_HAL_CTX(pAdapter), |
| sessionId, (SIR_MAC_MGMT_FRAME << 2) | |
| (SIR_MAC_MGMT_PROBE_REQ << 4), NULL, 0)) |
| hddLog(LOGE, FL("sme_RegisterMgmtFrame returned failure")); |
| } |
| } |
| else if ( ( WLAN_HDD_SOFTAP== pAdapter->device_mode ) || |
| ( WLAN_HDD_P2P_GO == pAdapter->device_mode ) |
| ) |
| { |
| //call sme API to start remain on channel. |
| if (VOS_STATUS_SUCCESS != WLANSAP_RemainOnChannel( |
| #ifdef WLAN_FEATURE_MBSSID |
| WLAN_HDD_GET_SAP_CTX_PTR(pAdapter), |
| #else |
| (WLAN_HDD_GET_CTX(pAdapter))->pvosContext, |
| #endif |
| pRemainChanCtx->chan.hw_value, duration, |
| wlan_hdd_remain_on_channel_callback, pAdapter)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: WLANSAP_RemainOnChannel returned fail", __func__); |
| |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| pAdapter->is_roc_inprogress = FALSE; |
| pRemainChanCtx = cfgState->remain_on_chan_ctx; |
| hddLog( LOG1, FL( |
| "Freeing ROC ctx cfgState->remain_on_chan_ctx=%pK"), |
| cfgState->remain_on_chan_ctx); |
| if (pRemainChanCtx) |
| { |
| if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy |
| (&pRemainChanCtx->hdd_remain_on_chan_timer))) |
| hddLog( LOGE, FL( |
| "Failed to destroy hdd_remain_on_chan_timer")); |
| vos_mem_free (pRemainChanCtx); |
| cfgState->remain_on_chan_ctx = NULL; |
| } |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| vos_runtime_pm_allow_suspend(pHddCtx->runtime_context.roc); |
| hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC); |
| return -EINVAL; |
| } |
| |
| |
| if (VOS_STATUS_SUCCESS != WLANSAP_RegisterMgmtFrame( |
| #ifdef WLAN_FEATURE_MBSSID |
| WLAN_HDD_GET_SAP_CTX_PTR(pAdapter), |
| #else |
| (WLAN_HDD_GET_CTX(pAdapter))->pvosContext, |
| #endif |
| (SIR_MAC_MGMT_FRAME << 2) | ( SIR_MAC_MGMT_PROBE_REQ << 4), |
| NULL, 0)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: WLANSAP_RegisterMgmtFrame returned fail", __func__); |
| WLANSAP_CancelRemainOnChannel( |
| #ifdef WLAN_FEATURE_MBSSID |
| WLAN_HDD_GET_SAP_CTX_PTR(pAdapter)); |
| #else |
| (WLAN_HDD_GET_CTX(pAdapter))->pvosContext); |
| #endif |
| vos_runtime_pm_allow_suspend(pHddCtx->runtime_context.roc); |
| hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC); |
| return -EINVAL; |
| } |
| |
| } |
| wlan_hdd_start_stop_tdls_source_timer(pHddCtx, eTDLS_SUPPORT_DISABLED); |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_roc_request_enqueue() - enqueue remain on channel request |
| * @adapter: Pointer to the adapter |
| * @remain_chan_ctx: Pointer to the remain on channel context |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int wlan_hdd_roc_request_enqueue(hdd_adapter_t *adapter, |
| hdd_remain_on_chan_ctx_t *remain_chan_ctx) |
| { |
| hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| hdd_roc_req_t *hdd_roc_req; |
| VOS_STATUS status; |
| |
| /* |
| * "Driver is busy" OR "there is already RoC request inside the queue" |
| * so enqueue this RoC Request and execute sequentially later. |
| */ |
| |
| hdd_roc_req = vos_mem_malloc(sizeof(*hdd_roc_req)); |
| |
| if (NULL == hdd_roc_req) { |
| hddLog(LOGP, FL("malloc failed for roc req context")); |
| return -ENOMEM; |
| } |
| |
| hdd_roc_req->pAdapter = adapter; |
| hdd_roc_req->pRemainChanCtx = remain_chan_ctx; |
| |
| /* Enqueue this RoC request */ |
| spin_lock(&hdd_ctx->hdd_roc_req_q.lock); |
| status = hdd_list_insert_back(&hdd_ctx->hdd_roc_req_q, |
| &hdd_roc_req->node); |
| spin_unlock(&hdd_ctx->hdd_roc_req_q.lock); |
| |
| if (VOS_STATUS_SUCCESS != status) { |
| hddLog(LOGP, FL("Not able to enqueue RoC Req context")); |
| vos_mem_free(hdd_roc_req); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_indicate_roc_drop() - Indicate roc drop to userspace |
| * @adapter: HDD adapter |
| * @ctx: Remain on channel context |
| * |
| * Send remain on channel ready and cancel event for the queued |
| * roc that is being dropped. This will ensure that the userspace |
| * will send more roc requests. If this drop is not indicated to |
| * userspace, subsequent roc will not be sent to the driver since |
| * the userspace times out waiting for the remain on channel ready |
| * event. |
| * |
| * Return: None |
| */ |
| void wlan_hdd_indicate_roc_drop(hdd_adapter_t *adapter, |
| hdd_remain_on_chan_ctx_t *ctx) |
| { |
| hddLog(LOG1, FL("indicate roc drop to userspace")); |
| cfg80211_ready_on_channel( |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) |
| adapter->dev->ieee80211_ptr, |
| #else |
| adapter->dev, |
| #endif |
| (uintptr_t)ctx, |
| &ctx->chan, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)) |
| ctx->chan_type, |
| #endif |
| ctx->duration, GFP_KERNEL); |
| |
| cfg80211_remain_on_channel_expired( |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) |
| ctx->dev->ieee80211_ptr, |
| #else |
| ctx->dev, |
| #endif |
| ctx->cookie, |
| &ctx->chan, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)) |
| ctx->chan_type, |
| #endif |
| GFP_KERNEL); |
| } |
| |
| /** |
| * wlan_hdd_roc_request_dequeue() - dequeue remain on channel request |
| * @work: Pointer to work queue struct |
| * |
| * Return: none |
| */ |
| void wlan_hdd_roc_request_dequeue(struct work_struct *work) |
| { |
| VOS_STATUS status; |
| int ret = 0; |
| hdd_roc_req_t *hdd_roc_req; |
| hdd_context_t *hdd_ctx = |
| container_of(work, hdd_context_t, rocReqWork.work); |
| |
| if (0 != (wlan_hdd_validate_context(hdd_ctx))) |
| return; |
| |
| /* |
| * The queued roc requests is dequeued and processed one at a time. |
| * Callback 'wlan_hdd_remain_on_channel_callback' ensures |
| * that any pending roc in the queue will be scheduled |
| * on the current roc completion by scheduling the work queue. |
| */ |
| spin_lock(&hdd_ctx->hdd_roc_req_q.lock); |
| if (list_empty(&hdd_ctx->hdd_roc_req_q.anchor)) { |
| spin_unlock(&hdd_ctx->hdd_roc_req_q.lock); |
| return; |
| } |
| status = hdd_list_remove_front(&hdd_ctx->hdd_roc_req_q, |
| (hdd_list_node_t **) &hdd_roc_req); |
| spin_unlock(&hdd_ctx->hdd_roc_req_q.lock); |
| if (VOS_STATUS_SUCCESS != status) { |
| hddLog(LOG1, FL("unable to remove roc element from list")); |
| return; |
| } |
| ret = wlan_hdd_execute_remain_on_channel( |
| hdd_roc_req->pAdapter, |
| hdd_roc_req->pRemainChanCtx); |
| if (ret == -EBUSY) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("dropping RoC request")); |
| wlan_hdd_indicate_roc_drop(hdd_roc_req->pAdapter, |
| hdd_roc_req->pRemainChanCtx); |
| vos_mem_free(hdd_roc_req->pRemainChanCtx); |
| } |
| vos_mem_free(hdd_roc_req); |
| } |
| |
| static int wlan_hdd_request_remain_on_channel( struct wiphy *wiphy, |
| struct net_device *dev, |
| struct ieee80211_channel *chan, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS) |
| enum nl80211_channel_type channel_type, |
| #endif |
| unsigned int duration, u64 *cookie, |
| rem_on_channel_request_type_t request_type ) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = NULL; |
| hdd_remain_on_chan_ctx_t *pRemainChanCtx; |
| v_BOOL_t isBusy = VOS_FALSE; |
| v_SIZE_t size = 0; |
| hdd_adapter_t *sta_adapter; |
| int ret = 0; |
| int status = 0; |
| |
| hddLog(LOG1, FL("Device_mode %s(%d)"), |
| hdd_device_mode_to_string(pAdapter->device_mode), |
| pAdapter->device_mode); |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS) |
| hddLog( LOG1, |
| "chan(hw_val)0x%x chan(centerfreq) %d chan type 0x%x, duration %d", |
| chan->hw_value, chan->center_freq, channel_type, duration ); |
| #else |
| hddLog( LOG1, |
| "chan(hw_val)0x%x chan(centerfreq) %d, duration %d", |
| chan->hw_value, chan->center_freq, duration ); |
| #endif |
| |
| /* |
| * When P2P-GO and if we are trying to unload the driver then |
| * wlan driver is keep on receiving the remain on channel command |
| * and which is resulting in crash. So not allowing any remain on |
| * channel requets when Load/Unload is in progress |
| */ |
| pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| ret = wlan_hdd_validate_context(pHddCtx); |
| if (0 != ret) |
| return ret; |
| |
| if (hdd_isConnectionInProgress((hdd_context_t *)pAdapter->pHddCtx, NULL, |
| NULL)) { |
| hddLog(LOGE, FL("Connection is in progress")); |
| isBusy = VOS_TRUE; |
| } |
| |
| pRemainChanCtx = vos_mem_malloc(sizeof(hdd_remain_on_chan_ctx_t)); |
| if (NULL == pRemainChanCtx) { |
| hddLog(VOS_TRACE_LEVEL_FATAL, |
| "%s: Not able to allocate memory for Channel context", |
| __func__); |
| return -ENOMEM; |
| } |
| |
| vos_mem_zero(pRemainChanCtx, sizeof(*pRemainChanCtx)); |
| vos_mem_copy(&pRemainChanCtx->chan, chan, |
| sizeof(struct ieee80211_channel)); |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS) |
| pRemainChanCtx->chan_type = channel_type; |
| #endif |
| pRemainChanCtx->duration = duration; |
| pRemainChanCtx->p2pRemOnChanTimeStamp = vos_timer_get_system_time(); |
| pRemainChanCtx->dev = dev; |
| *cookie = (uintptr_t) pRemainChanCtx; |
| pRemainChanCtx->cookie = *cookie; |
| pRemainChanCtx->rem_on_chan_request = request_type; |
| |
| pRemainChanCtx->action_pkt_buff.freq = 0; |
| pRemainChanCtx->action_pkt_buff.frame_ptr = NULL; |
| pRemainChanCtx->action_pkt_buff.frame_length = 0; |
| pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress = FALSE; |
| |
| if (REMAIN_ON_CHANNEL_REQUEST == request_type) { |
| sta_adapter = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION); |
| if ((NULL != sta_adapter)&& |
| hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter))) { |
| if (pAdapter->lastRocTs !=0 && |
| ((vos_timer_get_system_time() - pAdapter->lastRocTs ) |
| < pHddCtx->cfg_ini->p2p_listen_defer_interval)) { |
| if (pRemainChanCtx->duration > HDD_P2P_MAX_ROC_DURATION) |
| pRemainChanCtx->duration = HDD_P2P_MAX_ROC_DURATION; |
| |
| wlan_hdd_roc_request_enqueue(pAdapter, pRemainChanCtx); |
| schedule_delayed_work(&pHddCtx->rocReqWork, |
| msecs_to_jiffies(pHddCtx->cfg_ini->p2p_listen_defer_interval)); |
| hddLog(LOG1, "Defer interval is %hu, pAdapter %pK", |
| pHddCtx->cfg_ini->p2p_listen_defer_interval, pAdapter); |
| return 0; |
| } |
| } |
| } |
| |
| /* Check roc_req_Q has pending RoC Request or not */ |
| hdd_list_size(&(pHddCtx->hdd_roc_req_q), &size); |
| |
| if ((isBusy == VOS_FALSE) && (!size)) { |
| /* Media is free and no RoC request is in queue, execute directly */ |
| status = wlan_hdd_execute_remain_on_channel(pAdapter, |
| pRemainChanCtx); |
| if (status == -EBUSY) { |
| if (wlan_hdd_roc_request_enqueue(pAdapter, pRemainChanCtx)) { |
| vos_mem_free(pRemainChanCtx); |
| return -EAGAIN; |
| } |
| } |
| return 0; |
| } else { |
| if (wlan_hdd_roc_request_enqueue(pAdapter, pRemainChanCtx)) { |
| vos_mem_free(pRemainChanCtx); |
| return -EAGAIN; |
| } |
| } |
| |
| /* |
| * If a connection is not in progress (isBusy), before scheduling |
| * the work queue it is necessary to check if a roc in in progress |
| * or not because: if an roc is in progress, the dequeued roc |
| * that will be processed will be dropped. To ensure that this new |
| * roc request is not dropped, it is suggested to check if an roc |
| * is in progress or not. The existing roc completion will provide |
| * the trigger to dequeue the next roc request. |
| */ |
| if (isBusy == VOS_FALSE && pAdapter->is_roc_inprogress == false) { |
| hddLog(LOG1, FL("scheduling delayed work: no connection/roc active")); |
| schedule_delayed_work(&pHddCtx->rocReqWork, 0); |
| } |
| return 0; |
| } |
| |
| int __wlan_hdd_cfg80211_remain_on_channel( struct wiphy *wiphy, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| struct wireless_dev *wdev, |
| #else |
| struct net_device *dev, |
| #endif |
| struct ieee80211_channel *chan, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS) |
| enum nl80211_channel_type channel_type, |
| #endif |
| unsigned int duration, u64 *cookie ) |
| { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| struct net_device *dev = wdev->netdev; |
| #endif |
| hdd_adapter_t *pAdapter; |
| hdd_context_t *hdd_ctx; |
| int ret; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_ctx = WLAN_HDD_GET_CTX(pAdapter); |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_REMAIN_ON_CHANNEL, |
| pAdapter->sessionId, REMAIN_ON_CHANNEL_REQUEST)); |
| ret = wlan_hdd_request_remain_on_channel(wiphy, dev, |
| chan, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS) |
| channel_type, |
| #endif |
| duration, cookie, |
| REMAIN_ON_CHANNEL_REQUEST); |
| EXIT(); |
| return ret; |
| } |
| |
| int wlan_hdd_cfg80211_remain_on_channel( struct wiphy *wiphy, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| struct wireless_dev *wdev, |
| #else |
| struct net_device *dev, |
| #endif |
| struct ieee80211_channel *chan, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS) |
| enum nl80211_channel_type channel_type, |
| #endif |
| unsigned int duration, u64 *cookie ) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_remain_on_channel(wiphy, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| wdev, |
| #else |
| dev, |
| #endif |
| chan, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS) |
| channel_type, |
| #endif |
| duration, cookie); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| |
| void hdd_remainChanReadyHandler( hdd_adapter_t *pAdapter ) |
| { |
| hdd_cfg80211_state_t *cfgState = NULL; |
| hdd_remain_on_chan_ctx_t* pRemainChanCtx = NULL; |
| VOS_STATUS status; |
| |
| if (NULL == pAdapter) |
| { |
| hddLog(LOGE, FL("pAdapter is NULL")); |
| return; |
| } |
| cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter ); |
| hddLog( LOG1, "Ready on chan ind"); |
| |
| pAdapter->startRocTs = vos_timer_get_system_time(); |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| pRemainChanCtx = cfgState->remain_on_chan_ctx; |
| if( pRemainChanCtx != NULL ) |
| { |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_REMAINCHANREADYHANDLER, |
| pAdapter->sessionId, pRemainChanCtx->duration)); |
| |
| // Removing READY_EVENT_PROPOGATE_TIME from current time which gives |
| // more accurate Remain on Channel start time. |
| pRemainChanCtx->p2pRemOnChanTimeStamp = |
| vos_timer_get_system_time() - READY_EVENT_PROPOGATE_TIME; |
| |
| //start timer for actual duration |
| if(VOS_TIMER_STATE_RUNNING == |
| vos_timer_getCurrentState(&pRemainChanCtx->hdd_remain_on_chan_timer)) |
| { |
| hddLog( LOGE, "Timer Started before ready event!!!"); |
| if (!VOS_IS_STATUS_SUCCESS(vos_timer_stop( |
| &pRemainChanCtx->hdd_remain_on_chan_timer))) |
| hddLog( LOGE, FL("Failed to stop hdd_remain_on_chan_timer")); |
| } |
| status = vos_timer_start(&pRemainChanCtx->hdd_remain_on_chan_timer, |
| (pRemainChanCtx->duration + COMPLETE_EVENT_PROPOGATE_TIME)); |
| if (status != VOS_STATUS_SUCCESS) |
| { |
| hddLog( LOGE, "%s: Remain on Channel timer start failed", |
| __func__); |
| } |
| |
| if( REMAIN_ON_CHANNEL_REQUEST == pRemainChanCtx->rem_on_chan_request) |
| { |
| cfg80211_ready_on_channel( |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| pAdapter->dev->ieee80211_ptr, |
| #else |
| pAdapter->dev, |
| #endif |
| (uintptr_t)pRemainChanCtx, |
| &pRemainChanCtx->chan, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS) |
| pRemainChanCtx->chan_type, |
| #endif |
| pRemainChanCtx->duration, GFP_KERNEL ); |
| } |
| else if( OFF_CHANNEL_ACTION_TX == pRemainChanCtx->rem_on_chan_request) |
| { |
| complete(&pAdapter->offchannel_tx_event); |
| } |
| |
| // Check for cached action frame |
| if(pRemainChanCtx->action_pkt_buff.frame_length != 0) |
| { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0)) || defined(WITH_BACKPORTS) |
| cfg80211_rx_mgmt(pAdapter->dev->ieee80211_ptr, |
| pRemainChanCtx->action_pkt_buff.freq, 0, |
| pRemainChanCtx->action_pkt_buff.frame_ptr, |
| pRemainChanCtx->action_pkt_buff.frame_length, |
| NL80211_RXMGMT_FLAG_ANSWERED); |
| #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0)) |
| cfg80211_rx_mgmt(pAdapter->dev->ieee80211_ptr, |
| pRemainChanCtx->action_pkt_buff.freq, 0, |
| pRemainChanCtx->action_pkt_buff.frame_ptr, |
| pRemainChanCtx->action_pkt_buff.frame_length, |
| NL80211_RXMGMT_FLAG_ANSWERED, GFP_ATOMIC); |
| #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) |
| cfg80211_rx_mgmt( pAdapter->dev->ieee80211_ptr,pRemainChanCtx->action_pkt_buff.freq, 0, |
| pRemainChanCtx->action_pkt_buff.frame_ptr, |
| pRemainChanCtx->action_pkt_buff.frame_length, |
| GFP_ATOMIC ); |
| #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) |
| cfg80211_rx_mgmt( pAdapter->dev, pRemainChanCtx->action_pkt_buff.freq, 0, |
| pRemainChanCtx->action_pkt_buff.frame_ptr, |
| pRemainChanCtx->action_pkt_buff.frame_length, |
| GFP_ATOMIC ); |
| #else |
| cfg80211_rx_mgmt( pAdapter->dev, pRemainChanCtx->action_pkt_buff.freq, |
| pRemainChanCtx->action_pkt_buff.frame_ptr, |
| pRemainChanCtx->action_pkt_buff.frame_length, |
| GFP_ATOMIC ); |
| #endif /* LINUX_VERSION_CODE */ |
| |
| vos_mem_free(pRemainChanCtx->action_pkt_buff.frame_ptr); |
| pRemainChanCtx->action_pkt_buff.frame_length = 0; |
| pRemainChanCtx->action_pkt_buff.freq = 0; |
| pRemainChanCtx->action_pkt_buff.frame_ptr = NULL; |
| } |
| complete(&pAdapter->rem_on_chan_ready_event); |
| } |
| else |
| { |
| hddLog( LOGW, "%s: No Pending Remain on channel Request", __func__); |
| } |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| return; |
| } |
| |
| int __wlan_hdd_cfg80211_cancel_remain_on_channel( struct wiphy *wiphy, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| struct wireless_dev *wdev, |
| #else |
| struct net_device *dev, |
| #endif |
| u64 cookie ) |
| { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| struct net_device *dev = wdev->netdev; |
| #endif |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter ); |
| hdd_remain_on_chan_ctx_t *pRemainChanCtx; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter ); |
| int status; |
| unsigned long rc; |
| hdd_list_node_t *tmp, *q; |
| hdd_roc_req_t *curr_roc_req; |
| |
| ENTER(); |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return status; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| /* Remove RoC request inside queue */ |
| spin_lock(&pHddCtx->hdd_roc_req_q.lock); |
| list_for_each_safe(tmp, q, &pHddCtx->hdd_roc_req_q.anchor) { |
| curr_roc_req = list_entry(tmp, hdd_roc_req_t, node); |
| if ((uintptr_t)curr_roc_req->pRemainChanCtx == cookie) { |
| status = hdd_list_remove_node(&pHddCtx->hdd_roc_req_q, |
| (hdd_list_node_t*)curr_roc_req); |
| spin_unlock(&pHddCtx->hdd_roc_req_q.lock); |
| if (status == VOS_STATUS_SUCCESS) { |
| vos_mem_free(curr_roc_req->pRemainChanCtx); |
| vos_mem_free(curr_roc_req); |
| } |
| return 0; |
| } |
| } |
| spin_unlock(&pHddCtx->hdd_roc_req_q.lock); |
| |
| /* FIXME cancel currently running remain on chan. |
| * Need to check cookie and cancel accordingly |
| */ |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| pRemainChanCtx = cfgState->remain_on_chan_ctx; |
| |
| if (pRemainChanCtx) { |
| hddLog(LOGE, |
| "action_cookie = %08llx, roc cookie = %08llx, cookie = %08llx", |
| cfgState->action_cookie, pRemainChanCtx->cookie, cookie); |
| |
| if (pRemainChanCtx->cookie == cookie) { |
| /* request to cancel on-going roc */ |
| if (cfgState->buf) { |
| /* Tx frame pending */ |
| if (cfgState->action_cookie != cookie) { |
| hddLog( LOGE, |
| FL("Cookie matched with RoC cookie but not with tx cookie, indicate expired event for roc")); |
| /* RoC was extended to accomodate the tx frame */ |
| if (REMAIN_ON_CHANNEL_REQUEST == |
| pRemainChanCtx->rem_on_chan_request) { |
| cfg80211_remain_on_channel_expired(pRemainChanCtx->dev-> |
| ieee80211_ptr, |
| pRemainChanCtx->cookie, |
| &pRemainChanCtx->chan, |
| GFP_KERNEL); |
| } |
| pRemainChanCtx->rem_on_chan_request = OFF_CHANNEL_ACTION_TX; |
| pRemainChanCtx->cookie = cfgState->action_cookie; |
| return 0; |
| } |
| } |
| } else if (cfgState->buf && cfgState->action_cookie == cookie) { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog( LOGE, |
| FL("Cookie not matched with RoC cookie but matched with tx cookie, cleanup action frame")); |
| /*free the buf and return 0*/ |
| hdd_cleanup_actionframe(pHddCtx, pAdapter); |
| return 0; |
| } else { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog( LOGE, FL("No matching cookie")); |
| return -EINVAL; |
| } |
| } else { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog( LOGE, FL("RoC context is NULL, return success")); |
| return 0; |
| } |
| |
| if (NULL != cfgState->remain_on_chan_ctx) |
| { |
| if (!VOS_IS_STATUS_SUCCESS(vos_timer_stop( |
| &cfgState->remain_on_chan_ctx->hdd_remain_on_chan_timer))) |
| hddLog( LOGE, FL("Failed to stop hdd_remain_on_chan_timer")); |
| if (TRUE == pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress) |
| { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog( LOG1, |
| FL("ROC timer cancellation in progress," |
| " wait for completion")); |
| rc = wait_for_completion_timeout( |
| &pAdapter->cancel_rem_on_chan_var, |
| msecs_to_jiffies(WAIT_CANCEL_REM_CHAN)); |
| if (!rc) { |
| hddLog( LOGE, |
| "%s:wait on cancel_rem_on_chan_var timed out", |
| __func__); |
| } |
| return 0; |
| } |
| else |
| pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress = TRUE; |
| } |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| |
| /* wait until remain on channel ready event received |
| * for already issued remain on channel request */ |
| rc = wait_for_completion_timeout(&pAdapter->rem_on_chan_ready_event, |
| msecs_to_jiffies(WAIT_REM_CHAN_READY)); |
| if (!rc) { |
| hddLog( LOGE, |
| "%s: timeout waiting for remain on channel ready indication", |
| __func__); |
| |
| if (pHddCtx->isLogpInProgress) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: LOGP in Progress. Ignore!!!", __func__); |
| return -EAGAIN; |
| } |
| vos_flush_logs(WLAN_LOG_TYPE_FATAL, |
| WLAN_LOG_INDICATOR_HOST_DRIVER, |
| WLAN_LOG_REASON_HDD_TIME_OUT, |
| DUMP_VOS_TRACE); |
| } |
| INIT_COMPLETION(pAdapter->cancel_rem_on_chan_var); |
| /* Issue abort remain on chan request to sme. |
| * The remain on channel callback will make sure the remain_on_chan |
| * expired event is sent. |
| */ |
| if ((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) || |
| (WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) || |
| (WLAN_HDD_P2P_DEVICE == pAdapter->device_mode)) { |
| tANI_U8 sessionId = pAdapter->sessionId; |
| hdd_delete_all_action_frame_cookies(pAdapter); |
| sme_CancelRemainOnChannel( WLAN_HDD_GET_HAL_CTX( pAdapter ), |
| sessionId ); |
| } else if ((WLAN_HDD_SOFTAP== pAdapter->device_mode) || |
| (WLAN_HDD_P2P_GO == pAdapter->device_mode)) { |
| WLANSAP_CancelRemainOnChannel( |
| #ifdef WLAN_FEATURE_MBSSID |
| WLAN_HDD_GET_SAP_CTX_PTR(pAdapter)); |
| #else |
| (WLAN_HDD_GET_CTX(pAdapter))->pvosContext); |
| #endif |
| |
| } else { |
| hddLog(LOGE, FL("Invalid device_mode %s(%d)"), |
| hdd_device_mode_to_string(pAdapter->device_mode), |
| pAdapter->device_mode); |
| return -EIO; |
| } |
| rc = wait_for_completion_timeout(&pAdapter->cancel_rem_on_chan_var, |
| msecs_to_jiffies(WAIT_CANCEL_REM_CHAN)); |
| if (!rc) { |
| hddLog( LOGE, |
| "%s:wait on cancel_rem_on_chan_var timed out ", __func__); |
| } |
| vos_runtime_pm_allow_suspend(pHddCtx->runtime_context.roc); |
| hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC); |
| EXIT(); |
| return 0; |
| } |
| |
| int wlan_hdd_cfg80211_cancel_remain_on_channel( struct wiphy *wiphy, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| struct wireless_dev *wdev, |
| #else |
| struct net_device *dev, |
| #endif |
| u64 cookie ) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_cancel_remain_on_channel(wiphy, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| wdev, |
| #else |
| dev, |
| #endif |
| cookie); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| int __wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, |
| struct ieee80211_channel *chan, bool offchan, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS) |
| enum nl80211_channel_type channel_type, |
| bool channel_type_valid, |
| #endif |
| unsigned int wait, |
| const u8 *buf, size_t len, bool no_cck, |
| bool dont_wait_for_ack, u64 *cookie ) |
| #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) |
| int __wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, |
| struct ieee80211_channel *chan, bool offchan, |
| enum nl80211_channel_type channel_type, |
| bool channel_type_valid, unsigned int wait, |
| const u8 *buf, size_t len, bool no_cck, |
| bool dont_wait_for_ack, u64 *cookie ) |
| #else |
| int __wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, |
| struct ieee80211_channel *chan, bool offchan, |
| enum nl80211_channel_type channel_type, |
| bool channel_type_valid, unsigned int wait, |
| const u8 *buf, size_t len, u64 *cookie ) |
| #endif /* LINUX_VERSION_CODE */ |
| { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| struct net_device *dev = wdev->netdev; |
| #endif |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( dev ); |
| hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter ); |
| hdd_remain_on_chan_ctx_t *pRemainChanCtx; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter ); |
| tANI_U16 extendedWait = 0; |
| tANI_U8 type; |
| tANI_U8 subType; |
| tActionFrmType actionFrmType; |
| bool noack = 0; |
| int status; |
| unsigned long rc; |
| hdd_adapter_t *goAdapter; |
| uint8_t home_ch = 0; |
| bool enb_random_mac = false; |
| uint32_t mgmt_hdr_len = sizeof(struct ieee80211_hdr_3addr); |
| |
| ENTER(); |
| |
| if (len < mgmt_hdr_len + 1) { |
| hddLog(LOGE, FL("Invalid Length")); |
| return -EINVAL; |
| } |
| |
| type = WLAN_HDD_GET_TYPE_FRM_FC(buf[0]); |
| subType = WLAN_HDD_GET_SUBTYPE_FRM_FC(buf[0]); |
| |
| hddLog(LOG1, FL("wait: %d, offchan: %d"), wait, offchan); |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_ACTION, pAdapter->sessionId, |
| pAdapter->device_mode )); |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return status; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| hddLog(LOG1, FL("Device_mode %s(%d) type: %d"), |
| hdd_device_mode_to_string(pAdapter->device_mode), |
| pAdapter->device_mode, type); |
| |
| if (type == SIR_MAC_MGMT_FRAME && subType == SIR_MAC_MGMT_ACTION && |
| len > IEEE80211_MIN_ACTION_SIZE) |
| hddLog(LOG1, FL("category: %d, actionID: %d"), |
| buf[WLAN_HDD_PUBLIC_ACTION_FRAME_BODY_OFFSET + |
| WLAN_HDD_PUBLIC_ACTION_FRAME_CATEGORY_OFFSET], |
| buf[WLAN_HDD_PUBLIC_ACTION_FRAME_BODY_OFFSET + |
| WLAN_HDD_PUBLIC_ACTION_FRAME_ACTION_OFFSET]); |
| |
| #ifdef WLAN_FEATURE_P2P_DEBUG |
| if ((type == SIR_MAC_MGMT_FRAME) && |
| (subType == SIR_MAC_MGMT_ACTION) && |
| wlan_hdd_is_type_p2p_action( |
| &buf[WLAN_HDD_PUBLIC_ACTION_FRAME_BODY_OFFSET], |
| len - mgmt_hdr_len)) |
| { |
| actionFrmType = buf[WLAN_HDD_PUBLIC_ACTION_FRAME_TYPE_OFFSET]; |
| if(actionFrmType >= MAX_P2P_ACTION_FRAME_TYPE) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"[P2P] unknown[%d] ---> OTA to " |
| MAC_ADDRESS_STR, actionFrmType, |
| MAC_ADDR_ARRAY(&buf[WLAN_HDD_80211_FRM_DA_OFFSET])); |
| } |
| else |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"[P2P] %s ---> OTA to " |
| MAC_ADDRESS_STR, p2p_action_frame_type[actionFrmType], |
| MAC_ADDR_ARRAY(&buf[WLAN_HDD_80211_FRM_DA_OFFSET])); |
| if( (actionFrmType == WLAN_HDD_PROV_DIS_REQ) && |
| (globalP2PConnectionStatus == P2P_NOT_ACTIVE) ) |
| { |
| globalP2PConnectionStatus = P2P_GO_NEG_PROCESS; |
| hddLog(LOGE,"[P2P State]Inactive state to " |
| "GO negotiation progress state"); |
| } |
| else if( (actionFrmType == WLAN_HDD_GO_NEG_CNF) && |
| (globalP2PConnectionStatus == P2P_GO_NEG_PROCESS) ) |
| { |
| globalP2PConnectionStatus = P2P_GO_NEG_COMPLETED; |
| hddLog(LOGE,"[P2P State]GO nego progress to GO nego" |
| " completed state"); |
| } |
| } |
| } |
| #endif |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) || defined(WITH_BACKPORTS) |
| noack = dont_wait_for_ack; |
| #endif |
| |
| //If the wait is coming as 0 with off channel set |
| //then set the wait to 200 ms |
| if (offchan && !wait) |
| { |
| wait = ACTION_FRAME_DEFAULT_WAIT; |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| if (cfgState->remain_on_chan_ctx) |
| { |
| tANI_U32 current_time = vos_timer_get_system_time(); |
| int remaining_roc_time = |
| ((int) cfgState->remain_on_chan_ctx->duration - |
| (current_time - pAdapter->startRocTs)); |
| if ( remaining_roc_time > ACTION_FRAME_DEFAULT_WAIT) |
| wait = remaining_roc_time; |
| } |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| } |
| |
| if ((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) && |
| (type == SIR_MAC_MGMT_FRAME && subType == SIR_MAC_MGMT_PROBE_RSP)) { |
| /* Drop Probe response recieved from supplicant in sta mode */ |
| goto err_rem_channel; |
| } |
| |
| //Call sme API to send out a action frame. |
| // OR can we send it directly through data path?? |
| // After tx completion send tx status back. |
| if ( ( WLAN_HDD_SOFTAP == pAdapter->device_mode ) || |
| ( WLAN_HDD_P2P_GO == pAdapter->device_mode ) |
| ) |
| { |
| if (type == SIR_MAC_MGMT_FRAME) |
| { |
| if (subType == SIR_MAC_MGMT_PROBE_RSP) |
| { |
| /* Drop Probe response recieved from supplicant, as for GO and |
| SAP PE itself sends probe response |
| */ |
| goto err_rem_channel; |
| } |
| else if ((subType == SIR_MAC_MGMT_DISASSOC) || |
| (subType == SIR_MAC_MGMT_DEAUTH)) |
| { |
| /* During EAP failure or P2P Group Remove supplicant |
| * is sending del_station command to driver. From |
| * del_station function, Driver will send deauth frame to |
| * p2p client. No need to send disassoc frame from here. |
| * so Drop the frame here and send tx indication back to |
| * supplicant. |
| */ |
| tANI_U8 dstMac[ETH_ALEN] = {0}; |
| memcpy(&dstMac, &buf[WLAN_HDD_80211_FRM_DA_OFFSET], ETH_ALEN); |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "%s: Deauth/Disassoc received for STA:" |
| MAC_ADDRESS_STR, |
| __func__, |
| MAC_ADDR_ARRAY(dstMac)); |
| goto err_rem_channel; |
| } |
| } |
| } |
| |
| if( NULL != cfgState->buf ) |
| { |
| if ( !noack ) |
| { |
| hddLog( LOGE, "(%s):Previous P2P Action frame packet pending", |
| __func__); |
| hdd_cleanup_actionframe(pAdapter->pHddCtx, pAdapter); |
| } |
| else |
| { |
| hddLog( LOGE, "(%s):Pending Action frame packet return EBUSY", |
| __func__); |
| return -EBUSY; |
| } |
| } |
| |
| if( subType == SIR_MAC_MGMT_ACTION) |
| { |
| hddLog( LOG1, "Action frame tx request : %s", |
| hdd_getActionString(buf[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET])); |
| } |
| |
| if ((pAdapter->device_mode == WLAN_HDD_SOFTAP) && |
| (test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags))) { |
| home_ch = pAdapter->sessionCtx.ap.operatingChannel; |
| } else if ((pAdapter->device_mode == WLAN_HDD_INFRA_STATION) && |
| (pAdapter->sessionCtx.station.conn_info.connState == |
| eConnectionState_Associated)) { |
| home_ch = pAdapter->sessionCtx.station.conn_info.operationChannel; |
| } else { |
| goAdapter = hdd_get_adapter( pAdapter->pHddCtx, WLAN_HDD_P2P_GO ); |
| if (goAdapter && |
| (test_bit(SOFTAP_BSS_STARTED, &goAdapter->event_flags))) |
| home_ch = goAdapter->sessionCtx.ap.operatingChannel; |
| } |
| |
| |
| if (ieee80211_frequency_to_channel(chan->center_freq) == home_ch) { |
| /* if adapter is already on requested ch, no need for ROC */ |
| wait = 0; |
| hddLog(LOGE, |
| FL("Adapter already on requested channel. No ROC requested")); |
| goto send_frame; |
| } |
| |
| if( offchan && wait) |
| { |
| int status; |
| rem_on_channel_request_type_t req_type = OFF_CHANNEL_ACTION_TX; |
| // In case of P2P Client mode if we are already |
| // on the same channel then send the frame directly |
| |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| pRemainChanCtx = cfgState->remain_on_chan_ctx; |
| if ((type == SIR_MAC_MGMT_FRAME) && |
| (subType == SIR_MAC_MGMT_ACTION) && |
| hdd_p2p_is_action_type_rsp( |
| &buf[WLAN_HDD_PUBLIC_ACTION_FRAME_BODY_OFFSET], |
| len - mgmt_hdr_len) && |
| cfgState->remain_on_chan_ctx && |
| cfgState->current_freq == chan->center_freq ) |
| { |
| if(VOS_TIMER_STATE_RUNNING == vos_timer_getCurrentState( |
| &cfgState->remain_on_chan_ctx->hdd_remain_on_chan_timer)) |
| { |
| /* In the latest wpa_supplicant, the wait time for go |
| * negotiation response is set to 100msec, due to which, |
| * there could be a possibility that, if the go negotaition |
| * confirmation frame is not received within 100 msec, ROC |
| * would be timeout and resulting in connection failures as |
| * the device will not be on the listen channel anymore to |
| * receive the confirmation frame. |
| * Also wpa_supplicant has set the wait to 50msec for go |
| * negotiation confirmation, invitation response and |
| * provisional discovery response frames. So increase the |
| * wait time for all these frames. |
| */ |
| actionFrmType = |
| buf[WLAN_HDD_PUBLIC_ACTION_FRAME_TYPE_OFFSET]; |
| if ( actionFrmType == WLAN_HDD_GO_NEG_RESP || |
| actionFrmType == WLAN_HDD_PROV_DIS_RESP) |
| wait = wait + ACTION_FRAME_RSP_WAIT; |
| else if ( actionFrmType == WLAN_HDD_GO_NEG_CNF || |
| actionFrmType == WLAN_HDD_INVITATION_RESP ) |
| wait = wait + ACTION_FRAME_ACK_WAIT; |
| |
| hddLog( LOG1, "%s: Extending the wait time %d for actionFrmType=%d", |
| __func__,wait,actionFrmType); |
| |
| if (!VOS_IS_STATUS_SUCCESS( |
| vos_timer_stop(&cfgState->remain_on_chan_ctx-> |
| hdd_remain_on_chan_timer))) |
| hddLog( LOGE, FL("Failed to stop hdd_remain_on_chan_timer")); |
| status = vos_timer_start(&cfgState->remain_on_chan_ctx->hdd_remain_on_chan_timer, |
| wait); |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| if(status != VOS_STATUS_SUCCESS) |
| { |
| hddLog( LOGE, "%s: Remain on Channel timer start failed", |
| __func__); |
| } |
| goto send_frame; |
| } else { |
| |
| if( (pRemainChanCtx != NULL) && |
| (pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress == |
| TRUE)) |
| { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "action frame tx: waiting for completion of ROC "); |
| |
| rc = wait_for_completion_timeout( |
| &pAdapter->cancel_rem_on_chan_var, |
| msecs_to_jiffies(WAIT_CANCEL_REM_CHAN)); |
| if (!rc) { |
| hddLog( LOGE, |
| "%s:wait on cancel_rem_on_chan_var timed out", |
| __func__); |
| } |
| |
| } else |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| } |
| } else |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| if((cfgState->remain_on_chan_ctx != NULL) && |
| (cfgState->current_freq == chan->center_freq) |
| ) |
| { |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| hddLog(LOG1,"action frame: extending the wait time"); |
| extendedWait = (tANI_U16)wait; |
| goto send_frame; |
| } |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| INIT_COMPLETION(pAdapter->offchannel_tx_event); |
| status = wlan_hdd_request_remain_on_channel(wiphy, dev, |
| chan, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS) |
| channel_type, |
| #endif |
| wait, cookie, |
| req_type); |
| if(0 != status) |
| { |
| if( (-EBUSY == status) && |
| (cfgState->current_freq == chan->center_freq) ) |
| { |
| goto send_frame; |
| } |
| goto err_rem_channel; |
| } |
| /* This will extend timer in LIM when sending Any action frame |
| * It will cover remain on channel timer till next action frame |
| * in rx direction. |
| */ |
| extendedWait = (tANI_U16)wait; |
| /* Wait for driver to be ready on the requested channel */ |
| rc = wait_for_completion_timeout( |
| &pAdapter->offchannel_tx_event, |
| msecs_to_jiffies(WAIT_CHANGE_CHANNEL_FOR_OFFCHANNEL_TX)); |
| if(!rc) { |
| hddLog( LOGE, "wait on offchannel_tx_event timed out"); |
| goto err_rem_channel; |
| } |
| } |
| else if ( offchan ) |
| { |
| /* Check before sending action frame |
| whether we already remain on channel */ |
| if(NULL == cfgState->remain_on_chan_ctx) |
| { |
| goto err_rem_channel; |
| } |
| } |
| send_frame: |
| |
| if(!noack) |
| { |
| cfgState->buf = vos_mem_malloc( len ); //buf; |
| if( cfgState->buf == NULL ) |
| return -ENOMEM; |
| |
| cfgState->len = len; |
| |
| vos_mem_copy( cfgState->buf, buf, len); |
| |
| mutex_lock(&cfgState->remain_on_chan_ctx_lock); |
| if( cfgState->remain_on_chan_ctx ) |
| { |
| cfgState->action_cookie = cfgState->remain_on_chan_ctx->cookie; |
| *cookie = cfgState->action_cookie; |
| } |
| else |
| { |
| *cookie = (uintptr_t) cfgState->buf; |
| cfgState->action_cookie = *cookie; |
| } |
| mutex_unlock(&cfgState->remain_on_chan_ctx_lock); |
| } |
| |
| if ( (WLAN_HDD_INFRA_STATION == pAdapter->device_mode) || |
| (WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) || |
| ( WLAN_HDD_P2P_DEVICE == pAdapter->device_mode ) |
| ) |
| { |
| tANI_U8 sessionId = pAdapter->sessionId; |
| |
| if ((type == SIR_MAC_MGMT_FRAME) && |
| (subType == SIR_MAC_MGMT_ACTION) && |
| wlan_hdd_is_type_p2p_action( |
| &buf[WLAN_HDD_PUBLIC_ACTION_FRAME_BODY_OFFSET], |
| len - mgmt_hdr_len)) |
| { |
| actionFrmType = buf[WLAN_HDD_PUBLIC_ACTION_FRAME_TYPE_OFFSET]; |
| hddLog(LOG1, "Tx Action Frame %u", actionFrmType); |
| if (actionFrmType == WLAN_HDD_PROV_DIS_REQ) |
| { |
| cfgState->actionFrmState = HDD_PD_REQ_ACK_PENDING; |
| hddLog(LOG1, "%s: HDD_PD_REQ_ACK_PENDING", __func__); |
| } |
| else if (actionFrmType == WLAN_HDD_GO_NEG_REQ) |
| { |
| cfgState->actionFrmState = HDD_GO_NEG_REQ_ACK_PENDING; |
| hddLog(LOG1, "%s: HDD_GO_NEG_REQ_ACK_PENDING", __func__); |
| } |
| } |
| |
| if (!vos_mem_compare((uint8_t *)(&buf[WLAN_HDD_80211_FRM_SA_OFFSET]), |
| &pAdapter->macAddressCurrent, VOS_MAC_ADDR_SIZE)) { |
| hddLog(LOG1, "%s: sa of action frame is randomized with mac-addr: " |
| MAC_ADDRESS_STR, __func__, |
| MAC_ADDR_ARRAY((uint8_t *)(&buf[WLAN_HDD_80211_FRM_SA_OFFSET]))); |
| enb_random_mac = true; |
| } |
| |
| if (enb_random_mac && !noack) |
| hdd_set_action_frame_random_mac(pAdapter, |
| (uint8_t *)(&buf[WLAN_HDD_80211_FRM_SA_OFFSET]), |
| *cookie); |
| |
| if (eHAL_STATUS_SUCCESS != |
| sme_sendAction( WLAN_HDD_GET_HAL_CTX(pAdapter), |
| sessionId, buf, len, extendedWait, noack)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: sme_sendAction returned fail", __func__); |
| goto err; |
| } |
| } |
| else if( ( WLAN_HDD_SOFTAP== pAdapter->device_mode ) || |
| ( WLAN_HDD_P2P_GO == pAdapter->device_mode ) |
| ) |
| { |
| if( VOS_STATUS_SUCCESS != |
| #ifdef WLAN_FEATURE_MBSSID |
| WLANSAP_SendAction( WLAN_HDD_GET_SAP_CTX_PTR(pAdapter), |
| #else |
| WLANSAP_SendAction( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext, |
| #endif |
| buf, len, 0 ) ) |
| |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: WLANSAP_SendAction returned fail", __func__); |
| goto err; |
| } |
| } |
| |
| return 0; |
| err: |
| if(!noack) |
| { |
| if (enb_random_mac && |
| ((pAdapter->device_mode == WLAN_HDD_INFRA_STATION) || |
| (pAdapter->device_mode == WLAN_HDD_P2P_CLIENT) || |
| (pAdapter->device_mode == WLAN_HDD_P2P_DEVICE))) |
| hdd_reset_action_frame_random_mac(pAdapter, |
| (uint8_t *)(&buf[WLAN_HDD_80211_FRM_SA_OFFSET]), |
| *cookie); |
| hdd_sendActionCnf( pAdapter, FALSE ); |
| } |
| return 0; |
| err_rem_channel: |
| *cookie = (uintptr_t)cfgState; |
| cfg80211_mgmt_tx_status( |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| pAdapter->dev->ieee80211_ptr, |
| #else |
| pAdapter->dev, |
| #endif |
| *cookie, buf, len, FALSE, GFP_KERNEL ); |
| EXIT(); |
| return 0; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS) |
| int wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, |
| struct cfg80211_mgmt_tx_params *params, u64 *cookie) |
| #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) |
| int wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, |
| struct ieee80211_channel *chan, bool offchan, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS) |
| enum nl80211_channel_type channel_type, |
| bool channel_type_valid, |
| #endif |
| unsigned int wait, |
| const u8 *buf, size_t len, bool no_cck, |
| bool dont_wait_for_ack, u64 *cookie ) |
| #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) |
| int wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, |
| struct ieee80211_channel *chan, bool offchan, |
| enum nl80211_channel_type channel_type, |
| bool channel_type_valid, unsigned int wait, |
| const u8 *buf, size_t len, bool no_cck, |
| bool dont_wait_for_ack, u64 *cookie ) |
| #else |
| int wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, |
| struct ieee80211_channel *chan, bool offchan, |
| enum nl80211_channel_type channel_type, |
| bool channel_type_valid, unsigned int wait, |
| const u8 *buf, size_t len, u64 *cookie ) |
| #endif /* LINUX_VERSION_CODE */ |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS) |
| ret = __wlan_hdd_mgmt_tx(wiphy, wdev, params->chan, params->offchan, |
| params->wait, params->buf, params->len, |
| params->no_cck, params->dont_wait_for_ack, |
| cookie); |
| #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) |
| ret = __wlan_hdd_mgmt_tx(wiphy, wdev, chan, offchan, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS) |
| channel_type, channel_type_valid, |
| #endif |
| wait, buf, len, no_cck, |
| dont_wait_for_ack, cookie); |
| #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)) |
| ret = __wlan_hdd_mgmt_tx(wiphy, dev, chan, offchan, |
| channel_type, channel_type_valid, wait, |
| buf, len, no_cck, dont_wait_for_ack, cookie); |
| #else |
| ret = __wlan_hdd_mgmt_tx(wiphy, dev, chan, offchan, |
| channel_type, channel_type_valid, wait, |
| buf, len, cookie); |
| #endif /* LINUX_VERSION_CODE */ |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * hdd_wlan_delete_mgmt_tx_cookie() - Wrapper to delete action frame cookie |
| * @wdev: Pointer to wireless device |
| * @cookie: Cookie to be deleted |
| * |
| * This is a wrapper function which actually invokes the hdd api to delete |
| * cookie based on the device mode of adapter. |
| * |
| * Return: 0 - for success else negative value |
| */ |
| static int hdd_wlan_delete_mgmt_tx_cookie(struct wireless_dev *wdev, |
| u64 cookie) |
| { |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| |
| if ((adapter->device_mode == WLAN_HDD_INFRA_STATION) || |
| (adapter->device_mode == WLAN_HDD_P2P_CLIENT) || |
| (adapter->device_mode == WLAN_HDD_P2P_DEVICE)) { |
| hdd_delete_action_frame_cookie(adapter, cookie); |
| } |
| |
| return 0; |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| int __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| u64 cookie) |
| { |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| hdd_wlan_delete_mgmt_tx_cookie(wdev, cookie); |
| |
| return wlan_hdd_cfg80211_cancel_remain_on_channel(wiphy, wdev, cookie); |
| } |
| #else |
| int __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, |
| struct net_device *dev, |
| u64 cookie) |
| { |
| return wlan_hdd_cfg80211_cancel_remain_on_channel(wiphy, dev, cookie); |
| } |
| #endif |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| int wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| u64 cookie) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(wiphy, wdev, cookie); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| #else |
| int wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, |
| struct net_device *dev, |
| u64 cookie) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(wiphy, dev, cookie); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| #endif |
| |
| void hdd_sendActionCnf( hdd_adapter_t *pAdapter, tANI_BOOLEAN actionSendSuccess ) |
| { |
| hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter ); |
| |
| cfgState->actionFrmState = HDD_IDLE; |
| |
| if( NULL == cfgState->buf ) |
| { |
| return; |
| } |
| if (cfgState->is_go_neg_ack_received) { |
| |
| cfgState->is_go_neg_ack_received = 0 ; |
| /* Sometimes its possible that host may receive the ack for GO |
| * negotiation req after sending go negotaition confirmation, |
| * in such case drop the ack received for the go negotiation |
| * request, so that supplicant waits for the confirmation ack |
| * from firmware. |
| */ |
| hddLog( LOG1, FL("Drop the pending ack received in cfgState->actionFrmState %d"), |
| cfgState->actionFrmState); |
| return; |
| } |
| |
| hddLog( LOG1, "Send Action cnf, actionSendSuccess %d", actionSendSuccess); |
| |
| /* |
| * buf is the same pointer it passed us to send. Since we are sending |
| * it through control path, we use different buffers. |
| * In case of mac80211, they just push it to the skb and pass the same |
| * data while sending tx ack status. |
| * */ |
| cfg80211_mgmt_tx_status( |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| pAdapter->dev->ieee80211_ptr, |
| #else |
| pAdapter->dev, |
| #endif |
| cfgState->action_cookie, |
| cfgState->buf, cfgState->len, actionSendSuccess, GFP_KERNEL ); |
| vos_mem_free(cfgState->buf); |
| cfgState->buf = NULL; |
| |
| complete(&pAdapter->tx_action_cnf_event); |
| } |
| |
| /** |
| * hdd_send_action_cnf_cb - action confirmation callback |
| * @session_id: SME session ID |
| * @tx_completed: ack status |
| * |
| * This function invokes hdd_sendActionCnf to update ack status to |
| * supplicant. |
| */ |
| void hdd_send_action_cnf_cb(uint32_t session_id, bool tx_completed) |
| { |
| v_CONTEXT_t vos_context; |
| hdd_context_t *hdd_ctx; |
| hdd_adapter_t *adapter; |
| |
| ENTER(); |
| |
| /* Get the global VOSS context */ |
| vos_context = vos_get_global_context(VOS_MODULE_ID_SYS, NULL); |
| if (!vos_context) { |
| hddLog(LOGE, FL("Global VOS context is Null")); |
| return; |
| } |
| |
| /* Get the HDD context.*/ |
| hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_context); |
| if (0 != wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| |
| adapter = hdd_get_adapter_by_sme_session_id(hdd_ctx, session_id); |
| if (NULL == adapter) { |
| hddLog(LOGE, FL("adapter not found")); |
| return; |
| } |
| |
| if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { |
| hddLog(LOGE, FL("adapter has invalid magic")); |
| return; |
| } |
| |
| hdd_sendActionCnf(adapter, tx_completed) ; |
| } |
| |
| /** |
| * hdd_setP2pNoa |
| * |
| *FUNCTION: |
| * This function is called from hdd_hostapd_ioctl function when Driver |
| * get P2P_SET_NOA command from wpa_supplicant using private ioctl |
| * |
| *LOGIC: |
| * Fill NoA Struct According to P2P Power save Option and Pass it to SME layer |
| * |
| *ASSUMPTIONS: |
| * |
| * |
| *NOTE: |
| * |
| * @param dev Pointer to net device structure |
| * @param command Pointer to command |
| * |
| * @return Status |
| */ |
| |
| int hdd_setP2pNoa( struct net_device *dev, tANI_U8 *command ) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter); |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| tP2pPsConfig NoA; |
| int count, duration, start_time; |
| char *param; |
| int ret; |
| |
| param = strnchr(command, strlen(command), ' '); |
| if (param == NULL) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: strnchr failed to find delimeter", __func__); |
| return -EINVAL; |
| } |
| param++; |
| ret = sscanf(param, "%d %d %d", &count, &start_time, &duration); |
| if (ret != 3) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: P2P_SET GO NoA: fail to read params, ret=%d", |
| __func__, ret); |
| return -EINVAL; |
| } |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: P2P_SET GO NoA: count=%d start_time=%d duration=%d", |
| __func__, count, start_time, duration); |
| duration = MS_TO_MUS(duration); |
| /* PS Selection |
| * Periodic NoA (2) |
| * Single NOA (4) |
| */ |
| NoA.opp_ps = 0; |
| NoA.ctWindow = 0; |
| if (count == 1) |
| { |
| NoA.duration = 0; |
| NoA.single_noa_duration = duration; |
| NoA.psSelection = P2P_POWER_SAVE_TYPE_SINGLE_NOA; |
| } |
| else |
| { |
| NoA.duration = duration; |
| NoA.single_noa_duration = 0; |
| NoA.psSelection = P2P_POWER_SAVE_TYPE_PERIODIC_NOA; |
| } |
| /* NOA interval in TU */ |
| NoA.interval = NOA_INTERVAL_IN_TU; |
| NoA.count = count; |
| NoA.sessionid = pAdapter->sessionId; |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: P2P_PS_ATTR:oppPS %d ctWindow %d duration %d " |
| "interval %d count %d single noa duration %d " |
| "PsSelection %x", __func__, NoA.opp_ps, |
| NoA.ctWindow, NoA.duration, NoA.interval, |
| NoA.count, NoA.single_noa_duration, |
| NoA.psSelection); |
| |
| sme_p2pSetPs(hHal, &NoA); |
| return status; |
| } |
| |
| /** |
| * hdd_setP2pOpps |
| * |
| *FUNCTION: |
| * This function is called from hdd_hostapd_ioctl function when Driver |
| * get P2P_SET_PS command from wpa_supplicant using private ioctl |
| * |
| *LOGIC: |
| * Fill NoA Struct According to P2P Power save Option and Pass it to SME layer |
| * |
| *ASSUMPTIONS: |
| * |
| * |
| *NOTE: |
| * |
| * @param dev Pointer to net device structure |
| * @param command Pointer to command |
| * |
| * @return Status |
| */ |
| |
| int hdd_setP2pOpps( struct net_device *dev, tANI_U8 *command ) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter); |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| tP2pPsConfig NoA; |
| char *param; |
| int legacy_ps, opp_ps, ctwindow; |
| int ret; |
| |
| param = strnchr(command, strlen(command), ' '); |
| if (param == NULL) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: strnchr failed to find delimiter", __func__); |
| return -EINVAL; |
| } |
| param++; |
| ret = sscanf(param, "%d %d %d", &legacy_ps, &opp_ps, &ctwindow); |
| if (ret != 3) { |
| VOS_TRACE (VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: P2P_SET GO PS: fail to read params, ret=%d", |
| __func__, ret); |
| return -EINVAL; |
| } |
| VOS_TRACE (VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: P2P_SET GO PS: legacy_ps=%d opp_ps=%d ctwindow=%d", |
| __func__, legacy_ps, opp_ps, ctwindow); |
| |
| /* PS Selection |
| * Opportunistic Power Save (1) |
| */ |
| |
| /* From wpa_cli user need to use separate command to set ctWindow and Opps |
| * When user want to set ctWindow during that time other parameters |
| * values are coming from wpa_supplicant as -1. |
| * Example : User want to set ctWindow with 30 then wpa_cli command : |
| * P2P_SET ctwindow 30 |
| * Command Received at hdd_hostapd_ioctl is as below: |
| * P2P_SET_PS -1 -1 30 (legacy_ps = -1, opp_ps = -1, ctwindow = 30) |
| */ |
| if (ctwindow != -1) |
| { |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "Opportunistic Power Save is %s", |
| (TRUE == pAdapter->ops) ? "Enable" : "Disable" ); |
| |
| if (ctwindow != pAdapter->ctw) |
| { |
| pAdapter->ctw = ctwindow; |
| |
| if(pAdapter->ops) |
| { |
| NoA.opp_ps = pAdapter->ops; |
| NoA.ctWindow = pAdapter->ctw; |
| NoA.duration = 0; |
| NoA.single_noa_duration = 0; |
| NoA.interval = 0; |
| NoA.count = 0; |
| NoA.psSelection = P2P_POWER_SAVE_TYPE_OPPORTUNISTIC; |
| NoA.sessionid = pAdapter->sessionId; |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: P2P_PS_ATTR:oppPS %d ctWindow %d duration %d " |
| "interval %d count %d single noa duration %d " |
| "PsSelection %x", __func__, NoA.opp_ps, |
| NoA.ctWindow, NoA.duration, NoA.interval, |
| NoA.count, NoA.single_noa_duration, |
| NoA.psSelection); |
| |
| sme_p2pSetPs(hHal, &NoA); |
| } |
| return 0; |
| } |
| } |
| |
| if (opp_ps != -1) |
| { |
| pAdapter->ops = opp_ps; |
| |
| |
| if ((opp_ps != -1) && (pAdapter->ctw)) |
| { |
| NoA.opp_ps = opp_ps; |
| NoA.ctWindow = pAdapter->ctw; |
| NoA.duration = 0; |
| NoA.single_noa_duration = 0; |
| NoA.interval = 0; |
| NoA.count = 0; |
| NoA.psSelection = P2P_POWER_SAVE_TYPE_OPPORTUNISTIC; |
| NoA.sessionid = pAdapter->sessionId; |
| |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: P2P_PS_ATTR:oppPS %d ctWindow %d duration %d " |
| "interval %d count %d single noa duration %d " |
| "PsSelection %x", __func__, NoA.opp_ps, |
| NoA.ctWindow, NoA.duration, NoA.interval, |
| NoA.count, NoA.single_noa_duration, |
| NoA.psSelection); |
| |
| sme_p2pSetPs(hHal, &NoA); |
| } |
| } |
| return status; |
| } |
| |
| int hdd_setP2pPs( struct net_device *dev, void *msgData ) |
| { |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter); |
| VOS_STATUS status = VOS_STATUS_SUCCESS; |
| tP2pPsConfig NoA; |
| p2p_app_setP2pPs_t *pappNoA = (p2p_app_setP2pPs_t *) msgData; |
| |
| NoA.opp_ps = pappNoA->opp_ps; |
| NoA.ctWindow = pappNoA->ctWindow; |
| NoA.duration = pappNoA->duration; |
| NoA.interval = pappNoA->interval; |
| NoA.count = pappNoA->count; |
| NoA.single_noa_duration = pappNoA->single_noa_duration; |
| NoA.psSelection = pappNoA->psSelection; |
| NoA.sessionid = pAdapter->sessionId; |
| |
| sme_p2pSetPs(hHal, &NoA); |
| return status; |
| } |
| |
| static tANI_U8 wlan_hdd_get_session_type( enum nl80211_iftype type ) |
| { |
| tANI_U8 sessionType; |
| |
| switch( type ) |
| { |
| case NL80211_IFTYPE_AP: |
| sessionType = WLAN_HDD_SOFTAP; |
| break; |
| case NL80211_IFTYPE_P2P_GO: |
| sessionType = WLAN_HDD_P2P_GO; |
| break; |
| case NL80211_IFTYPE_P2P_CLIENT: |
| sessionType = WLAN_HDD_P2P_CLIENT; |
| break; |
| case NL80211_IFTYPE_STATION: |
| sessionType = WLAN_HDD_INFRA_STATION; |
| break; |
| case NL80211_IFTYPE_MONITOR: |
| sessionType = WLAN_HDD_MONITOR; |
| break; |
| default: |
| sessionType = WLAN_HDD_INFRA_STATION; |
| break; |
| } |
| |
| return sessionType; |
| } |
| |
| struct wireless_dev* __wlan_hdd_add_virtual_intf( |
| struct wiphy *wiphy, const char *name, |
| unsigned char name_assign_type, |
| enum nl80211_iftype type, |
| u32 *flags, struct vif_params *params ) |
| { |
| hdd_context_t *pHddCtx = (hdd_context_t*) wiphy_priv(wiphy); |
| hdd_adapter_t* pAdapter = NULL; |
| hdd_scaninfo_t *scan_info = NULL; |
| int ret; |
| |
| ENTER(); |
| |
| ret = wlan_hdd_validate_context(pHddCtx); |
| if (0 != ret) |
| return ERR_PTR(ret); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return ERR_PTR(-EINVAL); |
| } |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_ADD_VIRTUAL_INTF, NO_SESSION, type)); |
| /*Allow addition multiple interface for WLAN_HDD_P2P_CLIENT and |
| WLAN_HDD_SOFTAP session type*/ |
| if ((hdd_get_adapter(pHddCtx, wlan_hdd_get_session_type(type)) != NULL) |
| #ifdef WLAN_FEATURE_MBSSID |
| && WLAN_HDD_SOFTAP != wlan_hdd_get_session_type(type) |
| #endif |
| && WLAN_HDD_P2P_CLIENT != wlan_hdd_get_session_type(type) |
| && WLAN_HDD_INFRA_STATION != wlan_hdd_get_session_type(type) |
| ) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Interface type %d already exists. " |
| "Two interfaces of same type are not supported currently.", |
| __func__, type); |
| return ERR_PTR(-EINVAL); |
| } |
| |
| pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION); |
| if (pAdapter != NULL) { |
| scan_info = &pAdapter->scan_info; |
| if ((scan_info != NULL) && (scan_info->mScanPending)) { |
| hdd_abort_mac_scan(pHddCtx, pAdapter->sessionId, |
| eCSR_SCAN_ABORT_DEFAULT); |
| hddLog(LOG1, FL("Abort Scan while adding virtual interface")); |
| } |
| } |
| |
| pAdapter = NULL; |
| |
| wlan_hdd_tdls_disable_offchan_and_teardown_links(pHddCtx); |
| |
| if (pHddCtx->cfg_ini->isP2pDeviceAddrAdministrated && |
| ((NL80211_IFTYPE_P2P_GO == type) || |
| (NL80211_IFTYPE_P2P_CLIENT == type))) |
| { |
| /* Generate the P2P Interface Address. this address must be |
| * different from the P2P Device Address. |
| */ |
| v_MACADDR_t p2pDeviceAddress = pHddCtx->p2pDeviceAddress; |
| p2pDeviceAddress.bytes[4] ^= 0x80; |
| pAdapter = hdd_open_adapter( pHddCtx, |
| wlan_hdd_get_session_type(type), |
| name, p2pDeviceAddress.bytes, |
| name_assign_type, |
| VOS_TRUE ); |
| if (WLAN_HDD_RX_HANDLE_RPS == pHddCtx->cfg_ini->rxhandle) |
| hdd_dp_util_send_rps_ind(pAdapter); |
| } |
| else |
| { |
| pAdapter = hdd_open_adapter( pHddCtx, wlan_hdd_get_session_type(type), |
| name, wlan_hdd_get_intf_addr(pHddCtx), |
| name_assign_type, |
| VOS_TRUE); |
| if (WLAN_HDD_RX_HANDLE_RPS == pHddCtx->cfg_ini->rxhandle) |
| hdd_dp_util_send_rps_ind(pAdapter); |
| } |
| |
| if( NULL == pAdapter) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: hdd_open_adapter failed",__func__); |
| return ERR_PTR(-ENOSPC); |
| } |
| #ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| if(pAdapter->device_mode == WLAN_HDD_SOFTAP) |
| { |
| if(pHddCtx->ch_switch_ctx.chan_sw_timer_initialized == VOS_FALSE) |
| { |
| //Initialize the channel switch timer |
| ret = vos_timer_init(&pHddCtx->ch_switch_ctx.hdd_ap_chan_switch_timer, VOS_TIMER_TYPE_SW, |
| hdd_hostapd_chan_switch_cb, (v_PVOID_t)pAdapter); |
| if(!VOS_IS_STATUS_SUCCESS(ret)) |
| { |
| hddLog(LOGE, FL("Failed to initialize AP channel switch timer!!\n")); |
| EXIT(); |
| return ERR_PTR(ret); |
| } |
| pHddCtx->ch_switch_ctx.chan_sw_timer_initialized = VOS_TRUE; |
| } |
| } |
| #endif //WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN |
| |
| EXIT(); |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| return pAdapter->dev->ieee80211_ptr; |
| #else |
| return pAdapter->dev; |
| #endif |
| } |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) |
| /** |
| * wlan_hdd_add_virtual_intf() - Add virtual interface wrapper |
| * @wiphy: wiphy pointer |
| * @name: User-visible name of the interface |
| * @name_assign_type: the name of assign type of the netdev |
| * @nl80211_iftype: (virtual) interface types |
| * @vif_params: virtual interface parameters (not used) |
| * |
| * Return: the pointer of wireless dev, otherwise NULL. |
| */ |
| struct wireless_dev *wlan_hdd_add_virtual_intf(struct wiphy *wiphy, |
| const char *name, |
| unsigned char name_assign_type, |
| enum nl80211_iftype type, |
| struct vif_params *params) |
| { |
| struct wireless_dev *wdev; |
| |
| vos_ssr_protect(__func__); |
| wdev = __wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type, |
| type, ¶ms->flags, params); |
| vos_ssr_unprotect(__func__); |
| return wdev; |
| } |
| #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) |
| /** |
| * wlan_hdd_add_virtual_intf() - Add virtual interface wrapper |
| * @wiphy: wiphy pointer |
| * @name: User-visible name of the interface |
| * @name_assign_type: the name of assign type of the netdev |
| * @nl80211_iftype: (virtual) interface types |
| * @flags: monitor mode configuration flags (not used) |
| * @vif_params: virtual interface parameters (not used) |
| * |
| * Return: the pointer of wireless dev, otherwise NULL. |
| */ |
| struct wireless_dev *wlan_hdd_add_virtual_intf(struct wiphy *wiphy, |
| const char *name, |
| unsigned char name_assign_type, |
| enum nl80211_iftype type, |
| u32 *flags, |
| struct vif_params *params) |
| { |
| struct wireless_dev *wdev; |
| |
| vos_ssr_protect(__func__); |
| wdev = __wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type, |
| type, flags, params); |
| vos_ssr_unprotect(__func__); |
| return wdev; |
| } |
| #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)) || defined(WITH_BACKPORTS) |
| struct wireless_dev* wlan_hdd_add_virtual_intf( |
| struct wiphy *wiphy, const char *name, |
| enum nl80211_iftype type, |
| u32 *flags, struct vif_params *params ) |
| { |
| struct wireless_dev* wdev; |
| unsigned char name_assign_type = 0; |
| |
| vos_ssr_protect(__func__); |
| wdev = __wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type, |
| type, flags, params); |
| vos_ssr_unprotect(__func__); |
| return wdev; |
| } |
| #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) |
| struct wireless_dev* wlan_hdd_add_virtual_intf( |
| struct wiphy *wiphy, char *name, enum nl80211_iftype type, |
| u32 *flags, struct vif_params *params ) |
| { |
| struct wireless_dev* wdev; |
| unsigned char name_assign_type = 0; |
| |
| vos_ssr_protect(__func__); |
| wdev = __wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type, |
| type, flags, params); |
| vos_ssr_unprotect(__func__); |
| return wdev; |
| } |
| #else |
| struct net_device* wlan_hdd_add_virtual_intf( |
| struct wiphy *wiphy, char *name, enum nl80211_iftype type, |
| u32 *flags, struct vif_params *params ) |
| { |
| struct net_device* ndev; |
| unsigned char name_assign_type = 0; |
| |
| vos_ssr_protect(__func__); |
| ndev = __wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type, |
| type, flags, params); |
| vos_ssr_unprotect(__func__); |
| return ndev; |
| } |
| #endif |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| int __wlan_hdd_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) |
| #else |
| int __wlan_hdd_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev) |
| #endif |
| { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS) |
| struct net_device *dev = wdev->netdev; |
| #endif |
| hdd_context_t *pHddCtx = (hdd_context_t*) wiphy_priv(wiphy); |
| hdd_adapter_t *pVirtAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| int status; |
| ENTER(); |
| |
| MTRACE(vos_trace(VOS_MODULE_ID_HDD, |
| TRACE_CODE_HDD_DEL_VIRTUAL_INTF, |
| pVirtAdapter->sessionId, pVirtAdapter->device_mode)); |
| hddLog(LOG1, FL("Device_mode %s(%d)"), |
| hdd_device_mode_to_string(pVirtAdapter->device_mode), |
| pVirtAdapter->device_mode); |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return status; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| |