blob: 89c5aef54a952a92ad9a1a2226465b5f2fc91827 [file] [log] [blame]
/*
* Copyright (c) 2017-2019 The Linux Foundation. All rights reserved.
*
* 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.
*/
/**
* DOC: wlan_tdls_main.c
*
* TDLS core function definitions
*/
#include "wlan_tdls_main.h"
#include "wlan_tdls_cmds_process.h"
#include "wlan_tdls_peer.h"
#include "wlan_tdls_ct.h"
#include "wlan_tdls_mgmt.h"
#include "wlan_tdls_tgt_api.h"
#include "wlan_policy_mgr_public_struct.h"
#include "wlan_policy_mgr_api.h"
#include "wlan_scan_ucfg_api.h"
/* Global tdls soc pvt object
* this is useful for some functions which does not receive either vdev or psoc
* objects.
*/
static struct tdls_soc_priv_obj *tdls_soc_global;
#ifdef WLAN_DEBUG
/**
* tdls_get_cmd_type_str() - parse cmd to string
* @cmd_type: tdls cmd type
*
* This function parse tdls cmd to string.
*
* Return: command string
*/
static char *tdls_get_cmd_type_str(enum tdls_command_type cmd_type)
{
switch (cmd_type) {
case TDLS_CMD_TX_ACTION:
return "TDLS_CMD_TX_ACTION";
case TDLS_CMD_ADD_STA:
return "TDLS_CMD_ADD_STA";
case TDLS_CMD_CHANGE_STA:
return "TDLS_CMD_CHANGE_STA";
case TDLS_CMD_ENABLE_LINK:
return "TDLS_CMD_ENABLE_LINK";
case TDLS_CMD_DISABLE_LINK:
return "TDLS_CMD_DISABLE_LINK";
case TDLS_CMD_CONFIG_FORCE_PEER:
return "TDLS_CMD_CONFIG_FORCE_PEER";
case TDLS_CMD_REMOVE_FORCE_PEER:
return "TDLS_CMD_REMOVE_FORCE_PEER";
case TDLS_CMD_STATS_UPDATE:
return "TDLS_CMD_STATS_UPDATE";
case TDLS_CMD_CONFIG_UPDATE:
return "TDLS_CMD_CONFIG_UPDATE";
case TDLS_CMD_SET_RESPONDER:
return "TDLS_CMD_SET_RESPONDER";
case TDLS_CMD_SCAN_DONE:
return "TDLS_CMD_SCAN_DONE";
case TDLS_NOTIFY_STA_CONNECTION:
return "TDLS_NOTIFY_STA_CONNECTION";
case TDLS_NOTIFY_STA_DISCONNECTION:
return "TDLS_NOTIFY_STA_DISCONNECTION";
case TDLS_CMD_SET_TDLS_MODE:
return "TDLS_CMD_SET_TDLS_MODE";
case TDLS_CMD_SESSION_DECREMENT:
return "TDLS_CMD_SESSION_DECREMENT";
case TDLS_CMD_SESSION_INCREMENT:
return "TDLS_CMD_SESSION_INCREMENT";
case TDLS_CMD_TEARDOWN_LINKS:
return "TDLS_CMD_TEARDOWN_LINKS";
case TDLS_NOTIFY_RESET_ADAPTERS:
return "TDLS_NOTIFY_RESET_ADAPTERS";
case TDLS_CMD_ANTENNA_SWITCH:
return "TDLS_CMD_ANTENNA_SWITCH";
default:
return "Invalid TDLS command";
}
}
/**
* tdls_get_event_type_str() - parase event to string
* @event_type: tdls event type
*
* This function parse tdls event to string.
*
* Return: event string
*/
static char *tdls_get_event_type_str(enum tdls_event_type event_type)
{
switch (event_type) {
case TDLS_SHOULD_DISCOVER:
return "TDLS_SHOULD_DISCOVER";
case TDLS_SHOULD_TEARDOWN:
return "TDLS_SHOULD_TEARDOWN";
case TDLS_PEER_DISCONNECTED:
return "TDLS_PEER_DISCONNECTED";
case TDLS_CONNECTION_TRACKER_NOTIFY:
return "TDLS_CONNECTION_TRACKER_NOTIFY";
default:
return "Invalid TDLS event";
}
}
#else
static char *tdls_get_cmd_type_str(enum tdls_command_type cmd_type)
{
return "";
}
static char *tdls_get_event_type_str(enum tdls_event_type event_type)
{
return "";
}
#endif
QDF_STATUS tdls_psoc_obj_create_notification(struct wlan_objmgr_psoc *psoc,
void *arg_list)
{
QDF_STATUS status;
struct tdls_soc_priv_obj *tdls_soc_obj;
tdls_soc_obj = qdf_mem_malloc(sizeof(*tdls_soc_obj));
if (!tdls_soc_obj) {
tdls_err("Failed to allocate memory for tdls object");
return QDF_STATUS_E_NOMEM;
}
tdls_soc_obj->soc = psoc;
status = wlan_objmgr_psoc_component_obj_attach(psoc,
WLAN_UMAC_COMP_TDLS,
(void *)tdls_soc_obj,
QDF_STATUS_SUCCESS);
if (QDF_IS_STATUS_ERROR(status)) {
tdls_err("Failed to attach psoc tdls component");
qdf_mem_free(tdls_soc_obj);
return status;
}
tdls_soc_global = tdls_soc_obj;
tdls_notice("TDLS obj attach to psoc successfully");
return status;
}
QDF_STATUS tdls_psoc_obj_destroy_notification(struct wlan_objmgr_psoc *psoc,
void *arg_list)
{
QDF_STATUS status;
struct tdls_soc_priv_obj *tdls_soc_obj;
tdls_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc,
WLAN_UMAC_COMP_TDLS);
if (!tdls_soc_obj) {
tdls_err("Failed to get tdls obj in psoc");
return QDF_STATUS_E_FAILURE;
}
status = wlan_objmgr_psoc_component_obj_detach(psoc,
WLAN_UMAC_COMP_TDLS,
tdls_soc_obj);
if (QDF_IS_STATUS_ERROR(status))
tdls_err("Failed to detach psoc tdls component");
qdf_mem_free(tdls_soc_obj);
return status;
}
static QDF_STATUS tdls_vdev_init(struct tdls_vdev_priv_obj *vdev_obj)
{
uint8_t i;
struct tdls_config_params *config;
struct tdls_user_config *user_config;
struct tdls_soc_priv_obj *soc_obj;
soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev);
if (!soc_obj) {
tdls_err("tdls soc obj NULL");
return QDF_STATUS_E_FAILURE;
}
config = &vdev_obj->threshold_config;
user_config = &soc_obj->tdls_configs;
config->tx_period_t = user_config->tdls_tx_states_period;
config->tx_packet_n = user_config->tdls_tx_pkt_threshold;
config->discovery_tries_n = user_config->tdls_max_discovery_attempt;
config->idle_timeout_t = user_config->tdls_idle_timeout;
config->idle_packet_n = user_config->tdls_idle_pkt_threshold;
config->rssi_trigger_threshold =
user_config->tdls_rssi_trigger_threshold;
config->rssi_teardown_threshold =
user_config->tdls_rssi_teardown_threshold;
config->rssi_delta = user_config->tdls_rssi_delta;
for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) {
qdf_list_create(&vdev_obj->peer_list[i],
WLAN_TDLS_PEER_SUB_LIST_SIZE);
}
qdf_mc_timer_init(&vdev_obj->peer_update_timer, QDF_TIMER_TYPE_SW,
tdls_ct_handler, vdev_obj->vdev);
qdf_mc_timer_init(&vdev_obj->peer_discovery_timer, QDF_TIMER_TYPE_SW,
tdls_discovery_timeout_peer_cb, vdev_obj);
return QDF_STATUS_SUCCESS;
}
static void tdls_vdev_deinit(struct tdls_vdev_priv_obj *vdev_obj)
{
qdf_mc_timer_stop(&vdev_obj->peer_update_timer);
qdf_mc_timer_stop(&vdev_obj->peer_discovery_timer);
qdf_mc_timer_destroy(&vdev_obj->peer_update_timer);
qdf_mc_timer_destroy(&vdev_obj->peer_discovery_timer);
tdls_peer_idle_timers_destroy(vdev_obj);
tdls_free_peer_list(vdev_obj);
}
QDF_STATUS tdls_vdev_obj_create_notification(struct wlan_objmgr_vdev *vdev,
void *arg)
{
QDF_STATUS status;
struct tdls_vdev_priv_obj *tdls_vdev_obj;
struct wlan_objmgr_pdev *pdev;
struct tdls_soc_priv_obj *tdls_soc_obj;
uint32_t tdls_feature_flags;
tdls_debug("tdls vdev mode %d", wlan_vdev_mlme_get_opmode(vdev));
if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE &&
wlan_vdev_mlme_get_opmode(vdev) != QDF_P2P_CLIENT_MODE)
return QDF_STATUS_SUCCESS;
tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(vdev);
if (!tdls_soc_obj) {
tdls_err("get soc by vdev failed");
return QDF_STATUS_E_NOMEM;
}
tdls_feature_flags = tdls_soc_obj->tdls_configs.tdls_feature_flags;
if (!TDLS_IS_ENABLED(tdls_feature_flags)) {
tdls_debug("disabled in ini");
return QDF_STATUS_E_NOSUPPORT;
}
if (tdls_soc_obj->tdls_osif_init_cb) {
status = tdls_soc_obj->tdls_osif_init_cb(vdev);
if (QDF_IS_STATUS_ERROR(status))
return status;
}
/* TODO: Add concurrency check */
tdls_vdev_obj = qdf_mem_malloc(sizeof(*tdls_vdev_obj));
if (!tdls_vdev_obj) {
tdls_err("Failed to allocate memory for tdls vdev object");
status = QDF_STATUS_E_NOMEM;
goto err;
}
status = wlan_objmgr_vdev_component_obj_attach(vdev,
WLAN_UMAC_COMP_TDLS,
(void *)tdls_vdev_obj,
QDF_STATUS_SUCCESS);
if (QDF_IS_STATUS_ERROR(status)) {
tdls_err("Failed to attach vdev tdls component");
goto err;
}
tdls_vdev_obj->vdev = vdev;
status = tdls_vdev_init(tdls_vdev_obj);
if (QDF_IS_STATUS_ERROR(status))
goto err;
pdev = wlan_vdev_get_pdev(vdev);
status = ucfg_scan_register_event_handler(pdev,
tdls_scan_complete_event_handler,
tdls_soc_obj);
if (QDF_STATUS_SUCCESS != status) {
tdls_err("scan event register failed ");
tdls_vdev_deinit(tdls_vdev_obj);
goto err;
}
tdls_debug("tdls object attach to vdev successfully");
return status;
err:
if (tdls_soc_obj->tdls_osif_deinit_cb)
tdls_soc_obj->tdls_osif_deinit_cb(vdev);
if (tdls_vdev_obj) {
qdf_mem_free(tdls_vdev_obj);
tdls_vdev_obj = NULL;
}
return status;
}
QDF_STATUS tdls_vdev_obj_destroy_notification(struct wlan_objmgr_vdev *vdev,
void *arg)
{
QDF_STATUS status;
void *tdls_vdev_obj;
struct tdls_soc_priv_obj *tdls_soc_obj;
uint32_t tdls_feature_flags;
tdls_debug("tdls vdev mode %d", wlan_vdev_mlme_get_opmode(vdev));
if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE &&
wlan_vdev_mlme_get_opmode(vdev) != QDF_P2P_CLIENT_MODE)
return QDF_STATUS_SUCCESS;
tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(vdev);
if (!tdls_soc_obj) {
tdls_err("get soc by vdev failed");
return QDF_STATUS_E_NOMEM;
}
tdls_feature_flags = tdls_soc_obj->tdls_configs.tdls_feature_flags;
if (!TDLS_IS_ENABLED(tdls_feature_flags)) {
tdls_debug("disabled in ini");
return QDF_STATUS_E_NOSUPPORT;
}
tdls_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev,
WLAN_UMAC_COMP_TDLS);
if (!tdls_vdev_obj) {
tdls_err("Failed to get tdls vdev object");
return QDF_STATUS_E_FAILURE;
}
status = wlan_objmgr_vdev_component_obj_detach(vdev,
WLAN_UMAC_COMP_TDLS,
tdls_vdev_obj);
if (QDF_IS_STATUS_ERROR(status))
tdls_err("Failed to detach vdev tdls component");
tdls_vdev_deinit(tdls_vdev_obj);
qdf_mem_free(tdls_vdev_obj);
if (tdls_soc_obj->tdls_osif_deinit_cb)
tdls_soc_obj->tdls_osif_deinit_cb(vdev);
return status;
}
/**
* __tdls_get_all_peers_from_list() - get all the tdls peers from the list
* @get_tdls_peers: get_tdls_peers object
*
* Return: int
*/
static int __tdls_get_all_peers_from_list(
struct tdls_get_all_peers *get_tdls_peers)
{
int i;
int len, init_len;
qdf_list_t *head;
qdf_list_node_t *p_node;
struct tdls_peer *curr_peer;
char *buf;
int buf_len;
struct tdls_vdev_priv_obj *tdls_vdev;
QDF_STATUS status;
tdls_notice("Enter ");
buf = get_tdls_peers->buf;
buf_len = get_tdls_peers->buf_len;
if (wlan_vdev_is_up(get_tdls_peers->vdev) != QDF_STATUS_SUCCESS) {
len = qdf_scnprintf(buf, buf_len,
"\nSTA is not associated\n");
return len;
}
tdls_vdev = wlan_vdev_get_tdls_vdev_obj(get_tdls_peers->vdev);
if (!tdls_vdev) {
len = qdf_scnprintf(buf, buf_len, "TDLS not enabled\n");
return len;
}
init_len = buf_len;
len = qdf_scnprintf(buf, buf_len,
"\n%-18s%-3s%-4s%-3s%-5s\n",
"MAC", "Id", "cap", "up", "RSSI");
buf += len;
buf_len -= len;
len = qdf_scnprintf(buf, buf_len,
"---------------------------------\n");
buf += len;
buf_len -= len;
for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) {
head = &tdls_vdev->peer_list[i];
status = qdf_list_peek_front(head, &p_node);
while (QDF_IS_STATUS_SUCCESS(status)) {
curr_peer = qdf_container_of(p_node,
struct tdls_peer, node);
if (buf_len < 32 + 1)
break;
len = qdf_scnprintf(buf, buf_len,
QDF_MAC_ADDR_STR "%4s%3s%5d\n",
QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes),
(curr_peer->tdls_support ==
TDLS_CAP_SUPPORTED) ? "Y" : "N",
TDLS_IS_LINK_CONNECTED(curr_peer) ? "Y" :
"N", curr_peer->rssi);
buf += len;
buf_len -= len;
status = qdf_list_peek_next(head, p_node, &p_node);
}
}
tdls_notice("Exit ");
return init_len - buf_len;
}
/**
* tdls_get_all_peers_from_list() - get all the tdls peers from the list
* @get_tdls_peers: get_tdls_peers object
*
* Return: None
*/
static void tdls_get_all_peers_from_list(
struct tdls_get_all_peers *get_tdls_peers)
{
int32_t len;
struct tdls_soc_priv_obj *tdls_soc_obj;
struct tdls_osif_indication indication;
if (!get_tdls_peers->vdev) {
qdf_mem_free(get_tdls_peers);
return;
}
len = __tdls_get_all_peers_from_list(get_tdls_peers);
indication.status = len;
indication.vdev = get_tdls_peers->vdev;
tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(get_tdls_peers->vdev);
if (tdls_soc_obj && tdls_soc_obj->tdls_event_cb)
tdls_soc_obj->tdls_event_cb(tdls_soc_obj->tdls_evt_cb_data,
TDLS_EVENT_USER_CMD, &indication);
qdf_mem_free(get_tdls_peers);
}
/**
* tdls_process_reset_all_peers() - Reset all tdls peers
* @delete_all_peers_ind: Delete all peers indication
*
* This function is called to reset all tdls peers and
* notify upper layers of teardown inidcation
*
* Return: QDF_STATUS
*/
static QDF_STATUS tdls_process_reset_all_peers(struct wlan_objmgr_vdev *vdev)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
uint8_t staidx;
struct tdls_peer *curr_peer = NULL;
struct tdls_vdev_priv_obj *tdls_vdev;
struct tdls_soc_priv_obj *tdls_soc;
uint8_t reset_session_id;
status = tdls_get_vdev_objects(vdev, &tdls_vdev, &tdls_soc);
if (QDF_STATUS_SUCCESS != status) {
tdls_err("tdls objects are NULL ");
return status;
}
reset_session_id = tdls_vdev->session_id;
for (staidx = 0; staidx < tdls_soc->max_num_tdls_sta;
staidx++) {
if (!tdls_soc->tdls_conn_info[staidx].valid_entry)
continue;
if (tdls_soc->tdls_conn_info[staidx].session_id !=
reset_session_id)
continue;
curr_peer =
tdls_find_all_peer(tdls_soc,
tdls_soc->tdls_conn_info[staidx].
peer_mac.bytes);
if (!curr_peer)
continue;
tdls_notice("indicate TDLS teardown %pM",
curr_peer->peer_mac.bytes);
/* Indicate teardown to supplicant */
tdls_indicate_teardown(tdls_vdev,
curr_peer,
TDLS_TEARDOWN_PEER_UNSPEC_REASON);
tdls_reset_peer(tdls_vdev, curr_peer->peer_mac.bytes);
if (tdls_soc->tdls_dereg_peer)
tdls_soc->tdls_dereg_peer(
tdls_soc->tdls_peer_context,
wlan_vdev_get_id(vdev),
&curr_peer->peer_mac);
tdls_decrement_peer_count(tdls_soc);
tdls_soc->tdls_conn_info[staidx].valid_entry = false;
tdls_soc->tdls_conn_info[staidx].session_id = 255;
tdls_soc->tdls_conn_info[staidx].index =
INVALID_TDLS_PEER_INDEX;
qdf_mem_zero(&tdls_soc->tdls_conn_info[staidx].peer_mac,
sizeof(struct qdf_mac_addr));
}
return status;
}
/**
* tdls_reset_all_peers() - Reset all tdls peers
* @delete_all_peers_ind: Delete all peers indication
*
* This function is called to reset all tdls peers and
* notify upper layers of teardown inidcation
*
* Return: QDF_STATUS
*/
static QDF_STATUS tdls_reset_all_peers(
struct tdls_delete_all_peers_params *delete_all_peers_ind)
{
QDF_STATUS status;
if (!delete_all_peers_ind || !delete_all_peers_ind->vdev) {
tdls_err("invalid param");
return QDF_STATUS_E_INVAL;
}
status = tdls_process_reset_all_peers(delete_all_peers_ind->vdev);
wlan_objmgr_vdev_release_ref(delete_all_peers_ind->vdev,
WLAN_TDLS_SB_ID);
qdf_mem_free(delete_all_peers_ind);
return status;
}
QDF_STATUS tdls_process_cmd(struct scheduler_msg *msg)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
if (!msg || !msg->bodyptr) {
tdls_err("msg: 0x%pK", msg);
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
tdls_debug("TDLS process command: %s(%d)",
tdls_get_cmd_type_str(msg->type), msg->type);
switch (msg->type) {
case TDLS_CMD_TX_ACTION:
tdls_process_mgmt_req(msg->bodyptr);
break;
case TDLS_CMD_ADD_STA:
tdls_process_add_peer(msg->bodyptr);
break;
case TDLS_CMD_CHANGE_STA:
tdls_process_update_peer(msg->bodyptr);
break;
case TDLS_CMD_ENABLE_LINK:
tdls_process_enable_link(msg->bodyptr);
break;
case TDLS_CMD_DISABLE_LINK:
tdls_process_del_peer(msg->bodyptr);
break;
case TDLS_CMD_CONFIG_FORCE_PEER:
tdls_process_setup_peer(msg->bodyptr);
break;
case TDLS_CMD_REMOVE_FORCE_PEER:
tdls_process_remove_force_peer(msg->bodyptr);
break;
case TDLS_CMD_STATS_UPDATE:
break;
case TDLS_CMD_CONFIG_UPDATE:
break;
case TDLS_CMD_SET_RESPONDER:
tdls_set_responder(msg->bodyptr);
break;
case TDLS_CMD_SCAN_DONE:
tdls_scan_done_callback(msg->bodyptr);
break;
case TDLS_NOTIFY_STA_CONNECTION:
tdls_notify_sta_connect(msg->bodyptr);
break;
case TDLS_NOTIFY_STA_DISCONNECTION:
tdls_notify_sta_disconnect(msg->bodyptr);
break;
case TDLS_CMD_SET_TDLS_MODE:
tdls_set_operation_mode(msg->bodyptr);
break;
case TDLS_CMD_SESSION_DECREMENT:
tdls_process_decrement_active_session(msg->bodyptr);
/*Fall through to take decision on connection tracker.*/
case TDLS_CMD_SESSION_INCREMENT:
tdls_process_policy_mgr_notification(msg->bodyptr);
break;
case TDLS_CMD_TEARDOWN_LINKS:
tdls_teardown_connections(msg->bodyptr);
break;
case TDLS_NOTIFY_RESET_ADAPTERS:
tdls_notify_reset_adapter(msg->bodyptr);
break;
case TDLS_CMD_ANTENNA_SWITCH:
tdls_process_antenna_switch(msg->bodyptr);
break;
case TDLS_CMD_GET_ALL_PEERS:
tdls_get_all_peers_from_list(msg->bodyptr);
break;
case TDLS_CMD_SET_OFFCHANNEL:
tdls_process_set_offchannel(msg->bodyptr);
break;
case TDLS_CMD_SET_OFFCHANMODE:
tdls_process_set_offchan_mode(msg->bodyptr);
break;
case TDLS_CMD_SET_SECOFFCHANOFFSET:
tdls_process_set_secoffchanneloffset(msg->bodyptr);
break;
case TDLS_DELETE_ALL_PEERS_INDICATION:
tdls_reset_all_peers(msg->bodyptr);
break;
default:
break;
}
return status;
}
QDF_STATUS tdls_process_evt(struct scheduler_msg *msg)
{
struct wlan_objmgr_vdev *vdev;
struct tdls_event_notify *notify;
struct tdls_event_info *event;
if (!msg || !msg->bodyptr) {
tdls_err("msg is not valid: %pK", msg);
return QDF_STATUS_E_NULL_VALUE;
}
notify = msg->bodyptr;
vdev = notify->vdev;
if (!vdev) {
tdls_err("NULL vdev object");
qdf_mem_free(notify);
return QDF_STATUS_E_NULL_VALUE;
}
event = &notify->event;
tdls_debug("evt type: %s(%d)",
tdls_get_event_type_str(event->message_type),
event->message_type);
switch (event->message_type) {
case TDLS_SHOULD_DISCOVER:
tdls_process_should_discover(vdev, event);
break;
case TDLS_SHOULD_TEARDOWN:
case TDLS_PEER_DISCONNECTED:
tdls_process_should_teardown(vdev, event);
break;
case TDLS_CONNECTION_TRACKER_NOTIFY:
tdls_process_connection_tracker_notify(vdev, event);
break;
default:
break;
}
wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID);
qdf_mem_free(notify);
return QDF_STATUS_SUCCESS;
}
void tdls_timer_restart(struct wlan_objmgr_vdev *vdev,
qdf_mc_timer_t *timer,
uint32_t expiration_time)
{
qdf_mc_timer_start(timer, expiration_time);
}
/**
* wlan_hdd_tdls_monitor_timers_stop() - stop all monitoring timers
* @hdd_tdls_ctx: TDLS context
*
* Return: none
*/
static void tdls_monitor_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev)
{
qdf_mc_timer_stop(&tdls_vdev->peer_discovery_timer);
}
/**
* tdls_peer_idle_timers_stop() - stop peer idle timers
* @tdls_vdev: TDLS vdev object
*
* Loop through the idle peer list and stop their timers
*
* Return: None
*/
static void tdls_peer_idle_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev)
{
int i;
qdf_list_t *head;
qdf_list_node_t *p_node;
struct tdls_peer *curr_peer;
QDF_STATUS status;
tdls_vdev->discovery_peer_cnt = 0;
for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) {
head = &tdls_vdev->peer_list[i];
status = qdf_list_peek_front(head, &p_node);
while (QDF_IS_STATUS_SUCCESS(status)) {
curr_peer = qdf_container_of(p_node, struct tdls_peer,
node);
if (curr_peer->is_peer_idle_timer_initialised)
qdf_mc_timer_stop(&curr_peer->peer_idle_timer);
status = qdf_list_peek_next(head, p_node, &p_node);
}
}
}
/**
* wlan_hdd_tdls_ct_timers_stop() - stop tdls connection tracker timers
* @tdls_vdev: TDLS vdev
*
* Return: None
*/
static void tdls_ct_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev)
{
qdf_mc_timer_stop(&tdls_vdev->peer_update_timer);
tdls_peer_idle_timers_stop(tdls_vdev);
}
/**
* wlan_hdd_tdls_timers_stop() - stop all the tdls timers running
* @tdls_vdev: TDLS vdev
*
* Return: none
*/
void tdls_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev)
{
tdls_monitor_timers_stop(tdls_vdev);
tdls_ct_timers_stop(tdls_vdev);
}
QDF_STATUS tdls_get_vdev_objects(struct wlan_objmgr_vdev *vdev,
struct tdls_vdev_priv_obj **tdls_vdev_obj,
struct tdls_soc_priv_obj **tdls_soc_obj)
{
enum QDF_OPMODE device_mode;
if (!vdev)
return QDF_STATUS_E_FAILURE;
*tdls_vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev);
if (NULL == (*tdls_vdev_obj))
return QDF_STATUS_E_FAILURE;
*tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(vdev);
if (NULL == (*tdls_soc_obj))
return QDF_STATUS_E_FAILURE;
device_mode = wlan_vdev_mlme_get_opmode(vdev);
if (device_mode != QDF_STA_MODE &&
device_mode != QDF_P2P_CLIENT_MODE)
return QDF_STATUS_E_FAILURE;
return QDF_STATUS_SUCCESS;
}
/**
* tdls_state_param_setting_dump() - print tdls state & parameters to send to fw
* @info: tdls setting to be sent to fw
*
* Return: void
*/
static void tdls_state_param_setting_dump(struct tdls_info *info)
{
if (!info)
return;
tdls_debug("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, teardown_notification_ms: %d, tdls_peer_kickout_threshold: %d, tdls_discovery_wake_timeout: %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,
info->teardown_notification_ms,
info->tdls_peer_kickout_threshold,
info->tdls_discovery_wake_timeout);
}
QDF_STATUS tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc,
struct tdls_channel_switch_params *param)
{
QDF_STATUS status;
/* wmi_unified_set_tdls_offchan_mode_cmd() will be called directly */
status = tgt_tdls_set_offchan_mode(psoc, param);
if (!QDF_IS_STATUS_SUCCESS(status))
status = QDF_STATUS_E_FAILURE;
return status;
}
/**
* tdls_update_fw_tdls_state() - update tdls status info
* @tdls_soc_obj: TDLS soc object
* @tdls_info_to_fw: TDLS state info to update in f/w.
*
* send message to WMA to set TDLS state in f/w
*
* Return: QDF_STATUS.
*/
static
QDF_STATUS tdls_update_fw_tdls_state(struct tdls_soc_priv_obj *tdls_soc_obj,
struct tdls_info *tdls_info_to_fw)
{
QDF_STATUS status;
/* wmi_unified_update_fw_tdls_state_cmd() will be called directly */
status = tgt_tdls_set_fw_state(tdls_soc_obj->soc, tdls_info_to_fw);
if (!QDF_IS_STATUS_SUCCESS(status))
status = QDF_STATUS_E_FAILURE;
return status;
}
bool tdls_check_is_tdls_allowed(struct wlan_objmgr_vdev *vdev)
{
struct tdls_vdev_priv_obj *tdls_vdev_obj;
struct tdls_soc_priv_obj *tdls_soc_obj;
bool state = false;
if (QDF_STATUS_SUCCESS != wlan_objmgr_vdev_try_get_ref(vdev,
WLAN_TDLS_NB_ID))
return state;
if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj,
&tdls_soc_obj)) {
wlan_objmgr_vdev_release_ref(vdev,
WLAN_TDLS_NB_ID);
return state;
}
if (policy_mgr_get_connection_count(tdls_soc_obj->soc) == 1)
state = true;
else
tdls_warn("Concurrent sessions are running or TDLS disabled");
/* If any concurrency is detected */
/* print session information */
wlan_objmgr_vdev_release_ref(vdev,
WLAN_TDLS_NB_ID);
return state;
}
/**
* cds_set_tdls_ct_mode() - Set the tdls connection tracker mode
* @hdd_ctx: hdd context
*
* This routine is called to set the tdls connection tracker operation status
*
* Return: NONE
*/
void tdls_set_ct_mode(struct wlan_objmgr_psoc *psoc)
{
bool state = false;
struct tdls_soc_priv_obj *tdls_soc_obj;
tdls_soc_obj = wlan_psoc_get_tdls_soc_obj(psoc);
if (!tdls_soc_obj)
return;
/* If any concurrency is detected, skip tdls pkt tracker */
if (policy_mgr_get_connection_count(psoc) > 1) {
state = false;
goto set_state;
}
if (TDLS_SUPPORT_DISABLED == tdls_soc_obj->tdls_current_mode ||
TDLS_SUPPORT_SUSPENDED == tdls_soc_obj->tdls_current_mode ||
!TDLS_IS_IMPLICIT_TRIG_ENABLED(
tdls_soc_obj->tdls_configs.tdls_feature_flags)) {
state = false;
goto set_state;
} else if (policy_mgr_mode_specific_connection_count(psoc,
PM_STA_MODE,
NULL) == 1) {
state = true;
} else if (policy_mgr_mode_specific_connection_count(psoc,
PM_P2P_CLIENT_MODE,
NULL) == 1){
state = true;
} else {
state = false;
goto set_state;
}
/* In case of TDLS external control, peer should be added
* by the user space to start connection tracker.
*/
if (TDLS_IS_EXTERNAL_CONTROL_ENABLED(
tdls_soc_obj->tdls_configs.tdls_feature_flags)) {
if (tdls_soc_obj->tdls_external_peer_count)
state = true;
else
state = false;
}
set_state:
tdls_soc_obj->enable_tdls_connection_tracker = state;
tdls_debug("enable_tdls_connection_tracker %d",
tdls_soc_obj->enable_tdls_connection_tracker);
}
QDF_STATUS
tdls_process_policy_mgr_notification(struct wlan_objmgr_psoc *psoc)
{
struct tdls_vdev_priv_obj *tdls_priv_vdev;
struct wlan_objmgr_vdev *tdls_obj_vdev;
struct tdls_soc_priv_obj *tdls_priv_soc;
if (!psoc) {
tdls_err("psoc: %pK", psoc);
return QDF_STATUS_E_NULL_VALUE;
}
tdls_obj_vdev = tdls_get_vdev(psoc, WLAN_TDLS_NB_ID);
tdls_debug("enter ");
tdls_set_ct_mode(psoc);
if (tdls_obj_vdev && (tdls_get_vdev_objects(tdls_obj_vdev,
&tdls_priv_vdev, &tdls_priv_soc) == QDF_STATUS_SUCCESS) &&
tdls_priv_soc->enable_tdls_connection_tracker)
tdls_implicit_enable(tdls_priv_vdev);
if (tdls_obj_vdev)
wlan_objmgr_vdev_release_ref(tdls_obj_vdev, WLAN_TDLS_NB_ID);
tdls_debug("exit ");
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
tdls_process_decrement_active_session(struct wlan_objmgr_psoc *psoc)
{
struct tdls_soc_priv_obj *tdls_priv_soc;
struct tdls_vdev_priv_obj *tdls_priv_vdev;
struct wlan_objmgr_vdev *tdls_obj_vdev;
uint8_t vdev_id;
tdls_debug("Enter");
if (!psoc)
return QDF_STATUS_E_NULL_VALUE;
if(!policy_mgr_is_hw_dbs_2x2_capable(psoc) &&
!policy_mgr_is_hw_dbs_required_for_band(
psoc, HW_MODE_MAC_BAND_2G) &&
policy_mgr_is_current_hwmode_dbs(psoc)) {
tdls_err("Current HW mode is 1*1 DBS. Wait for Opportunistic timer to expire to enable TDLS in FW");
return QDF_STATUS_SUCCESS;
}
tdls_obj_vdev = tdls_get_vdev(psoc, WLAN_TDLS_NB_ID);
if (tdls_obj_vdev) {
tdls_debug("Enable TDLS in FW and host as only one active sta/p2p_cli interface is present");
vdev_id = wlan_vdev_get_id(tdls_obj_vdev);
if (tdls_get_vdev_objects(tdls_obj_vdev, &tdls_priv_vdev,
&tdls_priv_soc) == QDF_STATUS_SUCCESS)
tdls_send_update_to_fw(tdls_priv_vdev, tdls_priv_soc,
false, false, true, vdev_id);
wlan_objmgr_vdev_release_ref(tdls_obj_vdev, WLAN_TDLS_NB_ID);
}
return QDF_STATUS_SUCCESS;
}
/**
* tdls_get_vdev() - Get tdls specific vdev object manager
* @psoc: wlan psoc object manager
* @dbg_id: debug id
*
* If TDLS possible, return the corresponding vdev
* to enable TDLS in the system.
*
* Return: vdev manager pointer or NULL.
*/
struct wlan_objmgr_vdev *tdls_get_vdev(struct wlan_objmgr_psoc *psoc,
wlan_objmgr_ref_dbgid dbg_id)
{
uint32_t vdev_id;
if (policy_mgr_get_connection_count(psoc) > 1)
return NULL;
vdev_id = policy_mgr_mode_specific_vdev_id(psoc, PM_STA_MODE);
if (WLAN_INVALID_VDEV_ID != vdev_id)
return wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
vdev_id,
dbg_id);
vdev_id = policy_mgr_mode_specific_vdev_id(psoc, PM_P2P_CLIENT_MODE);
if (WLAN_INVALID_VDEV_ID != vdev_id)
return wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
vdev_id,
dbg_id);
return NULL;
}
static QDF_STATUS tdls_post_msg_flush_cb(struct scheduler_msg *msg)
{
void *ptr = msg->bodyptr;
struct wlan_objmgr_vdev *vdev = NULL;
switch (msg->type) {
case TDLS_NOTIFY_STA_DISCONNECTION:
vdev = ((struct tdls_sta_notify_params *)ptr)->vdev;
wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
qdf_mem_free(ptr);
break;
case TDLS_DELETE_ALL_PEERS_INDICATION:
vdev = ((struct tdls_delete_all_peers_params *)ptr)->vdev;
wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID);
qdf_mem_free(ptr);
break;
case TDLS_CMD_SCAN_DONE:
case TDLS_CMD_SESSION_INCREMENT:
case TDLS_CMD_SESSION_DECREMENT:
break;
}
return QDF_STATUS_SUCCESS;
}
/**
* tdls_process_session_update() - update session count information
* @psoc: soc object
* @notification: TDLS os if notification
*
* update the session information in connection tracker
*
* Return: None
*/
static void tdls_process_session_update(struct wlan_objmgr_psoc *psoc,
enum tdls_command_type cmd_type)
{
struct scheduler_msg msg = {0};
QDF_STATUS status;
msg.bodyptr = psoc;
msg.callback = tdls_process_cmd;
msg.flush_callback = tdls_post_msg_flush_cb;
msg.type = (uint16_t)cmd_type;
status = scheduler_post_message(QDF_MODULE_ID_TDLS,
QDF_MODULE_ID_TDLS,
QDF_MODULE_ID_OS_IF, &msg);
if (QDF_IS_STATUS_ERROR(status))
tdls_alert("message post failed ");
}
void tdls_notify_increment_session(struct wlan_objmgr_psoc *psoc)
{
tdls_process_session_update(psoc, TDLS_CMD_SESSION_INCREMENT);
}
void tdls_notify_decrement_session(struct wlan_objmgr_psoc *psoc)
{
tdls_process_session_update(psoc, TDLS_CMD_SESSION_DECREMENT);
}
void tdls_send_update_to_fw(struct tdls_vdev_priv_obj *tdls_vdev_obj,
struct tdls_soc_priv_obj *tdls_soc_obj,
bool tdls_prohibited,
bool tdls_chan_swit_prohibited,
bool sta_connect_event,
uint8_t session_id)
{
struct tdls_info *tdls_info_to_fw;
struct tdls_config_params *threshold_params;
uint32_t tdls_feature_flags;
QDF_STATUS status;
uint8_t set_state_cnt;
tdls_debug("Enter");
tdls_feature_flags = tdls_soc_obj->tdls_configs.tdls_feature_flags;
if (!TDLS_IS_ENABLED(tdls_feature_flags)) {
tdls_debug("TDLS mode is not enabled");
return;
}
set_state_cnt = tdls_soc_obj->set_state_info.set_state_cnt;
if ((set_state_cnt == 0 && !sta_connect_event) ||
(set_state_cnt && sta_connect_event)) {
tdls_debug("FW TDLS state is already in requested state");
return;
}
/* If AP or caller indicated TDLS Prohibited then disable tdls mode */
if (sta_connect_event) {
if (tdls_prohibited) {
tdls_soc_obj->tdls_current_mode =
TDLS_SUPPORT_DISABLED;
} else {
tdls_debug("TDLS feature flags from ini %d ",
tdls_feature_flags);
if (!TDLS_IS_IMPLICIT_TRIG_ENABLED(tdls_feature_flags))
tdls_soc_obj->tdls_current_mode =
TDLS_SUPPORT_EXP_TRIG_ONLY;
else if (TDLS_IS_EXTERNAL_CONTROL_ENABLED(
tdls_feature_flags))
tdls_soc_obj->tdls_current_mode =
TDLS_SUPPORT_EXT_CONTROL;
else
tdls_soc_obj->tdls_current_mode =
TDLS_SUPPORT_IMP_MODE;
}
} else {
tdls_soc_obj->tdls_current_mode =
TDLS_SUPPORT_DISABLED;
}
tdls_info_to_fw = qdf_mem_malloc(sizeof(struct tdls_info));
if (!tdls_info_to_fw) {
tdls_err("memory allocation failed for tdlsParams");
QDF_ASSERT(0);
return;
}
threshold_params = &tdls_vdev_obj->threshold_config;
tdls_info_to_fw->notification_interval_ms =
threshold_params->tx_period_t;
tdls_info_to_fw->tx_discovery_threshold =
threshold_params->tx_packet_n;
tdls_info_to_fw->tx_teardown_threshold =
threshold_params->idle_packet_n;
tdls_info_to_fw->rssi_teardown_threshold =
threshold_params->rssi_teardown_threshold;
tdls_info_to_fw->rssi_delta = threshold_params->rssi_delta;
tdls_info_to_fw->vdev_id = session_id;
/* record the session id in vdev context */
tdls_vdev_obj->session_id = session_id;
tdls_info_to_fw->tdls_state = tdls_soc_obj->tdls_current_mode;
tdls_info_to_fw->tdls_options = 0;
/* Do not enable TDLS offchannel, if AP prohibited TDLS
* channel switch
*/
if (TDLS_IS_OFF_CHANNEL_ENABLED(tdls_feature_flags) &&
(!tdls_chan_swit_prohibited))
tdls_info_to_fw->tdls_options = ENA_TDLS_OFFCHAN;
if (TDLS_IS_BUFFER_STA_ENABLED(tdls_feature_flags))
tdls_info_to_fw->tdls_options |= ENA_TDLS_BUFFER_STA;
if (TDLS_IS_SLEEP_STA_ENABLED(tdls_feature_flags))
tdls_info_to_fw->tdls_options |= ENA_TDLS_SLEEP_STA;
tdls_info_to_fw->peer_traffic_ind_window =
tdls_soc_obj->tdls_configs.tdls_uapsd_pti_window;
tdls_info_to_fw->peer_traffic_response_timeout =
tdls_soc_obj->tdls_configs.tdls_uapsd_ptr_timeout;
tdls_info_to_fw->puapsd_mask =
tdls_soc_obj->tdls_configs.tdls_uapsd_mask;
tdls_info_to_fw->puapsd_inactivity_time =
tdls_soc_obj->tdls_configs.tdls_uapsd_inactivity_time;
tdls_info_to_fw->puapsd_rx_frame_threshold =
tdls_soc_obj->tdls_configs.tdls_rx_pkt_threshold;
tdls_info_to_fw->teardown_notification_ms =
tdls_soc_obj->tdls_configs.tdls_idle_timeout;
tdls_info_to_fw->tdls_peer_kickout_threshold =
tdls_soc_obj->tdls_configs.tdls_peer_kickout_threshold;
tdls_info_to_fw->tdls_discovery_wake_timeout =
tdls_soc_obj->tdls_configs.tdls_discovery_wake_timeout;
tdls_state_param_setting_dump(tdls_info_to_fw);
status = tdls_update_fw_tdls_state(tdls_soc_obj, tdls_info_to_fw);
if (QDF_STATUS_SUCCESS != status)
goto done;
if (sta_connect_event) {
tdls_soc_obj->set_state_info.set_state_cnt++;
tdls_soc_obj->set_state_info.vdev_id = session_id;
} else {
tdls_soc_obj->set_state_info.set_state_cnt--;
}
tdls_debug("TDLS Set state cnt %d",
tdls_soc_obj->set_state_info.set_state_cnt);
done:
qdf_mem_free(tdls_info_to_fw);
return;
}
static QDF_STATUS
tdls_process_sta_connect(struct tdls_sta_notify_params *notify)
{
struct tdls_vdev_priv_obj *tdls_vdev_obj;
struct tdls_soc_priv_obj *tdls_soc_obj;
if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(notify->vdev,
&tdls_vdev_obj,
&tdls_soc_obj))
return QDF_STATUS_E_INVAL;
tdls_debug("Check and update TDLS state");
if (policy_mgr_get_connection_count(tdls_soc_obj->soc) > 1) {
tdls_debug("Concurrent sessions exist, TDLS can't be enabled");
return QDF_STATUS_SUCCESS;
}
/* Association event */
if (!tdls_soc_obj->tdls_disable_in_progress) {
tdls_send_update_to_fw(tdls_vdev_obj,
tdls_soc_obj,
notify->tdls_prohibited,
notify->tdls_chan_swit_prohibited,
true,
notify->session_id);
}
/* check and set the connection tracker */
tdls_set_ct_mode(tdls_soc_obj->soc);
if (tdls_soc_obj->enable_tdls_connection_tracker)
tdls_implicit_enable(tdls_vdev_obj);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS tdls_notify_sta_connect(struct tdls_sta_notify_params *notify)
{
QDF_STATUS status;
if (!notify || !notify->vdev) {
tdls_err("invalid param");
return QDF_STATUS_E_INVAL;
}
status = tdls_process_sta_connect(notify);
wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_NB_ID);
qdf_mem_free(notify);
return status;
}
static QDF_STATUS
tdls_process_sta_disconnect(struct tdls_sta_notify_params *notify)
{
struct tdls_vdev_priv_obj *tdls_vdev_obj;
struct tdls_vdev_priv_obj *curr_tdls_vdev;
struct tdls_soc_priv_obj *tdls_soc_obj;
struct tdls_soc_priv_obj *curr_tdls_soc;
struct wlan_objmgr_vdev *temp_vdev = NULL;
uint8_t vdev_id;
QDF_STATUS status = QDF_STATUS_SUCCESS;
if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(notify->vdev,
&tdls_vdev_obj,
&tdls_soc_obj))
return QDF_STATUS_E_INVAL;
/* if the disconnect comes from user space, we have to delete all the
* tdls peers before sending the set state cmd.
*/
if (notify->user_disconnect)
return tdls_delete_all_tdls_peers(notify->vdev, tdls_soc_obj);
tdls_debug("Check and update TDLS state");
curr_tdls_vdev = tdls_vdev_obj;
curr_tdls_soc = tdls_soc_obj;
/* Disassociation event */
if (!tdls_soc_obj->tdls_disable_in_progress)
tdls_send_update_to_fw(tdls_vdev_obj, tdls_soc_obj, false,
false, false, notify->session_id);
/* If concurrency is not marked, then we have to
* check, whether TDLS could be enabled in the
* system after this disassoc event.
*/
if (!notify->lfr_roam && !tdls_soc_obj->tdls_disable_in_progress) {
temp_vdev = tdls_get_vdev(tdls_soc_obj->soc, WLAN_TDLS_NB_ID);
if (temp_vdev) {
vdev_id = wlan_vdev_get_id(temp_vdev);
status = tdls_get_vdev_objects(temp_vdev,
&tdls_vdev_obj,
&tdls_soc_obj);
if (QDF_STATUS_SUCCESS == status) {
tdls_send_update_to_fw(tdls_vdev_obj,
tdls_soc_obj,
false,
false,
true,
vdev_id);
curr_tdls_vdev = tdls_vdev_obj;
curr_tdls_soc = tdls_soc_obj;
}
}
}
/* Check and set the connection tracker and implicit timers */
tdls_set_ct_mode(curr_tdls_soc->soc);
if (curr_tdls_soc->enable_tdls_connection_tracker)
tdls_implicit_enable(curr_tdls_vdev);
else
tdls_implicit_disable(curr_tdls_vdev);
/* release the vdev ref , if temp vdev was acquired */
if (temp_vdev)
wlan_objmgr_vdev_release_ref(temp_vdev,
WLAN_TDLS_NB_ID);
return status;
}
QDF_STATUS tdls_notify_sta_disconnect(struct tdls_sta_notify_params *notify)
{
QDF_STATUS status;
if (!notify || !notify->vdev) {
tdls_err("invalid param");
return QDF_STATUS_E_INVAL;
}
status = tdls_process_sta_disconnect(notify);
wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_NB_ID);
qdf_mem_free(notify);
return status;
}
static void tdls_process_reset_adapter(struct wlan_objmgr_vdev *vdev)
{
struct tdls_vdev_priv_obj *tdls_vdev;
tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev);
if (!tdls_vdev)
return;
tdls_timers_stop(tdls_vdev);
}
void tdls_notify_reset_adapter(struct wlan_objmgr_vdev *vdev)
{
if (!vdev) {
QDF_ASSERT(0);
return;
}
if (QDF_STATUS_SUCCESS != wlan_objmgr_vdev_try_get_ref(vdev,
WLAN_TDLS_NB_ID))
return;
tdls_process_reset_adapter(vdev);
wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
}
QDF_STATUS tdls_peers_deleted_notification(struct wlan_objmgr_psoc *psoc,
uint8_t vdev_id)
{
struct scheduler_msg msg = {0, };
struct tdls_sta_notify_params *notify;
QDF_STATUS status;
struct wlan_objmgr_vdev *vdev;
notify = qdf_mem_malloc(sizeof(*notify));
if (!notify) {
tdls_err("memory allocation failed !!!");
return QDF_STATUS_E_NULL_VALUE;
}
vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
vdev_id,
WLAN_TDLS_NB_ID);
if (!vdev) {
tdls_err("vdev not exist for the vdev id %d",
vdev_id);
qdf_mem_free(notify);
return QDF_STATUS_E_INVAL;
}
notify->lfr_roam = true;
notify->tdls_chan_swit_prohibited = false;
notify->tdls_prohibited = false;
notify->session_id = vdev_id;
notify->vdev = vdev;
notify->user_disconnect = false;
msg.bodyptr = notify;
msg.callback = tdls_process_cmd;
msg.flush_callback = tdls_post_msg_flush_cb;
msg.type = TDLS_NOTIFY_STA_DISCONNECTION;
status = scheduler_post_message(QDF_MODULE_ID_TDLS,
QDF_MODULE_ID_TDLS,
QDF_MODULE_ID_OS_IF, &msg);
if (QDF_IS_STATUS_ERROR(status)) {
wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
qdf_mem_free(notify);
tdls_alert("message post failed ");
return QDF_STATUS_E_FAILURE;
}
return QDF_STATUS_SUCCESS;
}
QDF_STATUS tdls_delete_all_peers_indication(struct wlan_objmgr_psoc *psoc,
uint8_t vdev_id)
{
struct scheduler_msg msg = {0, };
struct tdls_delete_all_peers_params *indication;
QDF_STATUS status;
struct wlan_objmgr_vdev *vdev;
indication = qdf_mem_malloc(sizeof(*indication));
if (!indication) {
tdls_err("memory allocation failed !!!");
return QDF_STATUS_E_NULL_VALUE;
}
vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
vdev_id,
WLAN_TDLS_SB_ID);
if (!vdev) {
tdls_err("vdev not exist for the session id %d",
vdev_id);
qdf_mem_free(indication);
return QDF_STATUS_E_INVAL;
}
indication->vdev = vdev;
msg.bodyptr = indication;
msg.callback = tdls_process_cmd;
msg.type = TDLS_DELETE_ALL_PEERS_INDICATION;
msg.flush_callback = tdls_post_msg_flush_cb;
status = scheduler_post_message(QDF_MODULE_ID_TDLS,
QDF_MODULE_ID_TDLS,
QDF_MODULE_ID_OS_IF, &msg);
if (QDF_IS_STATUS_ERROR(status)) {
wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID);
qdf_mem_free(indication);
tdls_alert("message post failed ");
return QDF_STATUS_E_FAILURE;
}
return QDF_STATUS_SUCCESS;
}
/**
* tdls_set_mode_in_vdev() - set TDLS mode
* @tdls_vdev: tdls vdev object
* @tdls_soc: tdls soc object
* @tdls_mode: TDLS mode
* @source: TDLS disable source enum values
*
* Return: Void
*/
static void tdls_set_mode_in_vdev(struct tdls_vdev_priv_obj *tdls_vdev,
struct tdls_soc_priv_obj *tdls_soc,
enum tdls_feature_mode tdls_mode,
enum tdls_disable_sources source)
{
if (!tdls_vdev)
return;
tdls_debug("enter tdls mode is %d", tdls_mode);
if (TDLS_SUPPORT_IMP_MODE == tdls_mode ||
TDLS_SUPPORT_EXT_CONTROL == tdls_mode) {
clear_bit((unsigned long)source,
&tdls_soc->tdls_source_bitmap);
/*
* Check if any TDLS source bit is set and if
* bitmap is not zero then we should not
* enable TDLS
*/
if (tdls_soc->tdls_source_bitmap) {
tdls_notice("Don't enable TDLS, source bitmap: %lu",
tdls_soc->tdls_source_bitmap);
return;
}
tdls_implicit_enable(tdls_vdev);
/* tdls implicit mode is enabled, so
* enable the connection tracker
*/
tdls_soc->enable_tdls_connection_tracker =
true;
} else if (TDLS_SUPPORT_DISABLED == tdls_mode) {
set_bit((unsigned long)source,
&tdls_soc->tdls_source_bitmap);
tdls_implicit_disable(tdls_vdev);
/* If tdls implicit mode is disabled, then
* stop the connection tracker.
*/
tdls_soc->enable_tdls_connection_tracker =
false;
} else if (TDLS_SUPPORT_EXP_TRIG_ONLY ==
tdls_mode) {
clear_bit((unsigned long)source,
&tdls_soc->tdls_source_bitmap);
tdls_implicit_disable(tdls_vdev);
/* If tdls implicit mode is disabled, then
* stop the connection tracker.
*/
tdls_soc->enable_tdls_connection_tracker =
false;
/*
* Check if any TDLS source bit is set and if
* bitmap is not zero then we should not
* enable TDLS
*/
if (tdls_soc->tdls_source_bitmap)
return;
}
tdls_debug("exit ");
}
/**
* tdls_set_current_mode() - set TDLS mode
* @tdls_soc: tdls soc object
* @tdls_mode: TDLS mode
* @update_last: indicate to record the last tdls mode
* @source: TDLS disable source enum values
*
* Return: Void
*/
static void tdls_set_current_mode(struct tdls_soc_priv_obj *tdls_soc,
enum tdls_feature_mode tdls_mode,
bool update_last,
enum tdls_disable_sources source)
{
struct wlan_objmgr_vdev *vdev;
struct tdls_vdev_priv_obj *tdls_vdev;
if (!tdls_soc)
return;
tdls_debug("mode %d", (int)tdls_mode);
if (update_last)
tdls_soc->tdls_last_mode = tdls_mode;
if (tdls_soc->tdls_current_mode == tdls_mode) {
tdls_debug("already in mode %d", tdls_mode);
switch (tdls_mode) {
/* TDLS is already enabled hence clear source mask, return */
case TDLS_SUPPORT_IMP_MODE:
case TDLS_SUPPORT_EXP_TRIG_ONLY:
case TDLS_SUPPORT_EXT_CONTROL:
clear_bit((unsigned long)source,
&tdls_soc->tdls_source_bitmap);
tdls_debug("clear source mask:%d", source);
return;
/* TDLS is already disabled hence set source mask, return */
case TDLS_SUPPORT_DISABLED:
set_bit((unsigned long)source,
&tdls_soc->tdls_source_bitmap);
tdls_debug("set source mask:%d", source);
return;
default:
return;
}
}
/* get sta vdev */
vdev = wlan_objmgr_get_vdev_by_opmode_from_psoc(tdls_soc->soc,
QDF_STA_MODE,
WLAN_TDLS_NB_ID);
if (vdev) {
tdls_debug("set mode in tdls vdev ");
tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev);
if (!tdls_vdev)
tdls_set_mode_in_vdev(tdls_vdev, tdls_soc,
tdls_mode, source);
wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
}
/* get p2p client vdev */
vdev = wlan_objmgr_get_vdev_by_opmode_from_psoc(tdls_soc->soc,
QDF_P2P_CLIENT_MODE,
WLAN_TDLS_NB_ID);
if (vdev) {
tdls_debug("set mode in tdls vdev ");
tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev);
if (!tdls_vdev)
tdls_set_mode_in_vdev(tdls_vdev, tdls_soc,
tdls_mode, source);
wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
}
if (!update_last)
tdls_soc->tdls_last_mode = tdls_soc->tdls_current_mode;
tdls_soc->tdls_current_mode = tdls_mode;
}
QDF_STATUS tdls_set_operation_mode(struct tdls_set_mode_params *tdls_set_mode)
{
struct tdls_soc_priv_obj *tdls_soc;
struct tdls_vdev_priv_obj *tdls_vdev;
QDF_STATUS status;
if (!tdls_set_mode || !tdls_set_mode->vdev)
return QDF_STATUS_E_INVAL;
status = tdls_get_vdev_objects(tdls_set_mode->vdev,
&tdls_vdev, &tdls_soc);
if (QDF_IS_STATUS_ERROR(status))
goto release_mode_ref;
tdls_set_current_mode(tdls_soc,
tdls_set_mode->tdls_mode,
tdls_set_mode->update_last,
tdls_set_mode->source);
release_mode_ref:
wlan_objmgr_vdev_release_ref(tdls_set_mode->vdev, WLAN_TDLS_NB_ID);
qdf_mem_free(tdls_set_mode);
return status;
}
/**
* wlan_hdd_tdls_scan_done_callback() - callback for tdls scan done event
* @pAdapter: HDD adapter
*
* Return: Void
*/
void tdls_scan_done_callback(struct tdls_soc_priv_obj *tdls_soc)
{
if (!tdls_soc)
return;
if (TDLS_SUPPORT_DISABLED == tdls_soc->tdls_current_mode) {
tdls_debug("TDLS mode is disabled OR not enabled");
return;
}
/* if tdls was enabled before scan, re-enable tdls mode */
if (TDLS_SUPPORT_IMP_MODE == tdls_soc->tdls_last_mode ||
TDLS_SUPPORT_EXT_CONTROL == tdls_soc->tdls_last_mode ||
TDLS_SUPPORT_EXP_TRIG_ONLY == tdls_soc->tdls_last_mode) {
tdls_debug("revert tdls mode %d",
tdls_soc->tdls_last_mode);
tdls_set_current_mode(tdls_soc, tdls_soc->tdls_last_mode,
false,
TDLS_SET_MODE_SOURCE_SCAN);
}
}
/**
* tdls_post_scan_done_msg() - post scan done message to tdls cmd queue
* @tdls_soc: tdls soc object
*
* Return: QDF_STATUS_SUCCESS or QDF_STATUS_E_NULL_VALUE
*/
static QDF_STATUS tdls_post_scan_done_msg(struct tdls_soc_priv_obj *tdls_soc)
{
struct scheduler_msg msg = {0, };
if (!tdls_soc) {
tdls_err("tdls_soc: %pK ", tdls_soc);
return QDF_STATUS_E_NULL_VALUE;
}
msg.bodyptr = tdls_soc;
msg.callback = tdls_process_cmd;
msg.flush_callback = tdls_post_msg_flush_cb;
msg.type = TDLS_CMD_SCAN_DONE;
return scheduler_post_message(QDF_MODULE_ID_TDLS,
QDF_MODULE_ID_TDLS,
QDF_MODULE_ID_OS_IF, &msg);
}
void tdls_scan_complete_event_handler(struct wlan_objmgr_vdev *vdev,
struct scan_event *event,
void *arg)
{
enum QDF_OPMODE device_mode;
struct tdls_soc_priv_obj *tdls_soc;
if (!vdev || !event || !arg)
return;
if (SCAN_EVENT_TYPE_COMPLETED != event->type)
return;
device_mode = wlan_vdev_mlme_get_opmode(vdev);
if (device_mode != QDF_STA_MODE &&
device_mode != QDF_P2P_CLIENT_MODE)
return;
tdls_soc = (struct tdls_soc_priv_obj *) arg;
tdls_post_scan_done_msg(tdls_soc);
}
QDF_STATUS tdls_scan_callback(struct tdls_soc_priv_obj *tdls_soc)
{
struct tdls_vdev_priv_obj *tdls_vdev;
struct wlan_objmgr_vdev *vdev;
QDF_STATUS status = QDF_STATUS_SUCCESS;
/* if tdls is not enabled, then continue scan */
if (TDLS_SUPPORT_DISABLED == tdls_soc->tdls_current_mode)
return status;
/* Get the vdev based on vdev operating mode*/
vdev = tdls_get_vdev(tdls_soc->soc, WLAN_TDLS_NB_ID);
if (!vdev)
return status;
tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev);
if (!tdls_vdev)
goto return_success;
if (tdls_is_progress(tdls_vdev, NULL, 0)) {
if (tdls_soc->scan_reject_count++ >= TDLS_SCAN_REJECT_MAX) {
tdls_notice("Allow this scan req. as already max no of scan's are rejected");
tdls_soc->scan_reject_count = 0;
status = QDF_STATUS_SUCCESS;
} else {
tdls_warn("tdls in progress. scan rejected %d",
tdls_soc->scan_reject_count);
status = QDF_STATUS_E_BUSY;
}
}
return_success:
wlan_objmgr_vdev_release_ref(vdev,
WLAN_TDLS_NB_ID);
return status;
}
void tdls_scan_serialization_comp_info_cb(struct wlan_objmgr_vdev *vdev,
union wlan_serialization_rules_info *comp_info)
{
struct tdls_soc_priv_obj *tdls_soc;
QDF_STATUS status;
if (!comp_info)
return;
tdls_soc = tdls_soc_global;
comp_info->scan_info.is_tdls_in_progress = false;
status = tdls_scan_callback(tdls_soc);
if (QDF_STATUS_E_BUSY == status)
comp_info->scan_info.is_tdls_in_progress = true;
}
uint8_t tdls_get_opclass_from_bandwidth(struct tdls_soc_priv_obj *soc_obj,
uint8_t channel, uint8_t bw_offset)
{
uint8_t opclass;
if (bw_offset & (1 << BW_80_OFFSET_BIT)) {
opclass = tdls_find_opclass(soc_obj->soc,
channel, BW80);
} else if (bw_offset & (1 << BW_40_OFFSET_BIT)) {
opclass = tdls_find_opclass(soc_obj->soc,
channel, BW40_LOW_PRIMARY);
if (!opclass) {
opclass = tdls_find_opclass(soc_obj->soc,
channel, BW40_HIGH_PRIMARY);
}
} else if (bw_offset & (1 << BW_20_OFFSET_BIT)) {
opclass = tdls_find_opclass(soc_obj->soc,
channel, BW20);
} else {
opclass = tdls_find_opclass(soc_obj->soc,
channel, BWALL);
}
return opclass;
}