| /* |
| * 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 = ¬ify->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; |
| } |