1 /*
2  * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /**
21  * DOC: nan_datapath.c
22  *
23  * MAC NAN Data path API implementation
24  */
25 
26 #include "lim_utils.h"
27 #include "lim_api.h"
28 #include "lim_assoc_utils.h"
29 #include "nan_datapath.h"
30 #include "lim_types.h"
31 #include "lim_send_messages.h"
32 #include "wma_nan_datapath.h"
33 #include "os_if_nan.h"
34 #include "nan_public_structs.h"
35 #include "nan_ucfg_api.h"
36 
37 /**
38  * lim_add_ndi_peer() - Function to add ndi peer
39  * @mac_ctx: handle to mac structure
40  * @vdev_id: vdev id on which peer is added
41  * @peer_mac_addr: peer to be added
42  *
43  * Return: QDF_STATUS_SUCCESS on success; error number otherwise
44  */
lim_add_ndi_peer(struct mac_context * mac_ctx,uint32_t vdev_id,struct qdf_mac_addr peer_mac_addr)45 static QDF_STATUS lim_add_ndi_peer(struct mac_context *mac_ctx,
46 	uint32_t vdev_id, struct qdf_mac_addr peer_mac_addr)
47 {
48 	struct pe_session *session;
49 	tpDphHashNode sta_ds;
50 	uint16_t assoc_id, peer_idx;
51 	QDF_STATUS status;
52 	uint8_t zero_mac_addr[QDF_MAC_ADDR_SIZE] = { 0, 0, 0, 0, 0, 0 };
53 
54 	if (!wlan_is_vdev_id_up(mac_ctx->pdev, vdev_id)) {
55 		pe_err_rl("NDI vdev is not up");
56 		return QDF_STATUS_E_FAILURE;
57 	}
58 
59 	if (!qdf_mem_cmp(&zero_mac_addr, &peer_mac_addr.bytes[0],
60 			QDF_MAC_ADDR_SIZE)) {
61 		pe_err("Failing to add peer with all zero mac addr");
62 		return QDF_STATUS_E_FAILURE;
63 	}
64 
65 	session = pe_find_session_by_vdev_id(mac_ctx, vdev_id);
66 	if (!session) {
67 		/* couldn't find session */
68 		pe_err("Session not found for vdev_id: %d", vdev_id);
69 		return QDF_STATUS_E_FAILURE;
70 	}
71 
72 	sta_ds = dph_lookup_hash_entry(mac_ctx,
73 				peer_mac_addr.bytes,
74 				&assoc_id, &session->dph.dphHashTable);
75 	/* peer exists, don't do anything */
76 	if (sta_ds) {
77 		pe_err("NDI Peer already exists!!");
78 		return QDF_STATUS_SUCCESS;
79 	}
80 	pe_info("Need to create NDI Peer :" QDF_MAC_ADDR_FMT,
81 		QDF_MAC_ADDR_REF(peer_mac_addr.bytes));
82 
83 	ucfg_nan_set_peer_mc_list(session->vdev, peer_mac_addr);
84 
85 	peer_idx = lim_assign_peer_idx(mac_ctx, session);
86 	if (!peer_idx) {
87 		pe_err("Invalid peer_idx: %d", peer_idx);
88 		return QDF_STATUS_SUCCESS;
89 	}
90 
91 	sta_ds = dph_add_hash_entry(mac_ctx, peer_mac_addr.bytes, peer_idx,
92 			&session->dph.dphHashTable);
93 	if (!sta_ds) {
94 		pe_err("Couldn't add dph entry");
95 		/* couldn't add dph entry */
96 		return QDF_STATUS_E_FAILURE;
97 	}
98 
99 	/* wma decides NDI mode from wma->interface struct */
100 	sta_ds->staType = STA_ENTRY_NDI_PEER;
101 	status = lim_add_sta(mac_ctx, sta_ds, false, session);
102 	if (QDF_STATUS_SUCCESS != status) {
103 		/* couldn't add peer */
104 		pe_err("limAddSta failed status: %d",
105 			status);
106 		return QDF_STATUS_E_FAILURE;
107 	}
108 
109 	return QDF_STATUS_SUCCESS;
110 }
111 
lim_add_ndi_peer_converged(uint32_t vdev_id,struct qdf_mac_addr peer_mac_addr)112 QDF_STATUS lim_add_ndi_peer_converged(uint32_t vdev_id,
113 				struct qdf_mac_addr peer_mac_addr)
114 {
115 	struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
116 
117 	if (!mac_ctx)
118 		return QDF_STATUS_E_NULL_VALUE;
119 
120 	return lim_add_ndi_peer(mac_ctx, vdev_id, peer_mac_addr);
121 }
122 
123 /**
124  * lim_ndp_delete_peer_by_addr() - Delete NAN data peer, given addr and vdev_id
125  * @mac_ctx: handle to mac context
126  * @vdev_id: vdev_id on which peer was added
127  * @peer_ndi_mac_addr: mac addr of peer
128  * This function deletes a peer if there was NDP_Confirm with REJECT
129  *
130  * Return: None
131  */
lim_ndp_delete_peer_by_addr(struct mac_context * mac_ctx,uint8_t vdev_id,struct qdf_mac_addr peer_ndi_mac_addr)132 static void lim_ndp_delete_peer_by_addr(struct mac_context *mac_ctx, uint8_t vdev_id,
133 					struct qdf_mac_addr peer_ndi_mac_addr)
134 {
135 	struct pe_session *session;
136 	tpDphHashNode sta_ds;
137 	uint16_t peer_idx;
138 	uint8_t zero_mac_addr[QDF_MAC_ADDR_SIZE] = { 0, 0, 0, 0, 0, 0 };
139 
140 	if (!qdf_mem_cmp(&zero_mac_addr, &peer_ndi_mac_addr.bytes[0],
141 			QDF_MAC_ADDR_SIZE)) {
142 		pe_err("Failing to delete the peer with all zero mac addr");
143 		return;
144 	}
145 
146 	pe_info("deleting peer: "QDF_MAC_ADDR_FMT" confirm rejected",
147 		QDF_MAC_ADDR_REF(peer_ndi_mac_addr.bytes));
148 
149 	session = pe_find_session_by_vdev_id(mac_ctx, vdev_id);
150 	if (!session || (session->bssType != eSIR_NDI_MODE)) {
151 		pe_err("PE session is NULL or non-NDI for sme session %d",
152 			vdev_id);
153 		return;
154 	}
155 
156 	sta_ds = dph_lookup_hash_entry(mac_ctx, peer_ndi_mac_addr.bytes,
157 				    &peer_idx, &session->dph.dphHashTable);
158 	if (!sta_ds) {
159 		pe_err("Unknown NDI Peer");
160 		return;
161 	}
162 	if (sta_ds->staType != STA_ENTRY_NDI_PEER) {
163 		pe_err("Non-NDI Peer ignored");
164 		return;
165 	}
166 	/*
167 	 * Call lim_del_sta() with response required set true. Hence
168 	 * DphHashEntry will be deleted after receiving that response.
169 	 */
170 
171 	lim_del_sta(mac_ctx, sta_ds, true, session);
172 }
173 
lim_ndp_delete_peers_by_addr_converged(uint8_t vdev_id,struct qdf_mac_addr peer_ndi_mac_addr)174 void lim_ndp_delete_peers_by_addr_converged(uint8_t vdev_id,
175 					struct qdf_mac_addr peer_ndi_mac_addr)
176 {
177 	struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
178 
179 	if (!mac_ctx)
180 		return;
181 
182 	lim_ndp_delete_peer_by_addr(mac_ctx, vdev_id, peer_ndi_mac_addr);
183 }
184 
185 /**
186  * lim_ndp_delete_peers() - Delete NAN data peers
187  * @mac_ctx: handle to mac context
188  * @ndp_map: NDP instance/peer map
189  * @num_peers: number of peers entries in peer_map
190  * This function deletes a peer if there are no active NDPs left with that peer
191  *
192  * Return: None
193  */
lim_ndp_delete_peers(struct mac_context * mac_ctx,struct peer_ndp_map * ndp_map,uint8_t num_peers)194 static void lim_ndp_delete_peers(struct mac_context *mac_ctx,
195 				struct peer_ndp_map *ndp_map, uint8_t num_peers)
196 {
197 	tpDphHashNode sta_ds = NULL;
198 	uint16_t deleted_num = 0;
199 	int i, j;
200 	struct pe_session *session;
201 	struct qdf_mac_addr *deleted_peers;
202 	uint16_t peer_idx;
203 	bool found;
204 
205 	deleted_peers = qdf_mem_malloc(num_peers * sizeof(*deleted_peers));
206 	if (!deleted_peers)
207 		return;
208 
209 	for (i = 0; i < num_peers; i++) {
210 		pe_debug("ndp_map[%d]: MAC: " QDF_MAC_ADDR_FMT " num_active %d",
211 			 i,
212 			 QDF_MAC_ADDR_REF(ndp_map[i].peer_ndi_mac_addr.bytes),
213 			 ndp_map[i].num_active_ndp_sessions);
214 
215 		/* Do not delete a peer with active NDPs */
216 		if (ndp_map[i].num_active_ndp_sessions > 0)
217 			continue;
218 
219 		session = pe_find_session_by_vdev_id(mac_ctx,
220 						     ndp_map[i].vdev_id);
221 		if (!session || (session->bssType != eSIR_NDI_MODE)) {
222 			pe_err("PE session is NULL or non-NDI for sme session %d",
223 				ndp_map[i].vdev_id);
224 			continue;
225 		}
226 
227 		/* Check if this peer is already in the deleted list */
228 		found = false;
229 		for (j = 0; j < deleted_num && !found; j++) {
230 			if (!qdf_mem_cmp(
231 				&deleted_peers[j].bytes,
232 				&ndp_map[i].peer_ndi_mac_addr.bytes,
233 				QDF_MAC_ADDR_SIZE)) {
234 				found = true;
235 				break;
236 			}
237 		}
238 		if (found)
239 			continue;
240 
241 		sta_ds = dph_lookup_hash_entry(mac_ctx,
242 				ndp_map[i].peer_ndi_mac_addr.bytes,
243 				&peer_idx, &session->dph.dphHashTable);
244 		if (!sta_ds) {
245 			pe_err("Unknown NDI Peer");
246 			continue;
247 		}
248 		if (sta_ds->staType != STA_ENTRY_NDI_PEER) {
249 			pe_err("Non-NDI Peer ignored");
250 			continue;
251 		}
252 		/*
253 		 * Call lim_del_sta() with response required set true.
254 		 * Hence DphHashEntry will be deleted after receiving
255 		 * that response.
256 		 */
257 		lim_del_sta(mac_ctx, sta_ds, true, session);
258 		qdf_copy_macaddr(&deleted_peers[deleted_num++],
259 			&ndp_map[i].peer_ndi_mac_addr);
260 	}
261 	qdf_mem_free(deleted_peers);
262 }
263 
lim_ndp_delete_peers_converged(struct peer_nan_datapath_map * ndp_map,uint8_t num_peers)264 void lim_ndp_delete_peers_converged(struct peer_nan_datapath_map *ndp_map,
265 				    uint8_t num_peers)
266 {
267 	struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
268 
269 	if (!mac_ctx)
270 		return;
271 
272 	lim_ndp_delete_peers(mac_ctx, (struct peer_ndp_map *)ndp_map,
273 			     num_peers);
274 }
275 
276 /**
277  * lim_process_ndi_del_sta_rsp() - Handle WDA_DELETE_STA_RSP in eLIM_NDI_ROLE
278  * @mac_ctx: Global MAC context
279  * @lim_msg: LIM message
280  * @pe_session: PE session
281  *
282  * Return: None
283  */
lim_process_ndi_del_sta_rsp(struct mac_context * mac_ctx,struct scheduler_msg * lim_msg,struct pe_session * pe_session)284 void lim_process_ndi_del_sta_rsp(struct mac_context *mac_ctx,
285 				 struct scheduler_msg *lim_msg,
286 				 struct pe_session *pe_session)
287 {
288 	tpDphHashNode sta_ds;
289 	tpDeleteStaParams del_sta_params = (tpDeleteStaParams) lim_msg->bodyptr;
290 	struct wlan_objmgr_vdev *vdev;
291 	struct wlan_objmgr_psoc *psoc = mac_ctx->psoc;
292 	struct nan_datapath_peer_ind peer_ind;
293 
294 	if (!del_sta_params) {
295 		pe_err("del_sta_params is NULL");
296 		return;
297 	}
298 	if (!LIM_IS_NDI_ROLE(pe_session)) {
299 		pe_err("Session %d is not NDI role", del_sta_params->assocId);
300 		goto skip_event;
301 	}
302 
303 	sta_ds = dph_get_hash_entry(mac_ctx, del_sta_params->assocId,
304 			&pe_session->dph.dphHashTable);
305 	if (!sta_ds) {
306 		pe_err("DPH Entry for STA %X is missing",
307 			del_sta_params->assocId);
308 		goto skip_event;
309 	}
310 
311 	if (QDF_STATUS_SUCCESS != del_sta_params->status) {
312 		pe_err("DEL STA failed!");
313 		goto skip_event;
314 	}
315 	pe_info("Deleted STA AssocID %d MAC " QDF_MAC_ADDR_FMT,
316 		sta_ds->assocId,
317 		QDF_MAC_ADDR_REF(sta_ds->staAddr));
318 
319 	qdf_mem_copy(&peer_ind.peer_mac_addr.bytes,
320 		sta_ds->staAddr, sizeof(tSirMacAddr));
321 	lim_release_peer_idx(mac_ctx, sta_ds->assocId, pe_session);
322 	lim_delete_dph_hash_entry(mac_ctx, sta_ds->staAddr, sta_ds->assocId,
323 			pe_session);
324 	pe_session->limMlmState = eLIM_MLM_IDLE_STATE;
325 
326 	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
327 						    pe_session->smeSessionId,
328 						    WLAN_NAN_ID);
329 	if (!vdev) {
330 		pe_err("Failed to get vdev from id");
331 		goto skip_event;
332 	}
333 	ucfg_nan_datapath_event_handler(psoc, vdev, NDP_PEER_DEPARTED,
334 					&peer_ind);
335 	wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID);
336 
337 skip_event:
338 	qdf_mem_free(del_sta_params);
339 	lim_msg->bodyptr = NULL;
340 }
341 
lim_process_ndi_mlm_add_bss_rsp(struct mac_context * mac_ctx,struct add_bss_rsp * add_bss_rsp,struct pe_session * session_entry)342 void lim_process_ndi_mlm_add_bss_rsp(struct mac_context *mac_ctx,
343 				     struct add_bss_rsp *add_bss_rsp,
344 				     struct pe_session *session_entry)
345 {
346 	tLimMlmStartCnf mlm_start_cnf;
347 
348 	if (!add_bss_rsp) {
349 		pe_err("add_bss_rsp is NULL");
350 		return;
351 	}
352 	pe_debug("Status %d", add_bss_rsp->status);
353 	if (QDF_IS_STATUS_SUCCESS(add_bss_rsp->status)) {
354 		pe_debug("WDA_ADD_BSS_RSP returned QDF_STATUS_SUCCESS");
355 		session_entry->limMlmState = eLIM_MLM_BSS_STARTED_STATE;
356 		MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE,
357 			session_entry->peSessionId,
358 			session_entry->limMlmState));
359 		session_entry->vdev_id = add_bss_rsp->vdev_id;
360 		session_entry->limSystemRole = eLIM_NDI_ROLE;
361 		session_entry->statypeForBss = STA_ENTRY_SELF;
362 		/* Apply previously set configuration at HW */
363 		lim_apply_configuration(mac_ctx, session_entry);
364 		mlm_start_cnf.resultCode = eSIR_SME_SUCCESS;
365 
366 		/* Initialize peer ID pool */
367 		lim_init_peer_idxpool(mac_ctx, session_entry);
368 	} else {
369 		pe_err("WDA_ADD_BSS_REQ failed with status %d",
370 			add_bss_rsp->status);
371 		mlm_start_cnf.resultCode = eSIR_SME_HAL_SEND_MESSAGE_FAIL;
372 	}
373 	mlm_start_cnf.sessionId = session_entry->peSessionId;
374 	lim_send_start_bss_confirm(mac_ctx, &mlm_start_cnf);
375 }
376 
lim_ndi_del_bss_rsp(struct mac_context * mac_ctx,struct del_bss_resp * del_bss,struct pe_session * session_entry)377 void lim_ndi_del_bss_rsp(struct mac_context * mac_ctx,
378 			 struct del_bss_resp *del_bss,
379 			 struct pe_session *session_entry)
380 {
381 	tSirResultCodes rc = eSIR_SME_SUCCESS;
382 
383 	SET_LIM_PROCESS_DEFD_MESGS(mac_ctx, true);
384 	if (!del_bss) {
385 		pe_err("NDI: DEL_BSS_RSP with no body!");
386 		rc = eSIR_SME_STOP_BSS_FAILURE;
387 		goto end;
388 	}
389 	session_entry = pe_find_session_by_vdev_id(mac_ctx, del_bss->vdev_id);
390 	if (!session_entry) {
391 		pe_err("Session Does not exist for given sessionID");
392 		goto end;
393 	}
394 
395 	if (del_bss->status != QDF_STATUS_SUCCESS) {
396 		pe_err("NDI: DEL_BSS_RSP error (%x)", del_bss->status);
397 		rc = eSIR_SME_STOP_BSS_FAILURE;
398 		goto end;
399 	}
400 
401 	session_entry->limMlmState = eLIM_MLM_IDLE_STATE;
402 
403 end:
404 	/* Delete PE session once BSS is deleted */
405 	if (session_entry) {
406 		lim_send_stop_bss_response(mac_ctx,
407 					   session_entry->vdev_id,
408 					   rc);
409 		pe_delete_session(mac_ctx, session_entry);
410 		session_entry = NULL;
411 	}
412 }
413 
lim_send_sme_ndp_add_sta_rsp(struct mac_context * mac_ctx,struct pe_session * session,tAddStaParams * add_sta_rsp)414 static QDF_STATUS lim_send_sme_ndp_add_sta_rsp(struct mac_context *mac_ctx,
415 						struct pe_session *session,
416 						tAddStaParams *add_sta_rsp)
417 {
418 	struct nan_datapath_peer_ind *new_peer_ind;
419 	struct wlan_objmgr_psoc *psoc = mac_ctx->psoc;
420 	struct wlan_objmgr_vdev *vdev;
421 
422 	if (!add_sta_rsp) {
423 		pe_debug("Invalid add_sta_rsp");
424 		return QDF_STATUS_E_INVAL;
425 	}
426 
427 	if (!psoc) {
428 		pe_debug("Invalid psoc");
429 		return QDF_STATUS_E_INVAL;
430 	}
431 
432 	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
433 						    add_sta_rsp->smesessionId,
434 						    WLAN_NAN_ID);
435 	if (!vdev) {
436 		pe_err("Failed to get vdev from id");
437 		return QDF_STATUS_E_INVAL;
438 	}
439 
440 	new_peer_ind = qdf_mem_malloc(sizeof(*new_peer_ind));
441 	if (!new_peer_ind) {
442 		wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID);
443 		return QDF_STATUS_E_NOMEM;
444 	}
445 
446 	qdf_mem_copy(new_peer_ind->peer_mac_addr.bytes, add_sta_rsp->staMac,
447 		     sizeof(tSirMacAddr));
448 
449 	ucfg_nan_datapath_event_handler(psoc, vdev, NDP_NEW_PEER, new_peer_ind);
450 	qdf_mem_free(new_peer_ind);
451 	wlan_objmgr_vdev_release_ref(vdev, WLAN_NAN_ID);
452 	return QDF_STATUS_SUCCESS;
453 }
454 
455 /**
456  * lim_ndp_add_sta_rsp() - handles add sta rsp for NDP from WMA
457  * @mac_ctx: handle to mac structure
458  * @session: session pointer
459  * @add_sta_rsp: add sta response struct
460  *
461  * Return: None
462  */
lim_ndp_add_sta_rsp(struct mac_context * mac_ctx,struct pe_session * session,tAddStaParams * add_sta_rsp)463 void lim_ndp_add_sta_rsp(struct mac_context *mac_ctx, struct pe_session *session,
464 			 tAddStaParams *add_sta_rsp)
465 {
466 	tpDphHashNode sta_ds;
467 	uint16_t peer_idx;
468 
469 	if (!add_sta_rsp) {
470 		pe_err("Invalid add_sta_rsp");
471 		qdf_mem_free(add_sta_rsp);
472 		return;
473 	}
474 
475 	SET_LIM_PROCESS_DEFD_MESGS(mac_ctx, true);
476 	sta_ds = dph_lookup_hash_entry(mac_ctx, add_sta_rsp->staMac, &peer_idx,
477 				    &session->dph.dphHashTable);
478 	if (!sta_ds) {
479 		pe_err("NAN: ADD_STA_RSP for unknown MAC addr "
480 			QDF_MAC_ADDR_FMT,
481 			QDF_MAC_ADDR_REF(add_sta_rsp->staMac));
482 		qdf_mem_free(add_sta_rsp);
483 		return;
484 	}
485 
486 	if (add_sta_rsp->status != QDF_STATUS_SUCCESS) {
487 		pe_err("NAN: ADD_STA_RSP error %x for MAC addr: "QDF_MAC_ADDR_FMT,
488 			add_sta_rsp->status,
489 			QDF_MAC_ADDR_REF(add_sta_rsp->staMac));
490 		/* delete the sta_ds allocated during ADD STA */
491 		lim_delete_dph_hash_entry(mac_ctx, add_sta_rsp->staMac,
492 				      peer_idx, session);
493 		qdf_mem_free(add_sta_rsp);
494 		return;
495 	}
496 	sta_ds->valid = 1;
497 	sta_ds->mlmStaContext.mlmState = eLIM_MLM_LINK_ESTABLISHED_STATE;
498 	lim_send_sme_ndp_add_sta_rsp(mac_ctx, session, add_sta_rsp);
499 	qdf_mem_free(add_sta_rsp);
500 }
501