blob: 459724608f9bc54f0cf668259e46b45434274e02 [file] [log] [blame]
/*
* Copyright (c) 2012-2015 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.
*/
/**
* DOC: wlan_hdd_tdls.c
*
* WLAN Host Device Driver implementation for TDLS
*/
#include <wlan_hdd_includes.h>
#include <wlan_hdd_hostapd.h>
#include <wlan_hdd_trace.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 "cds_sched.h"
#include "wma_types.h"
static int32_t wlan_hdd_tdls_peer_reset_discovery_processed(tdlsCtx_t *
pHddTdlsCtx);
static void wlan_hdd_tdls_timers_destroy(tdlsCtx_t *pHddTdlsCtx);
int wpa_tdls_is_allowed_force_peer(tdlsCtx_t *pHddTdlsCtx, u8 *mac);
static void wlan_hdd_tdls_pre_setup(struct work_struct *work);
/**
* wlan_hdd_tdls_hash_key() - calculate tdls hash key given mac address
* @mac: mac address
*
* Return: hash key
*/
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;
}
/**
* 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;
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"));
return;
}
connected_tdls_peers = wlan_hdd_tdls_connected_peers(adapter);
if (!connected_tdls_peers)
return ;
/* TDLS is not supported in case of concurrency.
* Disable TDLS Offchannel in FW to avoid more
* than two concurrent channels and generate TDLS
* teardown indication to supplicant.
* Below function Finds the first connected peer and
* disables TDLS offchannel for that peer.
* FW enables TDLS offchannel only when there is
* one TDLS peer. When there are more than one TDLS peer,
* there will not be TDLS offchannel in FW.
* So to avoid sending multiple request to FW, for now,
* just invoke offchannel mode functions only once
*/
hdd_set_tdls_offchannel(hddctx, hddctx->config->fTDLSPrefOffChanNum);
hdd_set_tdls_secoffchanneloffset(hddctx,
TDLS_SEC_OFFCHAN_OFFSET_40PLUS);
hdd_set_tdls_offchannelmode(adapter, DISABLE_CHANSWITCH);
for (staidx = 0; staidx < hddctx->max_num_tdls_sta;
staidx++) {
if (!hddctx->tdlsConnInfo[staidx].staId)
continue;
curr_peer = wlan_hdd_tdls_find_all_peer(hddctx,
hddctx->tdlsConnInfo[staidx].peerMac.bytes);
if (!curr_peer)
continue;
hddLog(LOG1, FL("indicate TDLS teardown (staId %d)"),
curr_peer->staId);
wlan_hdd_tdls_indicate_teardown(
curr_peer->pHddTdlsCtx->pAdapter,
curr_peer,
eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
}
}
/**
* 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)
{
if (adapter->device_mode != WLAN_HDD_INFRA_STATION)
wlan_hdd_tdls_disable_offchan_and_teardown_links(hddctx);
}
/**
* wlan_hdd_tdls_pre_setup_init_work() - schedule work for tdls pre-setup
* @pHddTdlsCtx: HDD TDLS context
* @curr_candidate: current candidate peer
*
* Return: None
*/
void wlan_hdd_tdls_pre_setup_init_work(tdlsCtx_t *pHddTdlsCtx,
hddTdlsPeer_t *curr_candidate)
{
if (!pHddTdlsCtx || !curr_candidate) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: either pHddTdlsCtx or curr_candidate is null",
__func__);
return;
}
if (TDLS_CTX_MAGIC != pHddTdlsCtx->magic) {
/* When TDLS discovery attempt for a peer reaches to max configured
* threshold then tdls support for that peer would be disabled and
* in that case, ignore discovery trigger from FW for that peer.
*/
if (eTDLS_CAP_NOT_SUPPORTED == curr_candidate->tdls_support) {
hddLog(LOGW,
"%s: tdls_support is marked disabled for peer: "
MAC_ADDRESS_STR
", ignore pre_setup_init_work", __func__,
MAC_ADDR_ARRAY(curr_candidate->peerMac));
return;
}
pHddTdlsCtx->curr_candidate = curr_candidate;
pHddTdlsCtx->magic = TDLS_CTX_MAGIC;
schedule_work(&pHddTdlsCtx->implicit_setup);
}
}
/**
* wlan_hdd_tdls_pre_setup_init_work() - get value of discovery counter sent
* @pHddCtx: HDD context
*
* Return: the value of the transmitted TDLS discovery counter
*/
static uint32_t 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;
CDF_STATUS status = 0;
uint32_t count = 0;
status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
while (NULL != pAdapterNode && CDF_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;
}
/**
* wlan_hdd_tdls_check_power_save_prohibited() - set/clear proper TDLS power
* save probihited bit
* @pAdapter: HDD adapter handle
*
* Ensure TDLS power save probihited bit is set/cleared properly
*
* Return: None
*/
static void wlan_hdd_tdls_check_power_save_prohibited(hdd_adapter_t *pAdapter)
{
tdlsCtx_t *pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if ((NULL == pHddTdlsCtx) || (NULL == pHddCtx)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_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_set_tdls_power_save_prohibited(WLAN_HDD_GET_HAL_CTX
(pHddTdlsCtx->pAdapter),
pAdapter->sessionId, 0);
return;
}
sme_set_tdls_power_save_prohibited(WLAN_HDD_GET_HAL_CTX
(pHddTdlsCtx->pAdapter),
pAdapter->sessionId, 1);
return;
}
/**
* wlan_hdd_tdls_free_scan_request() - free tdls scan request
* @tdls_scan_ctx: tdls scan context
*
* Return: None
*/
static void wlan_hdd_tdls_free_scan_request(tdls_scan_context_t *tdls_scan_ctx)
{
if (NULL == tdls_scan_ctx) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("tdls_scan_ctx is NULL"));
return;
}
tdls_scan_ctx->attempt = 0;
tdls_scan_ctx->reject = 0;
tdls_scan_ctx->magic = 0;
tdls_scan_ctx->scan_request = NULL;
return;
}
/**
* wlan_hdd_tdls_discovery_timeout_peer_cb() - tdls discovery timeout callback
* @userData: tdls context
*
* Return: None
*/
static void wlan_hdd_tdls_discovery_timeout_peer_cb(void *userData)
{
int i;
struct list_head *head;
hddTdlsPeer_t *tmp;
struct list_head *pos, *q;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx;
pHddTdlsCtx = (tdlsCtx_t *) userData;
if ((NULL == pHddTdlsCtx) || (NULL == pHddTdlsCtx->pAdapter)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddTdlsCtx or pAdapter points to NULL"));
return;
}
if (WLAN_HDD_ADAPTER_MAGIC != pHddTdlsCtx->pAdapter->magic) {
hddLog(LOGE, FL("pAdapter has invalid magic"));
return;
}
pHddCtx = WLAN_HDD_GET_CTX(pHddTdlsCtx->pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx is not valid"));
return;
}
if (NULL == pHddCtx)
return;
mutex_lock(&pHddCtx->tdls_lock);
for (i = 0; i < TDLS_PEER_LIST_SIZE; 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) {
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(LOG1,
"%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);
mutex_lock(&pHddCtx->tdls_lock);
}
}
}
pHddTdlsCtx->discovery_sent_cnt = 0;
wlan_hdd_tdls_check_power_save_prohibited(pHddTdlsCtx->pAdapter);
mutex_unlock(&pHddCtx->tdls_lock);
return;
}
/**
* wlan_hdd_tdls_free_list() - free TDLS peer list
* @pHddTdlsCtx: TDLS context
*
* Return: None
*/
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) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddTdlsCtx is NULL"));
return;
}
for (i = 0; i < TDLS_PEER_LIST_SIZE; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each_safe(pos, q, head) {
tmp = list_entry(pos, hddTdlsPeer_t, node);
list_del(pos);
cdf_mem_free(tmp);
tmp = NULL;
}
}
}
/**
* wlan_hdd_tdls_schedule_scan() - schedule scan for tdls
* @work: work_struct used to find tdls scan context
*
* Return: None
*/
static void wlan_hdd_tdls_schedule_scan(struct work_struct *work)
{
tdls_scan_context_t *scan_ctx =
container_of(work, tdls_scan_context_t, tdls_scan_work.work);
if (NULL == scan_ctx) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("scan_ctx is NULL"));
return;
}
if (unlikely(TDLS_CTX_MAGIC != scan_ctx->magic))
return;
scan_ctx->attempt++;
wlan_hdd_cfg80211_scan(scan_ctx->wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
scan_ctx->dev,
#endif
scan_ctx->scan_request);
}
/**
* dump_tdls_state_param_setting() - print tdls state & parameters to send to fw
* @info: tdls setting to be sent to fw
*
* Return: void
*/
static void dump_tdls_state_param_setting(tdlsInfo_t *info)
{
if (!info)
return;
hddLog(LOG1,
FL(
"Setting tdls state and param in fw: vdev_id: %d, tdls_state: %d, notification_interval_ms: %d, tx_discovery_threshold: %d, tx_teardown_threshold: %d, rssi_teardown_threshold: %d, rssi_delta: %d, tdls_options: 0x%x, peer_traffic_ind_window: %d, peer_traffic_response_timeout: %d, puapsd_mask: 0x%x, puapsd_inactivity_time: %d, puapsd_rx_frame_threshold: %d"
),
info->vdev_id,
info->tdls_state,
info->notification_interval_ms,
info->tx_discovery_threshold,
info->tx_teardown_threshold,
info->rssi_teardown_threshold,
info->rssi_delta,
info->tdls_options,
info->peer_traffic_ind_window,
info->peer_traffic_response_timeout,
info->puapsd_mask,
info->puapsd_inactivity_time,
info->puapsd_rx_frame_threshold);
}
/**
* wlan_hdd_tdls_monitor_timers_stop() - stop all monitoring timers
* @hdd_tdls_ctx: TDLS context
*
* Return: none
*/
static void wlan_hdd_tdls_monitor_timers_stop(tdlsCtx_t *hdd_tdls_ctx)
{
cdf_mc_timer_stop(&hdd_tdls_ctx->peerDiscoveryTimeoutTimer);
}
/**
* wlan_hdd_tdls_timers_stop() - stop all the tdls timers running
* @hdd_tdls_ctx: TDLS context
*
* Return: none
*/
static void wlan_hdd_tdls_timers_stop(tdlsCtx_t *hdd_tdls_ctx)
{
wlan_hdd_tdls_monitor_timers_stop(hdd_tdls_ctx);
}
/**
* wlan_hdd_tdls_del_non_forced_peers() - delete non forced tdls peers
* @hdd_tdls_ctx: TDLS context
*
* Return: none
*/
static void wlan_hdd_tdls_del_non_forced_peers(tdlsCtx_t *hdd_tdls_ctx)
{
struct list_head *head, *pos, *q;
hddTdlsPeer_t *peer = NULL;
int i;
/* remove entries from peer list only if peer is not forced */
for (i = 0; i < TDLS_PEER_LIST_SIZE; i++) {
head = &hdd_tdls_ctx->peer_list[i];
list_for_each_safe(pos, q, head) {
peer = list_entry(pos, hddTdlsPeer_t, node);
if (false == peer->isForcedPeer) {
list_del(pos);
cdf_mem_free(peer);
} else {
peer->link_status = eTDLS_LINK_IDLE;
peer->reason = eTDLS_LINK_UNSPECIFIED;
peer->staId = 0;
peer->discovery_attempt = 0;
}
}
}
}
/**
* wlan_hdd_tdls_init() - tdls initializaiton
* @pAdapter: hdd adapter
*
* Return: 0 for success or negative errno otherwise
*/
int wlan_hdd_tdls_init(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
tdlsCtx_t *pHddTdlsCtx;
int i;
uint8_t staIdx;
tdlsInfo_t *tInfo;
CDF_STATUS cdf_ret_status = CDF_STATUS_E_FAILURE;
if (NULL == pHddCtx)
return -EINVAL;
mutex_lock(&pHddCtx->tdls_lock);
if (false == pHddCtx->config->fEnableTDLSSupport) {
pHddCtx->tdls_mode = eTDLS_SUPPORT_NOT_ENABLED;
pAdapter->sessionCtx.station.pHddTdlsCtx = NULL;
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(LOGE,
FL("TDLS not enabled (%d) or FW doesn't support"),
pHddCtx->config->fEnableTDLSSupport);
return 0;
}
/* TDLS is supported only in STA / P2P Client modes,
* hence the check for TDLS support in a specific Device mode.
* Do not return a failure rather do not continue further
* with the initialization as tdls_init would be called
* during the open adapter for a p2p interface at which point
* the device mode would be a P2P_DEVICE. The point here is to
* continue initialization for STA / P2P Client modes.
* TDLS exit also check for the device mode for clean up hence
* there is no issue even if success is returned.
*/
if (0 == WLAN_HDD_IS_TDLS_SUPPORTED_ADAPTER(pAdapter)) {
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
/* 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 = cdf_mem_malloc(sizeof(tdlsCtx_t));
if (NULL == pHddTdlsCtx) {
pAdapter->sessionCtx.station.pHddTdlsCtx = NULL;
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(LOGE, FL("malloc failed!"));
return -ENOMEM;
}
/* initialize TDLS pAdater context */
cdf_mem_zero(pHddTdlsCtx, sizeof(tdlsCtx_t));
cdf_mc_timer_init(&pHddTdlsCtx->peerDiscoveryTimeoutTimer,
CDF_TIMER_TYPE_SW,
wlan_hdd_tdls_discovery_timeout_peer_cb,
pHddTdlsCtx);
pAdapter->sessionCtx.station.pHddTdlsCtx = pHddTdlsCtx;
for (i = 0; i < TDLS_PEER_LIST_SIZE; i++)
INIT_LIST_HEAD(&pHddTdlsCtx->peer_list[i]);
} else {
pHddTdlsCtx = pAdapter->sessionCtx.station.pHddTdlsCtx;
wlan_hdd_tdls_timers_stop(pHddTdlsCtx);
wlan_hdd_tdls_del_non_forced_peers(pHddTdlsCtx);
pHddCtx->connected_peer_count = 0;
}
/* initialize TDLS global context */
pHddCtx->connected_peer_count = 0;
sme_set_tdls_power_save_prohibited(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, 0);
pHddCtx->tdls_scan_ctxt.magic = 0;
pHddCtx->tdls_scan_ctxt.attempt = 0;
pHddCtx->tdls_scan_ctxt.reject = 0;
pHddCtx->tdls_scan_ctxt.scan_request = NULL;
if (pHddCtx->config->fEnableTDLSSleepSta ||
pHddCtx->config->fEnableTDLSBufferSta ||
pHddCtx->config->fEnableTDLSOffChannel)
pHddCtx->max_num_tdls_sta = HDD_MAX_NUM_TDLS_STA_P_UAPSD_OFFCHAN;
else
pHddCtx->max_num_tdls_sta = HDD_MAX_NUM_TDLS_STA;
hddLog(LOG1, FL("max_num_tdls_sta: %d"), pHddCtx->max_num_tdls_sta);
for (staIdx = 0; staIdx < pHddCtx->max_num_tdls_sta; staIdx++) {
pHddCtx->tdlsConnInfo[staIdx].staId = 0;
pHddCtx->tdlsConnInfo[staIdx].sessionId = 255;
cdf_mem_zero(&pHddCtx->tdlsConnInfo[staIdx].peerMac,
CDF_MAC_ADDR_SIZE);
}
pHddTdlsCtx->pAdapter = pAdapter;
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->config->fTDLSTxStatsPeriod;
pHddTdlsCtx->threshold_config.tx_packet_n =
pHddCtx->config->fTDLSTxPacketThreshold;
pHddTdlsCtx->threshold_config.discovery_tries_n =
pHddCtx->config->fTDLSMaxDiscoveryAttempt;
pHddTdlsCtx->threshold_config.idle_packet_n =
pHddCtx->config->fTDLSIdlePacketThreshold;
pHddTdlsCtx->threshold_config.rssi_trigger_threshold =
pHddCtx->config->fTDLSRSSITriggerThreshold;
pHddTdlsCtx->threshold_config.rssi_teardown_threshold =
pHddCtx->config->fTDLSRSSITeardownThreshold;
pHddTdlsCtx->threshold_config.rssi_delta =
pHddCtx->config->fTDLSRSSIDelta;
if (false == pHddCtx->config->fEnableTDLSImplicitTrigger) {
pHddCtx->tdls_mode = eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY;
hddLog(CDF_TRACE_LEVEL_ERROR,
"%s TDLS Implicit trigger not enabled!", __func__);
} else {
pHddCtx->tdls_mode = eTDLS_SUPPORT_ENABLED;
}
#ifdef CONFIG_CNSS
cnss_init_work(&pHddTdlsCtx->implicit_setup, wlan_hdd_tdls_pre_setup);
#else
INIT_WORK(&pHddTdlsCtx->implicit_setup, wlan_hdd_tdls_pre_setup);
#endif
#ifdef CONFIG_CNSS
cnss_init_delayed_work(&pHddCtx->tdls_scan_ctxt.tdls_scan_work,
wlan_hdd_tdls_schedule_scan);
#else
INIT_DELAYED_WORK(&pHddCtx->tdls_scan_ctxt.tdls_scan_work,
wlan_hdd_tdls_schedule_scan);
#endif
/*
* Release tdls lock before calling in SME api
* which would try to acquire sme lock.
*/
mutex_unlock(&pHddCtx->tdls_lock);
tInfo = cdf_mem_malloc(sizeof(tdlsInfo_t));
if (NULL == tInfo) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("cdf_mem_alloc failed for tInfo"));
cdf_mc_timer_destroy(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
cdf_mem_free(pHddTdlsCtx);
return -ENOMEM;
}
tInfo->vdev_id = pAdapter->sessionId;
tInfo->tdls_state = pHddCtx->tdls_mode;
tInfo->notification_interval_ms =
pHddTdlsCtx->threshold_config.tx_period_t;
tInfo->tx_discovery_threshold =
pHddTdlsCtx->threshold_config.tx_packet_n;
tInfo->tx_teardown_threshold =
pHddTdlsCtx->threshold_config.idle_packet_n;
tInfo->rssi_teardown_threshold =
pHddTdlsCtx->threshold_config.rssi_teardown_threshold;
tInfo->rssi_delta = pHddTdlsCtx->threshold_config.rssi_delta;
tInfo->tdls_options = 0;
if (pHddCtx->config->fEnableTDLSOffChannel)
tInfo->tdls_options |= ENA_TDLS_OFFCHAN;
if (pHddCtx->config->fEnableTDLSBufferSta)
tInfo->tdls_options |= ENA_TDLS_BUFFER_STA;
if (pHddCtx->config->fEnableTDLSSleepSta)
tInfo->tdls_options |= ENA_TDLS_SLEEP_STA;
tInfo->peer_traffic_ind_window = pHddCtx->config->fTDLSPuapsdPTIWindow;
tInfo->peer_traffic_response_timeout =
pHddCtx->config->fTDLSPuapsdPTRTimeout;
tInfo->puapsd_mask = pHddCtx->config->fTDLSUapsdMask;
tInfo->puapsd_inactivity_time =
pHddCtx->config->fTDLSPuapsdInactivityTimer;
tInfo->puapsd_rx_frame_threshold =
pHddCtx->config->fTDLSRxFrameThreshold;
dump_tdls_state_param_setting(tInfo);
cdf_ret_status = sme_update_fw_tdls_state(pHddCtx->hHal, tInfo, true);
if (CDF_STATUS_SUCCESS != cdf_ret_status) {
cdf_mem_free(tInfo);
cdf_mc_timer_destroy(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
cdf_mem_free(pHddTdlsCtx);
return -EINVAL;
}
return 0;
}
/**
* wlan_hdd_tdls_exit() - TDLS de-initialization
* @pAdapter: HDD adapter
*
* Return: None
*/
void wlan_hdd_tdls_exit(hdd_adapter_t *pAdapter)
{
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx;
tdlsInfo_t *tInfo;
CDF_STATUS cdf_ret_status = CDF_STATUS_E_FAILURE;
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (!pHddCtx) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx is NULL"));
return;
}
if (!test_bit(TDLS_INIT_DONE, &pAdapter->event_flags)) {
hddLog(LOGE, FL("TDLS init was not done, exit"));
return;
}
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx) {
/* TDLS context can be null and might have been freed up during
* cleanup for STA adapter
*/
hddLog(LOG2,
FL("pHddTdlsCtx is NULL, adapter device mode: %s(%d)"),
hdd_device_mode_to_string(pAdapter->device_mode),
pAdapter->device_mode);
goto done;
}
cds_flush_work(&pHddTdlsCtx->implicit_setup);
cds_flush_delayed_work(&pHddCtx->tdls_scan_ctxt.tdls_scan_work);
mutex_lock(&pHddCtx->tdls_lock);
/* 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);
mutex_unlock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_free_scan_request(&pHddCtx->tdls_scan_ctxt);
/* No need to post message during driver unlaod because MC thread is
already shutdown */
if (!pHddCtx->isUnloadInProgress) {
tInfo = cdf_mem_malloc(sizeof(tdlsInfo_t));
if (NULL != tInfo) {
tInfo->vdev_id = pAdapter->sessionId;
tInfo->tdls_state = eTDLS_SUPPORT_DISABLED;
tInfo->notification_interval_ms =
pHddTdlsCtx->threshold_config.tx_period_t;
tInfo->tx_discovery_threshold =
pHddTdlsCtx->threshold_config.tx_packet_n;
tInfo->tx_teardown_threshold =
pHddTdlsCtx->threshold_config.idle_packet_n;
tInfo->rssi_teardown_threshold =
pHddTdlsCtx->threshold_config.
rssi_teardown_threshold;
tInfo->rssi_delta =
pHddTdlsCtx->threshold_config.rssi_delta;
tInfo->tdls_options = 0;
if (pHddCtx->config->fEnableTDLSOffChannel)
tInfo->tdls_options |= ENA_TDLS_OFFCHAN;
if (pHddCtx->config->fEnableTDLSBufferSta)
tInfo->tdls_options |= ENA_TDLS_BUFFER_STA;
if (pHddCtx->config->fEnableTDLSSleepSta)
tInfo->tdls_options |= ENA_TDLS_SLEEP_STA;
tInfo->peer_traffic_ind_window =
pHddCtx->config->fTDLSPuapsdPTIWindow;
tInfo->peer_traffic_response_timeout =
pHddCtx->config->fTDLSPuapsdPTRTimeout;
tInfo->puapsd_mask = pHddCtx->config->fTDLSUapsdMask;
tInfo->puapsd_inactivity_time =
pHddCtx->config->fTDLSPuapsdInactivityTimer;
tInfo->puapsd_rx_frame_threshold =
pHddCtx->config->fTDLSRxFrameThreshold;
dump_tdls_state_param_setting(tInfo);
cdf_ret_status =
sme_update_fw_tdls_state(pHddCtx->hHal, tInfo, false);
if (CDF_STATUS_SUCCESS != cdf_ret_status) {
cdf_mem_free(tInfo);
}
} else {
hddLog(CDF_TRACE_LEVEL_ERROR,
"%s: cdf_mem_alloc failed for tInfo", __func__);
}
}
pHddTdlsCtx->magic = 0;
pHddTdlsCtx->pAdapter = NULL;
cdf_mem_free(pHddTdlsCtx);
pAdapter->sessionCtx.station.pHddTdlsCtx = NULL;
pHddTdlsCtx = NULL;
done:
clear_bit(TDLS_INIT_DONE, &pAdapter->event_flags);
}
/**
* wlan_hdd_tdls_monitor_timers_destroy() - destroy all tdls monitoring timers
* @pHddTdlsCtx: TDLS context
*
* Return: Void
*/
static void wlan_hdd_tdls_monitor_timers_destroy(tdlsCtx_t *pHddTdlsCtx)
{
cdf_mc_timer_stop(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
cdf_mc_timer_destroy(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
}
/**
* wlan_hdd_tdls_timers_destroy() - Destroy all the tdls timers running
* @pHddTdlsCtx: TDLS Context
*
* Return: Void
*/
static void wlan_hdd_tdls_timers_destroy(tdlsCtx_t *pHddTdlsCtx)
{
wlan_hdd_tdls_monitor_timers_destroy(pHddTdlsCtx);
}
/**
* wlan_hdd_tdls_get_peer() - find or add an peer given mac address
* @pAdapter: HDD adapter
* @mac: MAC address used to find or create peer
*
* Search peer given an MAC address and create one if not found.
*
* Return: Pointer to peer if mac address exist or peer creation
* succeeds; NULL if peer creation fails
*/
hddTdlsPeer_t *wlan_hdd_tdls_get_peer(hdd_adapter_t *pAdapter, const u8 *mac)
{
struct list_head *head;
hddTdlsPeer_t *peer;
u8 key;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx is not valid"));
return NULL;
}
/* if already there, just update */
peer = wlan_hdd_tdls_find_peer(pAdapter, mac, true);
if (peer != NULL) {
return peer;
}
/* not found, allocate and add the list */
peer = cdf_mem_malloc(sizeof(hddTdlsPeer_t));
if (NULL == peer) {
hddLog(CDF_TRACE_LEVEL_ERROR, "%s peer malloc failed!",
__func__);
return NULL;
}
mutex_lock(&pHddCtx->tdls_lock);
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx) {
cdf_mem_free(peer);
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(LOG1, FL("pHddTdlsCtx is NULL"));
return NULL;
}
key = wlan_hdd_tdls_hash_key(mac);
head = &pHddTdlsCtx->peer_list[key];
cdf_mem_zero(peer, sizeof(hddTdlsPeer_t));
cdf_mem_copy(peer->peerMac, mac, sizeof(peer->peerMac));
peer->pHddTdlsCtx = pHddTdlsCtx;
peer->pref_off_chan_num = pHddCtx->config->fTDLSPrefOffChanNum;
list_add_tail(&peer->node, head);
mutex_unlock(&pHddCtx->tdls_lock);
return peer;
}
/**
* wlan_hdd_tdls_set_cap() - set TDLS capability type
* @pAdapter: HDD adapter
* @mac: peer mac address
* @cap: TDLS capability type
*
* Return: 0 if successful or negative errno otherwise
*/
int wlan_hdd_tdls_set_cap(hdd_adapter_t *pAdapter, const uint8_t *mac,
tTDLSCapType cap)
{
hddTdlsPeer_t *curr_peer;
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL) {
hddLog(LOGE, FL("curr_peer is NULL"));
return -EINVAL;
}
curr_peer->tdls_support = cap;
return 0;
}
/**
* wlan_hdd_tdls_set_peer_link_status() - set TDLS peer link status
* @curr_peer: peer
* @status: status
* @reason: reason
*
* Return: Void
*/
void wlan_hdd_tdls_set_peer_link_status(hddTdlsPeer_t *curr_peer,
tTDLSLinkStatus status,
tTDLSLinkReason reason)
{
int32_t state = 0;
int32_t res = 0;
hdd_context_t *pHddCtx;
if (curr_peer == NULL) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
return;
}
if (curr_peer->pHddTdlsCtx == NULL) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("curr_peer->pHddTdlsCtx is NULL"));
return;
}
pHddCtx = WLAN_HDD_GET_CTX(curr_peer->pHddTdlsCtx->pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx is not valid"));
return;
}
hddLog(CDF_TRACE_LEVEL_WARN,
"tdls set peer " MAC_ADDRESS_STR " link status to %u",
MAC_ADDR_ARRAY(curr_peer->peerMac), status);
mutex_lock(&pHddCtx->tdls_lock);
curr_peer->link_status = status;
/* If TDLS link status is already passed the discovery state
* then clear discovery attempt count
*/
if (status >= eTDLS_LINK_DISCOVERED) {
curr_peer->discovery_attempt = 0;
}
mutex_unlock(&pHddCtx->tdls_lock);
if (curr_peer->isForcedPeer && curr_peer->state_change_notification) {
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);
}
return;
}
/**
* wlan_hdd_tdls_set_link_status() - set TDLS peer link status
* @pAdapter: HDD adapter
* @mac: mac address of TDLS peer
* @linkStatus: status
* @reason: reason
*
* Return: Void
*/
void wlan_hdd_tdls_set_link_status(hdd_adapter_t *pAdapter,
const uint8_t *mac,
tTDLSLinkStatus linkStatus,
tTDLSLinkReason reason)
{
int32_t state = 0;
int32_t res = 0;
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx is not valid"));
return;
}
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, true);
if (curr_peer == NULL) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
return;
}
mutex_lock(&pHddCtx->tdls_lock);
curr_peer->link_status = linkStatus;
/* If TDLS link status is already passed the discovery state
* then clear discovery attempt count
*/
if (linkStatus >= eTDLS_LINK_DISCOVERED) {
curr_peer->discovery_attempt = 0;
}
mutex_unlock(&pHddCtx->tdls_lock);
if (curr_peer->isForcedPeer && curr_peer->state_change_notification) {
curr_peer->reason = reason;
wlan_hdd_tdls_get_wifi_hal_state(curr_peer, &state, &res);
(curr_peer->state_change_notification)(mac,
state,
res,
curr_peer->pHddTdlsCtx->
pAdapter);
}
return;
}
/**
* wlan_hdd_tdls_recv_discovery_resp() - handling of tdls discovery response
* @pAdapter: HDD adapter
* @mac: mac address of peer from which the response was received
*
* Return: 0 for success or negative errno otherwise
*/
int wlan_hdd_tdls_recv_discovery_resp(hdd_adapter_t *pAdapter,
const uint8_t *mac)
{
hddTdlsPeer_t *curr_peer;
tdlsCtx_t *pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
hdd_context_t *pHddCtx;
if (NULL == pHddTdlsCtx) {
hddLog(LOGE, FL("pHddTdlsCtx is NULL"));
return -EINVAL;
}
pHddCtx = WLAN_HDD_GET_CTX(pHddTdlsCtx->pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
hddLog(LOGE, FL("pHddCtx is not valid"));
return -EINVAL;
}
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (NULL == curr_peer) {
hddLog(LOGE, FL("curr_peer is NULL"));
return -EINVAL;
}
if (pHddTdlsCtx->discovery_sent_cnt)
pHddTdlsCtx->discovery_sent_cnt--;
mutex_lock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_check_power_save_prohibited(pAdapter);
mutex_unlock(&pHddCtx->tdls_lock);
if (0 == pHddTdlsCtx->discovery_sent_cnt) {
cdf_mc_timer_stop(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
}
hddLog(LOG1,
"Discovery(%u) Response from " MAC_ADDRESS_STR
" link_status %d", pHddTdlsCtx->discovery_sent_cnt,
MAC_ADDR_ARRAY(curr_peer->peerMac), curr_peer->link_status);
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 ((int32_t) curr_peer->rssi >
(int32_t) pHddTdlsCtx->threshold_config.
rssi_trigger_threshold) {
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_DISCOVERED,
eTDLS_LINK_SUCCESS);
hddLog(LOG1,
"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);
cfg80211_tdls_oper_request(pAdapter->dev,
curr_peer->peerMac,
NL80211_TDLS_SETUP, false,
GFP_KERNEL);
} else {
hddLog(LOG1,
"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);
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED);
/* 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--;
}
}
curr_peer->tdls_support = eTDLS_CAP_SUPPORTED;
return 0;
}
/**
* wlan_hdd_tdls_set_peer_caps() - set TDLS peer capability
* @pAdapter: HDD adapter
* @mac: MAC address of the TDLS peer
* @StaParams: CSR Station Parameter
* @isBufSta: is peer buffer station
* @isOffChannelSupported: Is off channel supported
*
* Return: 0 for success or negative errno otherwise
*/
int wlan_hdd_tdls_set_peer_caps(hdd_adapter_t *pAdapter,
const uint8_t *mac,
tCsrStaParams *StaParams,
bool isBufSta, bool isOffChannelSupported)
{
hddTdlsPeer_t *curr_peer;
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL) {
hddLog(LOGE, FL("curr_peer is NULL"));
return -EINVAL;
}
curr_peer->uapsdQueues = StaParams->uapsd_queues;
curr_peer->maxSp = StaParams->max_sp;
curr_peer->isBufSta = isBufSta;
curr_peer->isOffChannelSupported = isOffChannelSupported;
cdf_mem_copy(curr_peer->supported_channels,
StaParams->supported_channels,
StaParams->supported_channels_len);
curr_peer->supported_channels_len = StaParams->supported_channels_len;
cdf_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;
return 0;
}
/**
* wlan_hdd_tdls_get_link_establish_params() - get TDLS link establish
* parameter
* @pAdapter: HDD adapter
* @mac: mac address
* @tdlsLinkEstablishParams: output parameter to store the result
*
* Return: 0 for success or negative errno otherwise
*/
int wlan_hdd_tdls_get_link_establish_params(hdd_adapter_t *pAdapter,
const u8 *mac,
tCsrTdlsLinkEstablishParams *
tdlsLinkEstablishParams)
{
hddTdlsPeer_t *curr_peer;
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL) {
hddLog(LOGE, FL("curr_peer is NULL"));
return -EINVAL;
}
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;
cdf_mem_copy(tdlsLinkEstablishParams->supportedChannels,
curr_peer->supported_channels,
curr_peer->supported_channels_len);
tdlsLinkEstablishParams->supportedChannelsLen =
curr_peer->supported_channels_len;
cdf_mem_copy(tdlsLinkEstablishParams->supportedOperClasses,
curr_peer->supported_oper_classes,
curr_peer->supported_oper_classes_len);
tdlsLinkEstablishParams->supportedOperClassesLen =
curr_peer->supported_oper_classes_len;
return 0;
}
/**
* wlan_hdd_tdls_set_rssi() - Set TDLS RSSI on peer given by mac
* @pAdapter: HDD adapter
* @mac: MAC address of Peer
* @rxRssi: rssi value
*
* Set RSSI on TDSL peer
*
* Return: 0 for success or -EINVAL otherwise
*/
int wlan_hdd_tdls_set_rssi(hdd_adapter_t *pAdapter, const uint8_t *mac,
int8_t rxRssi)
{
hddTdlsPeer_t *curr_peer;
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, true);
if (curr_peer == NULL) {
hddLog(LOGE, FL("curr_peer is NULL"));
return -EINVAL;
}
curr_peer->rssi = rxRssi;
return 0;
}
/**
* wlan_hdd_tdls_set_responder() - Set/clear TDLS peer's responder role
* @pAdapter: HDD adapter
* @mac: MAC address of Peer
* @responder: flag that indicates if the TDLS peer should be responder or not
*
* Return: 0 for success or -EINVAL otherwise
*/
int wlan_hdd_tdls_set_responder(hdd_adapter_t *pAdapter, const uint8_t *mac,
uint8_t responder)
{
hddTdlsPeer_t *curr_peer;
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL) {
hddLog(LOGE, FL("curr_peer is NULL"));
return -EINVAL;
}
curr_peer->is_responder = responder;
return 0;
}
/**
* wlan_hdd_tdls_set_signature() - Set TDLS peer's signature
* @pAdapter: HDD adapter
* @mac: MAC address of TDLS Peer
* @uSignature: signature value
*
* Return: 0 for success or -EINVAL otherwise
*/
int wlan_hdd_tdls_set_signature(hdd_adapter_t *pAdapter, const uint8_t *mac,
uint8_t uSignature)
{
hddTdlsPeer_t *curr_peer;
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL) {
hddLog(LOGE, FL("curr_peer is NULL"));
return -EINVAL;
}
curr_peer->signature = uSignature;
return 0;
}
/**
* wlan_hdd_tdls_extract_da() - Extract destination address from socket buffer
* @skb: socket buffer
* @mac: output mac address buffer to store the destination address
*
* Return: Void
*/
void wlan_hdd_tdls_extract_da(struct sk_buff *skb, uint8_t *mac)
{
memcpy(mac, skb->data, 6);
}
/**
* wlan_hdd_tdls_extract_sa() - Extract source address from socket buffer
* @skb: socket buffer
* @mac: output mac address buffer to store the source address
*
* Return: Void
*/
void wlan_hdd_tdls_extract_sa(struct sk_buff *skb, uint8_t *mac)
{
memcpy(mac, skb->data + 6, 6);
}
/**
* wlan_hdd_tdls_increment_pkt_count() - update statistics counter on tdls peer
* @pAdapter: HDD adapter
* @mac: MAC address of the TDLS peer
* @tx: If 1, increment tx packet counter, if 0, increment rx packet counter
*
* Return: 0 for success or negative errno otherwise
*/
int wlan_hdd_tdls_increment_pkt_count(hdd_adapter_t *pAdapter,
const uint8_t *mac, uint8_t tx)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (eTDLS_SUPPORT_ENABLED != pHddCtx->tdls_mode)
return -EINVAL;
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL) {
hddLog(LOG1, FL("curr_peer is NULL"));
return -EINVAL;
}
if (tx)
curr_peer->tx_pkt++;
else
curr_peer->rx_pkt++;
return 0;
}
/**
* wlan_hdd_tdls_check_config() - validate tdls configuration parameters
* @config: tdls configuration parameter structure
*
* Return: 0 if all parameters are valid; -EINVAL otherwise
*/
static int wlan_hdd_tdls_check_config(tdls_config_params_t *config)
{
if (config->tdls > 2) {
hddLog(LOGE,
FL("Invalid 1st argument %d. <0...2>"),
config->tdls);
return -EINVAL;
}
if (config->tx_period_t < CFG_TDLS_TX_STATS_PERIOD_MIN ||
config->tx_period_t > CFG_TDLS_TX_STATS_PERIOD_MAX) {
hddLog(LOGE,
FL("Invalid 2nd argument %d. <%d...%ld>"),
config->tx_period_t, CFG_TDLS_TX_STATS_PERIOD_MIN,
CFG_TDLS_TX_STATS_PERIOD_MAX);
return -EINVAL;
}
if (config->tx_packet_n < CFG_TDLS_TX_PACKET_THRESHOLD_MIN ||
config->tx_packet_n > CFG_TDLS_TX_PACKET_THRESHOLD_MAX) {
hddLog(LOGE,
FL("Invalid 3rd argument %d. <%d...%ld>"),
config->tx_packet_n, CFG_TDLS_TX_PACKET_THRESHOLD_MIN,
CFG_TDLS_TX_PACKET_THRESHOLD_MAX);
return -EINVAL;
}
if (config->discovery_tries_n < CFG_TDLS_MAX_DISCOVERY_ATTEMPT_MIN ||
config->discovery_tries_n > CFG_TDLS_MAX_DISCOVERY_ATTEMPT_MAX) {
hddLog(LOGE,
FL("Invalid 5th argument %d. <%d...%d>"),
config->discovery_tries_n,
CFG_TDLS_MAX_DISCOVERY_ATTEMPT_MIN,
CFG_TDLS_MAX_DISCOVERY_ATTEMPT_MAX);
return -EINVAL;
}
if (config->idle_packet_n < CFG_TDLS_IDLE_PACKET_THRESHOLD_MIN ||
config->idle_packet_n > CFG_TDLS_IDLE_PACKET_THRESHOLD_MAX) {
hddLog(LOGE,
FL("Invalid 7th argument %d. <%d...%d>"),
config->idle_packet_n,
CFG_TDLS_IDLE_PACKET_THRESHOLD_MIN,
CFG_TDLS_IDLE_PACKET_THRESHOLD_MAX);
return -EINVAL;
}
if (config->rssi_trigger_threshold < CFG_TDLS_RSSI_TRIGGER_THRESHOLD_MIN
|| config->rssi_trigger_threshold >
CFG_TDLS_RSSI_TRIGGER_THRESHOLD_MAX) {
hddLog(LOGE,
FL("Invalid 9th argument %d. <%d...%d>"),
config->rssi_trigger_threshold,
CFG_TDLS_RSSI_TRIGGER_THRESHOLD_MIN,
CFG_TDLS_RSSI_TRIGGER_THRESHOLD_MAX);
return -EINVAL;
}
if (config->rssi_teardown_threshold <
CFG_TDLS_RSSI_TEARDOWN_THRESHOLD_MIN
|| config->rssi_teardown_threshold >
CFG_TDLS_RSSI_TEARDOWN_THRESHOLD_MAX) {
hddLog(LOGE,
FL("Invalid 10th argument %d. <%d...%d>"),
config->rssi_teardown_threshold,
CFG_TDLS_RSSI_TEARDOWN_THRESHOLD_MIN,
CFG_TDLS_RSSI_TEARDOWN_THRESHOLD_MAX);
return -EINVAL;
}
return 0;
}
/**
* wlan_tdd_tdls_reset_tx_rx() - reset tx/rx counters for all tdls peers
* @pHddTdlsCtx: TDLS context
*
* Caller has to take the TDLS lock before calling this function
*
* Return: Void
*/
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 < TDLS_PEER_LIST_SIZE; 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;
}
/**
* wlan_hdd_tdls_implicit_disable() - disable implicit tdls triggering
* @pHddTdlsCtx: TDLS context
*
* Return: Void
*/
static void wlan_hdd_tdls_implicit_disable(tdlsCtx_t *pHddTdlsCtx)
{
wlan_hdd_tdls_timers_stop(pHddTdlsCtx);
}
/**
* wlan_hdd_tdls_implicit_enable() - enable implicit tdls triggering
* @pHddTdlsCtx: TDLS context
*
* Return: Void
*/
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_set_mode() - set TDLS mode
* @pHddCtx: HDD context
* @tdls_mode: TDLS mode
* @bUpdateLast: Switch on if to set pHddCtx->tdls_mode_last to tdls_mode.
* If 1, set pHddCtx->tdls_mode_last to tdls_mode, otherwise
* set pHddCtx->tdls_mode_last to pHddCtx->tdls_mode
*
* Return: Void
*/
void wlan_hdd_tdls_set_mode(hdd_context_t *pHddCtx,
eTDLSSupportMode tdls_mode, bool bUpdateLast)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
CDF_STATUS status;
hdd_adapter_t *pAdapter;
tdlsCtx_t *pHddTdlsCtx;
hddLog(LOG1, "%s mode %d", __func__, (int)tdls_mode);
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx is not valid"));
return;
}
mutex_lock(&pHddCtx->tdls_lock);
if (pHddCtx->tdls_mode == tdls_mode) {
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(LOG1, FL("already in mode %d"), (int)tdls_mode);
return;
}
status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
while (NULL != pAdapterNode && CDF_STATUS_SUCCESS == status) {
pAdapter = pAdapterNode->pAdapter;
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL != pHddTdlsCtx) {
if (eTDLS_SUPPORT_ENABLED == tdls_mode)
wlan_hdd_tdls_implicit_enable(pHddTdlsCtx);
else if ((eTDLS_SUPPORT_DISABLED == tdls_mode) ||
(eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY ==
tdls_mode))
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);
}
/**
* wlan_hdd_tdls_set_params() - set TDLS parameters
* @dev: net device
* @config: TDLS configuration parameters
*
* Return: 0 if success or negative errno otherwise
*/
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;
tdlsInfo_t *tdlsParams;
CDF_STATUS cdf_ret_status = CDF_STATUS_E_FAILURE;
if (NULL == pHddTdlsCtx) {
hddLog(LOGE, FL("TDLS not enabled!"));
return -EINVAL;
}
if (wlan_hdd_tdls_check_config(config) != 0) {
return -EINVAL;
}
/* config->tdls is mapped to 0->1, 1->2, 2->3 */
req_tdls_mode = config->tdls + 1;
if (pHddCtx->tdls_mode == req_tdls_mode) {
hddLog(LOGE, FL("Already in mode %d"), config->tdls);
return -EINVAL;
}
/* 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));
}
hddLog(LOGE,
FL("iw set tdls params: %d %d %d %d %d %d %d"),
config->tdls,
config->tx_period_t,
config->tx_packet_n,
config->discovery_tries_n,
config->idle_packet_n,
config->rssi_trigger_threshold,
config->rssi_teardown_threshold);
wlan_hdd_tdls_set_mode(pHddCtx, req_tdls_mode, true);
tdlsParams = cdf_mem_malloc(sizeof(tdlsInfo_t));
if (NULL == tdlsParams) {
hddLog(CDF_TRACE_LEVEL_ERROR,
"%s: cdf_mem_alloc failed for tdlsParams", __func__);
return -ENOMEM;
}
tdlsParams->vdev_id = pAdapter->sessionId;
tdlsParams->tdls_state = config->tdls;
tdlsParams->notification_interval_ms = config->tx_period_t;
tdlsParams->tx_discovery_threshold = config->tx_packet_n;
tdlsParams->tx_teardown_threshold = config->idle_packet_n;
tdlsParams->rssi_teardown_threshold = config->rssi_teardown_threshold;
tdlsParams->rssi_delta = config->rssi_delta;
tdlsParams->tdls_options = 0;
if (pHddCtx->config->fEnableTDLSOffChannel)
tdlsParams->tdls_options |= ENA_TDLS_OFFCHAN;
if (pHddCtx->config->fEnableTDLSBufferSta)
tdlsParams->tdls_options |= ENA_TDLS_BUFFER_STA;
if (pHddCtx->config->fEnableTDLSSleepSta)
tdlsParams->tdls_options |= ENA_TDLS_SLEEP_STA;
tdlsParams->peer_traffic_ind_window =
pHddCtx->config->fTDLSPuapsdPTIWindow;
tdlsParams->peer_traffic_response_timeout =
pHddCtx->config->fTDLSPuapsdPTRTimeout;
tdlsParams->puapsd_mask = pHddCtx->config->fTDLSUapsdMask;
tdlsParams->puapsd_inactivity_time =
pHddCtx->config->fTDLSPuapsdInactivityTimer;
tdlsParams->puapsd_rx_frame_threshold =
pHddCtx->config->fTDLSRxFrameThreshold;
hddLog(LOG1,
"%s: Setting tdls state and param in fw: "
"vdev_id: %d, "
"tdls_state: %d, "
"notification_interval_ms: %d, "
"tx_discovery_threshold: %d, "
"tx_teardown_threshold: %d, "
"rssi_teardown_threshold: %d, "
"rssi_delta: %d, "
"tdls_options: 0x%x, "
"peer_traffic_ind_window: %d, "
"peer_traffic_response_timeout: %d, "
"puapsd_mask: 0x%x, "
"puapsd_inactivity_time: %d, "
"puapsd_rx_frame_threshold: %d ",
__func__,
tdlsParams->vdev_id,
tdlsParams->tdls_state,
tdlsParams->notification_interval_ms,
tdlsParams->tx_discovery_threshold,
tdlsParams->tx_teardown_threshold,
tdlsParams->rssi_teardown_threshold,
tdlsParams->rssi_delta,
tdlsParams->tdls_options,
tdlsParams->peer_traffic_ind_window,
tdlsParams->peer_traffic_response_timeout,
tdlsParams->puapsd_mask,
tdlsParams->puapsd_inactivity_time,
tdlsParams->puapsd_rx_frame_threshold);
cdf_ret_status = sme_update_fw_tdls_state(pHddCtx->hHal, tdlsParams, true);
if (CDF_STATUS_SUCCESS != cdf_ret_status) {
cdf_mem_free(tdlsParams);
return -EINVAL;
}
return 0;
}
/**
* wlan_hdd_update_tdls_info - update tdls status info
* @adapter: ptr to device adapter.
* @tdls_prohibited: indicates whether tdls is prohibited.
* @tdls_chan_swit_prohibited: indicates whether tdls channel switch
* is prohibited.
*
* Normally an AP does not influence TDLS connection between STAs
* associated to it. But AP may set bits for TDLS Prohibited or
* TDLS Channel Switch Prohibited in Extended Capability IE in
* Assoc/Re-assoc response to STA. So after STA is connected to
* an AP, call this function to update TDLS status as per those
* bits set in Ext Cap IE in received Assoc/Re-assoc response
* from AP.
*
* Return: None.
*/
void wlan_hdd_update_tdls_info(hdd_adapter_t *adapter, bool tdls_prohibited,
bool tdls_chan_swit_prohibited)
{
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
tdlsCtx_t *hdd_tdls_ctx = WLAN_HDD_GET_TDLS_CTX_PTR(adapter);
tdlsInfo_t *tdls_param;
CDF_STATUS cdf_ret_status = CDF_STATUS_E_FAILURE;
if (!hdd_tdls_ctx) {
/* may be TDLS is not applicable for this adapter */
hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD TDLS context is null"));
return;
}
/* If TDLS support is disabled then no need to update target */
if (false == hdd_ctx->config->fEnableTDLSSupport) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("TDLS not enabled"));
return;
}
/* If AP indicated TDLS Prohibited then disable tdls mode */
mutex_lock(&hdd_ctx->tdls_lock);
if (tdls_prohibited) {
hdd_ctx->tdls_mode = eTDLS_SUPPORT_NOT_ENABLED;
} else {
if (false == hdd_ctx->config->fEnableTDLSImplicitTrigger) {
hdd_ctx->tdls_mode =
eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY;
} else {
hdd_ctx->tdls_mode = eTDLS_SUPPORT_ENABLED;
}
}
mutex_unlock(&hdd_ctx->tdls_lock);
tdls_param = cdf_mem_malloc(sizeof(*tdls_param));
if (!tdls_param) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("memory allocation failed for tdlsParams"));
return;
}
tdls_param->vdev_id = adapter->sessionId;
tdls_param->tdls_state = hdd_ctx->tdls_mode;
tdls_param->notification_interval_ms =
hdd_tdls_ctx->threshold_config.tx_period_t;
tdls_param->tx_discovery_threshold =
hdd_tdls_ctx->threshold_config.tx_packet_n;
tdls_param->tx_teardown_threshold =
hdd_tdls_ctx->threshold_config.idle_packet_n;
tdls_param->rssi_teardown_threshold =
hdd_tdls_ctx->threshold_config.rssi_teardown_threshold;
tdls_param->rssi_delta = hdd_tdls_ctx->threshold_config.rssi_delta;
tdls_param->tdls_options = 0;
/* Do not enable TDLS offchannel, if AP prohibited TDLS channel switch */
if ((hdd_ctx->config->fEnableTDLSOffChannel) &&
(!tdls_chan_swit_prohibited)) {
tdls_param->tdls_options |= ENA_TDLS_OFFCHAN;
}
if (hdd_ctx->config->fEnableTDLSBufferSta)
tdls_param->tdls_options |= ENA_TDLS_BUFFER_STA;
if (hdd_ctx->config->fEnableTDLSSleepSta)
tdls_param->tdls_options |= ENA_TDLS_SLEEP_STA;
tdls_param->peer_traffic_ind_window =
hdd_ctx->config->fTDLSPuapsdPTIWindow;
tdls_param->peer_traffic_response_timeout =
hdd_ctx->config->fTDLSPuapsdPTRTimeout;
tdls_param->puapsd_mask =
hdd_ctx->config->fTDLSUapsdMask;
tdls_param->puapsd_inactivity_time =
hdd_ctx->config->fTDLSPuapsdInactivityTimer;
tdls_param->puapsd_rx_frame_threshold =
hdd_ctx->config->fTDLSRxFrameThreshold;
hddLog(CDF_TRACE_LEVEL_DEBUG,
FL("Setting tdls state and param in fw: "
"vdev_id: %d, "
"tdls_state: %d, "
"notification_interval_ms: %d, "
"tx_discovery_threshold: %d, "
"tx_teardown_threshold: %d, "
"rssi_teardown_threshold: %d, "
"rssi_delta: %d, "
"tdls_options: 0x%x, "
"peer_traffic_ind_window: %d, "
"peer_traffic_response_timeout: %d, "
"puapsd_mask: 0x%x, "
"puapsd_inactivity_time: %d, "
"puapsd_rx_frame_threshold: %d "),
tdls_param->vdev_id,
tdls_param->tdls_state,
tdls_param->notification_interval_ms,
tdls_param->tx_discovery_threshold,
tdls_param->tx_teardown_threshold,
tdls_param->rssi_teardown_threshold,
tdls_param->rssi_delta,
tdls_param->tdls_options,
tdls_param->peer_traffic_ind_window,
tdls_param->peer_traffic_response_timeout,
tdls_param->puapsd_mask,
tdls_param->puapsd_inactivity_time,
tdls_param->puapsd_rx_frame_threshold);
cdf_ret_status = sme_update_fw_tdls_state(hdd_ctx->hHal,
tdls_param,
true);
if (CDF_STATUS_SUCCESS != cdf_ret_status) {
cdf_mem_free(tdls_param);
return;
}
return;
}
/**
* wlan_hdd_tdls_set_sta_id() - set station ID on a tdls peer
* @pAdapter: HDD adapter
* @mac: MAC address of a tdls peer
* @staId: station ID
*
* Return: 0 if success; negative errno otherwise
*/
int wlan_hdd_tdls_set_sta_id(hdd_adapter_t *pAdapter, const uint8_t *mac,
uint8_t staId)
{
hddTdlsPeer_t *curr_peer;
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL) {
hddLog(LOGE, FL("curr_peer is NULL"));
return -EINVAL;
}
curr_peer->staId = staId;
return 0;
}
/**
* wlan_hdd_tdls_set_extctrl_param() - set external control parameter on a peer
* @pAdapter: HDD adapter
* @mac: MAC address of the peer
* @chan: Channel
* @max_latency: Maximum latency
* @op_class: Operation class
* @min_bandwidth: Minimal bandwidth
*
* Return: 0 for success; negative errno otherwise
*/
int wlan_hdd_tdls_set_extctrl_param(hdd_adapter_t *pAdapter, const uint8_t *mac,
uint32_t chan, uint32_t max_latency,
uint32_t op_class, uint32_t min_bandwidth)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (!pHddCtx)
return -EINVAL;
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, false);
if (curr_peer == NULL) {
mutex_unlock(&pHddCtx->tdls_lock);
return -EINVAL;
}
curr_peer->op_class_for_pref_off_chan = (uint8_t) op_class;
curr_peer->pref_off_chan_num = (uint8_t) chan;
if (curr_peer->op_class_for_pref_off_chan)
curr_peer->op_class_for_pref_off_chan_is_set = 1;
else
curr_peer->op_class_for_pref_off_chan_is_set = 0;
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
/**
* wlan_hdd_tdls_set_force_peer() - set/clear isForcedPeer flag on a peer
* @pAdapter: HDD adapter
* @mac: MAC address of the tdls peer
* @forcePeer: value used to set isForcedPeer flag on the peer
*
* Return: 0 for success; negative errno otherwise
*/
int wlan_hdd_tdls_set_force_peer(hdd_adapter_t *pAdapter, const uint8_t *mac,
bool forcePeer)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (!pHddCtx)
return -EINVAL;
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, false);
if (curr_peer == NULL) {
mutex_unlock(&pHddCtx->tdls_lock);
return -EINVAL;
}
curr_peer->isForcedPeer = forcePeer;
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
/**
* wlan_hdd_tdls_find_peer() - find TDLS peer given its MAC address
* @pAdapter: HDD adapter
* @mac: MAC address of peer
* @mutexLock: Option to indicate if mutex locking is required for searching
*
* Return: 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 uint8_t *mac, bool mutexLock)
{
uint8_t 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))) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx is not valid"));
return NULL;
}
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)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
"findTdlsPeer: found staId %d",
curr_peer->staId);
if (mutexLock)
mutex_unlock(&pHddCtx->tdls_lock);
return curr_peer;
}
}
if (mutexLock)
mutex_unlock(&pHddCtx->tdls_lock);
return NULL;
}
/**
* wlan_hdd_tdls_find_all_peer() - find all peers matching the input MAC
* @pHddCtx: HDD context
* @mac: MAC address
*
* Return: TDLS peer if a matching is detected; NULL otherwise
*/
hddTdlsPeer_t *wlan_hdd_tdls_find_all_peer(hdd_context_t *pHddCtx,
const u8 *mac)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter = NULL;
tdlsCtx_t *pHddTdlsCtx = NULL;
hddTdlsPeer_t *curr_peer = NULL;
CDF_STATUS status = 0;
mutex_lock(&pHddCtx->tdls_lock);
status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
while (NULL != pAdapterNode && CDF_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) {
mutex_unlock(&pHddCtx->tdls_lock);
return curr_peer;
}
}
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
mutex_unlock(&pHddCtx->tdls_lock);
return curr_peer;
}
/**
* wlan_hdd_tdls_reset_peer() - reset TDLS peer identified by MAC address
* @pAdapter: HDD adapter
* @mac: MAC address of the peer
*
* Return: 0 for success; negative errno otherwise
*/
int wlan_hdd_tdls_reset_peer(hdd_adapter_t *pAdapter, const uint8_t *mac)
{
hdd_context_t *pHddCtx;
hddTdlsPeer_t *curr_peer;
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (curr_peer == NULL) {
hddLog(LOGE, FL("curr_peer is NULL"));
return -EINVAL;
}
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED);
curr_peer->staId = 0;
return 0;
}
/**
* wlan_hdd_tdls_peer_reset_discovery_processed() - reset discovery status
* @pHddTdlsCtx: TDLS context
*
* This function resets discovery processing bit for all TDLS peers
*
* Caller has to take the lock before calling this function
*
* Return: 0
*/
static int32_t 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 < TDLS_PEER_LIST_SIZE; 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;
}
/**
* wlan_hdd_tdls_connected_peers() - Find the number of connected TDLS peers
* @pAdapter: HDD adapter
*
* Return: The number of connected TDLS peers or 0 if error is detected
*/
uint16_t wlan_hdd_tdls_connected_peers(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx is not valid"));
return 0;
}
return pHddCtx->connected_peer_count;
}
/**
* wlan_hdd_tdls_get_all_peers() - dump all TDLS peer info into output string
* @pAdapter: HDD adapter
* @buf: output string buffer to hold the peer info
* @buflen: the size of output string buffer
*
* Return: The size (in bytes) of the valid peer info in the output buffer
*/
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);
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx is not valid"));
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 < TDLS_PEER_LIST_SIZE; 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);
return init_len - buflen;
}
/**
* wlan_hdd_tdls_connection_callback() - callback after tdls connection
* @pAdapter: HDD adapter
*
* Return: Void
*/
void wlan_hdd_tdls_connection_callback(hdd_adapter_t *pAdapter)
{
tdlsCtx_t *pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if ((NULL == pHddCtx) || (NULL == pHddTdlsCtx)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx or pHddTdlsCtx points to NULL"));
return;
}
mutex_lock(&pHddCtx->tdls_lock);
hddLog(LOG1,
FL("update %d"),
pHddTdlsCtx->threshold_config.tx_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);
}
mutex_unlock(&pHddCtx->tdls_lock);
}
/**
* wlan_hdd_tdls_disconnection_callback() - callback after tdls disconnection
* @pAdapter: HDD adapter
*
* Return: Void
*/
void wlan_hdd_tdls_disconnection_callback(hdd_adapter_t *pAdapter)
{
tdlsCtx_t *pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if ((NULL == pHddCtx) || (NULL == pHddTdlsCtx)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx or pHddTdlsCtx points to NULL"));
return;
}
hddLog(LOG1, "%s", __func__);
mutex_lock(&pHddCtx->tdls_lock);
if (NULL == pHddTdlsCtx) {
mutex_unlock(&pHddCtx->tdls_lock);
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddTdlsCtx is NULL"));
return;
}
pHddTdlsCtx->discovery_sent_cnt = 0;
wlan_hdd_tdls_check_power_save_prohibited(pHddTdlsCtx->pAdapter);
wlan_hdd_tdls_monitor_timers_stop(pHddTdlsCtx);
wlan_hdd_tdls_free_list(pHddTdlsCtx);
pHddTdlsCtx->curr_candidate = NULL;
mutex_unlock(&pHddCtx->tdls_lock);
}
/**
* wlan_hdd_tdls_mgmt_completion_callback() - callback for TDLS management
* TX completion
* @pAdapter: HDD adapter
* @statusCode: management TX completion status
*
* Return: Void
*/
void wlan_hdd_tdls_mgmt_completion_callback(hdd_adapter_t *pAdapter,
uint32_t statusCode)
{
pAdapter->mgmtTxCompletionStatus = statusCode;
hddLog(LOG1,
"%s: Mgmt TX Completion %d", __func__, statusCode);
complete(&pAdapter->tdls_mgmt_comp);
}
/**
* wlan_hdd_tdls_increment_peer_count() - increment connected TDLS peer counter
* @pAdapter: HDD adapter
*
* Return: Void
*/
void wlan_hdd_tdls_increment_peer_count(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx is not valid"));
return;
}
mutex_lock(&pHddCtx->tdls_lock);
pHddCtx->connected_peer_count++;
wlan_hdd_tdls_check_power_save_prohibited(pAdapter);
hddLog(LOG1, "%s: %d",
__func__, pHddCtx->connected_peer_count);
mutex_unlock(&pHddCtx->tdls_lock);
}
/**
* wlan_hdd_tdls_decrement_peer_count() - decrement connected TDLS peer counter
* @pAdapter: HDD adapter
*
* Return: Void
*/
void wlan_hdd_tdls_decrement_peer_count(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx is not valid"));
return;
}
mutex_lock(&pHddCtx->tdls_lock);
if (pHddCtx->connected_peer_count)
pHddCtx->connected_peer_count--;
wlan_hdd_tdls_check_power_save_prohibited(pAdapter);
hddLog(LOG1, "%s: %d",
__func__, pHddCtx->connected_peer_count);
mutex_unlock(&pHddCtx->tdls_lock);
}
/**
* wlan_hdd_tdls_find_progress_peer() - find peer if TDLS is ongoing
* @pAdapter: HDD adapter
* @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.
*
* Return: Pointer to hddTdlsPeer_t if TDLS is ongoing. Otherwise return NULL.
*/
static hddTdlsPeer_t *wlan_hdd_tdls_find_progress_peer(hdd_adapter_t *pAdapter,
const u8 *mac,
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) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddTdlsCtx is NULL"));
return NULL;
}
for (i = 0; i < TDLS_PEER_LIST_SIZE; 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) {
hddLog(LOG1,
"%s:" MAC_ADDRESS_STR
" eTDLS_LINK_CONNECTING",
__func__,
MAC_ADDR_ARRAY(curr_peer->
peerMac));
return curr_peer;
}
}
}
}
return NULL;
}
/**
* wlan_hdd_tdls_is_progress() - find the peer with ongoing TDLS progress
* @pHddCtx: HDD context
* @mac: mac address of the peer
* @skip_self: if 1, skip checking self. If 0, search includes self
*
* Return: TDLS peer if found; NULL otherwise
*/
hddTdlsPeer_t *wlan_hdd_tdls_is_progress(hdd_context_t *pHddCtx,
const uint8_t *mac, uint8_t skip_self)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter = NULL;
tdlsCtx_t *pHddTdlsCtx = NULL;
hddTdlsPeer_t *curr_peer = NULL;
CDF_STATUS status = 0;
mutex_lock(&pHddCtx->tdls_lock);
status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
while (NULL != pAdapterNode && CDF_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) {
mutex_unlock(&pHddCtx->tdls_lock);
return curr_peer;
}
}
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
mutex_unlock(&pHddCtx->tdls_lock);
return NULL;
}
/**
* __wlan_hdd_tdls_pre_setup() - TDLS Pre implicit setup
* @work: work_struct used to find the TDLS context
*
* Return: Void
*/
static void __wlan_hdd_tdls_pre_setup(struct work_struct *work)
{
tdlsCtx_t *pHddTdlsCtx = container_of(work, tdlsCtx_t, implicit_setup);
hdd_context_t *pHddCtx;
hddTdlsPeer_t *curr_peer;
hddTdlsPeer_t *temp_peer;
int status;
tSirMacAddr peer_mac;
if (NULL == pHddTdlsCtx) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddTdlsCtx is NULL"));
return;
}
if (unlikely(TDLS_CTX_MAGIC != pHddTdlsCtx->magic)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: tdls magic number mis-match %u",
__func__, pHddTdlsCtx->magic);
return;
}
pHddCtx = WLAN_HDD_GET_CTX(pHddTdlsCtx->pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx is not valid"));
return;
}
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = pHddTdlsCtx->curr_candidate;
if (NULL == curr_peer) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx is not valid"));
goto done;
}
cdf_mem_copy(&peer_mac, curr_peer->peerMac, sizeof(peer_mac));
mutex_unlock(&pHddCtx->tdls_lock);
temp_peer = wlan_hdd_tdls_is_progress(pHddCtx, NULL, 0);
if (NULL != temp_peer) {
hddLog(LOG1,
"%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);
mutex_lock(&pHddCtx->tdls_lock);
/* Ignore discovery attempt if External Control is enabled, that
* is, peer is forced. In that case, continue discovery attempt
* regardless attempt count
*/
if (false == curr_peer->isForcedPeer) {
if (curr_peer->discovery_attempt >=
pHddTdlsCtx->threshold_config.discovery_tries_n) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: discovery attempt (%d) reached max (%d) for peer "
MAC_ADDRESS_STR
", ignore discovery trigger from fw",
__func__, curr_peer->discovery_attempt,
pHddTdlsCtx->threshold_config.
discovery_tries_n,
MAC_ADDR_ARRAY(curr_peer->peerMac));
curr_peer->tdls_support = eTDLS_CAP_NOT_SUPPORTED;
goto done;
}
}
mutex_unlock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_DISCOVERING,
eTDLS_LINK_SUCCESS);
status =
wlan_hdd_cfg80211_send_tdls_discover_req(pHddTdlsCtx->pAdapter->
wdev.wiphy,
pHddTdlsCtx->pAdapter->dev,
peer_mac);
mutex_lock(&pHddCtx->tdls_lock);
if (NULL == pHddTdlsCtx->curr_candidate) {
hddLog(LOGE,
"%s: current candidate Not valid any more", __func__);
goto done;
}
curr_peer = pHddTdlsCtx->curr_candidate;
if (0 != status) {
hddLog(LOGE,
"%s: " MAC_ADDRESS_STR " discovery could not sent",
__func__, MAC_ADDR_ARRAY(curr_peer->peerMac));
if (eTDLS_CAP_UNKNOWN != curr_peer->tdls_support) {
mutex_unlock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED);
mutex_lock(&pHddCtx->tdls_lock);
}
goto done;
}
pHddTdlsCtx->discovery_sent_cnt++;
curr_peer->discovery_attempt++;
wlan_hdd_tdls_check_power_save_prohibited(pHddTdlsCtx->pAdapter);
hddLog(LOG1,
"%s: discovery count %u timeout %u msec", __func__,
pHddTdlsCtx->discovery_sent_cnt,
pHddTdlsCtx->threshold_config.tx_period_t -
TDLS_DISCOVERY_TIMEOUT_BEFORE_UPDATE);
wlan_hdd_tdls_timer_restart(pHddTdlsCtx->pAdapter,
&pHddTdlsCtx->peerDiscoveryTimeoutTimer,
pHddTdlsCtx->threshold_config.tx_period_t -
TDLS_DISCOVERY_TIMEOUT_BEFORE_UPDATE);
done:
pHddTdlsCtx->curr_candidate = NULL;
pHddTdlsCtx->magic = 0;
mutex_unlock(&pHddCtx->tdls_lock);
return;
}
/**
* wlan_hdd_tdls_pre_setup() - TDLS Pre implicit setup
* @work: work_struct used to find the TDLS context
*
* Return: Void
*/
void wlan_hdd_tdls_pre_setup(struct work_struct *work)
{
cds_ssr_protect(__func__);
__wlan_hdd_tdls_pre_setup(work);
cds_ssr_unprotect(__func__);
}
/**
* wlan_hdd_tdls_copy_scan_context() - Copy TDLS scan context
* @pHddCtx: HDD context
* @wiphy: wiphy pointer
* @dev: net device
* request: source scan context
*
* Copy the source scan context into the HDD context's TDLS scan context
*
* Return: 0 for success; negative errno otherwise
*/
int wlan_hdd_tdls_copy_scan_context(hdd_context_t *pHddCtx,
struct wiphy *wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
struct net_device *dev,
#endif
struct cfg80211_scan_request *request)
{
tdls_scan_context_t *scan_ctx;
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
hddLog(LOGE, FL("pHddCtx is not valid"));
return -EINVAL;
}
scan_ctx = &pHddCtx->tdls_scan_ctxt;
scan_ctx->wiphy = wiphy;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
scan_ctx->dev = dev;
#endif
scan_ctx->scan_request = request;
return 0;
}
/**
* wlan_hdd_tdls_scan_init_work() - schedule tdls scan work
* @pHddCtx: HDD context
* @wiphy: wiphy pointer
* @dev: net device
* @request: scan request
* @delay: delay value to pass to the work scheduling
*
* Return: Void
*/
static void wlan_hdd_tdls_scan_init_work(hdd_context_t *pHddCtx,
struct wiphy *wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
struct net_device *dev,
#endif
struct cfg80211_scan_request *request,
unsigned long delay)
{
if (TDLS_CTX_MAGIC != pHddCtx->tdls_scan_ctxt.magic) {
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
wlan_hdd_tdls_copy_scan_context(pHddCtx, wiphy, dev, request);
#else
wlan_hdd_tdls_copy_scan_context(pHddCtx, wiphy, request);
#endif
pHddCtx->tdls_scan_ctxt.attempt = 0;
pHddCtx->tdls_scan_ctxt.magic = TDLS_CTX_MAGIC;
}
schedule_delayed_work(&pHddCtx->tdls_scan_ctxt.tdls_scan_work, delay);
}
/**
* wlan_hdd_tdls_scan_callback() - callback for TDLS scan operation
* @pAdapter: HDD adapter
* @wiphy: wiphy
* @dev: net device
* @request: scan request
*
* Return: negative = caller should stop and return error code immediately
* 0 = caller should stop and return success immediately
* 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;
unsigned long delay;
int ret;
ret = wlan_hdd_validate_context(pHddCtx);
if (ret)
return ret;
/* if tdls is not enabled, then continue scan */
if (eTDLS_SUPPORT_NOT_ENABLED == pHddCtx->tdls_mode)
return 1;
curr_peer = wlan_hdd_tdls_is_progress(pHddCtx, NULL, 0);
if (NULL != curr_peer) {
if (pHddCtx->tdls_scan_ctxt.reject++ >= TDLS_MAX_SCAN_REJECT) {
pHddCtx->tdls_scan_ctxt.reject = 0;
hddLog(LOG1,
"%s: " MAC_ADDRESS_STR
". scan rejected %d. force it to idle",
__func__, MAC_ADDR_ARRAY(curr_peer->peerMac),
pHddCtx->tdls_scan_ctxt.reject);
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED);
return 1;
}
hddLog(LOGW,
"%s: tdls in progress. scan rejected %d",
__func__, pHddCtx->tdls_scan_ctxt.reject);
return -EBUSY;
}
/* tdls teardown is ongoing */
if (eTDLS_SUPPORT_DISABLED == pHddCtx->tdls_mode) {
connectedTdlsPeers = wlan_hdd_tdls_connected_peers(pAdapter);
if (connectedTdlsPeers
&& (pHddCtx->tdls_scan_ctxt.attempt <
TDLS_MAX_SCAN_SCHEDULE)) {
delay =
(unsigned long)(TDLS_DELAY_SCAN_PER_CONNECTION *
connectedTdlsPeers);
hddLog(LOG1,
"%s: tdls disabled, but still connected_peers %d attempt %d. schedule scan %lu msec",
__func__, connectedTdlsPeers,
pHddCtx->tdls_scan_ctxt.attempt, delay);
wlan_hdd_tdls_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 */
hddLog(LOG1,
"%s: tdls disabled. connected_peers %d attempt %d. scan allowed",
__func__, connectedTdlsPeers,
pHddCtx->tdls_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) {
/* disable implicit trigger logic & tdls operatoin */
wlan_hdd_tdls_set_mode(pHddCtx, eTDLS_SUPPORT_DISABLED, false);
/* indicate the teardown all connected to peer */
connectedTdlsPeers = wlan_hdd_tdls_connected_peers(pAdapter);
if (connectedTdlsPeers) {
uint8_t staIdx;
uint8_t num = 0;
uint8_t i;
bool allPeersBufStas = 1;
hddTdlsPeer_t *curr_peer;
hddTdlsPeer_t *connectedPeerList[HDD_MAX_NUM_TDLS_STA];
/* If TDLSScan is enabled then allow scan and
* maintain tdls link regardless if peer is buffer
* sta capable or not and if device is sleep sta
* capable or not. If peer is not buffer sta
* capable, then Tx would stop when device
* initiates scan and there will be loss of Rx
* packets since peer would not know when device
* moves away from the tdls channel.
*/
if (1 == pHddCtx->config->enable_tdls_scan) {
hddLog(LOG1, FL(
"TDLSScan enabled, keep tdls link and allow scan, connectedTdlsPeers: %d"),
connectedTdlsPeers);
return 1;
}
for (staIdx = 0; staIdx < pHddCtx->max_num_tdls_sta;
staIdx++) {
if (pHddCtx->tdlsConnInfo[staIdx].staId) {
curr_peer =
wlan_hdd_tdls_find_all_peer(pHddCtx,
pHddCtx->
tdlsConnInfo
[staIdx].
peerMac.
bytes);
if (curr_peer) {
connectedPeerList[num++] =
curr_peer;
if (!(curr_peer->isBufSta))
allPeersBufStas = 0;
}
}
}
if ((TDLS_MAX_CONNECTED_PEERS_TO_ALLOW_SCAN ==
connectedTdlsPeers) &&
(pHddCtx->config->fEnableTDLSSleepSta) &&
(allPeersBufStas)) {
/* All connected peers bufStas and we can be sleepSta
* so allow scan
*/
hddLog(LOG1,
"%s: All peers (num %d) bufSTAs, we can be sleep sta, so allow scan, tdls mode changed to %d",
__func__, connectedTdlsPeers,
pHddCtx->tdls_mode);
return 1;
} else {
for (i = 0; i < num; i++) {
hddLog(LOG1,
"%s: indicate TDLS teadown (staId %d)",
__func__,
connectedPeerList[i]->staId);
wlan_hdd_tdls_indicate_teardown
(connectedPeerList[i]->pHddTdlsCtx->
pAdapter, connectedPeerList[i],
eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
}
}
/* schedule scan */
delay =
(unsigned long)(TDLS_DELAY_SCAN_PER_CONNECTION *
connectedTdlsPeers);
hddLog(LOG1,
"%s: tdls enabled (mode %d), connected_peers %d. schedule scan %lu msec",
__func__, pHddCtx->tdls_mode,
wlan_hdd_tdls_connected_peers(pAdapter), delay);
wlan_hdd_tdls_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 */
hddLog(LOG1,
"%s: tdls_mode %d, and no tdls connection. scan allowed",
__func__, pHddCtx->tdls_mode);
}
return 1;
}
/**
* wlan_hdd_tdls_scan_done_callback() - callback for tdls scan done event
* @pAdapter: HDD adapter
*
* Return: Void
*/
void wlan_hdd_tdls_scan_done_callback(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("pHddCtx is not valid"));
return;
}
/* free allocated memory at scan time */
wlan_hdd_tdls_free_scan_request(&pHddCtx->tdls_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) {
hddLog(LOG1,
("%s: revert tdls mode %d"), __func__,
pHddCtx->tdls_mode_last);
wlan_hdd_tdls_set_mode(pHddCtx, pHddCtx->tdls_mode_last, false);
}
}
/**
* wlan_hdd_tdls_timer_restart() - restart TDLS timer
* @pAdapter: HDD adapter
* @timer: timer to restart
* @expirationTime: new expiration time to set for the timer
*
* Return: Void
*/
void wlan_hdd_tdls_timer_restart(hdd_adapter_t *pAdapter,
cdf_mc_timer_t *timer,
uint32_t expirationTime)
{
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
/* Check whether driver load unload is in progress */
if (cds_is_load_unload_in_progress()) {
hddLog(LOGE, FL("Driver load/unload is in progress."));
return;
}
if (hdd_conn_is_connected(pHddStaCtx)) {
cdf_mc_timer_stop(timer);
cdf_mc_timer_start(timer, expirationTime);
}
}
/**
* wlan_hdd_tdls_indicate_teardown() - indicate tdls teardown
* @pAdapter: HDD adapter
* @curr_peer: peer tdls teardown happened
* @reason: teardown reason
*
* Return: Void
*/
void wlan_hdd_tdls_indicate_teardown(hdd_adapter_t *pAdapter,
hddTdlsPeer_t *curr_peer, uint16_t reason)
{
if (NULL == pAdapter || NULL == curr_peer) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("parameters passed are invalid"));
return;
}
if (eTDLS_LINK_CONNECTED != curr_peer->link_status)
return;
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_TEARING,
eTDLS_LINK_UNSPECIFIED);
cfg80211_tdls_oper_request(pAdapter->dev,
curr_peer->peerMac,
NL80211_TDLS_TEARDOWN, reason, GFP_KERNEL);
}
/**
* wlan_hdd_set_callback() - set state change callback on current TDLS peer
* @curr_peer: current TDLS peer
* @callback: state change callback
*
* Return: 0 if success; negative errno otherwise
*/
int wlan_hdd_set_callback(hddTdlsPeer_t *curr_peer,
cfg80211_exttdls_callback callback)
{
hdd_context_t *pHddCtx;
hdd_adapter_t *pAdapter;
if (!curr_peer)
return -EINVAL;
pAdapter = curr_peer->pHddTdlsCtx->pAdapter;
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if ((NULL == pHddCtx))
return -EINVAL;
mutex_lock(&pHddCtx->tdls_lock);
curr_peer->state_change_notification = callback;
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
/**
* wlan_hdd_tdls_get_wifi_hal_state() - get tdls wifi hal state on current peer
* @curr_peer: current TDLS peer
* @state: output parameter to store the tdls wifi hal state
* @reason: output parameter to store the reason of the current peer
*
* Return: Void
*/
void wlan_hdd_tdls_get_wifi_hal_state(hddTdlsPeer_t *curr_peer,
int32_t *state, int32_t *reason)
{
*reason = curr_peer->reason;
switch (curr_peer->link_status) {
case eTDLS_LINK_IDLE:
case eTDLS_LINK_DISCOVERED:
*state = QCA_WIFI_HAL_TDLS_ENABLED;
break;
case eTDLS_LINK_DISCOVERING:
case eTDLS_LINK_CONNECTING:
*state = QCA_WIFI_HAL_TDLS_ENABLED;
break;
case eTDLS_LINK_CONNECTED:
*state = QCA_WIFI_HAL_TDLS_ESTABLISHED;
break;
case eTDLS_LINK_TEARING:
*state = QCA_WIFI_HAL_TDLS_DROPPED;
break;
}
}
/**
* wlan_hdd_tdls_get_status() - get tdls status on current tdls peer
* @pAdapter: HDD adapter
* @mac: MAC address of current TDLS peer
* @state: output parameter to store the tdls wifi hal state
* @reason: output parameter to store the reason of the current peer
*
* Return: 0 if success; negative errno otherwise
*/
int wlan_hdd_tdls_get_status(hdd_adapter_t *pAdapter,
const uint8_t *mac, int32_t *state,
int32_t *reason)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, true);
if (curr_peer == NULL) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
*state = QCA_WIFI_HAL_TDLS_DISABLED;
*reason = eTDLS_LINK_UNSPECIFIED;
} else {
if (pHddCtx->config->fTDLSExternalControl &&
(false == curr_peer->isForcedPeer)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
FL("curr_peer is not Forced"));
*state = QCA_WIFI_HAL_TDLS_DISABLED;
*reason = eTDLS_LINK_UNSPECIFIED;
} else {
wlan_hdd_tdls_get_wifi_hal_state(curr_peer,
state, reason);
}
}
wlan_hdd_tdls_get_wifi_hal_state(curr_peer, state, reason);
return 0;
}
#ifdef FEATURE_WLAN_TDLS
static const struct nla_policy
wlan_hdd_tdls_config_enable_policy[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX +
1] = {
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR] = {.type = NLA_UNSPEC},
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL] = {.type = NLA_S32},
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS] = {.type =
NLA_S32},
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS] = {.type = NLA_S32},
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS] = {.type =
NLA_S32},
};
static const struct nla_policy
wlan_hdd_tdls_config_disable_policy[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX +
1] = {
[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR] = {.type = NLA_UNSPEC},
};
static const struct nla_policy
wlan_hdd_tdls_config_state_change_policy[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAX
+ 1] = {
[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAC_ADDR] = {.type = NLA_UNSPEC},
[QCA_WLAN_VENDOR_ATTR_TDLS_NEW_STATE] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_REASON] = {.type = NLA_S32},
[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_CHANNEL] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_GLOBAL_OPERATING_CLASS] = {.type =
NLA_U32},
};
static const struct nla_policy
wlan_hdd_tdls_config_get_status_policy
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR] = {.type = NLA_UNSPEC},
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON] = {.type = NLA_S32},
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_CHANNEL] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_GLOBAL_OPERATING_CLASS] = {
.type = NLA_U32},
};
/**
* __wlan_hdd_cfg80211_exttdls_get_status() - handle get status cfg80211 command
* @wiphy: wiphy
* @wdev: wireless dev
* @data: netlink buffer with the mac address of the peer to get the status for
* @data_len: length of data in bytes
*/
static int
__wlan_hdd_cfg80211_exttdls_get_status(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
uint8_t peer[ETH_ALEN] = { 0 };
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX + 1];
CDF_STATUS ret;
uint32_t state;
int32_t reason;
uint32_t global_operating_class = 0;
uint32_t channel = 0;
struct sk_buff *skb = NULL;
if (CDF_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret) {
return -EINVAL;
}
if (pHddCtx->config->fTDLSExternalControl == false) {
return -ENOTSUPP;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX,
data, data_len, wlan_hdd_tdls_config_get_status_policy)) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("Invalid attribute"));
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR]) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("attr mac addr failed"));
return -EINVAL;
}
memcpy(peer,
nla_data(tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR]),
sizeof(peer));
hddLog(CDF_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR), MAC_ADDR_ARRAY(peer));
ret = wlan_hdd_tdls_get_status(pAdapter, peer, &state, &reason);
if (0 != ret) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("get status Failed"));
return -EINVAL;
}
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
4 * sizeof(int32_t) +
NLMSG_HDRLEN);
if (!skb) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -EINVAL;
}
hddLog(CDF_TRACE_LEVEL_INFO, FL("Reason %d Status %d class %d channel %d peer " MAC_ADDRESS_STR),
reason, state, global_operating_class, channel,
MAC_ADDR_ARRAY(peer));
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE,
state) ||
nla_put_s32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON,
reason) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_GLOBAL_OPERATING_CLASS,
global_operating_class) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_CHANNEL,
channel)) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("nla put fail"));
goto nla_put_failure;
}
return cfg80211_vendor_cmd_reply(skb);
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_exttdls_get_status() - get ext tdls status
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Return: Return the Success or Failure code.
*/
int wlan_hdd_cfg80211_exttdls_get_status(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
cds_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_exttdls_get_status(wiphy, wdev, data,
data_len);
cds_ssr_unprotect(__func__);
return ret;
}
/**
* wlan_hdd_cfg80211_exttdls_callback() - notify cfg80211 state change
* @mac: MAC address of the peer with state change
* @state: New state
* @reason: Reason to enter new state
* @ctx: HDD adapter
*
* Return: 0 for success; negative errno otherwise
*/
static int wlan_hdd_cfg80211_exttdls_callback(const uint8_t *mac,
uint32_t state,
int32_t reason, void *ctx)
{
hdd_adapter_t *pAdapter = (hdd_adapter_t *) ctx;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
struct sk_buff *skb = NULL;
uint32_t global_operating_class = 0;
uint32_t channel = 0;
if (wlan_hdd_validate_context(pHddCtx)) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid "));
return -EINVAL;
}
if (pHddCtx->config->fTDLSExternalControl == false) {
return -ENOTSUPP;
}
skb = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
NULL,
EXTTDLS_EVENT_BUF_SIZE + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE_CHANGE_INDEX,
GFP_KERNEL);
if (!skb) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_event_alloc failed"));
return -EINVAL;
}
hddLog(CDF_TRACE_LEVEL_INFO, FL("Reason: %d Status: %d Class: %d Channel: %d tdls peer " MAC_ADDRESS_STR),
reason, state, global_operating_class, channel,
MAC_ADDR_ARRAY(mac));
if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAC_ADDR,
CDF_MAC_ADDR_SIZE, mac) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_TDLS_NEW_STATE,
state) ||
nla_put_s32(skb, QCA_WLAN_VENDOR_ATTR_TDLS_STATE_REASON,
reason) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_TDLS_STATE_CHANNEL,
channel) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_STATE_GLOBAL_OPERATING_CLASS,
global_operating_class)) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("nla put fail"));
goto nla_put_failure;
}
cfg80211_vendor_event(skb, GFP_KERNEL);
return 0;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
/**
* __wlan_hdd_cfg80211_exttdls_enable() - enable an externally controllable
* TDLS peer and set parameters
* wiphy: wiphy
* @wdev: wireless dev pointer
* @data: netlink buffer with peer MAC address and configuration parameters
* @data_len: size of data in bytes
*
* This function sets channel, operation class, maximum latency and minimal
* bandwidth parameters on a TDLS peer that's externally controllable.
*
* Return: 0 for success; negative errno otherwise
*/
static int
__wlan_hdd_cfg80211_exttdls_enable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
uint8_t peer[ETH_ALEN] = { 0 };
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX + 1];
CDF_STATUS status;
tdls_req_params_t pReqMsg = { 0 };
if (CDF_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
return -EINVAL;
}
if (pHddCtx->config->fTDLSExternalControl == false) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("TDLS External Control is not enabled"));
return -ENOTSUPP;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX,
data, data_len, wlan_hdd_tdls_config_enable_policy)) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR]) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("attr mac addr failed"));
return -EINVAL;
}
memcpy(peer, nla_data(tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR]),
sizeof(peer));
hddLog(CDF_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR), MAC_ADDR_ARRAY(peer));
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL]) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("attr channel failed"));
return -EINVAL;
}
pReqMsg.channel =
nla_get_s32(tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL]);
hddLog(CDF_TRACE_LEVEL_INFO, FL("Channel Num (%d)"), pReqMsg.channel);
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS]) {
hddLog(CDF_TRACE_LEVEL_ERROR,
FL("attr operating class failed"));
return -EINVAL;
}
pReqMsg.global_operating_class =
nla_get_s32(tb
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS]);
hddLog(CDF_TRACE_LEVEL_INFO, FL("Operating class (%d)"),
pReqMsg.global_operating_class);
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS]) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("attr latency failed"));
return -EINVAL;
}
pReqMsg.max_latency_ms =
nla_get_s32(tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS]);
hddLog(CDF_TRACE_LEVEL_INFO, FL("Latency (%d)"),
pReqMsg.max_latency_ms);
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS]) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("attr bandwidth failed"));
return -EINVAL;
}
pReqMsg.min_bandwidth_kbps =
nla_get_s32(tb
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS]);
hddLog(CDF_TRACE_LEVEL_INFO, FL("Bandwidth (%d)"),
pReqMsg.min_bandwidth_kbps);
return wlan_hdd_tdls_extctrl_config_peer(
pAdapter,
peer,
wlan_hdd_cfg80211_exttdls_callback,
pReqMsg.channel,
pReqMsg.max_latency_ms,
pReqMsg.
global_operating_class,
pReqMsg.min_bandwidth_kbps);
}
/**
* wlan_hdd_cfg80211_exttdls_enable() - enable ext tdls
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Return: Return the Success or Failure code.
*/
int wlan_hdd_cfg80211_exttdls_enable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
cds_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_exttdls_enable(wiphy, wdev, data, data_len);
cds_ssr_unprotect(__func__);
return ret;
}
/**
* __wlan_hdd_cfg80211_exttdls_disable() - disable an externally controllable
* TDLS peer
* wiphy: wiphy
* @wdev: wireless dev pointer
* @data: netlink buffer with peer MAC address
* @data_len: size of data in bytes
*
* This function disables an externally controllable TDLS peer
*
* Return: 0 for success; negative errno otherwise
*/
static int __wlan_hdd_cfg80211_exttdls_disable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
u8 peer[ETH_ALEN] = {0};
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX + 1];
CDF_STATUS status;
if (CDF_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
return -EINVAL;
}
if (pHddCtx->config->fTDLSExternalControl == false) {
return -ENOTSUPP;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX,
data, data_len, wlan_hdd_tdls_config_disable_policy)) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR]) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("attr mac addr failed"));
return -EINVAL;
}
memcpy(peer, nla_data(tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR]),
sizeof(peer));
hddLog(CDF_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR), MAC_ADDR_ARRAY(peer));
return wlan_hdd_tdls_extctrl_deconfig_peer(pAdapter, peer);
}
/**
* wlan_hdd_cfg80211_exttdls_disable() - disable ext tdls
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Return: Return the Success or Failure code.
*/
int wlan_hdd_cfg80211_exttdls_disable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
cds_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_exttdls_disable(wiphy, wdev, data, data_len);
cds_ssr_unprotect(__func__);
return ret;
}
/**
* wlan_hdd_tdls_add_station() - add or change a TDLS peer station
* @wiphy: wiphy
* @dev: net device
* @mac: MAC address of the TDLS peer
* @update: if non-0, modify the peer with StaParams; if 0, add new peer
* @StaParams: station parameters for the TDLS to change
*
* Return: 0 if success; negative errno otherwise
*/
int wlan_hdd_tdls_add_station(struct wiphy *wiphy,
struct net_device *dev, const uint8_t *mac,
bool update, tCsrStaParams *StaParams)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
CDF_STATUS status;
hddTdlsPeer_t *pTdlsPeer;
uint16_t numCurrTdlsPeers;
unsigned long rc;
long ret;
ENTER();
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret) {
hddLog(CDF_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
return ret;
}
if ((eTDLS_SUPPORT_NOT_ENABLED == pHddCtx->tdls_mode) ||
(eTDLS_SUPPORT_DISABLED == pHddCtx->tdls_mode)) {
hddLog(LOG1,
"%s: TDLS mode is disabled OR not enabled in FW."
MAC_ADDRESS_STR " Request declined.",
__func__, MAC_ADDR_ARRAY(mac));
return -ENOTSUPP;
}
pTdlsPeer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if (NULL == pTdlsPeer) {
hddLog(LOGE,
"%s: " MAC_ADDRESS_STR
" (update %d) not exist. return invalid", __func__,
MAC_ADDR_ARRAY(mac), update);
return -EINVAL;
}
/* in add station, we accept existing valid staId if there is */
if ((0 == update) &&
((pTdlsPeer->link_status >= eTDLS_LINK_CONNECTING) ||
(TDLS_STA_INDEX_VALID(pTdlsPeer->staId)))) {
hddLog(LOG1,
"%s: " MAC_ADDRESS_STR
" link_status %d. staId %d. add station ignored.",
__func__, MAC_ADDR_ARRAY(mac), pTdlsPeer->link_status,
pTdlsPeer->staId);
return 0;
}
/* in change station, we accept only when staId is valid */
if ((1 == update) &&
((pTdlsPeer->link_status > eTDLS_LINK_CONNECTING) ||
(!TDLS_STA_INDEX_VALID(pTdlsPeer->staId)))) {
hddLog(LOGE,
"%s: " MAC_ADDRESS_STR
" link status %d. staId %d. change station %s.",
__func__, MAC_ADDR_ARRAY(mac), pTdlsPeer->link_status,
pTdlsPeer->staId,
(TDLS_STA_INDEX_VALID(pTdlsPeer->staId)) ? "ignored" :
"declined");
return (TDLS_STA_INDEX_VALID(pTdlsPeer->staId)) ? 0 : -EPERM;
}
/* when others are on-going, we want to change link_status to idle */
if (NULL != wlan_hdd_tdls_is_progress(pHddCtx, mac, true)) {
hddLog(LOG1,
"%s: " MAC_ADDRESS_STR
" TDLS setup is ongoing. Request declined.",
__func__, MAC_ADDR_ARRAY(mac));
goto error;
}
/* first to check if we reached to maximum supported TDLS peer.
TODO: for now, return -EPERM looks working fine,
but need to check if any other errno fit into this category. */
numCurrTdlsPeers = wlan_hdd_tdls_connected_peers(pAdapter);
if (pHddCtx->max_num_tdls_sta <= numCurrTdlsPeers) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" TDLS Max peer already connected. Request declined."
" Num of peers (%d), Max allowed (%d).",
__func__, MAC_ADDR_ARRAY(mac), numCurrTdlsPeers,
pHddCtx->max_num_tdls_sta);
goto error;
} else {
hddTdlsPeer_t *pTdlsPeer;
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, mac, true);
if (pTdlsPeer && TDLS_IS_CONNECTED(pTdlsPeer)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" already connected. Request declined.",
__func__, MAC_ADDR_ARRAY(mac));
return -EPERM;
}
}
if (0 == update)
wlan_hdd_tdls_set_link_status(pAdapter,
mac,
eTDLS_LINK_CONNECTING,
eTDLS_LINK_SUCCESS);
/* debug code */
if (NULL != StaParams) {
hddLog(LOG1,
"%s: TDLS Peer Parameters.", __func__);
if (StaParams->htcap_present) {
hddLog(LOG1,
"ht_capa->cap_info: %0x",
StaParams->HTCap.capInfo);
hddLog(LOG1,
"ht_capa->extended_capabilities: %0x",
StaParams->HTCap.extendedHtCapInfo);
}
hddLog(LOG1,
"params->capability: %0x", StaParams->capability);
hddLog(LOG1,
"params->ext_capab_len: %0x",
StaParams->extn_capability[0]);
if (StaParams->vhtcap_present) {
hddLog(LOG1,
"rxMcsMap %x rxHighest %x txMcsMap %x txHighest %x",
StaParams->VHTCap.suppMcs.rxMcsMap,
StaParams->VHTCap.suppMcs.rxHighest,
StaParams->VHTCap.suppMcs.txMcsMap,
StaParams->VHTCap.suppMcs.txHighest);
}
{
int i = 0;
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
"Supported rates:");
for (i = 0; i < sizeof(StaParams->supported_rates); i++)
hddLog(LOG1, "[%d]: %x ", i,
StaParams->supported_rates[i]);
}
} /* end debug code */
else if ((1 == update) && (NULL == StaParams)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s : update is true, but staParams is NULL. Error!",
__func__);
return -EPERM;
}
INIT_COMPLETION(pAdapter->tdls_add_station_comp);
if (!update) {
status = sme_add_tdls_peer_sta(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, mac);
} else {
status = sme_change_tdls_peer_sta(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, mac,
StaParams);
}
rc = wait_for_completion_timeout(&pAdapter->tdls_add_station_comp,
msecs_to_jiffies
(WAIT_TIME_TDLS_ADD_STA));
if (!rc) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: timeout waiting for tdls add station indication",
__func__);
return -EPERM;
}
if (CDF_STATUS_SUCCESS != pAdapter->tdlsAddStaStatus) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: Add Station is unsuccessful", __func__);
return -EPERM;
}
return 0;
error:
wlan_hdd_tdls_set_link_status(pAdapter,
mac,
eTDLS_LINK_IDLE, eTDLS_LINK_UNSPECIFIED);
return -EPERM;
}
#if TDLS_MGMT_VERSION2
/**
* __wlan_hdd_cfg80211_tdls_mgmt() - handle management actions on a given peer
* @wiphy: wiphy
* @dev: net device
* @peer: MAC address of the TDLS peer
* @action_code: action code
* @dialog_token: dialog token
* @status_code: status code
* @peer_capability: peer capability
* @buf: additional IE to include
* @len: length of buf in bytes
*
* Return: 0 if success; negative errno otherwise
*/
static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev, u8 *peer,
u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capability,
const u8 *buf, size_t len)
#else
/**
* __wlan_hdd_cfg80211_tdls_mgmt() - handle management actions on a given peer
* @wiphy: wiphy
* @dev: net device
* @peer: MAC address of the TDLS peer
* @action_code: action code
* @dialog_token: dialog token
* @status_code: status code
* @buf: additional IE to include
* @len: length of buf in bytes
*
* Return: 0 if success; negative errno otherwise
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0))
static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev, const uint8_t *peer,
uint8_t action_code, uint8_t dialog_token,
uint16_t status_code, uint32_t peer_capability,
bool initiator, const uint8_t *buf,
size_t len)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev, const uint8_t *peer,
uint8_t action_code, uint8_t dialog_token,
uint16_t status_code, uint32_t peer_capability,
const uint8_t *buf, size_t len)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev, uint8_t *peer,
uint8_t action_code, uint8_t dialog_token,
uint16_t status_code, uint32_t peer_capability,
const uint8_t *buf, size_t len)
#else
static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev, uint8_t *peer,
uint8_t action_code, uint8_t dialog_token,
uint16_t status_code, const uint8_t *buf,
size_t len)
#endif
#endif
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
u8 peerMac[CDF_MAC_ADDR_SIZE];
CDF_STATUS status;
int max_sta_failed = 0;
int responder;
unsigned long rc;
uint16_t numCurrTdlsPeers;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0))
#if !(TDLS_MGMT_VERSION2)
u32 peer_capability;
peer_capability = 0;
#endif
#endif
if (CDF_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_TDLS_MGMT,
pAdapter->sessionId, action_code));
if (0 != wlan_hdd_validate_context(pHddCtx)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: HDD context is not valid", __func__);
return -EINVAL;
}
if (eTDLS_SUPPORT_NOT_ENABLED == pHddCtx->tdls_mode) {
hddLog(LOG1,
"%s: TDLS mode is disabled OR not enabled in FW."
MAC_ADDRESS_STR " action %d declined.",
__func__, MAC_ADDR_ARRAY(peer), action_code);
return -ENOTSUPP;
}
/* If any concurrency is detected */
if (((1 << CDF_STA_MODE) != pHddCtx->concurrency_mode) ||
(pHddCtx->no_of_active_sessions[CDF_STA_MODE] > 1)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO_HIGH,
"%s: Multiple STA OR Concurrency detected. Ignore TDLS MGMT frame. action_code=%d, concurrency_mode: 0x%x, active_sessions: %d",
__func__,
action_code,
pHddCtx->concurrency_mode,
pHddCtx->no_of_active_sessions[CDF_STA_MODE]);
return -EPERM;
}
/* other than teardown frame, mgmt frames are not sent if disabled */
if (SIR_MAC_TDLS_TEARDOWN != action_code) {
/* if tdls_mode is disabled to respond to peer's request */
if (eTDLS_SUPPORT_DISABLED == pHddCtx->tdls_mode) {
hddLog(LOG1,
"%s: " MAC_ADDRESS_STR
" TDLS mode is disabled. action %d declined.",
__func__, MAC_ADDR_ARRAY(peer), action_code);
return -ENOTSUPP;
}
}
if (WLAN_IS_TDLS_SETUP_ACTION(action_code)) {
if (NULL != wlan_hdd_tdls_is_progress(pHddCtx, peer, true)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" TDLS setup is ongoing. action %d declined.",
__func__, MAC_ADDR_ARRAY(peer), action_code);
return -EPERM;
}
}
if (SIR_MAC_TDLS_SETUP_REQ == action_code ||
SIR_MAC_TDLS_SETUP_RSP == action_code) {
numCurrTdlsPeers = wlan_hdd_tdls_connected_peers(pAdapter);
if (pHddCtx->max_num_tdls_sta <= numCurrTdlsPeers) {
/* supplicant still sends tdls_mgmt(SETUP_REQ) even after
we return error code at 'add_station()'. Hence we have this
check again in addtion to add_station().
Anyway, there is no hard to double-check. */
if (SIR_MAC_TDLS_SETUP_REQ == action_code) {
CDF_TRACE(CDF_MODULE_ID_HDD,
CDF_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" TDLS Max peer already connected. action (%d) declined. Num of peers (%d), Max allowed (%d).",
__func__, MAC_ADDR_ARRAY(peer),
action_code, numCurrTdlsPeers,
pHddCtx->max_num_tdls_sta);
return -EINVAL;
} else {
/* maximum reached. tweak to send error code to peer and return
error code to supplicant */
status_code = eSIR_MAC_UNSPEC_FAILURE_STATUS;
CDF_TRACE(CDF_MODULE_ID_HDD,
CDF_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" TDLS Max peer already connected, send response status (%d). Num of peers (%d), Max allowed (%d).",
__func__, MAC_ADDR_ARRAY(peer),
status_code, numCurrTdlsPeers,
pHddCtx->max_num_tdls_sta);
max_sta_failed = -EPERM;
/* fall through to send setup resp with failure status
code */
}
} else {
hddTdlsPeer_t *pTdlsPeer;
pTdlsPeer =
wlan_hdd_tdls_find_peer(pAdapter, peer, true);
if (pTdlsPeer && TDLS_IS_CONNECTED(pTdlsPeer)) {
CDF_TRACE(CDF_MODULE_ID_HDD,
CDF_TRACE_LEVEL_ERROR,
"%s:" MAC_ADDRESS_STR
" already connected. action %d declined.",
__func__, MAC_ADDR_ARRAY(peer),
action_code);
return -EPERM;
}
}
}
cdf_mem_copy(peerMac, peer, 6);
hddLog(LOG1,
"%s: " MAC_ADDRESS_STR
" action %d, dialog_token %d status %d, len = %zu",
"tdls_mgmt", MAC_ADDR_ARRAY(peer), action_code, dialog_token,
status_code, len);
/*Except teardown responder will not be used so just make 0 */
responder = 0;
if (SIR_MAC_TDLS_TEARDOWN == action_code) {
hddTdlsPeer_t *pTdlsPeer;
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peerMac, true);
if (pTdlsPeer && TDLS_IS_CONNECTED(pTdlsPeer))
responder = pTdlsPeer->is_responder;
else {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" peer doesn't exist or not connected %d dialog_token %d status %d, len = %zu",
__func__, MAC_ADDR_ARRAY(peer),
(NULL ==
pTdlsPeer) ? -1 : pTdlsPeer->link_status,
dialog_token, status_code, len);
return -EPERM;
}
}
/* For explicit trigger of DIS_REQ come out of BMPS for
successfully receiving DIS_RSP from peer. */
if ((SIR_MAC_TDLS_SETUP_RSP == action_code) ||
(SIR_MAC_TDLS_DIS_RSP == action_code) ||
(SIR_MAC_TDLS_DIS_REQ == action_code)) {
/* Fw will take care if PS offload is enabled. */
if (SIR_MAC_TDLS_DIS_REQ != action_code)
wlan_hdd_tdls_set_cap(pAdapter, peerMac,
eTDLS_CAP_SUPPORTED);
}
/* make sure doesn't call send_mgmt() while it is pending */
if (TDLS_CTX_MAGIC == pAdapter->mgmtTxCompletionStatus) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" action %d couldn't sent, as one is pending. return EBUSY",
__func__, MAC_ADDR_ARRAY(peer), action_code);
return -EBUSY;
}
pAdapter->mgmtTxCompletionStatus = TDLS_CTX_MAGIC;
INIT_COMPLETION(pAdapter->tdls_mgmt_comp);
status = sme_send_tdls_mgmt_frame(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, peerMac,
action_code, dialog_token, status_code,
peer_capability, (uint8_t *) buf, len,
!responder);
if (CDF_STATUS_SUCCESS != status) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: sme_send_tdls_mgmt_frame failed!", __func__);
pAdapter->mgmtTxCompletionStatus = false;
return -EINVAL;
}
rc = wait_for_completion_timeout(&pAdapter->tdls_mgmt_comp,
msecs_to_jiffies(WAIT_TIME_TDLS_MGMT));
if ((0 == rc) || (true != pAdapter->mgmtTxCompletionStatus)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: Mgmt Tx Completion timed out TxCompletion %u",
__func__, pAdapter->mgmtTxCompletionStatus);
if (pHddCtx->isLogpInProgress) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: LOGP in Progress. Ignore!!!", __func__);
return -EAGAIN;
}
if (pHddCtx->isUnloadInProgress) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: Unloading/Loading in Progress. Ignore!!!",
__func__);
return -EAGAIN;
}
pAdapter->mgmtTxCompletionStatus = false;
return -EINVAL;
}
if (max_sta_failed) {
return max_sta_failed;
}
if (SIR_MAC_TDLS_SETUP_RSP == action_code) {
return wlan_hdd_tdls_set_responder(pAdapter, peerMac, false);
} else if (SIR_MAC_TDLS_SETUP_CNF == action_code) {
return wlan_hdd_tdls_set_responder(pAdapter, peerMac, true);
}
return 0;
}
/**
* wlan_hdd_cfg80211_tdls_mgmt() - cfg80211 tdls mgmt handler function
* @wiphy: Pointer to wiphy structure.
* @dev: Pointer to net_device structure.
* @peer: peer address
* @action_code: action code
* @dialog_token: dialog token
* @status_code: status code
* @peer_capability: peer capability
* @buf: buffer
* @len: Length of @buf
*
* This is the cfg80211 tdls mgmt handler function which invokes
* the internal function @__wlan_hdd_cfg80211_tdls_mgmt with
* SSR protection.
*
* Return: 0 for success, error number on failure.
*/
#if TDLS_MGMT_VERSION2
int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev,
u8 *peer, u8 action_code,
u8 dialog_token,
u16 status_code, u32 peer_capability,
const u8 *buf, size_t len)
#else /* TDLS_MGMT_VERSION2 */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) || defined(WITH_BACKPORTS)
int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev,
const u8 *peer, u8 action_code,
u8 dialog_token, u16 status_code,
u32 peer_capability, bool initiator,
const u8 *buf, size_t len)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev,
const u8 *peer, u8 action_code,
u8 dialog_token, u16 status_code,
u32 peer_capability, const u8 *buf,
size_t len)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev,
u8 *peer, u8 action_code,
u8 dialog_token,
u16 status_code, u32 peer_capability,
const u8 *buf, size_t len)
#else
int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev,
u8 *peer, u8 action_code,
u8 dialog_token,
u16 status_code, const u8 *buf,
size_t len)
#endif
#endif
{
int ret;
cds_ssr_protect(__func__);
#if TDLS_MGMT_VERSION2
ret = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code,
dialog_token, status_code,
peer_capability, buf, len);
#else /* TDLS_MGMT_VERSION2 */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) || defined(WITH_BACKPORTS)
ret = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code,
dialog_token, status_code,
peer_capability, initiator,
buf, len);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
ret = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code,
dialog_token, status_code,
peer_capability, buf, len);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
ret = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code,
dialog_token, status_code,
peer_capability, buf, len);
#else
ret = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code,
dialog_token, status_code, buf, len);
#endif
#endif
cds_ssr_unprotect(__func__);
return ret;
}
/**
* wlan_hdd_tdls_extctrl_config_peer() - configure an externally controllable
* TDLS peer
* @pAdapter: HDD adapter
* @peer: MAC address of the TDLS peer
* @callback: Callback to set on the peer
* @chan: Channel
* @max_latency: Maximum latency
* @op_class: Operation class
* @min_bandwidth: Minimal bandwidth
*
* Return: 0 on success; negative otherwise
*/
int wlan_hdd_tdls_extctrl_config_peer(hdd_adapter_t *pAdapter,
const uint8_t *peer,
cfg80211_exttdls_callback callback,
u32 chan,
u32 max_latency,
u32 op_class, u32 min_bandwidth)
{
hddTdlsPeer_t *pTdlsPeer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
"%s : NL80211_TDLS_SETUP for " MAC_ADDRESS_STR,
__func__, MAC_ADDR_ARRAY(peer));
if ((false == pHddCtx->config->fTDLSExternalControl) ||
(false == pHddCtx->config->fEnableTDLSImplicitTrigger)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
"%s TDLS External control or Implicit Trigger not enabled ",
__func__);
return -ENOTSUPP;
}
pTdlsPeer = wlan_hdd_tdls_get_peer(pAdapter, peer);
if (pTdlsPeer == NULL) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: peer " MAC_ADDRESS_STR " does not exist",
__func__, MAC_ADDR_ARRAY(peer));
return -EINVAL;
}
if (0 != wlan_hdd_tdls_set_force_peer(pAdapter, peer, true)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s TDLS Add Force Peer Failed", __func__);
return -EINVAL;
}
if (0 != wlan_hdd_tdls_set_extctrl_param(pAdapter, peer,
chan, max_latency,
op_class, min_bandwidth)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s TDLS Set Peer's External Ctrl Parameter Failed",
__func__);
return -EINVAL;
}
if (0 != wlan_hdd_set_callback(pTdlsPeer, callback)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s TDLS set callback Failed", __func__);
return -EINVAL;
}
return 0;
}
/**
* wlan_hdd_tdls_extctrl_deconfig_peer() - de-configure an externally
* controllable TDLS peer
* @pAdapter: HDD adapter
* @peer: MAC address of the tdls peer
*
* Return: 0 if success; negative errno otherwisw
*/
int wlan_hdd_tdls_extctrl_deconfig_peer(hdd_adapter_t *pAdapter,
const uint8_t *peer)
{
hddTdlsPeer_t *pTdlsPeer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
"%s : NL80211_TDLS_TEARDOWN for " MAC_ADDRESS_STR,
__func__, MAC_ADDR_ARRAY(peer));
if ((false == pHddCtx->config->fTDLSExternalControl) ||
(false == pHddCtx->config->fEnableTDLSImplicitTrigger)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_INFO,
"%s TDLS External control or Implicit Trigger not enabled ",
__func__);
return -ENOTSUPP;
}
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, true);
if (NULL == pTdlsPeer) {
hddLog(CDF_TRACE_LEVEL_INFO, "%s: " MAC_ADDRESS_STR
"peer matching MAC_ADDRESS_STR not found",
__func__, MAC_ADDR_ARRAY(peer));
return -EINVAL;
} else {
wlan_hdd_tdls_indicate_teardown(pAdapter, pTdlsPeer,
eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
}
if (0 != wlan_hdd_tdls_set_force_peer(pAdapter, peer, false)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s Failed", __func__);
return -EINVAL;
}
if (0 != wlan_hdd_set_callback(pTdlsPeer, NULL)) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s TDLS set callback Failed", __func__);
return -EINVAL;
}
return 0;
}
/**
* __wlan_hdd_cfg80211_tdls_oper() - helper function to handle cfg80211 operation
* on an TDLS peer
* @wiphy: wiphy
* @dev: net device
* @peer: MAC address of the TDLS peer
* @oper: cfg80211 TDLS operation
*
* Return: 0 on success; negative errno otherwise
*/
static int __wlan_hdd_cfg80211_tdls_oper(struct wiphy *wiphy,
struct net_device *dev,
const uint8_t *peer,
enum nl80211_tdls_operation oper)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
int status;
tSmeTdlsPeerStateParams smeTdlsPeerStateParams;
CDF_STATUS cdf_ret_status = CDF_STATUS_E_FAILURE;
hddTdlsPeer_t *pTdlsPeer;
if (CDF_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
MTRACE(cdf_trace(CDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_TDLS_OPER,
pAdapter->sessionId, oper));
if (NULL == peer) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: Invalid arguments", __func__);
return -EINVAL;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: HDD context is not valid", __func__);
return status;
}
/* QCA 2.0 Discrete ANDs feature capability in HDD config with that
* received from target, so HDD config gives combined intersected result
*/
if (false == pHddCtx->config->fEnableTDLSSupport) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"TDLS Disabled in INI OR not enabled in FW. "
"Cannot process TDLS commands");
return -ENOTSUPP;
}
switch (oper) {
case NL80211_TDLS_ENABLE_LINK:
{
CDF_STATUS status;
unsigned long rc;
tCsrTdlsLinkEstablishParams tdlsLinkEstablishParams;
pTdlsPeer =
wlan_hdd_tdls_find_peer(pAdapter, peer, true);
if (NULL == pTdlsPeer) {
CDF_TRACE(CDF_MODULE_ID_HDD,
CDF_TRACE_LEVEL_ERROR,
"%s: peer matching " MAC_ADDRESS_STR
" not found, ignore NL80211_TDLS_ENABLE_LINK",
__func__, MAC_ADDR_ARRAY(peer));
return -EINVAL;
}
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: NL80211_TDLS_ENABLE_LINK for peer "
MAC_ADDRESS_STR " link_status: %d",
__func__, MAC_ADDR_ARRAY(peer),
pTdlsPeer->link_status);
if (!TDLS_STA_INDEX_VALID(pTdlsPeer->staId)) {
CDF_TRACE(CDF_MODULE_ID_HDD,
CDF_TRACE_LEVEL_ERROR,
"%s: invalid sta index %u for "
MAC_ADDRESS_STR
" TDLS_ENABLE_LINK failed", __func__,
pTdlsPeer->staId,
MAC_ADDR_ARRAY(peer));
return -EINVAL;
}
if (eTDLS_LINK_CONNECTED != pTdlsPeer->link_status) {
if (IS_ADVANCE_TDLS_ENABLE) {
if (0 !=
wlan_hdd_tdls_get_link_establish_params
(pAdapter, peer,
&tdlsLinkEstablishParams)) {
return -EINVAL;
}
INIT_COMPLETION(pAdapter->
tdls_link_establish_req_comp);
sme_send_tdls_link_establish_params
(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, peer,
&tdlsLinkEstablishParams);
/* Send TDLS peer UAPSD capabilities to the firmware and
* register with the TL on after the response for this operation
* is received .
*/
rc = wait_for_completion_timeout
(&pAdapter->
tdls_link_establish_req_comp,
msecs_to_jiffies
(WAIT_TIME_TDLS_LINK_ESTABLISH_REQ));
if (!rc) {
CDF_TRACE(CDF_MODULE_ID_HDD,
CDF_TRACE_LEVEL_ERROR,
"%s: Link Establish Request timed out",
__func__);
return -EINVAL;
}
}
wlan_hdd_tdls_set_peer_link_status(pTdlsPeer,
eTDLS_LINK_CONNECTED,
eTDLS_LINK_SUCCESS);
/* start TDLS client registration with TL */
status =
hdd_roam_register_tdlssta(pAdapter, peer,
pTdlsPeer->staId,
pTdlsPeer->
signature);
if (CDF_STATUS_SUCCESS == status) {
uint8_t i;
cdf_mem_zero(&smeTdlsPeerStateParams,
sizeof
(tSmeTdlsPeerStateParams));
smeTdlsPeerStateParams.vdevId =
pAdapter->sessionId;
cdf_mem_copy(&smeTdlsPeerStateParams.
peerMacAddr,
&pTdlsPeer->peerMac,
sizeof(tSirMacAddr));
smeTdlsPeerStateParams.peerState =
eSME_TDLS_PEER_STATE_CONNECTED;
smeTdlsPeerStateParams.peerCap.
isPeerResponder =
pTdlsPeer->is_responder;
smeTdlsPeerStateParams.peerCap.
peerUapsdQueue =
pTdlsPeer->uapsdQueues;
smeTdlsPeerStateParams.peerCap.
peerMaxSp = pTdlsPeer->maxSp;
smeTdlsPeerStateParams.peerCap.
peerBuffStaSupport =
pTdlsPeer->isBufSta;
smeTdlsPeerStateParams.peerCap.
peerOffChanSupport =
pTdlsPeer->isOffChannelSupported;
smeTdlsPeerStateParams.peerCap.
peerCurrOperClass = 0;
smeTdlsPeerStateParams.peerCap.
selfCurrOperClass = 0;
smeTdlsPeerStateParams.peerCap.
peerChanLen =
pTdlsPeer->supported_channels_len;
smeTdlsPeerStateParams.peerCap.
prefOffChanNum =
pTdlsPeer->pref_off_chan_num;
smeTdlsPeerStateParams.peerCap.
prefOffChanBandwidth =
pHddCtx->config->
fTDLSPrefOffChanBandwidth;
if (pTdlsPeer->
op_class_for_pref_off_chan_is_set) {
smeTdlsPeerStateParams.peerCap.
opClassForPrefOffChanIsSet =
pTdlsPeer->
op_class_for_pref_off_chan_is_set;
smeTdlsPeerStateParams.peerCap.
opClassForPrefOffChan =
pTdlsPeer->
op_class_for_pref_off_chan;
}
CDF_TRACE(CDF_MODULE_ID_HDD,
CDF_TRACE_LEVEL_INFO,
"%s: Peer " MAC_ADDRESS_STR
"vdevId: %d, peerState: %d, isPeerResponder: %d, uapsdQueues: 0x%x, maxSp: 0x%x, peerBuffStaSupport: %d, peerOffChanSupport: %d, peerCurrOperClass: %d, selfCurrOperClass: %d, peerChanLen: %d, peerOperClassLen: %d, prefOffChanNum: %d, prefOffChanBandwidth: %d, op_class_for_pref_off_chan_is_set: %d, op_class_for_pref_off_chan: %d",
__func__,
MAC_ADDR_ARRAY(peer),
smeTdlsPeerStateParams.vdevId,
smeTdlsPeerStateParams.
peerState,
smeTdlsPeerStateParams.
peerCap.isPeerResponder,
smeTdlsPeerStateParams.
peerCap.peerUapsdQueue,
smeTdlsPeerStateParams.
peerCap.peerMaxSp,
smeTdlsPeerStateParams.
peerCap.peerBuffStaSupport,
smeTdlsPeerStateParams.
peerCap.peerOffChanSupport,
smeTdlsPeerStateParams.
peerCap.peerCurrOperClass,
smeTdlsPeerStateParams.
peerCap.selfCurrOperClass,
smeTdlsPeerStateParams.
peerCap.peerChanLen,
smeTdlsPeerStateParams.
peerCap.peerOperClassLen,
smeTdlsPeerStateParams.
peerCap.prefOffChanNum,
smeTdlsPeerStateParams.
peerCap.prefOffChanBandwidth,
pTdlsPeer->
op_class_for_pref_off_chan_is_set,
pTdlsPeer->
op_class_for_pref_off_chan);
for (i = 0;
i <
pTdlsPeer->supported_channels_len;
i++) {
smeTdlsPeerStateParams.peerCap.
peerChan[i] =
pTdlsPeer->
supported_channels[i];
}
smeTdlsPeerStateParams.peerCap.
peerOperClassLen =
pTdlsPeer->
supported_oper_classes_len;
for (i = 0;
i <
pTdlsPeer->
supported_oper_classes_len; i++) {
smeTdlsPeerStateParams.peerCap.
peerOperClass[i] =
pTdlsPeer->
supported_oper_classes[i];
}
cdf_ret_status =
sme_update_tdls_peer_state(pHddCtx->
hHal,
&smeTdlsPeerStateParams);
if (CDF_STATUS_SUCCESS !=
cdf_ret_status) {
CDF_TRACE(CDF_MODULE_ID_HDD,
CDF_TRACE_LEVEL_ERROR,
"%s: sme_update_tdls_peer_state failed for "
MAC_ADDRESS_STR,
__func__,
MAC_ADDR_ARRAY(peer));
return -EPERM;
}
wlan_hdd_tdls_increment_peer_count
(pAdapter);
}
/* Update TL about the UAPSD masks , to route the packets to firmware */
if ((true ==
pHddCtx->config->fEnableTDLSBufferSta)
|| pHddCtx->config->fTDLSUapsdMask) {
int ac;
uint8_t ucAc[4] = { SME_AC_VO,
SME_AC_VI,
SME_AC_BK,
SME_AC_BE};
uint8_t tlTid[4] = { 7, 5, 2, 3 };
for (ac = 0; ac < 4; ac++) {
status = sme_enable_uapsd_for_ac(
(WLAN_HDD_GET_CTX(pAdapter))->pcds_context,
pTdlsPeer->staId, ucAc[ac],
tlTid[ac], tlTid[ac], 0, 0,
SME_BI_DIR, 1,
pAdapter->sessionId,
pHddCtx->config->DelayedTriggerFrmInt);
}
}
}
}
break;
case NL80211_TDLS_DISABLE_LINK:
{
pTdlsPeer =
wlan_hdd_tdls_find_peer(pAdapter, peer, true);
if (NULL == pTdlsPeer) {
CDF_TRACE(CDF_MODULE_ID_HDD,
CDF_TRACE_LEVEL_ERROR,
"%s: peer matching " MAC_ADDRESS_STR
" not found, ignore NL80211_TDLS_DISABLE_LINK",
__func__, MAC_ADDR_ARRAY(peer));
return -EINVAL;
}
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: NL80211_TDLS_DISABLE_LINK for peer "
MAC_ADDRESS_STR " link_status: %d",
__func__, MAC_ADDR_ARRAY(peer),
pTdlsPeer->link_status);
if (TDLS_STA_INDEX_VALID(pTdlsPeer->staId)) {
unsigned long rc;
INIT_COMPLETION(pAdapter->
tdls_del_station_comp);
sme_delete_tdls_peer_sta(WLAN_HDD_GET_HAL_CTX
(pAdapter),
pAdapter->sessionId,
peer);
rc = wait_for_completion_timeout(&pAdapter->
tdls_del_station_comp,
msecs_to_jiffies
(WAIT_TIME_TDLS_DEL_STA));
if (!rc) {
CDF_TRACE(CDF_MODULE_ID_HDD,
CDF_TRACE_LEVEL_ERROR,
"%s: Del station timed out",
__func__);
return -EPERM;
}
wlan_hdd_tdls_set_peer_link_status(pTdlsPeer,
eTDLS_LINK_IDLE,
(pTdlsPeer->link_status ==
eTDLS_LINK_TEARING) ?
eTDLS_LINK_UNSPECIFIED :
eTDLS_LINK_DROPPED_BY_REMOTE);
} else {
CDF_TRACE(CDF_MODULE_ID_HDD,
CDF_TRACE_LEVEL_ERROR,
"%s: TDLS Peer Station doesn't exist.",
__func__);
}
}
break;
case NL80211_TDLS_TEARDOWN:
{
status =
wlan_hdd_tdls_extctrl_deconfig_peer(pAdapter, peer);
if (0 != status) {
CDF_TRACE(CDF_MODULE_ID_HDD,
CDF_TRACE_LEVEL_ERROR,
"%s: Error in TDLS Teardown",
__func__);
return status;
}
}
break;
case NL80211_TDLS_SETUP:
{
status = wlan_hdd_tdls_extctrl_config_peer(pAdapter,
peer, NULL,
pHddCtx->config->fTDLSPrefOffChanNum, 0, 0, 0);
if (0 != status) {
CDF_TRACE(CDF_MODULE_ID_HDD,
CDF_TRACE_LEVEL_ERROR,
"%s: Error in TDLS Setup", __func__);
return status;
}
}
break;
case NL80211_TDLS_DISCOVERY_REQ:
/* We don't support in-driver setup/teardown/discovery */
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_WARN,
"%s: We don't support in-driver setup/teardown/discovery",
__func__);
return -ENOTSUPP;
default:
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: unsupported event", __func__);
return -ENOTSUPP;
}
return 0;
}
/**
* wlan_hdd_cfg80211_tdls_oper() - handle cfg80211 operation on an TDLS peer
* @wiphy: wiphy
* @dev: net device
* @peer: MAC address of the TDLS peer
* @oper: cfg80211 TDLS operation
*
* Return: 0 on success; negative errno otherwise
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
int wlan_hdd_cfg80211_tdls_oper(struct wiphy *wiphy,
struct net_device *dev,
const uint8_t *peer,
enum nl80211_tdls_operation oper)
#else
int wlan_hdd_cfg80211_tdls_oper(struct wiphy *wiphy,
struct net_device *dev,
uint8_t *peer,
enum nl80211_tdls_operation oper)
#endif
{
int ret;
cds_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_tdls_oper(wiphy, dev, peer, oper);
cds_ssr_unprotect(__func__);
return ret;
}
/**
* wlan_hdd_cfg80211_send_tdls_discover_req() - send out TDLS discovery for
* a TDLS peer
* @wiphy: wiphy
* @dev: net device
* @peer: MAC address of the peer
*
* Return: 0 if success; negative errno otherwise
*/
int wlan_hdd_cfg80211_send_tdls_discover_req(struct wiphy *wiphy,
struct net_device *dev, u8 *peer)
{
hddLog(CDF_TRACE_LEVEL_INFO,
"tdls send discover req: " MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(peer));
#if TDLS_MGMT_VERSION2
return wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer,
WLAN_TDLS_DISCOVERY_REQUEST, 1, 0, 0,
NULL, 0);
#else
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0))
return wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer,
WLAN_TDLS_DISCOVERY_REQUEST, 1, 0,
0, 0, NULL, 0);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
return wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer,
WLAN_TDLS_DISCOVERY_REQUEST, 1, 0,
0, NULL, 0);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
return wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer,
WLAN_TDLS_DISCOVERY_REQUEST, 1, 0,
0, NULL, 0);
#else
return wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer,
WLAN_TDLS_DISCOVERY_REQUEST, 1, 0,
NULL, 0);
#endif
#endif
}
#endif /* End of FEATURE_WLAN_TDLS */
/**
* wlan_hdd_tdls_find_first_connected_peer() - find the 1st connected tdls peer
* @adapter: Pointer to the HDD adapter
*
* This function searchs for the 1st connected TDLS peer
*
* Return: The first connected TDLS peer if found; NULL otherwise
*/
hddTdlsPeer_t *wlan_hdd_tdls_find_first_connected_peer(hdd_adapter_t *adapter)
{
int i;
struct list_head *head;
struct list_head *pos;
hddTdlsPeer_t *curr_peer = NULL;
tdlsCtx_t *hdd_tdls_ctx;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (0 != (wlan_hdd_validate_context(hdd_ctx))) {
hddLog(LOGE, FL("hdd_ctx is not valid"));
return NULL;
}
mutex_lock(&hdd_ctx->tdls_lock);
hdd_tdls_ctx = WLAN_HDD_GET_TDLS_CTX_PTR(adapter);
if (NULL == hdd_tdls_ctx) {
mutex_unlock(&hdd_ctx->tdls_lock);
return NULL;
}
for (i = 0; i < TDLS_PEER_LIST_SIZE; i++) {
head = &hdd_tdls_ctx->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)) {
mutex_unlock(&hdd_ctx->tdls_lock);
hddLog(LOG1,
FL(MAC_ADDRESS_STR
" eTDLS_LINK_CONNECTED"
),
MAC_ADDR_ARRAY(curr_peer->peerMac));
return curr_peer;
}
}
}
mutex_unlock(&hdd_ctx->tdls_lock);
return NULL;
}
/**
* hdd_set_tdls_offchannel() - set tdls off-channel number
* @adapter: Pointer to the HDD adapter
* @offchanmode: tdls off-channel number
*
* This function sets tdls off-channel number
*
* Return: 0 on success; negative errno otherwise
*/
int hdd_set_tdls_offchannel(hdd_context_t *hdd_ctx, int offchannel)
{
if ((true == hdd_ctx->config->fEnableTDLSOffChannel) &&
(eTDLS_SUPPORT_ENABLED == hdd_ctx->tdls_mode ||
eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY == hdd_ctx->tdls_mode)) {
if (offchannel < CFG_TDLS_PREFERRED_OFF_CHANNEL_NUM_MIN ||
offchannel > CFG_TDLS_PREFERRED_OFF_CHANNEL_NUM_MAX) {
hddLog(LOGE, FL("Invalid tdls off channel %u"),
offchannel);
return -EINVAL;
}
} else {
hddLog(LOGE,
FL("Either TDLS or TDLS Off-channel is not enabled"));
return -ENOTSUPP;
}
hddLog(LOG1,
FL("change tdls off channel from %d to %d"),
hdd_ctx->tdls_off_channel, offchannel);
hdd_ctx->tdls_off_channel = offchannel;
return 0;
}
/**
* hdd_set_tdls_secoffchanneloffset() - set secondary tdls off-channel offset
* @adapter: Pointer to the HDD adapter
* @offchanmode: tdls off-channel offset
*
* This function sets 2nd tdls off-channel offset
*
* Return: 0 on success; negative errno otherwise
*/
int hdd_set_tdls_secoffchanneloffset(hdd_context_t *hdd_ctx, int offchanoffset)
{
if ((true == hdd_ctx->config->fEnableTDLSOffChannel) &&
(eTDLS_SUPPORT_ENABLED == hdd_ctx->tdls_mode ||
eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY == hdd_ctx->tdls_mode)) {
hdd_ctx->tdls_channel_offset = 0;
switch (offchanoffset) {
case TDLS_SEC_OFFCHAN_OFFSET_0:
hdd_ctx->tdls_channel_offset = (1 << BW_20_OFFSET_BIT);
break;
case TDLS_SEC_OFFCHAN_OFFSET_40PLUS:
case TDLS_SEC_OFFCHAN_OFFSET_40MINUS:
hdd_ctx->tdls_channel_offset = (1 << BW_40_OFFSET_BIT);
break;
case TDLS_SEC_OFFCHAN_OFFSET_80:
hdd_ctx->tdls_channel_offset = (1 << BW_80_OFFSET_BIT);
break;
case TDLS_SEC_OFFCHAN_OFFSET_160:
hdd_ctx->tdls_channel_offset = (1 << BW_160_OFFSET_BIT);
break;
default:
hddLog(LOGE,
FL(
"Invalid tdls secondary off channel offset %d"
),
offchanoffset);
return -EINVAL;
} /* end switch */
} else {
hddLog(LOGE,
FL("Either TDLS or TDLS Off-channel is not enabled"));
return -ENOTSUPP;
}
hddLog(LOG1,
FL("change tdls secondary off channel offset to 0x%x"),
hdd_ctx->tdls_channel_offset);
return 0;
}
/**
* hdd_set_tdls_offchannelmode() - set tdls off-channel mode
* @adapter: Pointer to the HDD adapter
* @offchanmode: tdls off-channel mode
*
* This function sets tdls off-channel mode
*
* Return: 0 on success; negative errno otherwise
*/
int hdd_set_tdls_offchannelmode(hdd_adapter_t *adapter, int offchanmode)
{
hddTdlsPeer_t *conn_peer = NULL;
hdd_station_ctx_t *hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
sme_tdls_chan_switch_params chan_switch_params;
if (offchanmode < ENABLE_CHANSWITCH ||
offchanmode > DISABLE_CHANSWITCH) {
hddLog(LOGE,
FL("Invalid tdls off channel mode %d"),
offchanmode);
return -EINVAL;
}
if (eConnectionState_Associated != hdd_sta_ctx->conn_info.connState) {
hddLog(LOGE,
FL(
"tdls off channel mode req in not associated state %d"
),
offchanmode);
return -EPERM;
}
if ((true == hdd_ctx->config->fEnableTDLSOffChannel) &&
(eTDLS_SUPPORT_ENABLED == hdd_ctx->tdls_mode ||
eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY == hdd_ctx->tdls_mode)) {
conn_peer = wlan_hdd_tdls_find_first_connected_peer(adapter);
if (NULL == conn_peer) {
hddLog(LOGP,
FL("No TDLS Connected Peer"));
return -EPERM;
}
} else {
hddLog(LOGP,
FL("TDLS Connection not supported"));
return -ENOTSUPP;
}
hddLog(LOG1,
FL("TDLS Channel Switch in swmode=%d"),
offchanmode);
switch (offchanmode) {
case ENABLE_CHANSWITCH:
case DISABLE_CHANSWITCH:
hddLog(LOG1,
FL(
"change tdls off channel mode %d tdls_off_channel %d offchanoffset %d"
),
offchanmode, hdd_ctx->tdls_off_channel,
hdd_ctx->tdls_channel_offset);
if (hdd_ctx->tdls_off_channel && hdd_ctx->tdls_channel_offset) {
chan_switch_params.vdev_id = adapter->sessionId;
chan_switch_params.tdls_off_channel =
hdd_ctx->tdls_off_channel;
chan_switch_params.tdls_off_ch_bw_offset =
hdd_ctx->tdls_channel_offset;
chan_switch_params.tdls_off_ch_mode = offchanmode;
chan_switch_params.is_responder =
conn_peer->is_responder;
cdf_mem_copy(&chan_switch_params.peer_mac_addr,
&conn_peer->peerMac,
sizeof(tSirMacAddr));
hddLog(LOG1,
FL("Peer " MAC_ADDRESS_STR
" vdevId: %d, off channel: %d, offset: %d, mode: %d, is_responder: %d"
),
MAC_ADDR_ARRAY(chan_switch_params.
peer_mac_addr),
chan_switch_params.vdev_id,
chan_switch_params.tdls_off_channel,
chan_switch_params.tdls_off_ch_bw_offset,
chan_switch_params.tdls_off_ch_mode,
chan_switch_params.is_responder);
sme_send_tdls_chan_switch_req(
WLAN_HDD_GET_HAL_CTX(adapter),
&chan_switch_params);
} else {
hddLog(LOGE,
FL(
"TDLS off-channel parameters are not set yet!!!"
));
return -EINVAL;
}
break;
default:
hddLog(LOGE,
FL(
"Incorrect Parameters mode: %d tdls_off_channel: %d offchanoffset: %d"
),
offchanmode, hdd_ctx->tdls_off_channel,
hdd_ctx->tdls_channel_offset);
break;
} /* end switch */
return 0;
}
/**
* hdd_set_tdls_scan_type - set scan during active tdls session
* @hdd_ctx: ptr to hdd context.
* @val: scan type value: 0 or 1.
*
* Set scan type during tdls session. If set to 1, that means driver
* shall maintain tdls link and allow scan regardless if tdls peer is
* buffer sta capable or not and/or if device is sleep sta capable or
* not. If tdls peer is not buffer sta capable then during scan there
* will be loss of Rx packets and Tx would stop when device moves away
* from tdls channel. If set to 0, then driver shall teardown tdls link
* before initiating scan if peer is not buffer sta capable and device
* is not sleep sta capable. By default, scan type is set to 0.
*
* Return: success (0) or failure (errno value)
*/
int hdd_set_tdls_scan_type(hdd_context_t *hdd_ctx, int val)
{
if ((val != 0) && (val != 1)) {
hddLog(LOGE, FL("Incorrect value of tdls scan type: %d"),
val);
return -EINVAL;
} else {
hdd_ctx->config->enable_tdls_scan = val;
return 0;
}
}