qcacmn: Add TDLS off channel changes for TDLS UMAC
Add TDLS offchannel and related osif changes in TDLS
UMAC component.
Change-Id: Ic562709eb8a6361322860c4f7333633a7894af99
CRs-Fixed: 2034220
diff --git a/os_if/linux/tdls/inc/wlan_cfg80211_tdls.h b/os_if/linux/tdls/inc/wlan_cfg80211_tdls.h
index c4efaad..812c8bc 100644
--- a/os_if/linux/tdls/inc/wlan_cfg80211_tdls.h
+++ b/os_if/linux/tdls/inc/wlan_cfg80211_tdls.h
@@ -33,6 +33,7 @@
#include <qdf_types.h>
#include <wlan_tdls_ucfg_api.h>
+#ifdef CONVERGED_TDLS_ENABLE
#define TDLS_VDEV_MAGIC 0x54444c53 /* "TDLS" */
@@ -51,6 +52,7 @@
struct completion tdls_del_peer_comp;
struct completion tdls_mgmt_comp;
struct completion tdls_link_establish_req_comp;
+ struct completion tdls_teardown_comp;
QDF_STATUS tdls_add_peer_status;
uint32_t mgmt_tx_completion_status;
};
@@ -191,4 +193,76 @@
void wlan_cfg80211_tdls_rx_callback(void *user_data,
struct tdls_rx_mgmt_frame *rx_frame);
+/**
+ * hdd_notify_sta_connect() - notify sta connect to TDLS
+ * @session_id: pointer to soc object
+ * @tdls_chan_swit_prohibited: indicates channel switch capability
+ * @tdls_prohibited: indicates tdls allowed or not
+ * @vdev: vdev object manager
+ *
+ * Notify sta connect event to TDLS component
+ *
+ * Return: None
+ */
+void
+hdd_notify_sta_connect(uint8_t session_id,
+ bool tdls_chan_swit_prohibited,
+ bool tdls_prohibited,
+ struct wlan_objmgr_vdev *vdev);
+
+/**
+ * hdd_notify_sta_disconnect() - notify sta disconnect to TDLS
+ * @session_id: pointer to soc object
+ * @lfr_roam: indicate, whether disconnect due to lfr roam
+ * @vdev: vdev object manager
+ *
+ * Notify sta disconnect event to TDLS component
+ *
+ * Return: None
+ */
+void hdd_notify_sta_disconnect(uint8_t session_id,
+ bool lfr_roam,
+ struct wlan_objmgr_vdev *vdev);
+
+/**
+ * hdd_notify_teardown_tdls_links() - notify TDLS to teardown links
+ * @vdev: vdev object manager
+ *
+ * Notify tdls to teardown all the links, due to certain events
+ * in the system
+ *
+ * Return: None
+ */
+void hdd_notify_teardown_tdls_links(struct wlan_objmgr_vdev *vdev);
+
+#else
+static inline void
+hdd_notify_sta_connect(uint8_t session_id,
+ bool tdls_chan_swit_prohibited,
+ bool tdls_prohibited,
+ struct wlan_objmgr_vdev *vdev)
+{
+}
+
+static inline
+void hdd_notify_sta_disconnect(uint8_t session_id,
+ bool lfr_roam,
+ struct wlan_objmgr_vdev *vdev)
+{
+
+}
+
+static inline
+int wlan_cfg80211_tdls_configure_mode(struct wlan_objmgr_vdev *vdev,
+ uint32_t trigger_mode)
+{
+ return 0;
+}
+
+static inline
+void hdd_notify_teardown_tdls_links(struct wlan_objmgr_vdev *vdev)
+{
+
+}
+#endif
#endif
diff --git a/os_if/linux/tdls/src/wlan_cfg80211_tdls.c b/os_if/linux/tdls/src/wlan_cfg80211_tdls.c
index 714b5f5..238d27b 100644
--- a/os_if/linux/tdls/src/wlan_cfg80211_tdls.c
+++ b/os_if/linux/tdls/src/wlan_cfg80211_tdls.c
@@ -53,6 +53,7 @@
init_completion(&tdls_priv->tdls_del_peer_comp);
init_completion(&tdls_priv->tdls_mgmt_comp);
init_completion(&tdls_priv->tdls_link_establish_req_comp);
+ init_completion(&tdls_priv->tdls_teardown_comp);
osif_priv->osif_tdls = tdls_priv;
@@ -67,6 +68,72 @@
osif_priv->osif_tdls = NULL;
}
+void hdd_notify_teardown_tdls_links(struct wlan_objmgr_vdev *vdev)
+{
+ struct vdev_osif_priv *osif_priv;
+ struct osif_tdls_vdev *tdls_priv;
+ QDF_STATUS status;
+ unsigned long rc;
+
+ if (!vdev)
+ return;
+
+ wlan_vdev_obj_lock(vdev);
+ osif_priv = wlan_vdev_get_ospriv(vdev);
+ wlan_vdev_obj_unlock(vdev);
+
+ tdls_priv = osif_priv->osif_tdls;
+
+ reinit_completion(&tdls_priv->tdls_teardown_comp);
+ status = ucfg_tdls_teardown_links(vdev);
+ if (QDF_IS_STATUS_ERROR(status)) {
+ cfg80211_err("ucfg_tdls_teardown_links failed err %d", status);
+ return;
+ }
+
+ cfg80211_info("Wait for tdls teardown completion. Timeout %u ms",
+ WAIT_TIME_FOR_TDLS_TEARDOWN_LINKS);
+
+ rc = wait_for_completion_timeout(
+ &tdls_priv->tdls_teardown_comp,
+ msecs_to_jiffies(WAIT_TIME_FOR_TDLS_TEARDOWN_LINKS));
+
+ if (0 == rc) {
+ cfg80211_err(" Teardown Completion timed out rc: %ld", rc);
+ return;
+ }
+
+ cfg80211_info("TDLS teardown completion status %ld ", rc);
+}
+
+void
+hdd_notify_sta_connect(uint8_t session_id,
+ bool tdls_chan_swit_prohibited,
+ bool tdls_prohibited,
+ struct wlan_objmgr_vdev *vdev)
+{
+ struct tdls_sta_notify_params notify_info;
+
+ notify_info.session_id = session_id;
+ notify_info.vdev = vdev;
+ notify_info.tdls_chan_swit_prohibited = tdls_chan_swit_prohibited;
+ notify_info.tdls_prohibited = tdls_prohibited;
+ ucfg_tdls_notify_sta_connect(¬ify_info);
+
+}
+
+void hdd_notify_sta_disconnect(uint8_t session_id,
+ bool lfr_roam,
+ struct wlan_objmgr_vdev *vdev)
+{
+ struct tdls_sta_notify_params notify_info;
+
+ notify_info.session_id = session_id;
+ notify_info.lfr_roam = lfr_roam;
+ notify_info.vdev = vdev;
+ ucfg_tdls_notify_sta_disconnect(¬ify_info);
+
+}
int wlan_cfg80211_tdls_add_peer(struct wlan_objmgr_pdev *pdev,
struct net_device *dev, const uint8_t *mac)
{
@@ -385,14 +452,14 @@
switch (trigger_mode) {
case WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT:
- tdls_mode = TDLS_SUPPORT_IMP_MODE;
- break;
+ tdls_mode = TDLS_SUPPORT_EXP_TRIG_ONLY;
+ return 0;
case WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL:
tdls_mode = TDLS_SUPPORT_EXT_CONTROL;
break;
case WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT:
tdls_mode = TDLS_SUPPORT_IMP_MODE;
- break;
+ return 0;
default:
cfg80211_err("Invalid TDLS trigger mode");
return -EINVAL;
@@ -752,6 +819,9 @@
case TDLS_EVENT_SETUP_REQ:
wlan_cfg80211_tdls_indicate_setup(ind);
break;
+ case TDLS_EVENT_TEARDOWN_LINKS_DONE:
+ complete(&tdls_priv->tdls_teardown_comp);
+ break;
default:
break;
}
diff --git a/umac/tdls/core/src/wlan_tdls_cmds_process.c b/umac/tdls/core/src/wlan_tdls_cmds_process.c
index 26594e3..35cd7f3 100644
--- a/umac/tdls/core/src/wlan_tdls_cmds_process.c
+++ b/umac/tdls/core/src/wlan_tdls_cmds_process.c
@@ -43,7 +43,7 @@
*
* Return: None.
*/
-static void tdls_decrement_peer_count(struct tdls_soc_priv_obj *soc_obj)
+void tdls_decrement_peer_count(struct tdls_soc_priv_obj *soc_obj)
{
if (soc_obj->connected_peer_count)
soc_obj->connected_peer_count--;
diff --git a/umac/tdls/core/src/wlan_tdls_cmds_process.h b/umac/tdls/core/src/wlan_tdls_cmds_process.h
index ec2f40e..5e2c035 100644
--- a/umac/tdls/core/src/wlan_tdls_cmds_process.h
+++ b/umac/tdls/core/src/wlan_tdls_cmds_process.h
@@ -365,4 +365,14 @@
*/
int tdls_set_responder(struct tdls_set_responder_req *set_req);
+/**
+ * tdls_decrement_peer_count() - decrement connected TDLS peer counter
+ * @soc_obj: TDLS soc object
+ *
+ * Used in scheduler thread context, no lock needed.
+ *
+ * Return: None.
+ */
+void tdls_decrement_peer_count(struct tdls_soc_priv_obj *soc_obj);
+
#endif
diff --git a/umac/tdls/core/src/wlan_tdls_ct.c b/umac/tdls/core/src/wlan_tdls_ct.c
index cb48dd2..3b5b554 100644
--- a/umac/tdls/core/src/wlan_tdls_ct.c
+++ b/umac/tdls/core/src/wlan_tdls_ct.c
@@ -25,6 +25,7 @@
#include "wlan_tdls_main.h"
#include "wlan_tdls_peer.h"
#include "wlan_tdls_ct.h"
+#include "wlan_tdls_cmds_process.h"
bool tdls_is_vdev_connected(struct wlan_objmgr_vdev *vdev)
{
@@ -936,3 +937,386 @@
WLAN_TDLS_NB_ID);
}
+/**
+ * tdls_set_tdls_offchannel() - set tdls off-channel number
+ * @tdls_soc: tdls soc object
+ * @offchanmode: tdls off-channel number
+ *
+ * This function sets tdls off-channel number
+ *
+ * Return: 0 on success; negative errno otherwise
+ */
+static
+int tdls_set_tdls_offchannel(struct tdls_soc_priv_obj *tdls_soc,
+ int offchannel)
+{
+ uint32_t tdls_feature_flags;
+
+ tdls_feature_flags = tdls_soc->tdls_configs.tdls_feature_flags;
+
+ if (TDLS_IS_OFF_CHANNEL_ENABLED(tdls_feature_flags) &&
+ (TDLS_SUPPORT_EXP_TRIG_ONLY == tdls_soc->tdls_current_mode ||
+ TDLS_SUPPORT_IMP_MODE == tdls_soc->tdls_current_mode ||
+ TDLS_SUPPORT_EXT_CONTROL == tdls_soc->tdls_current_mode)) {
+ if (offchannel < TDLS_PREFERRED_OFF_CHANNEL_NUM_MIN ||
+ offchannel > TDLS_PREFERRED_OFF_CHANNEL_NUM_MAX) {
+ tdls_err("Invalid tdls off channel %u", offchannel);
+ return -EINVAL;
+ }
+ } else {
+ tdls_err("Either TDLS or TDLS Off-channel is not enabled");
+ return -ENOTSUPP;
+ }
+ tdls_notice("change tdls off channel from %d to %d",
+ tdls_soc->tdls_off_channel, offchannel);
+ tdls_soc->tdls_off_channel = offchannel;
+ return 0;
+}
+
+/**
+ * tdls_set_tdls_secoffchanneloffset() - set secondary tdls off-channel offset
+ * @tdls_soc: tdls soc object
+ * @offchanmode: tdls off-channel offset
+ *
+ * This function sets 2nd tdls off-channel offset
+ *
+ * Return: 0 on success; negative errno otherwise
+ */
+static
+int tdls_set_tdls_secoffchanneloffset(struct tdls_soc_priv_obj *tdls_soc,
+ int offchanoffset)
+{
+ uint32_t tdls_feature_flags;
+
+ tdls_feature_flags = tdls_soc->tdls_configs.tdls_feature_flags;
+
+ if (!TDLS_IS_OFF_CHANNEL_ENABLED(tdls_feature_flags) ||
+ TDLS_SUPPORT_SUSPENDED >= tdls_soc->tdls_current_mode) {
+ tdls_err("Either TDLS or TDLS Off-channel is not enabled");
+ return -ENOTSUPP;
+ }
+
+ tdls_soc->tdls_channel_offset = 0;
+
+ switch (offchanoffset) {
+ case TDLS_SEC_OFFCHAN_OFFSET_0:
+ tdls_soc->tdls_channel_offset = (1 << BW_20_OFFSET_BIT);
+ break;
+ case TDLS_SEC_OFFCHAN_OFFSET_40PLUS:
+ case TDLS_SEC_OFFCHAN_OFFSET_40MINUS:
+ tdls_soc->tdls_channel_offset = (1 << BW_40_OFFSET_BIT);
+ break;
+ case TDLS_SEC_OFFCHAN_OFFSET_80:
+ tdls_soc->tdls_channel_offset = (1 << BW_80_OFFSET_BIT);
+ break;
+ case TDLS_SEC_OFFCHAN_OFFSET_160:
+ tdls_soc->tdls_channel_offset = (1 << BW_160_OFFSET_BIT);
+ break;
+ default:
+ tdls_err("Invalid tdls secondary off channel offset %d",
+ offchanoffset);
+ return -EINVAL;
+ } /* end switch */
+
+ tdls_notice("change tdls secondary off channel offset to 0x%x",
+ tdls_soc->tdls_channel_offset);
+ return 0;
+}
+
+/**
+ * tdls_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
+ */
+static
+int tdls_set_tdls_offchannelmode(struct wlan_objmgr_vdev *vdev,
+ int offchanmode)
+{
+ struct tdls_peer *conn_peer = NULL;
+ struct tdls_channel_switch_params chan_switch_params;
+ QDF_STATUS status = QDF_STATUS_E_FAILURE;
+ int ret_value = 0;
+ struct tdls_vdev_priv_obj *tdls_vdev;
+ struct tdls_soc_priv_obj *tdls_soc;
+ uint32_t tdls_feature_flags;
+
+
+ status = tdls_get_vdev_objects(vdev, &tdls_vdev, &tdls_soc);
+
+ if (status != QDF_STATUS_SUCCESS)
+ return -EINVAL;
+
+
+ if (offchanmode < ENABLE_CHANSWITCH ||
+ offchanmode > DISABLE_CHANSWITCH) {
+ tdls_err("Invalid tdls off channel mode %d", offchanmode);
+ return -EINVAL;
+ }
+
+ if (tdls_is_vdev_connected(vdev)) {
+ tdls_err("tdls off channel req in not associated state %d",
+ offchanmode);
+ return -EPERM;
+ }
+
+ tdls_feature_flags = tdls_soc->tdls_configs.tdls_feature_flags;
+ if (!TDLS_IS_OFF_CHANNEL_ENABLED(tdls_feature_flags) ||
+ TDLS_SUPPORT_SUSPENDED >= tdls_soc->tdls_current_mode) {
+ tdls_err("Either TDLS or TDLS Off-channel is not enabled");
+ return -ENOTSUPP;
+ }
+
+ conn_peer = tdls_find_first_connected_peer(tdls_vdev);
+ if (NULL == conn_peer) {
+ tdls_err("No TDLS Connected Peer");
+ return -EPERM;
+ }
+
+ tdls_notice("TDLS Channel Switch in swmode=%d tdls_off_channel %d offchanoffset %d",
+ offchanmode, tdls_soc->tdls_off_channel,
+ tdls_soc->tdls_channel_offset);
+
+ switch (offchanmode) {
+ case ENABLE_CHANSWITCH:
+ if (tdls_soc->tdls_off_channel &&
+ tdls_soc->tdls_channel_offset) {
+ chan_switch_params.tdls_off_ch =
+ tdls_soc->tdls_off_channel;
+ chan_switch_params.tdls_off_ch_bw_offset =
+ tdls_soc->tdls_channel_offset;
+ chan_switch_params.oper_class =
+ tdls_find_opclass(tdls_soc->soc,
+ chan_switch_params.tdls_off_ch,
+ chan_switch_params.tdls_off_ch_bw_offset);
+ } else {
+ tdls_err("TDLS off-channel parameters are not set yet!!!");
+ return -EINVAL;
+
+ }
+ break;
+ case DISABLE_CHANSWITCH:
+ chan_switch_params.tdls_off_ch = 0;
+ chan_switch_params.tdls_off_ch_bw_offset = 0;
+ chan_switch_params.oper_class = 0;
+ break;
+ default:
+ tdls_err("Incorrect Parameters mode: %d tdls_off_channel: %d offchanoffset: %d",
+ offchanmode, tdls_soc->tdls_off_channel,
+ tdls_soc->tdls_channel_offset);
+ return -EINVAL;
+ } /* end switch */
+
+ chan_switch_params.vdev_id = tdls_vdev->session_id;
+ chan_switch_params.tdls_sw_mode = offchanmode;
+ chan_switch_params.is_responder =
+ conn_peer->is_responder;
+ qdf_mem_copy(&chan_switch_params.peer_mac_addr,
+ &conn_peer->peer_mac.bytes,
+ QDF_MAC_ADDR_SIZE);
+ tdls_notice("Peer " QDF_MAC_ADDRESS_STR " vdevId: %d, off channel: %d, offset: %d, mode: %d, is_responder: %d",
+ QDF_MAC_ADDR_ARRAY(chan_switch_params.peer_mac_addr),
+ chan_switch_params.vdev_id,
+ chan_switch_params.tdls_off_ch,
+ chan_switch_params.tdls_off_ch_bw_offset,
+ chan_switch_params.tdls_sw_mode,
+ chan_switch_params.is_responder);
+
+ status = tdls_set_offchan_mode(tdls_soc->soc,
+ &chan_switch_params);
+
+ if (status != QDF_STATUS_SUCCESS) {
+ tdls_err("Failed to send channel switch request to wmi");
+ return -EINVAL;
+ }
+
+ tdls_soc->tdls_fw_off_chan_mode = offchanmode;
+
+ if (ENABLE_CHANSWITCH == offchanmode) {
+ conn_peer = tdls_find_first_connected_peer(tdls_vdev);
+ if (NULL == conn_peer) {
+ tdls_err("No TDLS Connected Peer");
+ return -EPERM;
+ }
+ conn_peer->pref_off_chan_num =
+ chan_switch_params.tdls_off_ch;
+ conn_peer->op_class_for_pref_off_chan =
+ chan_switch_params.oper_class;
+ }
+
+ return ret_value;
+}
+
+/**
+ * tdls_delete_all_tdls_peers(): send request to delete tdls peers
+ * @vdev: vdev object
+ * @tdls_soc: tdls soc object
+ *
+ * This function sends request to lim to delete tdls peers
+ *
+ * Return: QDF_STATUS
+ */
+static
+QDF_STATUS tdls_delete_all_tdls_peers(struct wlan_objmgr_vdev *vdev,
+ struct tdls_soc_priv_obj *tdls_soc)
+{
+ struct wlan_objmgr_peer *peer;
+ struct tdls_del_all_tdls_peers *del_msg;
+ struct scheduler_msg msg;
+ QDF_STATUS status;
+
+
+ del_msg = qdf_mem_malloc(sizeof(*del_msg));
+ if (NULL == del_msg) {
+ tdls_err("memory alloc failed");
+ return QDF_STATUS_E_FAILURE;
+ }
+ qdf_mem_zero(del_msg, sizeof(*del_msg));
+
+ peer = wlan_vdev_get_bsspeer(vdev);
+ if (QDF_STATUS_SUCCESS != wlan_objmgr_peer_try_get_ref(peer,
+ WLAN_TDLS_SB_ID)) {
+ return QDF_STATUS_E_FAILURE;
+ }
+
+ qdf_mem_copy(del_msg->bssid.bytes,
+ wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE);
+
+ del_msg->msg_type = tdls_soc->tdls_del_all_peers;
+ del_msg->msg_len = (uint16_t) sizeof(*del_msg);
+
+ /* Send the request to PE. */
+ qdf_mem_zero(&msg, sizeof(msg));
+
+ tdls_debug("sending delete all peers req to PE ");
+
+ msg.type = del_msg->msg_type;
+ msg.bodyptr = del_msg;
+
+ status = scheduler_post_msg(QDF_MODULE_ID_PE, &msg);
+
+ wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_SB_ID);
+ return status;
+}
+
+void tdls_disable_offchan_and_teardown_links(
+ struct wlan_objmgr_vdev *vdev)
+{
+ uint16_t connected_tdls_peers = 0;
+ uint8_t staidx;
+ struct tdls_peer *curr_peer = NULL;
+ struct tdls_vdev_priv_obj *tdls_vdev;
+ struct tdls_soc_priv_obj *tdls_soc;
+ QDF_STATUS status;
+
+ status = tdls_get_vdev_objects(vdev, &tdls_vdev, &tdls_soc);
+ if (QDF_STATUS_SUCCESS != status) {
+ tdls_err("tdls objects are NULL ");
+ return;
+ }
+
+ if (TDLS_SUPPORT_SUSPENDED >= tdls_soc->tdls_current_mode) {
+ tdls_notice("TDLS mode %d is disabled OR not suspended now",
+ tdls_soc->tdls_current_mode);
+ return;
+ }
+
+ connected_tdls_peers = tdls_soc->connected_peer_count;
+
+ if (!connected_tdls_peers) {
+ tdls_notice("No TDLS connected peers to delete");
+ 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
+ */
+ tdls_set_tdls_offchannel(tdls_soc,
+ tdls_soc->tdls_configs.tdls_pre_off_chan_num);
+ tdls_set_tdls_secoffchanneloffset(tdls_soc,
+ TDLS_SEC_OFFCHAN_OFFSET_40PLUS);
+ tdls_set_tdls_offchannelmode(vdev, DISABLE_CHANSWITCH);
+
+ /* Send Msg to PE for deleting all the TDLS peers */
+ tdls_delete_all_tdls_peers(vdev, tdls_soc);
+
+ for (staidx = 0; staidx < tdls_soc->max_num_tdls_sta;
+ staidx++) {
+ if (!tdls_soc->tdls_conn_info[staidx].sta_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 (staId %d)",
+ curr_peer->sta_id);
+
+ /* Indicate teardown to supplicant */
+ tdls_indicate_teardown(tdls_vdev,
+ curr_peer,
+ TDLS_TEARDOWN_PEER_UNSPEC_REASON);
+
+ /*
+ * Del Sta happened already as part of tdls_delete_all_tdls_peers
+ * Hence clear tdls vdev data structure.
+ */
+ tdls_reset_peer(tdls_vdev, curr_peer->peer_mac.bytes);
+
+ if (tdls_soc->tdls_dereg_tl_peer)
+ tdls_soc->tdls_dereg_tl_peer(
+ tdls_soc->tdls_tl_peer_data,
+ wlan_vdev_get_id(vdev),
+ curr_peer->sta_id);
+
+ tdls_decrement_peer_count(tdls_soc);
+ tdls_soc->tdls_conn_info[staidx].sta_id = 0;
+ tdls_soc->tdls_conn_info[staidx].session_id = 255;
+
+ qdf_mem_zero(&tdls_soc->tdls_conn_info[staidx].peer_mac,
+ sizeof(struct qdf_mac_addr));
+ }
+}
+
+void tdls_teardown_connections(struct wlan_objmgr_vdev *vdev)
+{
+ struct tdls_osif_indication indication;
+ struct tdls_soc_priv_obj *tdls_soc;
+ struct wlan_objmgr_vdev *tdls_vdev_obj;
+
+ if (!vdev) {
+ QDF_ASSERT(0);
+ return;
+ }
+
+ tdls_soc = wlan_vdev_get_tdls_soc_obj(vdev);
+ if (!tdls_soc)
+ return;
+
+ /* Get the tdls specific vdev and clear the links */
+ tdls_vdev_obj = tdls_get_vdev(tdls_soc->soc, WLAN_TDLS_SB_ID);
+ if (tdls_vdev_obj) {
+ tdls_disable_offchan_and_teardown_links(tdls_vdev_obj);
+ wlan_objmgr_vdev_release_ref(tdls_vdev_obj, WLAN_TDLS_SB_ID);
+ }
+
+ indication.vdev = vdev;
+
+ if (tdls_soc->tdls_event_cb)
+ tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data,
+ TDLS_EVENT_TEARDOWN_LINKS_DONE,
+ &indication);
+}
diff --git a/umac/tdls/core/src/wlan_tdls_ct.h b/umac/tdls/core/src/wlan_tdls_ct.h
index e497dae..63e3063 100644
--- a/umac/tdls/core/src/wlan_tdls_ct.h
+++ b/umac/tdls/core/src/wlan_tdls_ct.h
@@ -31,6 +31,10 @@
*/
#define TDLS_DISCOVERY_TIMEOUT_ERE_UPDATE 1000
+#define TDLS_PREFERRED_OFF_CHANNEL_NUM_MIN 1
+#define TDLS_PREFERRED_OFF_CHANNEL_NUM_MAX 165
+#define TDLS_PREFERRED_OFF_CHANNEL_NUM_DEFAULT 36
+
/**
* tdls_is_vdev_connected() - check the vdev is connected to ap
* @vdev: vdev object manager
@@ -172,4 +176,22 @@
*/
bool tdls_is_vdev_authenticated(struct wlan_objmgr_vdev *vdev);
+/**
+ * tdls_teardown_connections() -teardown and delete all the tdls peers
+ * @vdev: vdev oobject
+ *
+ * Return: true or false
+ */
+void tdls_teardown_connections(struct wlan_objmgr_vdev *vdev);
+
+/**
+ * tdls_disable_offchan_and_teardown_links - Disable offchannel
+ * and teardown TDLS links
+ * @tdls_soc : tdls soc object
+ *
+ * Return: None
+ */
+void tdls_disable_offchan_and_teardown_links(
+ struct wlan_objmgr_vdev *vdev);
+
#endif
diff --git a/umac/tdls/core/src/wlan_tdls_main.c b/umac/tdls/core/src/wlan_tdls_main.c
index c58d868..f98d993 100644
--- a/umac/tdls/core/src/wlan_tdls_main.c
+++ b/umac/tdls/core/src/wlan_tdls_main.c
@@ -287,6 +287,9 @@
case TDLS_CMD_SESSION_DECREMENT:
tdls_process_policy_mgr_notification(msg->bodyptr);
break;
+ case TDLS_CMD_TEARDOWN_LINKS:
+ tdls_teardown_connections(msg->bodyptr);
+ break;
default:
break;
}
@@ -468,6 +471,20 @@
}
+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
@@ -745,12 +762,15 @@
tdls_soc_obj->set_state_info.set_state_cnt);
/* disable off channel and teardown links */
/* Go through the peer list and delete them */
+ tdls_disable_offchan_and_teardown_links(tdls_vdev_obj->vdev);
tdls_soc_obj->tdls_current_mode = TDLS_SUPPORT_DISABLED;
tdls_info_to_fw->vdev_id = tdls_soc_obj->set_state_info.vdev_id;
} else {
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;
diff --git a/umac/tdls/core/src/wlan_tdls_main.h b/umac/tdls/core/src/wlan_tdls_main.h
index bfbddf0..f6096f2 100644
--- a/umac/tdls/core/src/wlan_tdls_main.h
+++ b/umac/tdls/core/src/wlan_tdls_main.h
@@ -180,6 +180,7 @@
* @tdls_add_sta_req: store eWNI_SME_TDLS_ADD_STA_REQ value
* @tdls_del_sta_req: store eWNI_SME_TDLS_DEL_STA_REQ value
* @tdls_update_peer_state: store WMA_UPDATE_TDLS_PEER_STATE value
+ * @tdls_del_all_peers:store eWNI_SME_DEL_ALL_TDLS_PEERS
* @tdls_idle_peer_data: provide information about idle peer
* @tdls_ct_spinlock: connection tracker spin lock
*/
@@ -221,6 +222,7 @@
uint16_t tdls_add_sta_req;
uint16_t tdls_del_sta_req;
uint16_t tdls_update_peer_state;
+ uint16_t tdls_del_all_peers;
struct tdls_ct_idle_peer_data tdls_idle_peer_data;
qdf_spinlock_t tdls_ct_spinlock;
};
@@ -255,6 +257,7 @@
ct_peer_table[WLAN_TDLS_CT_TABLE_SIZE];
uint8_t valid_mac_entries;
uint32_t magic;
+ uint8_t session_id;
qdf_list_t tx_queue;
};
@@ -667,4 +670,16 @@
void tdls_scan_serialization_comp_info_cb(
union wlan_serialization_rules_info *comp_info);
+/**
+ * tdls_set_offchan_mode() - update tdls status info
+ * @psoc: soc object
+ * @param: channel switch params
+ *
+ * send message to WMI to set TDLS off channel in f/w
+ *
+ * Return: QDF_STATUS.
+ */
+QDF_STATUS tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc,
+ struct tdls_channel_switch_params *param);
+
#endif
diff --git a/umac/tdls/core/src/wlan_tdls_peer.c b/umac/tdls/core/src/wlan_tdls_peer.c
index a89736f..56e5ec7 100644
--- a/umac/tdls/core/src/wlan_tdls_peer.c
+++ b/umac/tdls/core/src/wlan_tdls_peer.c
@@ -125,7 +125,7 @@
return tdls_search_param.peer;
}
-static uint8_t tdls_find_opclass(struct wlan_objmgr_psoc *psoc, uint8_t channel,
+uint8_t tdls_find_opclass(struct wlan_objmgr_psoc *psoc, uint8_t channel,
uint8_t bw_offset)
{
char country[REG_ALPHA2_LEN + 1];
diff --git a/umac/tdls/core/src/wlan_tdls_peer.h b/umac/tdls/core/src/wlan_tdls_peer.h
index 511b4d4..7a510c1 100644
--- a/umac/tdls/core/src/wlan_tdls_peer.h
+++ b/umac/tdls/core/src/wlan_tdls_peer.h
@@ -85,6 +85,20 @@
tdls_find_all_peer(struct tdls_soc_priv_obj *soc_obj, const uint8_t *macaddr);
/**
+ * tdls_find_all_peer() - find peer matching the input MACaddr in soc range
+ * @soc_obj: TDLS soc object
+ * @channel:channel number
+ * @bw_offset: offset to bandwidth
+ *
+ * This is in scheduler thread context, no lock required.
+ *
+ * Return: Operating class
+ */
+uint8_t tdls_find_opclass(struct wlan_objmgr_psoc *psoc,
+ uint8_t channel,
+ uint8_t bw_offset);
+
+/**
* tdls_find_first_connected_peer() - find the 1st connected tdls peer from vdev
* @vdev_obj: tdls vdev object
*
diff --git a/umac/tdls/dispatcher/inc/wlan_tdls_public_structs.h b/umac/tdls/dispatcher/inc/wlan_tdls_public_structs.h
index 36e3a46..3632af5 100644
--- a/umac/tdls/dispatcher/inc/wlan_tdls_public_structs.h
+++ b/umac/tdls/dispatcher/inc/wlan_tdls_public_structs.h
@@ -29,7 +29,6 @@
#include <qdf_mc_timer.h>
#include <wlan_cmn.h>
#include <wlan_cmn_ieee80211.h>
-#include <wlan_objmgr_psoc_obj.h>
#define WLAN_TDLS_STA_MAX_NUM 8
@@ -63,6 +62,9 @@
/** Maximum time(ms) to wait for tdls mgmt to complete **/
#define WAIT_TIME_FOR_TDLS_MGMT 11000
+/** Maximum waittime for TDLS teardown links **/
+#define WAIT_TIME_FOR_TDLS_TEARDOWN_LINKS 10000
+
#define TDLS_TEARDOWN_PEER_UNREACHABLE 25
#define TDLS_TEARDOWN_PEER_UNSPEC_REASON 26
@@ -188,6 +190,7 @@
TDLS_CMD_SET_TDLS_MODE,
TDLS_CMD_SESSION_INCREMENT,
TDLS_CMD_SESSION_DECREMENT,
+ TDLS_CMD_TEARDOWN_LINKS,
};
/**
@@ -210,6 +213,7 @@
TDLS_EVENT_DISCOVERY_REQ,
TDLS_EVENT_TEARDOWN_REQ,
TDLS_EVENT_SETUP_REQ,
+ TDLS_EVENT_TEARDOWN_LINKS_DONE,
};
/**
@@ -547,6 +551,7 @@
uint16_t tdls_add_sta_req;
uint16_t tdls_del_sta_req;
uint16_t tdls_update_peer_state;
+ uint16_t tdls_del_all_peers;
tdls_rx_callback tdls_rx_cb;
void *tdls_rx_cb_data;
tdls_wmm_check tdls_wmm_cb;
@@ -1001,4 +1006,16 @@
enum tdls_disable_sources source;
};
+/**
+ * struct tdls_del_all_tdls_peers - delete all tdls peers
+ * @msg_type: type of message
+ * @msg_len: length of message
+ * @bssid: bssid of peer device
+ */
+struct tdls_del_all_tdls_peers {
+ uint16_t msg_type;
+ uint16_t msg_len;
+ struct qdf_mac_addr bssid;
+};
+
#endif
diff --git a/umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h b/umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h
index 6c7d117..5d99669 100644
--- a/umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h
+++ b/umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h
@@ -139,6 +139,14 @@
QDF_STATUS ucfg_tdls_responder(struct tdls_set_responder_req *msg_req);
/**
+ * ucfg_tdls_teardown_links() - teardown all TDLS links
+ * @vdev: vdev object manager
+ *
+ * Return: None
+ */
+QDF_STATUS ucfg_tdls_teardown_links(struct wlan_objmgr_vdev *vdev);
+
+/**
* ucfg_tdls_notify_sta_connect() - notify sta connect
* @notify_info: sta notification info
*
diff --git a/umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c b/umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c
index ecef61f..4412c58 100644
--- a/umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c
+++ b/umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c
@@ -216,6 +216,7 @@
soc_obj->tdls_add_sta_req = req->tdls_add_sta_req;
soc_obj->tdls_del_sta_req = req->tdls_del_sta_req;
soc_obj->tdls_update_peer_state = req->tdls_update_peer_state;
+ soc_obj->tdls_del_all_peers = req->tdls_del_all_peers;
tdls_pm_call_backs.tdls_notify_increment_session =
tdls_notify_increment_session;
@@ -238,6 +239,7 @@
else
soc_obj->tdls_current_mode = TDLS_SUPPORT_IMP_MODE;
+ soc_obj->tdls_last_mode = soc_obj->tdls_current_mode;
return QDF_STATUS_SUCCESS;
}
@@ -538,6 +540,26 @@
return status;
}
+QDF_STATUS ucfg_tdls_teardown_links(struct wlan_objmgr_vdev *vdev)
+{
+ QDF_STATUS status;
+ struct scheduler_msg msg = {0, };
+
+ if (!vdev) {
+ tdls_err("vdev is NULL ");
+ return QDF_STATUS_E_NULL_VALUE;
+ }
+ tdls_notice("Enter ");
+
+ msg.bodyptr = vdev;
+ msg.callback = tdls_process_cmd;
+ msg.type = TDLS_CMD_TEARDOWN_LINKS;
+ status = scheduler_post_msg(QDF_MODULE_ID_OS_IF, &msg);
+
+ tdls_notice("Exit ");
+ return status;
+}
+
QDF_STATUS ucfg_tdls_notify_sta_connect(
struct tdls_sta_notify_params *notify_info)
{