/* * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. 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: nan_datapath.c * * MAC NAN Data path API implementation */ #include "lim_utils.h" #include "lim_api.h" #include "lim_assoc_utils.h" #include "nan_datapath.h" #include "lim_types.h" #include "lim_send_messages.h" #include "wma_nan_datapath.h" #include "os_if_nan.h" #include "nan_public_structs.h" #include "nan_ucfg_api.h" /** * lim_add_ndi_peer() - Function to add ndi peer * @mac_ctx: handle to mac structure * @vdev_id: vdev id on which peer is added * @peer_mac_addr: peer to be added * * Return: QDF_STATUS_SUCCESS on success; error number otherwise */ static QDF_STATUS lim_add_ndi_peer(struct mac_context *mac_ctx, uint32_t vdev_id, struct qdf_mac_addr peer_mac_addr) { struct pe_session *session; tpDphHashNode sta_ds; uint16_t assoc_id, peer_idx; QDF_STATUS status; uint8_t zero_mac_addr[QDF_MAC_ADDR_SIZE] = { 0, 0, 0, 0, 0, 0 }; if (!wlan_is_vdev_id_up(mac_ctx->pdev, vdev_id)) { pe_err_rl("NDI vdev is not up"); return QDF_STATUS_E_FAILURE; } if (!qdf_mem_cmp(&zero_mac_addr, &peer_mac_addr.bytes[0], QDF_MAC_ADDR_SIZE)) { pe_err("Failing to add peer with all zero mac addr"); return QDF_STATUS_E_FAILURE; } session = pe_find_session_by_vdev_id(mac_ctx, vdev_id); if (!session) { /* couldn't find session */ pe_err("Session not found for vdev_id: %d", vdev_id); return QDF_STATUS_E_FAILURE; } sta_ds = dph_lookup_hash_entry(mac_ctx, peer_mac_addr.bytes, &assoc_id, &session->dph.dphHashTable); /* peer exists, don't do anything */ if (sta_ds) { pe_err("NDI Peer already exists!!"); return QDF_STATUS_SUCCESS; } pe_info("Need to create NDI Peer :" QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(peer_mac_addr.bytes)); ucfg_nan_set_peer_mc_list(session->vdev, peer_mac_addr); peer_idx = lim_assign_peer_idx(mac_ctx, session); if (!peer_idx) { pe_err("Invalid peer_idx: %d", peer_idx); return QDF_STATUS_SUCCESS; } sta_ds = dph_add_hash_entry(mac_ctx, peer_mac_addr.bytes, peer_idx, &session->dph.dphHashTable); if (!sta_ds) { pe_err("Couldn't add dph entry"); /* couldn't add dph entry */ return QDF_STATUS_E_FAILURE; } /* wma decides NDI mode from wma->interface struct */ sta_ds->staType = STA_ENTRY_NDI_PEER; status = lim_add_sta(mac_ctx, sta_ds, false, session); if (QDF_STATUS_SUCCESS != status) { /* couldn't add peer */ pe_err("limAddSta failed status: %d", status); return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } QDF_STATUS lim_add_ndi_peer_converged(uint32_t vdev_id, struct qdf_mac_addr peer_mac_addr) { struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); if (!mac_ctx) return QDF_STATUS_E_NULL_VALUE; return lim_add_ndi_peer(mac_ctx, vdev_id, peer_mac_addr); } /** * lim_ndp_delete_peer_by_addr() - Delete NAN data peer, given addr and vdev_id * @mac_ctx: handle to mac context * @vdev_id: vdev_id on which peer was added * @peer_ndi_mac_addr: mac addr of peer * This function deletes a peer if there was NDP_Confirm with REJECT * * Return: None */ static void lim_ndp_delete_peer_by_addr(struct mac_context *mac_ctx, uint8_t vdev_id, struct qdf_mac_addr peer_ndi_mac_addr) { struct pe_session *session; tpDphHashNode sta_ds; uint16_t peer_idx; uint8_t zero_mac_addr[QDF_MAC_ADDR_SIZE] = { 0, 0, 0, 0, 0, 0 }; if (!qdf_mem_cmp(&zero_mac_addr, &peer_ndi_mac_addr.bytes[0], QDF_MAC_ADDR_SIZE)) { pe_err("Failing to delete the peer with all zero mac addr"); return; } pe_info("deleting peer: "QDF_MAC_ADDR_FMT" confirm rejected", QDF_MAC_ADDR_REF(peer_ndi_mac_addr.bytes)); session = pe_find_session_by_vdev_id(mac_ctx, vdev_id); if (!session || (session->bssType != eSIR_NDI_MODE)) { pe_err("PE session is NULL or non-NDI for sme session %d", vdev_id); return; } sta_ds = dph_lookup_hash_entry(mac_ctx, peer_ndi_mac_addr.bytes, &peer_idx, &session->dph.dphHashTable); if (!sta_ds) { pe_err("Unknown NDI Peer"); return; } if (sta_ds->staType != STA_ENTRY_NDI_PEER) { pe_err("Non-NDI Peer ignored"); return; } /* * Call lim_del_sta() with response required set true. Hence * DphHashEntry will be deleted after receiving that response. */ lim_del_sta(mac_ctx, sta_ds, true, session); } void lim_ndp_delete_peers_by_addr_converged(uint8_t vdev_id, struct qdf_mac_addr peer_ndi_mac_addr) { struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); if (!mac_ctx) return; lim_ndp_delete_peer_by_addr(mac_ctx, vdev_id, peer_ndi_mac_addr); } /** * lim_ndp_delete_peers() - Delete NAN data peers * @mac_ctx: handle to mac context * @ndp_map: NDP instance/peer map * @num_peers: number of peers entries in peer_map * This function deletes a peer if there are no active NDPs left with that peer * * Return: None */ static void lim_ndp_delete_peers(struct mac_context *mac_ctx, struct peer_ndp_map *ndp_map, uint8_t num_peers) { tpDphHashNode sta_ds = NULL; uint16_t deleted_num = 0; int i, j; struct pe_session *session; struct qdf_mac_addr *deleted_peers; uint16_t peer_idx; bool found; deleted_peers = qdf_mem_malloc(num_peers * sizeof(*deleted_peers)); if (!deleted_peers) return; for (i = 0; i < num_peers; i++) { pe_debug("ndp_map[%d]: MAC: " QDF_MAC_ADDR_FMT " num_active %d", i, QDF_MAC_ADDR_REF(ndp_map[i].peer_ndi_mac_addr.bytes), ndp_map[i].num_active_ndp_sessions); /* Do not delete a peer with active NDPs */ if (ndp_map[i].num_active_ndp_sessions > 0) continue; session = pe_find_session_by_vdev_id(mac_ctx, ndp_map[i].vdev_id); if (!session || (session->bssType != eSIR_NDI_MODE)) { pe_err("PE session is NULL or non-NDI for sme session %d", ndp_map[i].vdev_id); continue; } /* Check if this peer is already in the deleted list */ found = false; for (j = 0; j < deleted_num && !found; j++) { if (!qdf_mem_cmp( &deleted_peers[j].bytes, &ndp_map[i].peer_ndi_mac_addr.bytes, QDF_MAC_ADDR_SIZE)) { found = true; break; } } if (found) continue; sta_ds = dph_lookup_hash_entry(mac_ctx, ndp_map[i].peer_ndi_mac_addr.bytes, &peer_idx, &session->dph.dphHashTable); if (!sta_ds) { pe_err("Unknown NDI Peer"); continue; } if (sta_ds->staType != STA_ENTRY_NDI_PEER) { pe_err("Non-NDI Peer ignored"); continue; } /* * Call lim_del_sta() with response required set true. * Hence DphHashEntry will be deleted after receiving * that response. */ lim_del_sta(mac_ctx, sta_ds, true, session); qdf_copy_macaddr(&deleted_peers[deleted_num++], &ndp_map[i].peer_ndi_mac_addr); } qdf_mem_free(deleted_peers); } void lim_ndp_delete_peers_converged(struct peer_nan_datapath_map *ndp_map, uint8_t num_peers) { struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); if (!mac_ctx) return; lim_ndp_delete_peers(mac_ctx, (struct peer_ndp_map *)ndp_map, num_peers); } /** * lim_process_ndi_del_sta_rsp() - Handle WDA_DELETE_STA_RSP in eLIM_NDI_ROLE * @mac_ctx: Global MAC context * @lim_msg: LIM message * @pe_session: PE session * * Return: None */ void lim_process_ndi_del_sta_rsp(struct mac_context *mac_ctx, struct scheduler_msg *lim_msg, struct pe_session *pe_session) { tpDphHashNode sta_ds; tpDeleteStaParams del_sta_params = (tpDeleteStaParams) lim_msg->bodyptr; struct wlan_objmgr_vdev *vdev; struct wlan_objmgr_psoc *psoc = mac_ctx->psoc; struct nan_datapath_peer_ind peer_ind; if (!del_sta_params) { pe_err("del_sta_params is NULL"); return; } if (!LIM_IS_NDI_ROLE(pe_session)) { pe_err("Session %d is not NDI role", del_sta_params->assocId); goto skip_event; } sta_ds = dph_get_hash_entry(mac_ctx, del_sta_params->assocId, &pe_session->dph.dphHashTable); if (!sta_ds) { pe_err("DPH Entry for STA %X is missing", del_sta_params->assocId); goto skip_event; } if (QDF_STATUS_SUCCESS != del_sta_params->status) { pe_err("DEL STA failed!"); goto skip_event; } pe_info("Deleted STA AssocID %d MAC " QDF_MAC_ADDR_FMT, sta_ds->assocId, QDF_MAC_ADDR_REF(sta_ds->staAddr)); qdf_mem_copy(&peer_ind.peer_mac_addr.bytes, sta_ds->staAddr, sizeof(tSirMacAddr)); lim_release_peer_idx(mac_ctx, sta_ds->assocId, pe_session); lim_delete_dph_hash_entry(mac_ctx, sta_ds->staAddr, sta_ds->assocId, pe_session); pe_session->limMlmState = eLIM_MLM_IDLE_STATE; vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, pe_session->smeSessionId, WLAN_NAN_ID); if (!vdev) { pe_err("Failed to get vdev from id"); goto skip_event; } ucfg_nan_datapath_event_handler(psoc, vdev, NDP_PEER_DEPARTED, &peer_ind); wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); skip_event: qdf_mem_free(del_sta_params); lim_msg->bodyptr = NULL; } void lim_process_ndi_mlm_add_bss_rsp(struct mac_context *mac_ctx, struct add_bss_rsp *add_bss_rsp, struct pe_session *session_entry) { tLimMlmStartCnf mlm_start_cnf; if (!add_bss_rsp) { pe_err("add_bss_rsp is NULL"); return; } pe_debug("Status %d", add_bss_rsp->status); if (QDF_IS_STATUS_SUCCESS(add_bss_rsp->status)) { pe_debug("WDA_ADD_BSS_RSP returned QDF_STATUS_SUCCESS"); session_entry->limMlmState = eLIM_MLM_BSS_STARTED_STATE; MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, session_entry->peSessionId, session_entry->limMlmState)); session_entry->vdev_id = add_bss_rsp->vdev_id; session_entry->limSystemRole = eLIM_NDI_ROLE; session_entry->statypeForBss = STA_ENTRY_SELF; /* Apply previously set configuration at HW */ lim_apply_configuration(mac_ctx, session_entry); mlm_start_cnf.resultCode = eSIR_SME_SUCCESS; /* Initialize peer ID pool */ lim_init_peer_idxpool(mac_ctx, session_entry); } else { pe_err("WDA_ADD_BSS_REQ failed with status %d", add_bss_rsp->status); mlm_start_cnf.resultCode = eSIR_SME_HAL_SEND_MESSAGE_FAIL; } mlm_start_cnf.sessionId = session_entry->peSessionId; lim_send_start_bss_confirm(mac_ctx, &mlm_start_cnf); } void lim_ndi_del_bss_rsp(struct mac_context * mac_ctx, struct del_bss_resp *del_bss, struct pe_session *session_entry) { tSirResultCodes rc = eSIR_SME_SUCCESS; SET_LIM_PROCESS_DEFD_MESGS(mac_ctx, true); if (!del_bss) { pe_err("NDI: DEL_BSS_RSP with no body!"); rc = eSIR_SME_STOP_BSS_FAILURE; goto end; } session_entry = pe_find_session_by_vdev_id(mac_ctx, del_bss->vdev_id); if (!session_entry) { pe_err("Session Does not exist for given sessionID"); goto end; } if (del_bss->status != QDF_STATUS_SUCCESS) { pe_err("NDI: DEL_BSS_RSP error (%x)", del_bss->status); rc = eSIR_SME_STOP_BSS_FAILURE; goto end; } session_entry->limMlmState = eLIM_MLM_IDLE_STATE; end: /* Delete PE session once BSS is deleted */ if (session_entry) { lim_send_stop_bss_response(mac_ctx, session_entry->vdev_id, rc); pe_delete_session(mac_ctx, session_entry); session_entry = NULL; } } static QDF_STATUS lim_send_sme_ndp_add_sta_rsp(struct mac_context *mac_ctx, struct pe_session *session, tAddStaParams *add_sta_rsp) { struct nan_datapath_peer_ind *new_peer_ind; struct wlan_objmgr_psoc *psoc = mac_ctx->psoc; struct wlan_objmgr_vdev *vdev; if (!add_sta_rsp) { pe_debug("Invalid add_sta_rsp"); return QDF_STATUS_E_INVAL; } if (!psoc) { pe_debug("Invalid psoc"); return QDF_STATUS_E_INVAL; } vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, add_sta_rsp->smesessionId, WLAN_NAN_ID); if (!vdev) { pe_err("Failed to get vdev from id"); return QDF_STATUS_E_INVAL; } new_peer_ind = qdf_mem_malloc(sizeof(*new_peer_ind)); if (!new_peer_ind) { wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); return QDF_STATUS_E_NOMEM; } qdf_mem_copy(new_peer_ind->peer_mac_addr.bytes, add_sta_rsp->staMac, sizeof(tSirMacAddr)); ucfg_nan_datapath_event_handler(psoc, vdev, NDP_NEW_PEER, new_peer_ind); qdf_mem_free(new_peer_ind); wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID); return QDF_STATUS_SUCCESS; } /** * lim_ndp_add_sta_rsp() - handles add sta rsp for NDP from WMA * @mac_ctx: handle to mac structure * @session: session pointer * @add_sta_rsp: add sta response struct * * Return: None */ void lim_ndp_add_sta_rsp(struct mac_context *mac_ctx, struct pe_session *session, tAddStaParams *add_sta_rsp) { tpDphHashNode sta_ds; uint16_t peer_idx; if (!add_sta_rsp) { pe_err("Invalid add_sta_rsp"); qdf_mem_free(add_sta_rsp); return; } SET_LIM_PROCESS_DEFD_MESGS(mac_ctx, true); sta_ds = dph_lookup_hash_entry(mac_ctx, add_sta_rsp->staMac, &peer_idx, &session->dph.dphHashTable); if (!sta_ds) { pe_err("NAN: ADD_STA_RSP for unknown MAC addr " QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(add_sta_rsp->staMac)); qdf_mem_free(add_sta_rsp); return; } if (add_sta_rsp->status != QDF_STATUS_SUCCESS) { pe_err("NAN: ADD_STA_RSP error %x for MAC addr: "QDF_MAC_ADDR_FMT, add_sta_rsp->status, QDF_MAC_ADDR_REF(add_sta_rsp->staMac)); /* delete the sta_ds allocated during ADD STA */ lim_delete_dph_hash_entry(mac_ctx, add_sta_rsp->staMac, peer_idx, session); qdf_mem_free(add_sta_rsp); return; } sta_ds->valid = 1; sta_ds->mlmStaContext.mlmState = eLIM_MLM_LINK_ESTABLISHED_STATE; lim_send_sme_ndp_add_sta_rsp(mac_ctx, session, add_sta_rsp); qdf_mem_free(add_sta_rsp); }