| /* |
| * Copyright (c) 2016-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: 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 (!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_sme_session_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 :" MAC_ADDRESS_STR, |
| QDF_MAC_ADDR_ARRAY(peer_mac_addr.bytes)); |
| |
| 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->inferface 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: "MAC_ADDRESS_STR" confirm rejected", |
| QDF_MAC_ADDR_ARRAY(peer_ndi_mac_addr.bytes)); |
| |
| session = pe_find_session_by_sme_session_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_info("ndp_map[%d]: MAC: " MAC_ADDRESS_STR " num_active %d", |
| i, |
| QDF_MAC_ADDR_ARRAY(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_sme_session_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 staId %d MAC " MAC_ADDRESS_STR, |
| sta_ds->assocId, sta_ds->staIndex, |
| QDF_MAC_ADDR_ARRAY(sta_ds->staAddr)); |
| |
| /* |
| * Copy peer info in del peer indication before |
| * lim_delete_dph_hash_entry is called as this will be lost. |
| */ |
| peer_ind.sta_id = sta_ds->staIndex; |
| 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; |
| } |
| |
| /** |
| * lim_process_ndi_mlm_add_bss_rsp() - Process ADD_BSS response for NDI |
| * @mac_ctx: Pointer to Global MAC structure |
| * @lim_msgq: The MsgQ header, which contains the response buffer |
| * @session_entry: PE session |
| * |
| * Return: None |
| */ |
| void lim_process_ndi_mlm_add_bss_rsp(struct mac_context *mac_ctx, |
| struct scheduler_msg *lim_msgq, |
| struct pe_session *session_entry) |
| { |
| tLimMlmStartCnf mlm_start_cnf; |
| tpAddBssParams add_bss_params = (tpAddBssParams) lim_msgq->bodyptr; |
| |
| if (!add_bss_params) { |
| pe_err("Invalid body pointer in message"); |
| goto end; |
| } |
| pe_debug("Status %d", add_bss_params->status); |
| if (QDF_STATUS_SUCCESS == add_bss_params->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->bssIdx = (uint8_t) add_bss_params->bssIdx; |
| session_entry->limSystemRole = eLIM_NDI_ROLE; |
| session_entry->statypeForBss = STA_ENTRY_SELF; |
| session_entry->staId = add_bss_params->staContext.staIdx; |
| /* 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_params->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); |
| end: |
| qdf_mem_free(lim_msgq->bodyptr); |
| lim_msgq->bodyptr = NULL; |
| } |
| |
| /** |
| * lim_ndi_del_bss_rsp() - Handler for DEL BSS resp for NDI interface |
| * @mac_ctx: handle to mac structure |
| * @msg: pointer to message |
| * @session_entry: session entry |
| * |
| * Return: None |
| */ |
| void lim_ndi_del_bss_rsp(struct mac_context * mac_ctx, |
| void *msg, struct pe_session *session_entry) |
| { |
| tSirResultCodes rc = eSIR_SME_SUCCESS; |
| tpDeleteBssParams del_bss = (tpDeleteBssParams) msg; |
| |
| 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_session_id(mac_ctx, del_bss->sessionId); |
| 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) Bss %d", |
| del_bss->status, del_bss->bssIdx); |
| rc = eSIR_SME_STOP_BSS_FAILURE; |
| goto end; |
| } |
| |
| if (lim_set_link_state(mac_ctx, eSIR_LINK_IDLE_STATE, |
| session_entry->selfMacAddr, |
| session_entry->selfMacAddr, NULL, NULL) |
| != QDF_STATUS_SUCCESS) { |
| pe_err("NDI: DEL_BSS_RSP setLinkState failed"); |
| goto end; |
| } |
| |
| session_entry->limMlmState = eLIM_MLM_IDLE_STATE; |
| |
| end: |
| if (del_bss) |
| qdf_mem_free(del_bss); |
| /* Delete PE session once BSS is deleted */ |
| if (session_entry) { |
| lim_send_sme_rsp(mac_ctx, eWNI_SME_STOP_BSS_RSP, |
| rc, session_entry->smeSessionId); |
| 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)); |
| new_peer_ind->sta_id = add_sta_rsp->staIdx; |
| |
| 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 " |
| MAC_ADDRESS_STR, |
| QDF_MAC_ADDR_ARRAY(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: %pM", |
| add_sta_rsp->status, 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->bssId = add_sta_rsp->bssIdx; |
| sta_ds->staIndex = add_sta_rsp->staIdx; |
| 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); |
| } |