blob: 13da124e4441b7f34c1d4b4e79802ce1311b85a8 [file] [log] [blame]
/*
* Copyright (c) 2012-2017 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_tdls.c
\brief WLAN Host Device Driver implementation for TDLS
========================================================================*/
#include <wlan_hdd_includes.h>
#include <wlan_hdd_hostapd.h>
#include <net/cfg80211.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/list.h>
#include <linux/etherdevice.h>
#include <net/ieee80211_radiotap.h>
#include "wlan_hdd_tdls.h"
#include "wlan_hdd_cfg80211.h"
#include "wlan_hdd_assoc.h"
#include "sme_Api.h"
#include "vos_sched.h"
static tANI_S32 wlan_hdd_get_tdls_discovery_peer_cnt(tdlsCtx_t *pHddTdlsCtx);
static tANI_S32 wlan_hdd_tdls_peer_reset_discovery_processed(tdlsCtx_t *pHddTdlsCtx);
static void wlan_hdd_tdls_timers_destroy(tdlsCtx_t *pHddTdlsCtx);
static void wlan_hdd_tdls_peer_timers_destroy(tdlsCtx_t *pHddTdlsCtx);
int wpa_tdls_is_allowed_force_peer(tdlsCtx_t *pHddTdlsCtx, u8 *mac);
#ifdef CONFIG_TDLS_IMPLICIT
static void wlan_hdd_tdls_implicit_send_discovery_request(tdlsCtx_t *pHddTdlsCtx);
#endif
static u8 wlan_hdd_tdls_hash_key (const u8 *mac)
{
int i;
u8 key = 0;
for (i = 0; i < 6; i++)
key ^= mac[i];
return key;
}
#ifdef FEATURE_WLAN_DIAG_SUPPORT
/**
* hdd_send_wlan_tdls_teardown_event()- send TDLS teardown event
*
* @reason: reason for tear down.
* @peer_mac: peer mac
*
* This Function send TDLS teardown diag event
*
* Return: void.
*/
void hdd_send_wlan_tdls_teardown_event(uint32_t reason,
uint8_t *peer_mac)
{
WLAN_VOS_DIAG_EVENT_DEF(tdls_tear_down,
struct vos_event_tdls_teardown);
vos_mem_zero(&tdls_tear_down,
sizeof(tdls_tear_down));
tdls_tear_down.reason = reason;
vos_mem_copy(tdls_tear_down.peer_mac,
peer_mac, HDD_MAC_ADDR_LEN);
WLAN_VOS_DIAG_EVENT_REPORT(&tdls_tear_down,
EVENT_WLAN_TDLS_TEARDOWN);
}
/**
* hdd_wlan_tdls_enable_link_event()- send TDLS enable link event
*
* @peer_mac: peer mac
* @is_off_chan_supported: Does peer supports off chan
* @is_off_chan_configured: If off channel is configured
* @is_off_chan_established: If off chan is established
*
* This Function send TDLS enable link diag event
*
* Return: void.
*/
void hdd_wlan_tdls_enable_link_event(const uint8_t *peer_mac,
uint8_t is_off_chan_supported,
uint8_t is_off_chan_configured,
uint8_t is_off_chan_established)
{
WLAN_VOS_DIAG_EVENT_DEF(tdls_event,
struct vos_event_tdls_enable_link);
vos_mem_zero(&tdls_event,
sizeof(tdls_event));
vos_mem_copy(tdls_event.peer_mac,
peer_mac, HDD_MAC_ADDR_LEN);
tdls_event.is_off_chan_supported =
is_off_chan_supported;
tdls_event.is_off_chan_configured =
is_off_chan_configured;
tdls_event.is_off_chan_established =
is_off_chan_established;
WLAN_VOS_DIAG_EVENT_REPORT(&tdls_event,
EVENT_WLAN_TDLS_ENABLE_LINK);
}
/**
* hdd_wlan_block_scan_by_tdls()- send event
* if scan is blocked by tdls
*
* This Function send send diag event if scan is
* blocked by tdls
*
* Return: void.
*/
void hdd_wlan_block_scan_by_tdls(void)
{
WLAN_VOS_DIAG_EVENT_DEF(tdls_scan_block_status,
struct vos_event_tdls_scan_rejected);
vos_mem_zero(&tdls_scan_block_status,
sizeof(tdls_scan_block_status));
tdls_scan_block_status.status = true;
WLAN_VOS_DIAG_EVENT_REPORT(&tdls_scan_block_status,
EVENT_TDLS_SCAN_BLOCK);
}
#endif
/**
* wlan_hdd_tdls_disable_offchan_and_teardown_links - Disable offchannel
* and teardown TDLS links
* @hddCtx : pointer to hdd context
*
* Return: None
*/
void wlan_hdd_tdls_disable_offchan_and_teardown_links(hdd_context_t *hddctx)
{
u16 connected_tdls_peers = 0;
u8 staIdx;
hddTdlsPeer_t *curr_peer = NULL;
hdd_adapter_t *adapter = NULL;
bool tdls_unlock = FALSE;
if (eTDLS_SUPPORT_NOT_ENABLED == hddctx->tdls_mode) {
hddLog(LOG1, FL("TDLS mode is disabled OR not enabled in FW"));
return ;
}
adapter = hdd_get_adapter(hddctx, WLAN_HDD_INFRA_STATION);
if (adapter == NULL) {
hddLog(LOGE, FL("Station Adapter Not Found"));
goto done;
}
connected_tdls_peers = wlan_hdd_tdlsConnectedPeers(adapter);
if (!connected_tdls_peers) {
hddLog(LOG1, FL("No TDLS connected peers to delete TDLS peers"));
goto done;
}
/* TDLS is not supported in case of concurrency
* Disable TDLS Offchannel to avoid more than two concurrent channels.
*/
if (connected_tdls_peers == 1) {
tSirMacAddr peer_mac;
int32_t channel;
mutex_lock(&hddctx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_connected_peer(adapter);
if (curr_peer && (curr_peer->isOffChannelConfigured == TRUE)) {
hddLog(LOG1, FL("%s: Concurrency detected, Disable "
"TDLS channel switch"), __func__);
curr_peer->isOffChannelEstablished = FALSE;
channel = curr_peer->peerParams.channel;
vos_mem_copy(peer_mac, curr_peer->peerMac, sizeof(tSirMacAddr));
mutex_unlock(&hddctx->tdls_lock);
sme_SendTdlsChanSwitchReq(WLAN_HDD_GET_HAL_CTX(adapter),
adapter->sessionId,
peer_mac,
channel,
TDLS_OFF_CHANNEL_BW_OFFSET,
TDLS_CHANNEL_SWITCH_DISABLE);
tdls_unlock = TRUE;
}
if (tdls_unlock == FALSE)
mutex_unlock(&hddctx->tdls_lock);
}
/* Send Msg to PE for sending deauth and deleting all the TDLS peers */
sme_DeleteAllTDLSPeers(hddctx->hHal, adapter->sessionId);
/* As mentioned above TDLS is not supported in case of concurrency
* Find the connected peer and generate TDLS teardown indication to
* supplicant.
*/
for (staIdx = 0; staIdx < HDD_MAX_NUM_TDLS_STA; staIdx++) {
if (!hddctx->tdlsConnInfo[staIdx].staId)
continue;
mutex_lock(&hddctx->tdls_lock);
curr_peer = wlan_hdd_tdls_find_all_peer(hddctx,
hddctx->tdlsConnInfo[staIdx].peerMac.bytes);
if (!curr_peer)
{
mutex_unlock(&hddctx->tdls_lock);
continue;
}
hddLog(LOG1, FL("indicate TDLS teardown (staId %d)"),
curr_peer->staId);
/* Indicate teardown to supplicant */
wlan_hdd_tdls_indicate_teardown(
curr_peer->pHddTdlsCtx->pAdapter,
curr_peer,
eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
hdd_send_wlan_tdls_teardown_event(eTDLS_TEARDOWN_CONCURRENCY,
curr_peer->peerMac);
wlan_hdd_tdls_reset_peer(adapter, curr_peer->peerMac);
mutex_unlock(&hddctx->tdls_lock);
/* Del Sta happened already as part of sme_DeleteAllTDLSPeers
* Hence clear hdd data structure.
*/
hdd_roamDeregisterTDLSSTA(adapter,
hddctx->tdlsConnInfo[staIdx].staId);
wlan_hdd_tdls_decrement_peer_count(adapter);
hddctx->tdlsConnInfo[staIdx].staId = 0 ;
hddctx->tdlsConnInfo[staIdx].sessionId = 255;
vos_mem_zero(&hddctx->tdlsConnInfo[staIdx].peerMac,
sizeof(v_MACADDR_t)) ;
wlan_hdd_tdls_check_bmps(adapter);
}
done:
wlan_hdd_tdls_set_mode(hddctx, eTDLS_SUPPORT_DISABLED, FALSE,
HDD_SET_TDLS_MODE_SOURCE_CONCURRENCY);
hddLog(LOG1, FL("TDLS Support Disabled"));
}
/**
* hdd_tdls_notify_mode_change - Notify mode change
* @adapter: pointer to hdd adapter
* @hddCtx : pointer to hdd context
*
* Return: None
*/
void hdd_tdls_notify_mode_change(hdd_adapter_t *adapter, hdd_context_t *hddctx)
{
wlan_hdd_tdls_disable_offchan_and_teardown_links(hddctx);
}
#ifdef TDLS_USE_SEPARATE_DISCOVERY_TIMER
static v_VOID_t wlan_hdd_tdls_discover_peer_cb( v_PVOID_t userData )
{
int i;
struct list_head *head;
struct list_head *pos;
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx;
tdlsCtx_t *pHddTdlsCtx;
int discover_req_sent = 0;
v_U32_t discover_expiry = TDLS_SUB_DISCOVERY_PERIOD;
v_CONTEXT_t pVosContext;
ENTER();
pVosContext = vos_get_global_context(VOS_MODULE_ID_HDD, NULL);
if (NULL == pVosContext)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pVosContext points to NULL"));
return;
}
pHddCtx = vos_get_context(VOS_MODULE_ID_HDD, pVosContext);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
{
return;
}
mutex_lock(&pHddCtx->tdls_lock);
pHddTdlsCtx = (tdlsCtx_t *)userData;
if (0 != (wlan_hdd_validate_tdls_context(pHddCtx, pHddTdlsCtx)))
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Invalid pHddTdlsCtx context"));
return;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: ", __func__);
if (0 == pHddTdlsCtx->discovery_peer_cnt)
pHddTdlsCtx->discovery_peer_cnt = wlan_hdd_get_tdls_discovery_peer_cnt(pHddTdlsCtx);
for (i = 0; i < 256; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each (pos, head) {
curr_peer = list_entry (pos, hddTdlsPeer_t, node);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%d " MAC_ADDRESS_STR " %d %d, %d %d %d %d", i,
MAC_ADDR_ARRAY(curr_peer->peerMac),
curr_peer->discovery_processed,
discover_req_sent,
curr_peer->tdls_support,
curr_peer->link_status,
curr_peer->discovery_attempt,
pHddTdlsCtx->threshold_config.discovery_tries_n);
if (discover_req_sent < TDLS_MAX_DISCOVER_REQS_PER_TIMER) {
if (!curr_peer->discovery_processed) {
curr_peer->discovery_processed = 1;
discover_req_sent++;
pHddTdlsCtx->discovery_peer_cnt--;
if ((eTDLS_CAP_UNKNOWN == curr_peer->tdls_support) &&
(eTDLS_LINK_IDLE == curr_peer->link_status) &&
(curr_peer->tx_pkt >=
pHddTdlsCtx->threshold_config.tx_packet_n)) {
if (curr_peer->discovery_attempt <
pHddTdlsCtx->threshold_config.discovery_tries_n) {
cfg80211_tdls_oper_request(
pHddTdlsCtx->pAdapter->dev,
curr_peer->peerMac,
NL80211_TDLS_DISCOVERY_REQ,
FALSE,
GFP_KERNEL);
curr_peer->discovery_attempt++;
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s: Maximum Discovery retries reached", __func__);
curr_peer->tdls_support = eTDLS_CAP_NOT_SUPPORTED;
}
}
}
}
else
goto exit_loop;
}
}
exit_loop:
if (0 != pHddTdlsCtx->discovery_peer_cnt) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"discovery_peer_cnt is %d , Starting SUB_DISCOVERY_TIMER",
pHddTdlsCtx->discovery_peer_cnt);
discover_expiry = TDLS_SUB_DISCOVERY_PERIOD;
goto done;
}
discover_expiry = pHddTdlsCtx->threshold_config.discovery_period_t;
wlan_hdd_tdls_peer_reset_discovery_processed(pHddTdlsCtx);
/* Commenting out the following function as it was introducing
* a race condition when pHddTdlsCtx is deleted. Also , this
* function is consuming more time in the timer callback.
* RSSI based trigger needs to revisit this part of the code.
*/
/*
* wlan_hdd_get_rssi(pAdapter, &pHddTdlsCtx->ap_rssi);
*/
done:
mutex_unlock(&pHddCtx->tdls_lock);
EXIT();
return;
}
#endif
static v_VOID_t wlan_hdd_tdls_idle_cb( v_PVOID_t userData )
{
#ifdef CONFIG_TDLS_IMPLICIT
tdlsConnInfo_t *tdlsInfo = (tdlsConnInfo_t *) userData;
hddTdlsPeer_t *curr_peer;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx;
v_CONTEXT_t pVosContext;
hdd_adapter_t *pAdapter = NULL;
ENTER();
if (!tdlsInfo->staId)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("peer (staidx %u) doesn't exists"), tdlsInfo->staId);
return;
}
pVosContext = vos_get_global_context(VOS_MODULE_ID_HDD, NULL);
if (NULL == pVosContext)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pVosContext points to NULL"));
return;
}
pHddCtx = vos_get_context(VOS_MODULE_ID_HDD, pVosContext);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
{
return;
}
pAdapter = hdd_get_adapter_by_sme_session_id(pHddCtx, tdlsInfo->sessionId);
if (!pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pAdapter is NULL"));
return;
}
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_find_peer(pAdapter,
(u8 *) &tdlsInfo->peerMac.bytes[0], FALSE);
if (NULL == curr_peer)
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Invalid tdls idle timer expired"));
return;
}
pHddTdlsCtx = curr_peer->pHddTdlsCtx;
if (0 != (wlan_hdd_validate_tdls_context(pHddCtx, pHddTdlsCtx)))
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Invalid pHddTdlsCtx context"));
return;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Tx/Rx Idle " MAC_ADDRESS_STR " tx_pkt: %d, rx_pkt: %d, idle_packet_n: %d",
__func__, MAC_ADDR_ARRAY(curr_peer->peerMac),
curr_peer->tx_pkt,
curr_peer->rx_pkt,
curr_peer->pHddTdlsCtx->threshold_config.idle_packet_n);
/* Check tx/rx statistics on this tdls link for recent activities and
* then decide whether to tear down the link or keep it.
*/
if ((curr_peer->tx_pkt >= curr_peer->pHddTdlsCtx->threshold_config.idle_packet_n) || (curr_peer->rx_pkt >= curr_peer->pHddTdlsCtx->threshold_config.idle_packet_n))
{
/* this tdls link got back to normal, so keep it */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: tdls link to " MAC_ADDRESS_STR " back to normal, will stay",
__func__, MAC_ADDR_ARRAY(curr_peer->peerMac));
}
else
{
/* this tdls link needs to get torn down */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: trigger tdls link to " MAC_ADDRESS_STR " down",
__func__, MAC_ADDR_ARRAY(curr_peer->peerMac));
wlan_hdd_tdls_indicate_teardown(curr_peer->pHddTdlsCtx->pAdapter,
curr_peer,
eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
hdd_send_wlan_tdls_teardown_event(eTDLS_TEARDOWN_TXRX_THRESHOLD,
curr_peer->peerMac);
}
mutex_unlock(&pHddCtx->tdls_lock);
EXIT();
#endif
}
static v_VOID_t wlan_hdd_tdls_update_peer_cb( v_PVOID_t userData )
{
int i;
struct list_head *head;
struct list_head *pos;
hddTdlsPeer_t *curr_peer;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx;
v_CONTEXT_t pVosContext;
ENTER();
pVosContext = vos_get_global_context(VOS_MODULE_ID_HDD, NULL);
if (NULL == pVosContext)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pVosContext points to NULL"));
return;
}
pHddCtx = vos_get_context(VOS_MODULE_ID_HDD, pVosContext);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
{
return;
}
mutex_lock(&pHddCtx->tdls_lock);
pHddTdlsCtx = (tdlsCtx_t *)userData;
if (0 != (wlan_hdd_validate_tdls_context(pHddCtx, pHddTdlsCtx)))
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Invalid pHddTdlsCtx context"));
return;
}
for (i = 0; i < 256; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each (pos, head) {
curr_peer = list_entry (pos, hddTdlsPeer_t, node);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: " MAC_ADDRESS_STR " link_status %d"
" tdls_support %d", __func__, MAC_ADDR_ARRAY(curr_peer->peerMac),
curr_peer->link_status, curr_peer->tdls_support);
if ((pHddCtx->tdls_mode == eTDLS_SUPPORT_DISABLED) &&
(eTDLS_LINK_DISCOVERING == curr_peer->link_status)) {
curr_peer->tdls_support = eTDLS_CAP_UNKNOWN;
wlan_hdd_tdls_set_peer_link_status(
curr_peer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: TDLS is disabled. Resetting link_status of peer "
MAC_ADDRESS_STR " to %d, tdls_support %d", __func__,
MAC_ADDR_ARRAY(curr_peer->peerMac),
curr_peer->link_status, curr_peer->tdls_support);
goto next_peer;
}
if (eTDLS_CAP_SUPPORTED == curr_peer->tdls_support) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"tx %d, rx %d (thr.pkt %d/idle %d), rssi %d (thr.trig %d/hys %d/tear %d)",
curr_peer->tx_pkt, curr_peer->rx_pkt,
pHddTdlsCtx->threshold_config.tx_packet_n,
pHddTdlsCtx->threshold_config.idle_packet_n,
curr_peer->rssi,
pHddTdlsCtx->threshold_config.rssi_trigger_threshold,
pHddTdlsCtx->threshold_config.rssi_hysteresis,
pHddTdlsCtx->threshold_config.rssi_teardown_threshold);
if ((eTDLS_LINK_IDLE == curr_peer->link_status) ||
(eTDLS_LINK_DISCOVERING == curr_peer->link_status)){
if (pHddCtx->cfg_ini->fTDLSExternalControl &&
(FALSE == curr_peer->isForcedPeer)) {
goto next_peer;
}
if (curr_peer->tx_pkt >=
pHddTdlsCtx->threshold_config.tx_packet_n) {
if (HDD_MAX_NUM_TDLS_STA > wlan_hdd_tdlsConnectedPeers(pHddTdlsCtx->pAdapter))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Tput trigger TDLS pre-setup");
#ifdef CONFIG_TDLS_IMPLICIT
pHddTdlsCtx->curr_candidate = curr_peer;
wlan_hdd_tdls_implicit_send_discovery_request(pHddTdlsCtx);
#endif
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Maximum peer connected already! %d",
__func__, wlan_hdd_tdlsConnectedPeers(pHddTdlsCtx->pAdapter) );
}
goto next_peer;
}
}
else if (eTDLS_LINK_CONNECTED == curr_peer->link_status) {
if ((tANI_S32)curr_peer->rssi <
(tANI_S32)pHddTdlsCtx->threshold_config.rssi_teardown_threshold) {
VOS_TRACE( VOS_MODULE_ID_HDD,
VOS_TRACE_LEVEL_WARN,
"Tear down - low RSSI: " MAC_ADDRESS_STR "!",
MAC_ADDR_ARRAY(curr_peer->peerMac));
#ifdef CONFIG_TDLS_IMPLICIT
wlan_hdd_tdls_indicate_teardown(pHddTdlsCtx->pAdapter,
curr_peer,
eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
hdd_send_wlan_tdls_teardown_event(
eTDLS_TEARDOWN_RSSI_THRESHOLD,
curr_peer->peerMac);
#endif
goto next_peer;
}
/* Only teardown based on non zero idle packet threshold, to address a use
* case where this threshold does not get consider for TEAR DOWN.
*/
if (( 0 != pHddTdlsCtx->threshold_config.idle_packet_n ) &&
((curr_peer->tx_pkt <
pHddTdlsCtx->threshold_config.idle_packet_n) &&
(curr_peer->rx_pkt <
pHddTdlsCtx->threshold_config.idle_packet_n))) {
if (!vos_timer_is_initialized(&curr_peer->peerIdleTimer))
{
v_U8_t staId = (v_U8_t)curr_peer->staId;
tdlsConnInfo_t *tdlsInfo;
tdlsInfo = wlan_hdd_get_conn_info(pHddCtx, staId);
vos_timer_init(&curr_peer->peerIdleTimer,
VOS_TIMER_TYPE_SW,
wlan_hdd_tdls_idle_cb,
tdlsInfo);
}
if (VOS_TIMER_STATE_RUNNING !=
vos_timer_getCurrentState(&curr_peer->peerIdleTimer)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"Tx/Rx Idle timer start: " MAC_ADDRESS_STR "!",
MAC_ADDR_ARRAY(curr_peer->peerMac));
wlan_hdd_tdls_timer_restart(pHddTdlsCtx->pAdapter,
&curr_peer->peerIdleTimer,
pHddTdlsCtx->threshold_config.idle_timeout_t);
}
} else {
if (vos_timer_is_initialized(
&curr_peer->peerIdleTimer) &&
VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(
&curr_peer->peerIdleTimer)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"Tx/Rx Idle timer stop: " MAC_ADDRESS_STR "!",
MAC_ADDR_ARRAY(curr_peer->peerMac));
vos_timer_stop( &curr_peer->peerIdleTimer);
}
}
// if (curr_peer->rssi <
// (pHddTdlsCtx->threshold_config.rssi_hysteresis +
// pHddTdlsCtx->ap_rssi)) {
//
//#ifdef CONFIG_TDLS_IMPLICIT
// cfg80211_tdls_oper_request(pHddTdlsCtx->dev,
// curr_peer->peerMac,
// NL80211_TDLS_TEARDOWN, FALSE,
// GFP_KERNEL);
//#endif
// }
}
} else if (eTDLS_CAP_UNKNOWN == curr_peer->tdls_support) {
if (pHddCtx->cfg_ini->fTDLSExternalControl &&
(FALSE == curr_peer->isForcedPeer)) {
goto next_peer;
}
if (!TDLS_IS_CONNECTED(curr_peer)) {
if (curr_peer->tx_pkt >=
pHddTdlsCtx->threshold_config.tx_packet_n) {
/* Ignore discovery attempt if External Control is enabled, that
* is, peer is forced. In that case, continue discovery attempt
* regardless attempt count
*/
if (curr_peer->isForcedPeer || curr_peer->discovery_attempt++ <
pHddTdlsCtx->threshold_config.discovery_tries_n) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"TDLS UNKNOWN discover ");
#ifdef CONFIG_TDLS_IMPLICIT
pHddTdlsCtx->curr_candidate = curr_peer;
wlan_hdd_tdls_implicit_send_discovery_request(pHddTdlsCtx);
#endif
}
else
{
curr_peer->tdls_support = eTDLS_CAP_NOT_SUPPORTED;
wlan_hdd_tdls_set_peer_link_status(
curr_peer,
eTDLS_LINK_IDLE,
eTDLS_LINK_NOT_SUPPORTED);
}
}
}
}
next_peer:
curr_peer->tx_pkt = 0;
curr_peer->rx_pkt = 0;
}
}
wlan_hdd_tdls_timer_restart(pHddTdlsCtx->pAdapter,
&pHddTdlsCtx->peerUpdateTimer,
pHddTdlsCtx->threshold_config.tx_period_t);
mutex_unlock(&pHddCtx->tdls_lock);
EXIT();
}
static v_VOID_t wlan_hdd_tdls_discovery_timeout_peer_cb(v_PVOID_t userData)
{
int i;
struct list_head *head;
hddTdlsPeer_t *tmp;
struct list_head *pos, *q;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx;
v_CONTEXT_t pVosContext;
ENTER();
pVosContext = vos_get_global_context(VOS_MODULE_ID_HDD, NULL);
if (NULL == pVosContext)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pVosContext points to NULL"));
return;
}
pHddCtx = vos_get_context(VOS_MODULE_ID_HDD, pVosContext);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
{
return;
}
mutex_lock(&pHddCtx->tdls_lock);
pHddTdlsCtx = (tdlsCtx_t *)userData;
if (0 != (wlan_hdd_validate_tdls_context(pHddCtx, pHddTdlsCtx)))
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Invalid pHddTdlsCtx context"));
return;
}
for (i = 0; i < 256; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each_safe (pos, q, head) {
tmp = list_entry(pos, hddTdlsPeer_t, node);
if (eTDLS_LINK_DISCOVERING == tmp->link_status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: " MAC_ADDRESS_STR " to idle state", __func__,
MAC_ADDR_ARRAY(tmp->peerMac));
wlan_hdd_tdls_set_peer_link_status(
tmp,
eTDLS_LINK_IDLE,
eTDLS_LINK_NOT_SUPPORTED);
}
}
}
pHddTdlsCtx->discovery_sent_cnt = 0;
wlan_hdd_tdls_check_power_save_prohibited(pHddTdlsCtx->pAdapter);
mutex_unlock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_check_bmps(pHddTdlsCtx->pAdapter);
EXIT();
return;
}
v_VOID_t wlan_hdd_tdls_initiator_wait_cb( v_PVOID_t userData )
{
tdlsConnInfo_t *tdlsInfo = (tdlsConnInfo_t *) userData;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx = NULL;
hdd_adapter_t *pAdapter = NULL;
v_CONTEXT_t pVosContext = vos_get_global_context(VOS_MODULE_ID_HDD, NULL);
hddTdlsPeer_t *curr_peer = NULL;
ENTER();
if (!tdlsInfo->staId)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("peer (staidx %u) doesn't exists"), tdlsInfo->staId);
return;
}
if (!pVosContext)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pVosContext is NULL"));
return;
}
pHddCtx = vos_get_context( VOS_MODULE_ID_HDD, pVosContext);
if (!pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pHddCtx is NULL"));
return;
}
pAdapter = hdd_get_adapter_by_sme_session_id(pHddCtx, tdlsInfo->sessionId);
if (!pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pAdapter is NULL"));
return;
}
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_find_peer(pAdapter,
(u8 *) &tdlsInfo->peerMac.bytes[0], FALSE);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("peer doesn't exists"));
mutex_unlock(&pHddCtx->tdls_lock);
return;
}
pHddTdlsCtx = curr_peer->pHddTdlsCtx;
if ( NULL == pHddTdlsCtx )
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddTdlsCtx is NULL"));
return;
}
if (0 != (wlan_hdd_validate_context(
WLAN_HDD_GET_CTX(pHddTdlsCtx->pAdapter))))
{
return;
}
WLANTL_ResumeDataTx( (WLAN_HDD_GET_CTX(pHddTdlsCtx->pAdapter))->pvosContext,
(v_U8_t *)&curr_peer->staId);
mutex_unlock(&pHddCtx->tdls_lock);
EXIT();
}
static void wlan_hdd_tdls_free_list(tdlsCtx_t *pHddTdlsCtx)
{
int i;
struct list_head *head;
hddTdlsPeer_t *tmp;
struct list_head *pos, *q;
if (NULL == pHddTdlsCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddTdlsCtx is NULL"));
return;
}
for (i = 0; i < 256; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each_safe (pos, q, head) {
tmp = list_entry(pos, hddTdlsPeer_t, node);
list_del(pos);
vos_mem_free(tmp);
tmp = NULL;
}
}
}
void wlan_hdd_tdls_btCoex_cb(void *data, int indType)
{
hdd_adapter_t *pAdapter;
hdd_context_t *pHddCtx;
u16 connectedTdlsPeers;
hddTdlsPeer_t *currPeer;
tANI_U16 numCurrTdlsPeers = 0;
ENTER();
if ((NULL == data) || (indType < 0))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Invalid arguments"));
return;
}
pHddCtx = (hdd_context_t *)data;
if (0 != (wlan_hdd_validate_context(pHddCtx)))
{
return;
}
/* if tdls is not enabled, then continue btCoex */
if (eTDLS_SUPPORT_NOT_ENABLED == pHddCtx->tdls_mode)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("tdls is not enabled"));
return;
}
/* get pAdapter context, do we need WLAN_HDD_P2P_CLIENT */
pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION);
if (NULL == pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pAdapter is not valid"));
return;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: BtCoex notification type %d", __func__, indType);
/* BtCoex notification type enabled, Disable TDLS */
if (indType == SIR_COEX_IND_TYPE_TDLS_DISABLE)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("BtCoex notification, Disable TDLS"));
mutex_lock(&pHddCtx->tdls_lock);
/* tdls is in progress */
currPeer = wlan_hdd_tdls_is_progress(pHddCtx, NULL, 0, FALSE);
if (NULL != currPeer)
{
wlan_hdd_tdls_set_peer_link_status (currPeer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED);
}
mutex_unlock(&pHddCtx->tdls_lock);
/* while tdls is up */
if (eTDLS_SUPPORT_ENABLED == pHddCtx->tdls_mode ||
eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY == pHddCtx->tdls_mode)
{
connectedTdlsPeers = wlan_hdd_tdlsConnectedPeers(pAdapter);
/* disable implicit trigger logic & tdls operatoin */
wlan_hdd_tdls_set_mode(pHddCtx, eTDLS_SUPPORT_DISABLED, FALSE,
HDD_SET_TDLS_MODE_SOURCE_BTC);
/* teardown the peers on the btcoex */
if (connectedTdlsPeers)
{
tANI_U8 staIdx;
hddTdlsPeer_t *curr_peer;
mutex_lock(&pHddCtx->tdls_lock);
for (staIdx = 0; staIdx < HDD_MAX_NUM_TDLS_STA; staIdx++)
{
if (pHddCtx->tdlsConnInfo[staIdx].staId)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
("%s: indicate TDLS teardown (staId %d)"),
__func__,pHddCtx->tdlsConnInfo[staIdx].staId);
#ifdef CONFIG_TDLS_IMPLICIT
curr_peer = wlan_hdd_tdls_find_all_peer(pHddCtx,
pHddCtx->tdlsConnInfo[staIdx].peerMac.bytes);
if(curr_peer) {
wlan_hdd_tdls_indicate_teardown(
curr_peer->pHddTdlsCtx->pAdapter, curr_peer,
eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
hdd_send_wlan_tdls_teardown_event(
eTDLS_TEARDOWN_BTCOEX,
curr_peer->peerMac);
}
#endif
}
}
mutex_unlock(&pHddCtx->tdls_lock);
}
}
/* stop TCP delack timer if BtCoex is enable */
set_bit(WLAN_BTCOEX_MODE, &pHddCtx->mode);
hdd_manage_delack_timer(pHddCtx);
}
/* BtCoex notification type enabled, Enable TDLS */
else if (indType == SIR_COEX_IND_TYPE_TDLS_ENABLE)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("BtCoex notification, Enable TDLS"));
/* if tdls was enabled before btCoex, re-enable tdls mode */
if (eTDLS_SUPPORT_ENABLED == pHddCtx->tdls_mode_last ||
eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY == pHddCtx->tdls_mode_last)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
("%s: revert tdls mode %d"), __func__,
pHddCtx->tdls_mode_last);
wlan_hdd_tdls_set_mode(pHddCtx, pHddCtx->tdls_mode_last, FALSE,
HDD_SET_TDLS_MODE_SOURCE_BTC);
}
clear_bit(WLAN_BTCOEX_MODE, &pHddCtx->mode);
numCurrTdlsPeers = wlan_hdd_tdlsConnectedPeers(pAdapter);
if(numCurrTdlsPeers == 0) {
/* start delack timer if BtCoex is disable and tdls is not present */
hdd_manage_delack_timer(pHddCtx);
}
}
EXIT();
return;
}
/* initialize TDLS global context */
void wlan_hdd_tdls_init(hdd_context_t *pHddCtx )
{
v_U8_t staIdx;
eHalStatus status;
pHddCtx->connected_peer_count = 0;
wlan_hdd_init_deinit_defer_scan_context(&pHddCtx->scan_ctxt);
for (staIdx = 0; staIdx < HDD_MAX_NUM_TDLS_STA; staIdx++)
{
pHddCtx->tdlsConnInfo[staIdx].staId = 0;
pHddCtx->tdlsConnInfo[staIdx].sessionId = 255;
vos_mem_zero(&pHddCtx->tdlsConnInfo[staIdx].peerMac,
sizeof(v_MACADDR_t)) ;
}
status = sme_RegisterBtCoexTDLSCallback(pHddCtx->hHal,
wlan_hdd_tdls_btCoex_cb);
if (status != eHAL_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to register BT Coex TDLS callback"));
}
if ((TRUE == pHddCtx->cfg_ini->fEnableTDLSSupport) &&
(TRUE == sme_IsFeatureSupportedByFW(TDLS)))
{
if (FALSE == pHddCtx->cfg_ini->fEnableTDLSImplicitTrigger)
{
pHddCtx->tdls_mode = eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY;
hddLog(LOGE, FL("TDLS Implicit trigger not enabled!"));
return;
}
pHddCtx->tdls_mode = eTDLS_SUPPORT_ENABLED;
}
else
{
hddLog(LOGE,
FL("TDLS not enabled (%d) or FW doesn't support (%d)"),
pHddCtx->cfg_ini->fEnableTDLSSupport,
sme_IsFeatureSupportedByFW(TDLS));
pHddCtx->tdls_mode = eTDLS_SUPPORT_NOT_ENABLED;
}
}
int wlan_hdd_sta_tdls_init(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
tdlsCtx_t *pHddTdlsCtx;
int i;
if (NULL == pHddCtx)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s pHddCtx is NULL", __func__);
return -EINVAL;
}
if (test_bit(TDLS_INIT_DONE, &pAdapter->event_flags))
{
hddLog(LOG1,
FL("TDLS INIT DONE set to 1, no point in re-init"));
/* Return success as TDLS is already initialized */
return 0;
}
if ((FALSE == pHddCtx->cfg_ini->fEnableTDLSSupport) ||
(FALSE == sme_IsFeatureSupportedByFW(TDLS)))
{
pHddCtx->tdls_mode = eTDLS_SUPPORT_NOT_ENABLED;
pAdapter->sessionCtx.station.pHddTdlsCtx = NULL;
hddLog(VOS_TRACE_LEVEL_INFO, "%s TDLS not enabled (%d) or FW doesn't support (%d)!",
__func__, pHddCtx->cfg_ini->fEnableTDLSSupport,
sme_IsFeatureSupportedByFW(TDLS));
return -EINVAL;
}
/* TDLS is supported only in STA / P2P Client modes,
* hence the check for TDLS support in a specific Device mode.
*/
if (0 == WLAN_HDD_IS_TDLS_SUPPORTED_ADAPTER(pAdapter))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s TDLS is not supported in mode : %d", __func__, pAdapter->device_mode);
return -EINVAL;
}
/* Check for the valid pHddTdlsCtx. If valid do not further
* allocate the memory, rather continue with the initialization.
* If tdls_initialization would get reinvoked without tdls_exit
* getting invoked (SSR) there is no point to further proceed
* with the memory allocations.
*/
if (NULL == pAdapter->sessionCtx.station.pHddTdlsCtx)
{
pHddTdlsCtx = vos_mem_malloc(sizeof(tdlsCtx_t));
if (NULL == pHddTdlsCtx) {
hddLog(VOS_TRACE_LEVEL_ERROR, "%s malloc failed!", __func__);
pAdapter->sessionCtx.station.pHddTdlsCtx = NULL;
return -ENOMEM;
}
/* initialize TDLS pAdater context */
vos_mem_zero(pHddTdlsCtx, sizeof(tdlsCtx_t));
#ifdef TDLS_USE_SEPARATE_DISCOVERY_TIMER
vos_timer_init(&pHddTdlsCtx->peerDiscoverTimer,
VOS_TIMER_TYPE_SW,
wlan_hdd_tdls_discover_peer_cb,
pHddTdlsCtx);
#endif
vos_timer_init(&pHddTdlsCtx->peerUpdateTimer,
VOS_TIMER_TYPE_SW,
wlan_hdd_tdls_update_peer_cb,
pHddTdlsCtx);
vos_timer_init(&pHddTdlsCtx->peerDiscoveryTimeoutTimer,
VOS_TIMER_TYPE_SW,
wlan_hdd_tdls_discovery_timeout_peer_cb,
pHddTdlsCtx);
pAdapter->sessionCtx.station.pHddTdlsCtx = pHddTdlsCtx;
}
pHddTdlsCtx = pAdapter->sessionCtx.station.pHddTdlsCtx;
sme_SetTdlsPowerSaveProhibited(WLAN_HDD_GET_HAL_CTX(pAdapter), 0);
pHddTdlsCtx->pAdapter = pAdapter;
for (i = 0; i < 256; i++)
{
INIT_LIST_HEAD(&pHddTdlsCtx->peer_list[i]);
}
pHddTdlsCtx->curr_candidate = NULL;
pHddTdlsCtx->magic = 0;
/* remember configuration even if it is not used right now. it could be used later */
pHddTdlsCtx->threshold_config.tx_period_t = pHddCtx->cfg_ini->fTDLSTxStatsPeriod;
pHddTdlsCtx->threshold_config.tx_packet_n = pHddCtx->cfg_ini->fTDLSTxPacketThreshold;
pHddTdlsCtx->threshold_config.discovery_period_t = pHddCtx->cfg_ini->fTDLSDiscoveryPeriod;
pHddTdlsCtx->threshold_config.discovery_tries_n = pHddCtx->cfg_ini->fTDLSMaxDiscoveryAttempt;
pHddTdlsCtx->threshold_config.idle_timeout_t = pHddCtx->cfg_ini->fTDLSIdleTimeout;
pHddTdlsCtx->threshold_config.idle_packet_n = pHddCtx->cfg_ini->fTDLSIdlePacketThreshold;
pHddTdlsCtx->threshold_config.rssi_hysteresis = pHddCtx->cfg_ini->fTDLSRSSIHysteresis;
pHddTdlsCtx->threshold_config.rssi_trigger_threshold = pHddCtx->cfg_ini->fTDLSRSSITriggerThreshold;
pHddTdlsCtx->threshold_config.rssi_teardown_threshold = pHddCtx->cfg_ini->fTDLSRSSITeardownThreshold;
set_bit(TDLS_INIT_DONE, &pAdapter->event_flags);
return 0;
}
void wlan_hdd_tdls_exit(hdd_adapter_t *pAdapter, tANI_BOOLEAN mutexLock)
{
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx;
if (!pAdapter) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD adpater is NULL"));
return;
}
/*
* NOTE: The Callers of this function should ensure to acquire the
* tdls_lock to avoid any concurrent access to the Adapter and logp
* protection has to be ensured.
*/
pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
if (NULL == pHddCtx || NULL == pHddCtx->cfg_ini)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD context is Null", __func__);
return ;
}
if (!test_bit(TDLS_INIT_DONE, &pAdapter->event_flags))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: TDLS INIT DONE set to 0, no point in exit", __func__);
return;
}
clear_bit(TDLS_INIT_DONE, &pAdapter->event_flags);
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddTdlsCtx is NULL"));
return;
}
/* must stop timer here before freeing peer list, because peerIdleTimer is
part of peer list structure. */
wlan_hdd_tdls_timers_destroy(pHddTdlsCtx);
wlan_hdd_tdls_free_list(pHddTdlsCtx);
wlan_hdd_init_deinit_defer_scan_context(&pHddCtx->scan_ctxt);
pHddTdlsCtx->magic = 0;
pHddTdlsCtx->pAdapter = NULL;
vos_mem_free(pHddTdlsCtx);
pAdapter->sessionCtx.station.pHddTdlsCtx = NULL;
pHddTdlsCtx = NULL;
}
/* stop all monitoring timers per Adapter */
static void wlan_hdd_tdls_monitor_timers_stop(tdlsCtx_t *pHddTdlsCtx)
{
#ifdef TDLS_USE_SEPARATE_DISCOVERY_TIMER
vos_timer_stop(&pHddTdlsCtx->peerDiscoverTimer);
#endif
vos_timer_stop(&pHddTdlsCtx->peerUpdateTimer);
vos_timer_stop(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
}
/* stop all per peer timers */
static void wlan_hdd_tdls_peer_timers_stop(tdlsCtx_t *pHddTdlsCtx)
{
int i;
struct list_head *head;
struct list_head *pos;
hddTdlsPeer_t *curr_peer;
for (i = 0; i < 256; i++)
{
head = &pHddTdlsCtx->peer_list[i];
list_for_each (pos, head) {
curr_peer = list_entry (pos, hddTdlsPeer_t, node);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: " MAC_ADDRESS_STR " -> stop idle timer",
__func__,
MAC_ADDR_ARRAY(curr_peer->peerMac));
if (vos_timer_is_initialized(&curr_peer->peerIdleTimer))
vos_timer_stop ( &curr_peer->peerIdleTimer );
if (vos_timer_is_initialized(&curr_peer->initiatorWaitTimeoutTimer))
vos_timer_stop( &curr_peer->initiatorWaitTimeoutTimer );
}
}
}
/* stop all the tdls timers running */
static void wlan_hdd_tdls_timers_stop(tdlsCtx_t *pHddTdlsCtx)
{
wlan_hdd_tdls_monitor_timers_stop(pHddTdlsCtx);
wlan_hdd_tdls_peer_timers_stop(pHddTdlsCtx);
}
static void wlan_hdd_tdls_monitor_timers_destroy(tdlsCtx_t *pHddTdlsCtx)
{
#ifdef TDLS_USE_SEPARATE_DISCOVERY_TIMER
vos_timer_stop(&pHddTdlsCtx->peerDiscoverTimer);
vos_timer_destroy(&pHddTdlsCtx->peerDiscoverTimer);
#endif
vos_timer_stop(&pHddTdlsCtx->peerUpdateTimer);
vos_timer_destroy(&pHddTdlsCtx->peerUpdateTimer);
vos_timer_stop(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
vos_timer_destroy(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
}
/*Free all the timers related to the TDLS peer */
static void wlan_hdd_tdls_peer_timers_destroy(tdlsCtx_t *pHddTdlsCtx)
{
int i;
struct list_head *head;
struct list_head *pos;
hddTdlsPeer_t *curr_peer;
for (i = 0; i < 256; i++)
{
head = &pHddTdlsCtx->peer_list[i];
list_for_each (pos, head) {
curr_peer = list_entry (pos, hddTdlsPeer_t, node);
if (vos_timer_is_initialized(&curr_peer->peerIdleTimer)){
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: " MAC_ADDRESS_STR " -> destroy idle timer",
__func__,
MAC_ADDR_ARRAY(curr_peer->peerMac));
vos_timer_stop ( &curr_peer->peerIdleTimer );
vos_timer_destroy ( &curr_peer->peerIdleTimer );
}
if (vos_timer_is_initialized(&curr_peer->initiatorWaitTimeoutTimer))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: " MAC_ADDRESS_STR " -> destroy initiatorWaitTimeoutTimer",
__func__,
MAC_ADDR_ARRAY(curr_peer->peerMac));
vos_timer_stop(&curr_peer->initiatorWaitTimeoutTimer);
vos_timer_destroy(&curr_peer->initiatorWaitTimeoutTimer);
}
}
}
}
/* destroy all the tdls timers running */
static void wlan_hdd_tdls_timers_destroy(tdlsCtx_t *pHddTdlsCtx)
{
wlan_hdd_tdls_monitor_timers_destroy(pHddTdlsCtx);
wlan_hdd_tdls_peer_timers_destroy(pHddTdlsCtx);
}
/* if mac address exist, return pointer
if mac address doesn't exist, create a list and add, return pointer
return NULL if fails to get new mac address
*/
hddTdlsPeer_t *wlan_hdd_tdls_get_peer(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac
#else
u8 *mac
#endif
)
{
struct list_head *head;
hddTdlsPeer_t *peer;
u8 key;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx;
if (!pAdapter) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD adpater is NULL"));
return NULL;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
{
return 0;
}
/* if already there, just update */
peer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if (peer != NULL)
{
return peer;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("peer mac address %pM"), mac);
/* not found, allocate and add the list */
peer = vos_mem_malloc(sizeof(hddTdlsPeer_t));
if (NULL == peer) {
hddLog(VOS_TRACE_LEVEL_ERROR, "%s peer malloc failed!", __func__);
return NULL;
}
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx)
{
vos_mem_free(peer);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddTdlsCtx is NULL"));
return NULL;
}
key = wlan_hdd_tdls_hash_key(mac);
head = &pHddTdlsCtx->peer_list[key];
vos_mem_zero(peer, sizeof(hddTdlsPeer_t));
vos_mem_copy(peer->peerMac, mac, sizeof(peer->peerMac));
peer->pHddTdlsCtx = pHddTdlsCtx;
list_add_tail(&peer->node, head);
return peer;
}
/*
* NOTE:
* The Callers of this function should ensure to release the
* tdls_lock before calling this function to avoid deadlocks.
*/
int wlan_hdd_tdls_set_cap(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8* mac,
#else
u8* mac,
#endif
tTDLSCapType cap)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return -EINVAL;
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: curr_peer is NULL", __func__);
mutex_unlock(&pHddCtx->tdls_lock);
return -1;
}
curr_peer->tdls_support = cap;
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
void wlan_hdd_tdls_set_peer_link_status(hddTdlsPeer_t *curr_peer,
tTDLSLinkStatus status,
tTDLSLinkReason reason)
{
/*EXT TDLS*/
tANI_S32 state = 0;
tANI_S32 res = 0;
/*EXT TDLS*/
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: curr_peer is NULL", __func__);
return;
}
hddLog(VOS_TRACE_LEVEL_WARN, "tdls set peer " MAC_ADDRESS_STR " link status to %u",
MAC_ADDR_ARRAY(curr_peer->peerMac), status);
curr_peer->link_status = status;
/*EXT TDLS*/
if (curr_peer->isForcedPeer && curr_peer->state_change_notification)
{
/*save the reason for any further query*/
curr_peer->reason = reason;
wlan_hdd_tdls_get_wifi_hal_state(curr_peer, &state, &res);
(curr_peer->state_change_notification)(
curr_peer->peerMac,
state,
res,
curr_peer->pHddTdlsCtx->pAdapter);
}
/*EXT TDLS*/
}
void wlan_hdd_tdls_set_link_status(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac,
#else
u8 *mac,
#endif
tTDLSLinkStatus linkStatus,
tTDLSLinkReason reason)
{
/*EXT TDLS*/
tANI_S32 state = 0;
tANI_S32 res = 0;
/*EXT TDLS*/
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (pHddCtx == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pHddCtx is NULL"));
return;
}
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL)
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: curr_peer is NULL", __func__);
return;
}
hddLog(VOS_TRACE_LEVEL_INFO,
"tdls set peer " MAC_ADDRESS_STR " link status to %u",
MAC_ADDR_ARRAY(curr_peer->peerMac), linkStatus);
curr_peer->link_status= linkStatus;
/*EXT TDLS*/
if (curr_peer->isForcedPeer && curr_peer->state_change_notification)
{
/*save the reason for any further query*/
curr_peer->reason = reason;
wlan_hdd_tdls_get_wifi_hal_state(curr_peer, &state, &res);
mutex_unlock(&pHddCtx->tdls_lock);
(*curr_peer->state_change_notification)(mac,
state,
res,
pAdapter);
}
else
mutex_unlock(&pHddCtx->tdls_lock);
/*EXT TDLS*/
return;
}
int wlan_hdd_tdls_recv_discovery_resp(hdd_adapter_t *pAdapter, u8 *mac)
{
hddTdlsPeer_t *curr_peer;
tdlsCtx_t *pHddTdlsCtx = NULL;
hdd_context_t *pHddCtx;
ENTER();
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != wlan_hdd_validate_context(pHddCtx))
return -1;
mutex_lock(&pHddCtx->tdls_lock);
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if ( NULL == pHddTdlsCtx )
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pHddTdlsCtx is NULL device mode = %d"),
pAdapter->device_mode);
return -1;
}
pHddCtx = WLAN_HDD_GET_CTX(pHddTdlsCtx->pAdapter);
if(0 != (wlan_hdd_validate_context(pHddCtx)))
{
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (NULL == curr_peer)
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
return -1;
}
if (pHddTdlsCtx->discovery_sent_cnt)
pHddTdlsCtx->discovery_sent_cnt--;
wlan_hdd_tdls_check_power_save_prohibited(pAdapter);
if (0 == pHddTdlsCtx->discovery_sent_cnt)
{
vos_timer_stop(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Discovery(%u) Response from " MAC_ADDRESS_STR " link_status %d",
pHddTdlsCtx->discovery_sent_cnt, MAC_ADDR_ARRAY(curr_peer->peerMac),
curr_peer->link_status);
curr_peer->tdls_support = eTDLS_CAP_SUPPORTED;
if (eTDLS_LINK_DISCOVERING == curr_peer->link_status)
{
/* Since we are here, it means Throughput threshold is alredy met. Make sure RSSI
threshold is also met before setting up TDLS link*/
if ((tANI_S32) curr_peer->rssi > (tANI_S32) pHddTdlsCtx->threshold_config.rssi_trigger_threshold)
{
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_DISCOVERED,
eTDLS_LINK_SUCCESS);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Rssi Threshold met: "MAC_ADDRESS_STR" rssi = %d threshold= %d" ,
MAC_ADDR_ARRAY(curr_peer->peerMac), curr_peer->rssi,
pHddTdlsCtx->threshold_config.rssi_trigger_threshold);
if (pHddCtx->tdls_mode != eTDLS_SUPPORT_DISABLED)
{
/* TDLS can be disabled from multiple sources like
* scan, p2p-listen, p2p, btc etc ...
* Dont initiate tdls setup if tdls is disabled
*/
cfg80211_tdls_oper_request(pAdapter->dev, curr_peer->peerMac,
NL80211_TDLS_SETUP, FALSE,
GFP_KERNEL);
}
else
{
curr_peer->tdls_support = eTDLS_CAP_UNKNOWN;
wlan_hdd_tdls_set_peer_link_status(
curr_peer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: TDLS is disabled. Resetting link_status of peer "
MAC_ADDRESS_STR " to %d, tdls_support %d", __func__,
MAC_ADDR_ARRAY(curr_peer->peerMac),
curr_peer->link_status, curr_peer->tdls_support);
}
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Rssi Threshold not met: "MAC_ADDRESS_STR" rssi = %d threshold = %d ",
MAC_ADDR_ARRAY(curr_peer->peerMac), curr_peer->rssi,
pHddTdlsCtx->threshold_config.rssi_trigger_threshold);
/* if RSSI threshold is not met then allow further discovery
* attempts by decrementing count for the last attempt
*/
if (curr_peer->discovery_attempt)
curr_peer->discovery_attempt--;
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED);
}
mutex_unlock(&pHddCtx->tdls_lock);
}
else
{
mutex_unlock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_check_bmps(pAdapter);
}
EXIT();
return 0;
}
int wlan_hdd_tdls_set_peer_caps(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac,
#else
u8 *mac,
#endif
tCsrStaParams *StaParams,
tANI_BOOLEAN isBufSta,
tANI_BOOLEAN isOffChannelSupported,
tANI_BOOLEAN isQosWmmSta)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return -EINVAL;
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: curr_peer is NULL", __func__);
mutex_unlock(&pHddCtx->tdls_lock);
return -1;
}
curr_peer->uapsdQueues = StaParams->uapsd_queues;
curr_peer->maxSp = StaParams->max_sp;
curr_peer->isBufSta = isBufSta;
curr_peer->isOffChannelSupported = isOffChannelSupported;
vos_mem_copy(curr_peer->supported_channels,
StaParams->supported_channels,
StaParams->supported_channels_len);
curr_peer->supported_channels_len =
StaParams->supported_channels_len;
vos_mem_copy(curr_peer->supported_oper_classes,
StaParams->supported_oper_classes,
StaParams->supported_oper_classes_len);
curr_peer->supported_oper_classes_len =
StaParams->supported_oper_classes_len;
curr_peer->qos = isQosWmmSta;
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
int wlan_hdd_tdls_get_link_establish_params(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac,
#else
u8 *mac,
#endif
tCsrTdlsLinkEstablishParams* tdlsLinkEstablishParams)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return -EINVAL;
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: curr_peer is NULL", __func__);
mutex_unlock(&pHddCtx->tdls_lock);
return -1;
}
tdlsLinkEstablishParams->isResponder = curr_peer->is_responder;
tdlsLinkEstablishParams->uapsdQueues = curr_peer->uapsdQueues;
tdlsLinkEstablishParams->maxSp = curr_peer->maxSp;
tdlsLinkEstablishParams->isBufSta = curr_peer->isBufSta;
tdlsLinkEstablishParams->isOffChannelSupported =
curr_peer->isOffChannelSupported;
vos_mem_copy(tdlsLinkEstablishParams->supportedChannels,
curr_peer->supported_channels,
curr_peer->supported_channels_len);
tdlsLinkEstablishParams->supportedChannelsLen =
curr_peer->supported_channels_len;
vos_mem_copy(tdlsLinkEstablishParams->supportedOperClasses,
curr_peer->supported_oper_classes,
curr_peer->supported_oper_classes_len);
tdlsLinkEstablishParams->supportedOperClassesLen =
curr_peer->supported_oper_classes_len;
tdlsLinkEstablishParams->qos = curr_peer->qos;
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
int wlan_hdd_tdls_set_rssi(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac,
#else
u8 *mac,
#endif
tANI_S8 rxRssi)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(hdd_ctx))) {
return -1;
}
mutex_lock(&hdd_ctx->tdls_lock);
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL)
{
mutex_unlock(&hdd_ctx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
return -1;
}
curr_peer->rssi = rxRssi;
mutex_unlock(&hdd_ctx->tdls_lock);
return 0;
}
int wlan_hdd_tdls_set_responder(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac,
#else
u8 *mac,
#endif
tANI_U8 responder)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return -EINVAL;
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: curr_peer is NULL", __func__);
mutex_unlock(&pHddCtx->tdls_lock);
return -1;
}
curr_peer->is_responder = responder;
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
int wlan_hdd_tdls_set_signature(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac,
#else
u8 *mac,
#endif
tANI_U8 uSignature)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return -EINVAL;
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: curr_peer is NULL", __func__);
mutex_unlock(&pHddCtx->tdls_lock);
return -1;
}
curr_peer->signature = uSignature;
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
void wlan_hdd_tdls_extract_da(struct sk_buff *skb, u8 *mac)
{
memcpy(mac, skb->data, 6);
}
void wlan_hdd_tdls_extract_sa(struct sk_buff *skb, u8 *mac)
{
memcpy(mac, skb->data+6, 6);
}
/**
* wlan_hdd_tdls_is_forced_peer - function to check if peer is forced peer
* @adapter: pointer to hdd apater
* @mac: peer mac address
*
* Function identified is the peer is forced for tdls connection
*
* return: true: peer is forced false: peer is not forced
*/
static bool wlan_hdd_tdls_is_forced_peer(hdd_adapter_t *adapter,
const u8 *mac)
{
hddTdlsPeer_t *peer;
hdd_context_t *hddctx = WLAN_HDD_GET_CTX(adapter);
bool is_forced_peer;
mutex_lock(&hddctx->tdls_lock);
peer = wlan_hdd_tdls_find_peer(adapter, mac, FALSE);
if (!peer)
{
is_forced_peer = false;
goto ret;
}
if (!peer->isForcedPeer)
{
is_forced_peer = false;
goto ret;
}
is_forced_peer = true;
ret:
mutex_unlock(&hddctx->tdls_lock);
return is_forced_peer;
}
/**
* wlan_hdd_tdls_increment_pkt_count - function to increment tdls tx packet cnt
* @pAdapter: pointer to hdd adapter
* @skb: pointer to sk_buff
*
* Function to increment packet count if packet is destined to tdls peer
*
* return: None
*/
static void wlan_hdd_tdls_increment_pkt_count(hdd_adapter_t *pAdapter,
struct sk_buff *skb)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_station_ctx_t *hdd_sta_ctx;
u8 mac[6];
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return;
if (eTDLS_SUPPORT_ENABLED != pHddCtx->tdls_mode)
goto error;
if (!pHddCtx->cfg_ini->fEnableTDLSImplicitTrigger)
goto error;
wlan_hdd_tdls_extract_da(skb, mac);
if (pHddCtx->cfg_ini->fTDLSExternalControl)
{
if (!wlan_hdd_tdls_is_forced_peer(pAdapter, mac))
goto error;
}
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (vos_is_macaddr_group((v_MACADDR_t *)mac))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED,
"broadcast packet, not adding to peer list");
goto error;
}
if (memcmp(hdd_sta_ctx->conn_info.bssId, mac, 6) == 0) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED,
"packet da is bssid, not adding to peer list");
goto error;
}
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("curr_peer is NULL"));
mutex_unlock(&pHddCtx->tdls_lock);
goto error;
}
curr_peer->tx_pkt++;
mutex_unlock(&pHddCtx->tdls_lock);
error:
return;
}
void wlan_hdd_tdls_notify_packet(hdd_adapter_t *adapter, struct sk_buff *skb)
{
wlan_hdd_tdls_increment_pkt_count(adapter, skb);
}
static int wlan_hdd_tdls_check_config(tdls_config_params_t *config)
{
if (config->tdls > 2)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 1st argument %d. <0...2>", __func__, config->tdls);
return -1;
}
if (config->tx_period_t < CFG_TDLS_TX_STATS_PERIOD_MIN ||
config->tx_period_t > CFG_TDLS_TX_STATS_PERIOD_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 2nd argument %d. <%d...%ld>", __func__, config->tx_period_t,
CFG_TDLS_TX_STATS_PERIOD_MIN, CFG_TDLS_TX_STATS_PERIOD_MAX);
return -1;
}
if (config->tx_packet_n < CFG_TDLS_TX_PACKET_THRESHOLD_MIN ||
config->tx_packet_n > CFG_TDLS_TX_PACKET_THRESHOLD_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 3rd argument %d. <%d...%ld>", __func__, config->tx_packet_n,
CFG_TDLS_TX_PACKET_THRESHOLD_MIN, CFG_TDLS_TX_PACKET_THRESHOLD_MAX);
return -1;
}
if (config->discovery_period_t < CFG_TDLS_DISCOVERY_PERIOD_MIN ||
config->discovery_period_t > CFG_TDLS_DISCOVERY_PERIOD_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 4th argument %d. <%d...%ld>", __func__, config->discovery_period_t,
CFG_TDLS_DISCOVERY_PERIOD_MIN, CFG_TDLS_DISCOVERY_PERIOD_MAX);
return -1;
}
if (config->discovery_tries_n < CFG_TDLS_MAX_DISCOVERY_ATTEMPT_MIN ||
config->discovery_tries_n > CFG_TDLS_MAX_DISCOVERY_ATTEMPT_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 5th argument %d. <%d...%d>", __func__, config->discovery_tries_n,
CFG_TDLS_MAX_DISCOVERY_ATTEMPT_MIN, CFG_TDLS_MAX_DISCOVERY_ATTEMPT_MAX);
return -1;
}
if (config->idle_timeout_t < CFG_TDLS_IDLE_TIMEOUT_MIN ||
config->idle_timeout_t > CFG_TDLS_IDLE_TIMEOUT_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 6th argument %d. <%d...%d>", __func__, config->idle_timeout_t,
CFG_TDLS_IDLE_TIMEOUT_MIN, CFG_TDLS_IDLE_TIMEOUT_MAX);
return -1;
}
if (config->idle_packet_n < CFG_TDLS_IDLE_PACKET_THRESHOLD_MIN ||
config->idle_packet_n > CFG_TDLS_IDLE_PACKET_THRESHOLD_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 7th argument %d. <%d...%d>", __func__, config->idle_packet_n,
CFG_TDLS_IDLE_PACKET_THRESHOLD_MIN, CFG_TDLS_IDLE_PACKET_THRESHOLD_MAX);
return -1;
}
if (config->rssi_hysteresis < CFG_TDLS_RSSI_HYSTERESIS_MIN ||
config->rssi_hysteresis > CFG_TDLS_RSSI_HYSTERESIS_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 8th argument %d. <%d...%d>", __func__, config->rssi_hysteresis,
CFG_TDLS_RSSI_HYSTERESIS_MIN, CFG_TDLS_RSSI_HYSTERESIS_MAX);
return -1;
}
if (config->rssi_trigger_threshold < CFG_TDLS_RSSI_TRIGGER_THRESHOLD_MIN ||
config->rssi_trigger_threshold > CFG_TDLS_RSSI_TRIGGER_THRESHOLD_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 9th argument %d. <%d...%d>", __func__, config->rssi_trigger_threshold,
CFG_TDLS_RSSI_TRIGGER_THRESHOLD_MIN, CFG_TDLS_RSSI_TRIGGER_THRESHOLD_MAX);
return -1;
}
if (config->rssi_teardown_threshold < CFG_TDLS_RSSI_TEARDOWN_THRESHOLD_MIN ||
config->rssi_teardown_threshold > CFG_TDLS_RSSI_TEARDOWN_THRESHOLD_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 10th argument %d. <%d...%d>", __func__, config->rssi_teardown_threshold,
CFG_TDLS_RSSI_TEARDOWN_THRESHOLD_MIN, CFG_TDLS_RSSI_TEARDOWN_THRESHOLD_MAX);
return -1;
}
return 0;
}
int wlan_hdd_tdls_set_params(struct net_device *dev, tdls_config_params_t *config)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
tdlsCtx_t *pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
eTDLSSupportMode req_tdls_mode;
if ((TRUE != pHddCtx->cfg_ini->fEnableTDLSSupport) &&
(TRUE != sme_IsFeatureSupportedByFW(TDLS)))
{
hddLog(LOGE,
FL("TDLS not enabled (%d) or FW doesn't support (%d)"),
pHddCtx->cfg_ini->fEnableTDLSSupport,
sme_IsFeatureSupportedByFW(TDLS));
pHddCtx->tdls_mode = eTDLS_SUPPORT_NOT_ENABLED;
return -EINVAL;
}
mutex_lock(&pHddCtx->tdls_lock);
if (NULL == pHddTdlsCtx)
{
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(VOS_TRACE_LEVEL_ERROR, FL("TDLS not enabled!"));
return -1;
}
if (wlan_hdd_tdls_check_config(config) != 0)
{
mutex_unlock(&pHddCtx->tdls_lock);
return -1;
}
/* config->tdls is mapped to 0->1, 1->2, 2->3 */
req_tdls_mode = config->tdls + 1;
if (pHddCtx->tdls_mode == req_tdls_mode)
{
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(VOS_TRACE_LEVEL_ERROR, "%s already in mode %d", __func__, config->tdls);
return -1;
}
/* copy the configuration only when given tdls mode is implicit trigger enable */
if (eTDLS_SUPPORT_ENABLED == req_tdls_mode)
{
memcpy(&pHddTdlsCtx->threshold_config, config, sizeof(tdls_config_params_t));
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"iw set tdls params: %d %d %d %d %d %d %d %d %d %d",
config->tdls,
config->tx_period_t,
config->tx_packet_n,
config->discovery_period_t,
config->discovery_tries_n,
config->idle_timeout_t,
config->idle_packet_n,
config->rssi_hysteresis,
config->rssi_trigger_threshold,
config->rssi_teardown_threshold);
mutex_unlock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_set_mode(pHddCtx, req_tdls_mode, TRUE,
HDD_SET_TDLS_MODE_SOURCE_USER);
return 0;
}
int wlan_hdd_tdls_set_sta_id(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac,
#else
u8 *mac,
#endif
u8 staId)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return -EINVAL;
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: curr_peer is NULL", __func__);
mutex_unlock(&pHddCtx->tdls_lock);
return -1;
}
curr_peer->staId = staId;
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
int wlan_hdd_tdls_set_force_peer(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac,
#else
u8 *mac,
#endif
tANI_BOOLEAN forcePeer)
{
/* NOTE:
* Hold mutex tdls_lock before calling this function
*/
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if ((NULL == pHddCtx)) return -1;
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL)
goto error;
curr_peer->isForcedPeer = forcePeer;
return 0;
error:
return -1;
}
/* if peerMac is found, then it returns pointer to hddTdlsPeer_t
otherwise, it returns NULL
*/
hddTdlsPeer_t *wlan_hdd_tdls_find_peer(hdd_adapter_t *pAdapter,
const u8 *mac,
tANI_BOOLEAN mutexLock)
{
u8 key;
struct list_head *pos;
struct list_head *head;
hddTdlsPeer_t *curr_peer;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if(0 != (wlan_hdd_validate_context(pHddCtx)))
{
return 0;
}
if ( mutexLock )
{
mutex_lock(&pHddCtx->tdls_lock);
}
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx)
{
if ( mutexLock )
mutex_unlock(&pHddCtx->tdls_lock);
return NULL;
}
key = wlan_hdd_tdls_hash_key(mac);
head = &pHddTdlsCtx->peer_list[key];
list_for_each(pos, head) {
curr_peer = list_entry (pos, hddTdlsPeer_t, node);
if (!memcmp(mac, curr_peer->peerMac, 6)) {
if ( mutexLock )
mutex_unlock(&pHddCtx->tdls_lock);
return curr_peer;
}
}
if ( mutexLock )
mutex_unlock(&pHddCtx->tdls_lock);
return NULL;
}
hddTdlsPeer_t *wlan_hdd_tdls_find_all_peer(hdd_context_t *pHddCtx,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac
#else
u8 *mac
#endif
)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter = NULL;
tdlsCtx_t *pHddTdlsCtx = NULL;
hddTdlsPeer_t *curr_peer= NULL;
VOS_STATUS status = 0;
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL != pHddTdlsCtx)
{
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if (curr_peer)
{
return curr_peer;
}
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
return curr_peer;
}
int wlan_hdd_tdls_reset_peer(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac
#else
u8 *mac
#endif
)
{
hdd_context_t *pHddCtx;
hddTdlsPeer_t *curr_peer;
pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return -EINVAL;
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: curr_peer is NULL", __func__);
return -1;
}
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED);
curr_peer->staId = 0;
/* Throughput Monitor shall disable the split scan when
* TDLS scan coexistance is disabled.At this point of time
* since TDLS scan coexistance is not meeting the criteria
* to be operational, explicitly make it false to enable
* throughput monitor takes the control of split scan.
*/
if (pHddCtx->isTdlsScanCoexistence == TRUE)
{
pHddCtx->isTdlsScanCoexistence = FALSE;
}
if((eTDLS_SUPPORT_ENABLED == pHddCtx->tdls_mode) &&
(vos_timer_is_initialized(&curr_peer->peerIdleTimer)) &&
(VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(&curr_peer->peerIdleTimer)))
{
vos_timer_stop( &curr_peer->peerIdleTimer );
}
return 0;
}
/* Caller has to take the lock before calling this function */
static void wlan_tdd_tdls_reset_tx_rx(tdlsCtx_t *pHddTdlsCtx)
{
int i;
struct list_head *head;
hddTdlsPeer_t *tmp;
struct list_head *pos, *q;
for (i = 0; i < 256; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each_safe (pos, q, head) {
tmp = list_entry(pos, hddTdlsPeer_t, node);
tmp->tx_pkt = 0;
tmp->rx_pkt = 0;
}
}
return ;
}
/* Caller has to take the lock before calling this function */
static tANI_S32 wlan_hdd_tdls_peer_reset_discovery_processed(tdlsCtx_t *pHddTdlsCtx)
{
int i;
struct list_head *head;
hddTdlsPeer_t *tmp;
struct list_head *pos, *q;
pHddTdlsCtx->discovery_peer_cnt = 0;
for (i = 0; i < 256; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each_safe (pos, q, head) {
tmp = list_entry(pos, hddTdlsPeer_t, node);
tmp->discovery_processed = 0;
}
}
return 0;
}
static tANI_S32 wlan_hdd_get_tdls_discovery_peer_cnt(tdlsCtx_t *pHddTdlsCtx)
{
int i;
struct list_head *head;
struct list_head *pos, *q;
int discovery_peer_cnt=0;
hddTdlsPeer_t *tmp;
/*
* This function expects the callers to acquire the Mutex.
*/
for (i = 0; i < 256; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each_safe (pos, q, head) {
tmp = list_entry(pos, hddTdlsPeer_t, node);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s, %d, " MAC_ADDRESS_STR, __func__, i,
MAC_ADDR_ARRAY(tmp->peerMac));
discovery_peer_cnt++;
}
}
return discovery_peer_cnt;
}
tANI_U16 wlan_hdd_tdlsConnectedPeers(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = NULL;
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("invalid pAdapter: %pK"), pAdapter);
return 0;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ENTER();
if(0 != (wlan_hdd_validate_context(pHddCtx)))
{
return 0;
}
EXIT();
return pHddCtx->connected_peer_count;
}
hddTdlsPeer_t *wlan_hdd_tdls_get_connected_peer(hdd_adapter_t *pAdapter)
{
/* NOTE:
* Hold mutext tdls_lock before calling this function
*/
int i;
struct list_head *head;
struct list_head *pos;
hddTdlsPeer_t *curr_peer = NULL;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ENTER();
if(0 != (wlan_hdd_validate_context(pHddCtx)))
{
return NULL;
}
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx) {
return NULL;
}
for (i = 0; i < 256; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each(pos, head) {
curr_peer= list_entry (pos, hddTdlsPeer_t, node);
if (curr_peer && (curr_peer->link_status == eTDLS_LINK_CONNECTED))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: " MAC_ADDRESS_STR " eTDLS_LINK_CONNECTED",
__func__, MAC_ADDR_ARRAY(curr_peer->peerMac));
return curr_peer;
}
}
}
EXIT();
return NULL;
}
int wlan_hdd_tdls_get_all_peers(hdd_adapter_t *pAdapter, char *buf, int buflen)
{
int i;
int len, init_len;
struct list_head *head;
struct list_head *pos;
hddTdlsPeer_t *curr_peer;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ENTER();
if(0 != (wlan_hdd_validate_context(pHddCtx)))
{
return 0;
}
init_len = buflen;
len = scnprintf(buf, buflen, "\n%-18s%-3s%-4s%-3s%-5s\n",
"MAC", "Id", "cap", "up", "RSSI");
buf += len;
buflen -= len;
/* 1234567890123456789012345678901234567 */
len = scnprintf(buf, buflen, "---------------------------------\n");
buf += len;
buflen -= len;
mutex_lock(&pHddCtx->tdls_lock);
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx) {
mutex_unlock(&pHddCtx->tdls_lock);
len = scnprintf(buf, buflen, "TDLS not enabled\n");
return len;
}
for (i = 0; i < 256; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each(pos, head) {
curr_peer= list_entry (pos, hddTdlsPeer_t, node);
if (buflen < 32+1)
break;
len = scnprintf(buf, buflen,
MAC_ADDRESS_STR"%3d%4s%3s%5d\n",
MAC_ADDR_ARRAY(curr_peer->peerMac),
curr_peer->staId,
(curr_peer->tdls_support == eTDLS_CAP_SUPPORTED) ? "Y":"N",
TDLS_IS_CONNECTED(curr_peer) ? "Y":"N",
curr_peer->rssi);
buf += len;
buflen -= len;
}
}
mutex_unlock(&pHddCtx->tdls_lock);
EXIT();
return init_len-buflen;
}
void wlan_hdd_tdls_connection_callback(hdd_adapter_t *pAdapter)
{
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if ((NULL == pHddCtx))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
FL("pHddCtx or pHddTdlsCtx points to NULL"));
return;
}
mutex_lock(&pHddCtx->tdls_lock);
if (0 != wlan_hdd_sta_tdls_init(pAdapter))
{
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(LOGE, FL("wlan_hdd_sta_tdls_init failed"));
return;
}
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if ((NULL == pHddTdlsCtx))
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
FL("pHddCtx or pHddTdlsCtx points to NULL device mode = %d"), pAdapter->device_mode);
return;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s, update %d discover %d", __func__,
pHddTdlsCtx->threshold_config.tx_period_t,
pHddTdlsCtx->threshold_config.discovery_period_t);
if (eTDLS_SUPPORT_ENABLED == pHddCtx->tdls_mode)
{
wlan_hdd_tdls_peer_reset_discovery_processed(pHddTdlsCtx);
pHddTdlsCtx->discovery_sent_cnt = 0;
wlan_hdd_tdls_check_power_save_prohibited(pHddTdlsCtx->pAdapter);
wlan_hdd_tdls_timer_restart(pHddTdlsCtx->pAdapter,
&pHddTdlsCtx->peerUpdateTimer,
pHddTdlsCtx->threshold_config.tx_period_t);
}
mutex_unlock(&pHddCtx->tdls_lock);
}
void wlan_hdd_tdls_disconnection_callback(hdd_adapter_t *pAdapter)
{
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"%s", __func__);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != wlan_hdd_validate_context(pHddCtx))
return;
mutex_lock(&pHddCtx->tdls_lock);
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx)
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddTdlsCtx is NULL"));
return;
}
pHddTdlsCtx->discovery_sent_cnt = 0;
wlan_hdd_tdls_check_power_save_prohibited(pHddTdlsCtx->pAdapter);
wlan_hdd_tdls_exit(pAdapter, TRUE);
mutex_unlock(&pHddCtx->tdls_lock);
}
void wlan_hdd_tdls_mgmt_completion_callback(hdd_adapter_t *pAdapter, tANI_U32 statusCode)
{
pAdapter->mgmtTxCompletionStatus = statusCode;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Mgmt TX Completion %d",__func__, statusCode);
complete(&pAdapter->tdls_mgmt_comp);
}
void wlan_hdd_tdls_increment_peer_count(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ENTER();
if(0 != (wlan_hdd_validate_context(pHddCtx)))
{
return;
}
mutex_lock(&pHddCtx->tdls_lock);
pHddCtx->connected_peer_count++;
wlan_hdd_tdls_check_power_save_prohibited(pAdapter);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: %d",
__func__, pHddCtx->connected_peer_count);
mutex_unlock(&pHddCtx->tdls_lock);
EXIT();
}
void wlan_hdd_tdls_decrement_peer_count(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ENTER();
if(0 != (wlan_hdd_validate_context(pHddCtx)))
{
return;
}
mutex_lock(&pHddCtx->tdls_lock);
if (pHddCtx->connected_peer_count)
pHddCtx->connected_peer_count--;
wlan_hdd_tdls_check_power_save_prohibited(pAdapter);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: %d",
__func__, pHddCtx->connected_peer_count);
mutex_unlock(&pHddCtx->tdls_lock);
EXIT();
}
void wlan_hdd_tdls_check_bmps(hdd_adapter_t *pAdapter)
{
tdlsCtx_t *pHddTdlsCtx = NULL;
hdd_context_t *pHddCtx = NULL;
hddTdlsPeer_t *curr_peer;
VOS_STATUS status;
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("invalid pAdapter: %pK"), pAdapter);
return;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (NULL == pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddCtx points to NULL"));
return;
}
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_is_progress(pHddCtx, NULL, 0, FALSE);
if (NULL != curr_peer)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: tdls in progress. Dont check for BMPS " MAC_ADDRESS_STR,
__func__, MAC_ADDR_ARRAY (curr_peer->peerMac));
mutex_unlock(&pHddCtx->tdls_lock);
return;
}
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx)
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddTdlsCtx points to NULL"));
return;
}
if ((TDLS_CTX_MAGIC != pHddCtx->scan_ctxt.magic) &&
(0 == pHddCtx->connected_peer_count) &&
(0 == pHddTdlsCtx->discovery_sent_cnt))
{
mutex_unlock(&pHddCtx->tdls_lock);
if (FALSE == sme_IsPmcBmps(WLAN_HDD_GET_HAL_CTX(pAdapter)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s: No TDLS peer connected/discovery sent. Enable BMPS",
__func__);
status = hdd_enable_bmps_imps(pHddCtx);
if (status == VOS_STATUS_SUCCESS)
pHddTdlsCtx->is_tdls_disabled_bmps = false;
}
}
else
{
mutex_unlock(&pHddCtx->tdls_lock);
if (TRUE == sme_IsPmcBmps(WLAN_HDD_GET_HAL_CTX(pAdapter)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: TDLS peer connected. Disable BMPS", __func__);
status = hdd_disable_bmps_imps(pHddCtx, WLAN_HDD_INFRA_STATION);
if (status == VOS_STATUS_SUCCESS)
pHddTdlsCtx->is_tdls_disabled_bmps = true;
}
}
return;
}
/* return pointer to hddTdlsPeer_t if TDLS is ongoing. Otherwise return NULL.
* mac - if NULL check for all the peer list, otherwise, skip this mac when skip_self is TRUE
* skip_self - if TRUE, skip this mac. otherwise, check all the peer list. if
mac is NULL, this argument is ignored, and check for all the peer list.
*/
static hddTdlsPeer_t *wlan_hdd_tdls_find_progress_peer(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac,
#else
u8 *mac,
#endif
u8 skip_self)
{
int i;
struct list_head *head;
hddTdlsPeer_t *curr_peer;
struct list_head *pos;
tdlsCtx_t *pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);;
if (NULL == pHddTdlsCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddTdlsCtx is NULL"));
return NULL;
}
for (i = 0; i < 256; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each(pos, head) {
curr_peer = list_entry (pos, hddTdlsPeer_t, node);
if (skip_self && mac && !memcmp(mac, curr_peer->peerMac, 6)) {
continue;
}
else
{
if (eTDLS_LINK_CONNECTING == curr_peer->link_status)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s:" MAC_ADDRESS_STR " eTDLS_LINK_CONNECTING",
__func__, MAC_ADDR_ARRAY(curr_peer->peerMac));
return curr_peer;
}
}
}
}
return NULL;
}
hddTdlsPeer_t *wlan_hdd_tdls_is_progress(hdd_context_t *pHddCtx,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac,
#else
u8 *mac,
#endif
u8 skip_self, tANI_BOOLEAN mutexLock)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter = NULL;
tdlsCtx_t *pHddTdlsCtx = NULL;
hddTdlsPeer_t *curr_peer= NULL;
VOS_STATUS status = 0;
if (mutexLock)
{
mutex_lock(&pHddCtx->tdls_lock);
}
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL != pHddTdlsCtx)
{
curr_peer = wlan_hdd_tdls_find_progress_peer(pAdapter, mac, skip_self);
if (curr_peer)
{
if (mutexLock)
mutex_unlock(&pHddCtx->tdls_lock);
return curr_peer;
}
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
if (mutexLock)
mutex_unlock(&pHddCtx->tdls_lock);
return NULL;
}
static void wlan_hdd_tdls_implicit_disable(tdlsCtx_t *pHddTdlsCtx)
{
wlan_hdd_tdls_timers_stop(pHddTdlsCtx);
}
static void wlan_hdd_tdls_implicit_enable(tdlsCtx_t *pHddTdlsCtx)
{
wlan_hdd_tdls_peer_reset_discovery_processed(pHddTdlsCtx);
pHddTdlsCtx->discovery_sent_cnt = 0;
wlan_tdd_tdls_reset_tx_rx(pHddTdlsCtx);
wlan_hdd_tdls_check_power_save_prohibited(pHddTdlsCtx->pAdapter);
wlan_hdd_tdls_timer_restart(pHddTdlsCtx->pAdapter,
&pHddTdlsCtx->peerUpdateTimer,
pHddTdlsCtx->threshold_config.tx_period_t);
}
void wlan_hdd_tdls_set_mode(hdd_context_t *pHddCtx,
eTDLSSupportMode tdls_mode,
v_BOOL_t bUpdateLast,
enum tdls_disable_source source)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
VOS_STATUS status;
hdd_adapter_t *pAdapter;
tdlsCtx_t *pHddTdlsCtx;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s mode %d", __func__, (int)tdls_mode);
if(0 != (wlan_hdd_validate_context(pHddCtx)))
{
return;
}
mutex_lock(&pHddCtx->tdls_lock);
if (pHddCtx->tdls_mode == tdls_mode)
{
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(VOS_TRACE_LEVEL_INFO, "%s already in mode %d", __func__,
(int)tdls_mode);
if (tdls_mode == eTDLS_SUPPORT_DISABLED)
{
/*
* TDLS is already disabled hence set source mask and return
*/
set_bit((unsigned long)source, &pHddCtx->tdls_source_bitmap);
return;
}
if (tdls_mode == eTDLS_SUPPORT_ENABLED)
{
/*
* TDLS is already disabled hence set source mask and return
*/
clear_bit((unsigned long)source, &pHddCtx->tdls_source_bitmap);
return;
}
return;
}
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL != pHddTdlsCtx)
{
if(eTDLS_SUPPORT_ENABLED == tdls_mode)
{
clear_bit((unsigned long)source, &pHddCtx->tdls_source_bitmap);
/*
* Check if any TDLS source bit is set and if bitmap is
* not zero then we should not enable TDLS
*/
if (pHddCtx->tdls_source_bitmap)
{
mutex_unlock(&pHddCtx->tdls_lock);
return;
}
wlan_hdd_tdls_implicit_enable(pHddTdlsCtx);
}
else if((eTDLS_SUPPORT_DISABLED == tdls_mode))
{
set_bit((unsigned long)source, &pHddCtx->tdls_source_bitmap);
wlan_hdd_tdls_implicit_disable(pHddTdlsCtx);
if (pHddTdlsCtx->is_tdls_disabled_bmps) {
if (FALSE == sme_IsPmcBmps(pHddCtx->hHal)) {
VOS_TRACE( VOS_MODULE_ID_HDD,
VOS_TRACE_LEVEL_DEBUG,
"%s: TDLS is disabled. Enable BMPS",
__func__);
status = hdd_enable_bmps_imps(pHddCtx);
if (status == VOS_STATUS_SUCCESS)
pHddTdlsCtx->is_tdls_disabled_bmps = false;
}
}
}
else if ((eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY == tdls_mode))
{
clear_bit((unsigned long)source, &pHddCtx->tdls_source_bitmap);
/*
* Check if any TDLS source bit is set and if bitmap is
* not zero then we should not enable TDLS
*/
if (pHddCtx->tdls_source_bitmap)
{
mutex_unlock(&pHddCtx->tdls_lock);
return;
}
wlan_hdd_tdls_implicit_disable(pHddTdlsCtx);
}
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
if(bUpdateLast)
{
pHddCtx->tdls_mode_last = tdls_mode;
}
else
{
pHddCtx->tdls_mode_last = pHddCtx->tdls_mode;
}
pHddCtx->tdls_mode = tdls_mode;
mutex_unlock(&pHddCtx->tdls_lock);
}
static
void wlan_hdd_tdls_implicit_send_discovery_request(tdlsCtx_t * pHddTdlsCtx)
{
hdd_context_t *pHddCtx = NULL;
hddTdlsPeer_t *curr_peer = NULL, *temp_peer = NULL;
ENTER();
if (NULL == pHddTdlsCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddTdlsCtx is NULL"));
return;
}
pHddCtx = WLAN_HDD_GET_CTX(pHddTdlsCtx->pAdapter);
if(0 != (wlan_hdd_validate_context(pHddCtx)))
{
return;
}
curr_peer = pHddTdlsCtx->curr_candidate;
if (NULL == curr_peer)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
return;
}
/* This function is called in mutex_lock */
temp_peer = wlan_hdd_tdls_is_progress(pHddCtx, NULL, 0, FALSE);
if (NULL != temp_peer)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: " MAC_ADDRESS_STR " ongoing. pre_setup ignored",
__func__, MAC_ADDR_ARRAY(temp_peer->peerMac));
goto done;
}
if (eTDLS_CAP_UNKNOWN != curr_peer->tdls_support)
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_DISCOVERING,
eTDLS_LINK_SUCCESS);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Implicit TDLS, Send Discovery request event", __func__);
cfg80211_tdls_oper_request(pHddTdlsCtx->pAdapter->dev,
curr_peer->peerMac,
NL80211_TDLS_DISCOVERY_REQ,
FALSE,
GFP_KERNEL);
pHddTdlsCtx->discovery_sent_cnt++;
wlan_hdd_tdls_check_power_save_prohibited(pHddTdlsCtx->pAdapter);
wlan_hdd_tdls_timer_restart(pHddTdlsCtx->pAdapter,
&pHddTdlsCtx->peerDiscoveryTimeoutTimer,
pHddTdlsCtx->threshold_config.tx_period_t - TDLS_DISCOVERY_TIMEOUT_BEFORE_UPDATE);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: discovery count %u timeout %u msec",
__func__, pHddTdlsCtx->discovery_sent_cnt,
pHddTdlsCtx->threshold_config.tx_period_t - TDLS_DISCOVERY_TIMEOUT_BEFORE_UPDATE);
done:
pHddTdlsCtx->curr_candidate = NULL;
pHddTdlsCtx->magic = 0;
EXIT();
return;
}
tANI_U32 wlan_hdd_tdls_discovery_sent_cnt(hdd_context_t *pHddCtx)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter = NULL;
tdlsCtx_t *pHddTdlsCtx = NULL;
VOS_STATUS status = 0;
tANI_U32 count = 0;
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL != pHddTdlsCtx)
{
count = count + pHddTdlsCtx->discovery_sent_cnt;
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
return count;
}
void wlan_hdd_tdls_check_power_save_prohibited(hdd_adapter_t *pAdapter)
{
tdlsCtx_t *pHddTdlsCtx = NULL;
hdd_context_t *pHddCtx = NULL;
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("invalid pAdapter: %pK"), pAdapter);
return;
}
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if ((NULL == pHddTdlsCtx) || (NULL == pHddCtx))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL(" pHddCtx or pHddTdlsCtx points to NULL"));
return;
}
if ((0 == pHddCtx->connected_peer_count) &&
(0 == wlan_hdd_tdls_discovery_sent_cnt(pHddCtx)))
{
sme_SetTdlsPowerSaveProhibited(WLAN_HDD_GET_HAL_CTX(pHddTdlsCtx->pAdapter), 0);
return;
}
sme_SetTdlsPowerSaveProhibited(WLAN_HDD_GET_HAL_CTX(pHddTdlsCtx->pAdapter), 1);
return;
}
/* return negative = caller should stop and return error code immediately
return 0 = caller should stop and return success immediately
return 1 = caller can continue to scan
*/
int wlan_hdd_tdls_scan_callback (hdd_adapter_t *pAdapter,
struct wiphy *wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
struct net_device *dev,
#endif
struct cfg80211_scan_request *request)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
u16 connectedTdlsPeers;
hddTdlsPeer_t *curr_peer, *connected_peer;
unsigned long delay;
hdd_config_t *cfg_param = pHddCtx->cfg_ini;
ENTER();
if(0 != (wlan_hdd_validate_context(pHddCtx)))
{
return 0;
}
/* if tdls is not enabled, then continue scan */
if ((eTDLS_SUPPORT_NOT_ENABLED == pHddCtx->tdls_mode))
return 1;
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_is_progress(pHddCtx, NULL, 0, FALSE);
if (NULL != curr_peer)
{
if (pHddCtx->scan_ctxt.reject++ >= TDLS_MAX_SCAN_REJECT)
{
pHddCtx->scan_ctxt.reject = 0;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: " MAC_ADDRESS_STR ". scan rejected %d. force it to idle",
__func__, MAC_ADDR_ARRAY (curr_peer->peerMac), pHddCtx->scan_ctxt.reject);
wlan_hdd_tdls_set_peer_link_status (curr_peer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED);
mutex_unlock(&pHddCtx->tdls_lock);
return 1;
}
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: tdls in progress. scan rejected %d",
__func__, pHddCtx->scan_ctxt.reject);
return -EBUSY;
}
else
mutex_unlock(&pHddCtx->tdls_lock);
/* if fEnableTDLSScan flag is 1 ; driverwill allow scan even if
* peer station is not buffer STA capable
*
* RX: If there is any RX activity, device will lose RX packets,
* as peer will not be aware that device is off channel.
* TX: TX is stopped whenever device initiate scan.
*/
if (pHddCtx->cfg_ini->fEnableTDLSScan == 1)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("Allow SCAN in all TDLS cases"));
return 1;
}
/* tdls teardown is ongoing */
if (eTDLS_SUPPORT_DISABLED == pHddCtx->tdls_mode)
{
connectedTdlsPeers = wlan_hdd_tdlsConnectedPeers(pAdapter);
if (connectedTdlsPeers && (pHddCtx->scan_ctxt.attempt < TDLS_MAX_SCAN_SCHEDULE))
{
delay = (unsigned long)(TDLS_DELAY_SCAN_PER_CONNECTION*connectedTdlsPeers);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: tdls disabled, but still connected_peers %d attempt %d. schedule scan %lu msec",
__func__, connectedTdlsPeers, pHddCtx->scan_ctxt.attempt, delay);
wlan_hdd_defer_scan_init_work (pHddCtx, wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
dev,
#endif
request,
msecs_to_jiffies(delay));
/* scan should not continue */
return 0;
}
/* no connected peer or max retry reached, scan continue */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: tdls disabled. connected_peers %d attempt %d. scan allowed",
__func__, connectedTdlsPeers, pHddCtx->scan_ctxt.attempt);
return 1;
}
/* while tdls is up, first time scan */
else if (eTDLS_SUPPORT_ENABLED == pHddCtx->tdls_mode ||
eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY == pHddCtx->tdls_mode)
{
connectedTdlsPeers = wlan_hdd_tdlsConnectedPeers(pAdapter);
/* check the TDLS link and Scan coexistance Capability */
if ( (TRUE == pHddCtx->cfg_ini->fEnableTDLSScanCoexSupport) &&
(TRUE == sme_IsFeatureSupportedByFW(TDLS_SCAN_COEXISTENCE)) &&
(connectedTdlsPeers == 1) )
{
mutex_lock(&pHddCtx->tdls_lock);
/* get connected peer information */
connected_peer = wlan_hdd_tdls_get_connected_peer(pAdapter);
if (NULL == connected_peer) {
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: Invalid connected_peer, Continue Scanning", __func__);
/* scan should continue */
return 1;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
("%s: TDLS Scan Co-exist supported connectedTdlsPeers =%d buffersta =%d"),
__func__,connectedTdlsPeers,connected_peer->isBufSta);
if (connected_peer->isBufSta)
{
mutex_unlock(&pHddCtx->tdls_lock);
pHddCtx->isTdlsScanCoexistence = TRUE;
if ((cfg_param->dynSplitscan) && (!pHddCtx->issplitscan_enabled))
{
pHddCtx->issplitscan_enabled = TRUE;
sme_enable_disable_split_scan(
WLAN_HDD_GET_HAL_CTX(pAdapter),
cfg_param->nNumStaChanCombinedConc,
cfg_param->nNumP2PChanCombinedConc);
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
("%s:%d TDLS Scan Co-exist supported splitscan_enabled =%d "),
__func__, __LINE__, pHddCtx->issplitscan_enabled);
return 1;
}
else
mutex_unlock(&pHddCtx->tdls_lock);
}
else
{
/* Throughput Monitor shall disable the split scan when
* TDLS scan coexistance is disabled.At this point of time
* since TDLS scan coexistance is not meeting the criteria
* to be operational, explicitly make it false to enable
* throughput monitor takes the control of split scan.
*/
pHddCtx->isTdlsScanCoexistence = FALSE;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
("%s: TDLS Scan Co-exist not supported connectedTdlsPeers =%d"
" TDLSScanCoexSupport param =%d TDLS_SCAN_COEXISTENCE =%d"),
__func__, connectedTdlsPeers,
pHddCtx->cfg_ini->fEnableTDLSScanCoexSupport,
sme_IsFeatureSupportedByFW(TDLS_SCAN_COEXISTENCE));
/* disable implicit trigger logic & tdls operatoin */
wlan_hdd_tdls_set_mode(pHddCtx, eTDLS_SUPPORT_DISABLED, FALSE,
HDD_SET_TDLS_MODE_SOURCE_SCAN);
/* fall back to the implementation of teardown the peers on the scan
* when the number of connected peers are more than one. TDLS Scan
* coexistance feature is exercised only when a single peer is
* connected and the DUT shall not advertize the Buffer Sta capability,
* so that the peer shall not go to the TDLS power save
*/
if (connectedTdlsPeers)
{
tANI_U8 staIdx;
hddTdlsPeer_t *curr_peer;
mutex_lock(&pHddCtx->tdls_lock);
for (staIdx = 0; staIdx < HDD_MAX_NUM_TDLS_STA; staIdx++)
{
if (pHddCtx->tdlsConnInfo[staIdx].staId)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
("%s: indicate TDLS teardown (staId %d)"),
__func__, pHddCtx->tdlsConnInfo[staIdx].staId) ;
#ifdef CONFIG_TDLS_IMPLICIT
curr_peer = wlan_hdd_tdls_find_all_peer(pHddCtx,
pHddCtx->tdlsConnInfo[staIdx].peerMac.bytes);
if(curr_peer) {
wlan_hdd_tdls_indicate_teardown(
curr_peer->pHddTdlsCtx->pAdapter, curr_peer,
eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
hdd_send_wlan_tdls_teardown_event(
eTDLS_TEARDOWN_SCAN,
curr_peer->peerMac);
}
#endif
}
}
mutex_unlock(&pHddCtx->tdls_lock);
/* schedule scan */
delay = (unsigned long)(TDLS_DELAY_SCAN_PER_CONNECTION*connectedTdlsPeers);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: tdls enabled (mode %d), connected_peers %d. schedule scan %lu msec",
__func__, pHddCtx->tdls_mode, wlan_hdd_tdlsConnectedPeers(pAdapter),
delay);
wlan_hdd_defer_scan_init_work (pHddCtx, wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
dev,
#endif
request,
msecs_to_jiffies(delay));
/* scan should not continue */
return 0;
}
/* no connected peer, scan continue */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: tdls_mode %d, and no tdls connection. scan allowed",
__func__, pHddCtx->tdls_mode);
}
EXIT();
return 1;
}
void wlan_hdd_tdls_scan_done_callback(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ENTER();
if(0 != (wlan_hdd_validate_context(pHddCtx)))
{
return;
}
/* if tdls is not enabled then don't revert tdls mode */
if ((eTDLS_SUPPORT_NOT_ENABLED == pHddCtx->tdls_mode)) {
hddLog(VOS_TRACE_LEVEL_INFO, FL("Failed to revert: Mode=%d"),
pHddCtx->tdls_mode);
return;
}
/* free allocated memory at scan time */
wlan_hdd_init_deinit_defer_scan_context(&pHddCtx->scan_ctxt);
/* if tdls was enabled before scan, re-enable tdls mode */
if(eTDLS_SUPPORT_ENABLED == pHddCtx->tdls_mode_last ||
eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY == pHddCtx->tdls_mode_last)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
("%s: revert tdls mode %d"), __func__, pHddCtx->tdls_mode_last);
wlan_hdd_tdls_set_mode(pHddCtx, pHddCtx->tdls_mode_last, FALSE,
HDD_SET_TDLS_MODE_SOURCE_SCAN);
}
wlan_hdd_tdls_check_bmps(pAdapter);
EXIT();
}
void wlan_hdd_tdls_timer_restart(hdd_adapter_t *pAdapter,
vos_timer_t *timer,
v_U32_t expirationTime)
{
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (NULL == pHddStaCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pHddStaCtx is NULL"));
return;
}
/* Check whether driver load unload is in progress */
if(vos_is_load_unload_in_progress( VOS_MODULE_ID_VOSS, NULL))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Driver load/unload is in progress.", __func__);
return;
}
if (hdd_connIsConnected(pHddStaCtx))
{
vos_timer_stop(timer);
vos_timer_start(timer, expirationTime);
}
}
void wlan_hdd_tdls_indicate_teardown(hdd_adapter_t *pAdapter,
hddTdlsPeer_t *curr_peer,
tANI_U16 reason)
{
hdd_context_t *pHddCtx;
if ((NULL == pAdapter || WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic) ||
(NULL == curr_peer))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("parameters passed are invalid"));
return;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if ((eTDLS_LINK_CONNECTED != curr_peer->link_status) &&
(eTDLS_LINK_CONNECTING != curr_peer->link_status))
return;
/* Throughput Monitor shall disable the split scan when
* TDLS scan coexistance is disabled.At this point of time
* since TDLS scan coexistance is not meeting the criteria
* to be operational, explicitly make it false to enable
* throughput monitor takes the control of split scan.
*/
if (pHddCtx && (pHddCtx->isTdlsScanCoexistence == TRUE))
{
pHddCtx->isTdlsScanCoexistence = FALSE;
}
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_TEARING,
eTDLS_LINK_UNSPECIFIED);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Setting NL80211_TDLS_TEARDOWN, reason %d"), reason);
cfg80211_tdls_oper_request(pAdapter->dev,
curr_peer->peerMac,
NL80211_TDLS_TEARDOWN,
reason,
GFP_KERNEL);
}
/*EXT TDLS*/
int wlan_hdd_set_callback(hddTdlsPeer_t *curr_peer,
cfg80211_exttdls_callback callback)
{
/* NOTE:
* Hold mutex tdls_lock before calling this function
*/
hdd_context_t *pHddCtx;
hdd_adapter_t *pAdapter;
if (!curr_peer) return -1;
pAdapter = curr_peer->pHddTdlsCtx->pAdapter;
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if ((NULL == pHddCtx)) return -1;
curr_peer->state_change_notification = callback;
return 0;
}
void wlan_hdd_tdls_get_wifi_hal_state(hddTdlsPeer_t *curr_peer,
tANI_S32 *state,
tANI_S32 *reason)
{
*reason = curr_peer->reason;
switch(curr_peer->link_status)
{
case eTDLS_LINK_IDLE:
case eTDLS_LINK_DISCOVERED:
*state = WIFI_TDLS_ENABLED;
break;
case eTDLS_LINK_DISCOVERING:
case eTDLS_LINK_CONNECTING:
*state = WIFI_TDLS_TRYING;
break;
case eTDLS_LINK_CONNECTED:
if (TRUE == curr_peer->isOffChannelEstablished)
{
*state = WIFI_TDLS_ESTABLISHED_OFF_CHANNEL;
}
else
{
*state = WIFI_TDLS_ESTABLISHED;
}
break;
case eTDLS_LINK_TEARING:
*state = WIFI_TDLS_DROPPED;
break;
}
}
int wlan_hdd_tdls_get_status(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const tANI_U8* mac,
#else
tANI_U8* mac,
#endif
tANI_S32 *state,
tANI_S32 *reason)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
*state = WIFI_TDLS_DISABLED;
*reason = eTDLS_LINK_UNSPECIFIED;
}
else
{
if (pHddCtx->cfg_ini->fTDLSExternalControl &&
(FALSE == curr_peer->isForcedPeer))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is not Forced"));
*state = WIFI_TDLS_DISABLED;
*reason = eTDLS_LINK_UNSPECIFIED;
}
else
{
wlan_hdd_tdls_get_wifi_hal_state(curr_peer, state, reason);
}
}
mutex_unlock(&pHddCtx->tdls_lock);
return (0);
}
int hdd_set_tdls_scan_type(hdd_adapter_t *pAdapter,
tANI_U8 *ptr)
{
int tdls_scan_type;
hdd_context_t *pHddCtx;
if (NULL == pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: pAdapter is NULL", __func__);
return -EINVAL;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
tdls_scan_type = ptr[9] - '0';
if (tdls_scan_type <= 2)
{
pHddCtx->cfg_ini->fEnableTDLSScan = tdls_scan_type;
return 0;
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
" Wrong value is given for tdls_scan_type "
" Making fEnableTDLSScan as 0 ");
pHddCtx->cfg_ini->fEnableTDLSScan = 0;
return -EINVAL;
}
}
int wlan_hdd_validate_tdls_context(hdd_context_t *pHddCtx,
tdlsCtx_t *pHddTdlsCtx)
{
VOS_STATUS status;
int found = 0;
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter;
if (NULL == pHddTdlsCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("TDLS context is NULL"));
return -EINVAL;
}
status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status)
{
pAdapter = pAdapterNode->pAdapter;
if (NULL != pAdapter)
{
if (pHddTdlsCtx == pAdapter->sessionCtx.station.pHddTdlsCtx &&
(NULL != pHddTdlsCtx->pAdapter) &&
(WLAN_HDD_ADAPTER_MAGIC == pHddTdlsCtx->pAdapter->magic))
{
found = 1;
break;
}
}
status = hdd_get_next_adapter (pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
if (found == 1)
{
return 0;
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("TDLS context doesnot belongs to valid adapter"));
return -EINVAL;
}
}
void wlan_hdd_tdls_update_rx_pkt_cnt_n_rssi(hdd_adapter_t *pAdapter,
u8 *mac, v_S7_t rssiAvg)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = NULL;
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if ((NULL != curr_peer) &&
(eTDLS_LINK_CONNECTED == curr_peer->link_status))
{
curr_peer->rx_pkt++;
curr_peer->rssi = rssiAvg;
}
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
"mac : " MAC_ADDRESS_STR "rssi is %d",
MAC_ADDR_ARRAY(mac), rssiAvg);
}
/**
* wlan_hdd_tdls_reenable() - Re-Enable TDLS
* @hddctx: pointer to hdd context
*
* Function re-enable's TDLS which might be disabled during concurrency
*
* Return: None
*/
void wlan_hdd_tdls_reenable(hdd_context_t *pHddCtx)
{
if ((TRUE != pHddCtx->cfg_ini->fEnableTDLSSupport) ||
(TRUE != sme_IsFeatureSupportedByFW(TDLS))) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("tdls support not enabled"));
return;
}
/* if tdls is not enabled then don't revert tdls mode */
if ((eTDLS_SUPPORT_NOT_ENABLED == pHddCtx->tdls_mode)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("TDLS disabled so no need to enable: Mode=%d"),
pHddCtx->tdls_mode);
return;
}
if (eTDLS_SUPPORT_ENABLED == pHddCtx->tdls_mode_last ||
eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY ==
pHddCtx->tdls_mode_last) {
/* Enable TDLS support Once P2P session ends since
* upond detection of concurrency TDLS might be disabled
*/
hddLog(LOG1, FL("TDLS mode set to %d"), pHddCtx->tdls_mode_last);
wlan_hdd_tdls_set_mode(pHddCtx, pHddCtx->tdls_mode_last,
FALSE,
HDD_SET_TDLS_MODE_SOURCE_CONCURRENCY);
}
}
tdlsConnInfo_t *wlan_hdd_get_conn_info(hdd_context_t *pHddCtx,
tANI_U8 idx)
{
tANI_U8 staIdx;
/* check if there is available index for this new TDLS STA */
for ( staIdx = 0; staIdx < HDD_MAX_NUM_TDLS_STA; staIdx++ )
{
if (idx == pHddCtx->tdlsConnInfo[staIdx].staId )
{
hddLog(LOG1, FL("tdls peer with staIdx %u exists"), idx );
return (&pHddCtx->tdlsConnInfo[staIdx]);
}
}
hddLog(LOGE, FL("tdls peer with staIdx %u not exists"), idx );
return NULL;
}
void wlan_hdd_change_tdls_mode(void *data)
{
hdd_context_t *hdd_ctx = (hdd_context_t *)data;
wlan_hdd_tdls_set_mode(hdd_ctx, eTDLS_SUPPORT_ENABLED, FALSE,
HDD_SET_TDLS_MODE_SOURCE_OFFCHANNEL);
}
void wlan_hdd_start_stop_tdls_source_timer(hdd_context_t *pHddCtx,
eTDLSSupportMode tdls_mode)
{
if (VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(&pHddCtx->tdls_source_timer))
vos_timer_stop(&pHddCtx->tdls_source_timer);
if (tdls_mode == eTDLS_SUPPORT_DISABLED) {
wlan_hdd_tdls_set_mode(pHddCtx, tdls_mode, FALSE,
HDD_SET_TDLS_MODE_SOURCE_OFFCHANNEL);
}
vos_timer_start(&pHddCtx->tdls_source_timer,
pHddCtx->cfg_ini->tdls_enable_defer_time);
return;
}
void wlan_hdd_get_tdls_stats(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = NULL;
tdlsCtx_t *pHddTdlsCtx = NULL;
tANI_U16 numConnectedTdlsPeers = 0;
tANI_U16 numDiscoverySentCnt = 0;
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ENTER();
if (0 != (wlan_hdd_validate_context(pHddCtx)))
{
return;
}
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
mutex_lock(&pHddCtx->tdls_lock);
if (NULL == pHddTdlsCtx)
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddTdlsCtx points to NULL"));
return;
}
numConnectedTdlsPeers = pHddCtx->connected_peer_count;
numDiscoverySentCnt = pHddTdlsCtx->discovery_sent_cnt;
mutex_unlock(&pHddCtx->tdls_lock);
hddLog( LOGE, "%s: TDLS Mode: %d TDLS connected peer count %d"
" DiscoverySentCnt=%d", __func__, pHddCtx->tdls_mode,
numConnectedTdlsPeers, numDiscoverySentCnt);
EXIT();
return;
}
/*EXT TDLS*/