From 7e189c343688a563e3f2cdde80163416bf560fd7 Mon Sep 17 00:00:00 2001 From: Kabilan Kannan Date: Mon, 17 Apr 2017 21:53:42 -0700 Subject: [PATCH] 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 --- os_if/linux/tdls/inc/wlan_cfg80211_tdls.h | 74 ++++ os_if/linux/tdls/src/wlan_cfg80211_tdls.c | 76 +++- umac/tdls/core/src/wlan_tdls_cmds_process.c | 2 +- umac/tdls/core/src/wlan_tdls_cmds_process.h | 10 + umac/tdls/core/src/wlan_tdls_ct.c | 384 ++++++++++++++++++ umac/tdls/core/src/wlan_tdls_ct.h | 22 + umac/tdls/core/src/wlan_tdls_main.c | 20 + umac/tdls/core/src/wlan_tdls_main.h | 15 + umac/tdls/core/src/wlan_tdls_peer.c | 2 +- umac/tdls/core/src/wlan_tdls_peer.h | 14 + .../dispatcher/inc/wlan_tdls_public_structs.h | 19 +- umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h | 8 + umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c | 22 + 13 files changed, 662 insertions(+), 6 deletions(-) diff --git a/os_if/linux/tdls/inc/wlan_cfg80211_tdls.h b/os_if/linux/tdls/inc/wlan_cfg80211_tdls.h index c4efaadb3ec2..812c8bccfc9c 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 #include +#ifdef CONVERGED_TDLS_ENABLE #define TDLS_VDEV_MAGIC 0x54444c53 /* "TDLS" */ @@ -51,6 +52,7 @@ struct osif_tdls_vdev { 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_event_callback(void *userdata, 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 714b5f594b02..238d27ba0f75 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 @@ QDF_STATUS wlan_cfg80211_tdls_priv_init(struct vdev_osif_priv *osif_priv) 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 @@ void wlan_cfg80211_tdls_priv_deinit(struct vdev_osif_priv *osif_priv) 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 @@ int wlan_cfg80211_tdls_configure_mode(struct wlan_objmgr_vdev *vdev, 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 @@ void wlan_cfg80211_tdls_event_callback(void *user_data, 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 26594e3f37f4..35cd7f3e7df8 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 @@ static uint16_t tdls_get_connected_peer(struct tdls_soc_priv_obj *soc_obj) * * 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 ec2f40e1070d..5e2c03508475 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_validate_mgmt_request(struct tdls_validate_action_req *tdls_validate); */ 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 cb48dd205771..3b5b554fb83b 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 @@ void tdls_ct_handler(void *user_data) 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 e497daed2bac..63e306378000 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_connected(struct wlan_objmgr_vdev *vdev); */ 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 c58d8682b38b..f98d993579c1 100644 --- a/umac/tdls/core/src/wlan_tdls_main.c +++ b/umac/tdls/core/src/wlan_tdls_main.c @@ -287,6 +287,9 @@ QDF_STATUS tdls_process_cmd(struct scheduler_msg *msg) 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 @@ static void tdls_state_param_setting_dump(struct tdls_info *info) } +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 @@ static void tdls_send_update_to_fw(struct tdls_vdev_priv_obj *tdls_vdev_obj, 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 bfbddf0dede2..f6096f2ef3e3 100644 --- a/umac/tdls/core/src/wlan_tdls_main.h +++ b/umac/tdls/core/src/wlan_tdls_main.h @@ -180,6 +180,7 @@ struct tdls_set_state_info { * @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 @@ struct tdls_soc_priv_obj { 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 @@ struct tdls_vdev_priv_obj { 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_done_callback(struct tdls_soc_priv_obj *tdls_soc); 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 a89736fb95e0..56e5ec715ea4 100644 --- a/umac/tdls/core/src/wlan_tdls_peer.c +++ b/umac/tdls/core/src/wlan_tdls_peer.c @@ -125,7 +125,7 @@ tdls_find_all_peer(struct tdls_soc_priv_obj *soc_obj, const uint8_t *macaddr) 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 511b4d4ed010..7a510c1dac64 100644 --- a/umac/tdls/core/src/wlan_tdls_peer.h +++ b/umac/tdls/core/src/wlan_tdls_peer.h @@ -84,6 +84,20 @@ struct tdls_peer *tdls_find_peer(struct tdls_vdev_priv_obj *vdev_obj, struct tdls_peer * 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 36e3a4676c90..3632af50a163 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 #include #include -#include #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 @@ enum tdls_command_type { TDLS_CMD_SET_TDLS_MODE, TDLS_CMD_SESSION_INCREMENT, TDLS_CMD_SESSION_DECREMENT, + TDLS_CMD_TEARDOWN_LINKS, }; /** @@ -210,6 +213,7 @@ enum tdls_event_type { TDLS_EVENT_DISCOVERY_REQ, TDLS_EVENT_TEARDOWN_REQ, TDLS_EVENT_SETUP_REQ, + TDLS_EVENT_TEARDOWN_LINKS_DONE, }; /** @@ -547,6 +551,7 @@ struct tdls_start_params { 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 @@ struct tdls_set_mode_params { 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 6c7d117b44d9..5d99669740ab 100644 --- a/umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h +++ b/umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h @@ -138,6 +138,14 @@ QDF_STATUS ucfg_tdls_send_mgmt_frame( */ 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 ecef61fdaeed..4412c5878232 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 @@ QDF_STATUS ucfg_tdls_update_config(struct wlan_objmgr_psoc *psoc, 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 @@ QDF_STATUS ucfg_tdls_update_config(struct wlan_objmgr_psoc *psoc, 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 @@ QDF_STATUS ucfg_tdls_responder(struct tdls_set_responder_req *req) 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) {