1  /*
2   * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
3   * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4   *
5   * Permission to use, copy, modify, and/or distribute this software for any
6   * purpose with or without fee is hereby granted, provided that the above
7   * copyright notice and this permission notice appear in all copies.
8   *
9   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16   */
17  
18  #include "wlan_cm_main.h"
19  #include "wlan_cm_roam_sm.h"
20  #include "wlan_cm_sm.h"
21  #include <include/wlan_mlme_cmn.h>
22  #include "wlan_cm_main_api.h"
23  #include <wlan_scan_api.h>
24  #include <wlan_serialization_api.h>
25  #include <wlan_utility.h>
26  #include <wlan_cm_api.h>
27  #ifdef WLAN_POLICY_MGR_ENABLE
28  #include "wlan_policy_mgr_api.h"
29  #endif
30  
31  static void
cm_fill_roam_fail_resp_from_cm_id(struct cnx_mgr * cm_ctx,struct wlan_cm_connect_resp * resp,wlan_cm_id cm_id,enum wlan_cm_connect_fail_reason reason)32  cm_fill_roam_fail_resp_from_cm_id(struct cnx_mgr *cm_ctx,
33  				  struct wlan_cm_connect_resp *resp,
34  				  wlan_cm_id cm_id,
35  				  enum wlan_cm_connect_fail_reason reason)
36  {
37  	resp->connect_status = QDF_STATUS_E_FAILURE;
38  	resp->cm_id = cm_id;
39  	resp->vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
40  	resp->reason = reason;
41  	cm_fill_bss_info_in_roam_rsp_by_cm_id(cm_ctx, cm_id, resp);
42  }
43  
44  static QDF_STATUS
cm_reassoc_fail_disconnect(struct wlan_objmgr_vdev * vdev,enum wlan_cm_source source,enum wlan_reason_code reason_code,struct qdf_mac_addr * bssid)45  cm_reassoc_fail_disconnect(struct wlan_objmgr_vdev *vdev,
46  			   enum wlan_cm_source source,
47  			   enum wlan_reason_code reason_code,
48  			   struct qdf_mac_addr *bssid)
49  {
50  	struct cnx_mgr *cm_ctx;
51  	struct cm_req *cm_req;
52  	struct cm_disconnect_req *disconnect_req;
53  	struct wlan_cm_disconnect_req req = {0};
54  	QDF_STATUS status;
55  
56  	cm_ctx = cm_get_cm_ctx(vdev);
57  	if (!cm_ctx)
58  		return QDF_STATUS_E_INVAL;
59  
60  	/*
61  	 * This would be freed as part of removal from cm req list if adding
62  	 * to list is success after posting WLAN_CM_SM_EV_DISCONNECT_REQ.
63  	 */
64  	cm_req = qdf_mem_malloc(sizeof(*cm_req));
65  	if (!cm_req)
66  		return QDF_STATUS_E_NOMEM;
67  
68  	req.vdev_id = wlan_vdev_get_id(vdev);
69  	req.source = source;
70  	req.reason_code = reason_code;
71  	if (bssid)
72  		qdf_copy_macaddr(&req.bssid, bssid);
73  
74  	disconnect_req = &cm_req->discon_req;
75  	disconnect_req->req = req;
76  
77  	status = cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_DISCONNECT_REQ,
78  					  sizeof(*disconnect_req),
79  					  disconnect_req);
80  	if (QDF_IS_STATUS_ERROR(status))
81  		qdf_mem_free(cm_req);
82  
83  	return status;
84  }
85  
86  QDF_STATUS
cm_send_reassoc_start_fail(struct cnx_mgr * cm_ctx,wlan_cm_id cm_id,enum wlan_cm_connect_fail_reason reason,bool sync)87  cm_send_reassoc_start_fail(struct cnx_mgr *cm_ctx,
88  			   wlan_cm_id cm_id,
89  			   enum wlan_cm_connect_fail_reason reason,
90  			   bool sync)
91  {
92  	struct wlan_cm_connect_resp *resp;
93  	QDF_STATUS status;
94  
95  	resp = qdf_mem_malloc(sizeof(*resp));
96  	if (!resp)
97  		return QDF_STATUS_E_NOMEM;
98  
99  	cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id, reason);
100  	if (sync)
101  		status = cm_sm_deliver_event_sync(
102  				cm_ctx, WLAN_CM_SM_EV_REASSOC_FAILURE,
103  				sizeof(*resp), resp);
104  	else
105  		status = cm_sm_deliver_event(cm_ctx->vdev,
106  					     WLAN_CM_SM_EV_REASSOC_FAILURE,
107  					     sizeof(*resp), resp);
108  
109  	if (QDF_IS_STATUS_ERROR(status))
110  		cm_reassoc_complete(cm_ctx, resp);
111  
112  	qdf_mem_free(resp);
113  
114  	return status;
115  }
116  
117  #ifdef CONN_MGR_ADV_FEATURE
118  static QDF_STATUS
cm_update_roam_scan_filter(struct wlan_objmgr_vdev * vdev,struct cm_roam_req * cm_req,struct scan_filter * filter,bool security_valid_for_6ghz)119  cm_update_roam_scan_filter(
120  		struct wlan_objmgr_vdev *vdev, struct cm_roam_req *cm_req,
121  		struct scan_filter *filter, bool security_valid_for_6ghz)
122  {
123  	return cm_update_advance_roam_scan_filter(vdev, filter);
124  }
125  #else
126  static QDF_STATUS
cm_update_roam_scan_filter(struct wlan_objmgr_vdev * vdev,struct cm_roam_req * cm_req,struct scan_filter * filter,bool security_valid_for_6ghz)127  cm_update_roam_scan_filter(
128  		struct wlan_objmgr_vdev *vdev, struct cm_roam_req *cm_req,
129  		struct scan_filter *filter, bool security_valid_for_6ghz)
130  {
131  	uint16_t rsn_caps;
132  
133  	filter->num_of_ssid = 1;
134  	wlan_vdev_mlme_get_ssid(vdev, filter->ssid_list[0].ssid,
135  				&filter->ssid_list[0].length);
136  
137  	if (cm_req->req.chan_freq) {
138  		filter->num_of_channels = 1;
139  		filter->chan_freq_list[0] = cm_req->req.chan_freq;
140  	}
141  
142  	/* Security is not valid for 6Ghz so ignore 6Ghz APs */
143  	if (!security_valid_for_6ghz)
144  		filter->ignore_6ghz_channel = true;
145  
146  	if (!QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_WAPI) &&
147  	    !QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_RSNA) &&
148  	    !QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_WPA)) {
149  		filter->ignore_auth_enc_type = 1;
150  	}
151  
152  	rsn_caps =
153  		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_RSN_CAP);
154  
155  	if (rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED)
156  		filter->pmf_cap = WLAN_PMF_REQUIRED;
157  	else if (rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_ENABLED)
158  		filter->pmf_cap = WLAN_PMF_CAPABLE;
159  	else
160  		filter->pmf_cap = WLAN_PMF_DISABLED;
161  	return QDF_STATUS_SUCCESS;
162  }
163  #endif
164  
cm_connect_prepare_scan_filter_for_roam(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req,struct scan_filter * filter,bool security_valid_for_6ghz)165  static QDF_STATUS cm_connect_prepare_scan_filter_for_roam(
166  		struct cnx_mgr *cm_ctx, struct cm_roam_req *cm_req,
167  		struct scan_filter *filter, bool security_valid_for_6ghz)
168  {
169  	struct wlan_objmgr_vdev *vdev = cm_ctx->vdev;
170  
171  	if (!qdf_is_macaddr_zero(&cm_req->req.bssid)) {
172  		filter->num_of_bssid = 1;
173  		qdf_copy_macaddr(&filter->bssid_list[0], &cm_req->req.bssid);
174  	}
175  
176  	filter->authmodeset =
177  		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_AUTH_MODE);
178  
179  	filter->ucastcipherset =
180  		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_UCAST_CIPHER);
181  
182  	filter->mcastcipherset =
183  		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_MCAST_CIPHER);
184  
185  	filter->key_mgmt =
186  		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT);
187  
188  	filter->mgmtcipherset =
189  		wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_MGMT_CIPHER);
190  
191  	return cm_update_roam_scan_filter(vdev, cm_req, filter,
192  					  security_valid_for_6ghz);
193  }
194  
cm_roam_get_candidates(struct wlan_objmgr_pdev * pdev,struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)195  static QDF_STATUS cm_roam_get_candidates(struct wlan_objmgr_pdev *pdev,
196  					 struct cnx_mgr *cm_ctx,
197  					 struct cm_roam_req *cm_req)
198  {
199  	struct scan_filter *filter;
200  	uint32_t num_bss = 0;
201  	enum QDF_OPMODE op_mode;
202  	qdf_list_t *candidate_list;
203  	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
204  	qdf_list_node_t *cur_node = NULL;
205  	bool security_valid_for_6ghz = true;
206  
207  	filter = qdf_mem_malloc(sizeof(*filter));
208  	if (!filter)
209  		return QDF_STATUS_E_NOMEM;
210  
211  	cm_connect_prepare_scan_filter_for_roam(cm_ctx, cm_req, filter,
212  						security_valid_for_6ghz);
213  
214  	candidate_list = wlan_scan_get_result(pdev, filter);
215  	if (candidate_list) {
216  		num_bss = qdf_list_size(candidate_list);
217  		mlme_debug(CM_PREFIX_FMT "num_entries found %d",
218  			   CM_PREFIX_REF(vdev_id, cm_req->cm_id), num_bss);
219  	}
220  
221  	op_mode = wlan_vdev_mlme_get_opmode(cm_ctx->vdev);
222  	if (num_bss && op_mode == QDF_STA_MODE)
223  		cm_calculate_scores(cm_ctx, pdev, filter, candidate_list);
224  
225  	qdf_mem_free(filter);
226  
227  	if (!candidate_list || !qdf_list_size(candidate_list)) {
228  		if (candidate_list)
229  			wlan_scan_purge_results(candidate_list);
230  
231  		mlme_info(CM_PREFIX_FMT "no valid candidate found, num_bss %d",
232  			  CM_PREFIX_REF(vdev_id, cm_req->cm_id), num_bss);
233  		cm_req->candidate_list = NULL;
234  		return QDF_STATUS_E_EMPTY;
235  	}
236  
237  	qdf_list_peek_front(candidate_list, &cur_node);
238  	cm_req->candidate_list = candidate_list;
239  	cm_req->cur_candidate = qdf_container_of(cur_node,
240  						 struct scan_cache_node,
241  						 node);
242  	return QDF_STATUS_SUCCESS;
243  }
244  
245  #ifdef WLAN_FEATURE_PREAUTH_ENABLE
cm_handle_reassoc_timer(struct cnx_mgr * cm_ctx,wlan_cm_id * cm_id)246  QDF_STATUS cm_handle_reassoc_timer(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id)
247  {
248  	struct cm_req *cm_req;
249  
250  	if (!cm_id)
251  		return QDF_STATUS_E_INVAL;
252  
253  	cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
254  	if (!cm_req)
255  		return QDF_STATUS_E_INVAL;
256  
257  	return cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_START_REASSOC,
258  					sizeof(cm_req->roam_req),
259  					&cm_req->roam_req);
260  }
261  
cm_host_roam_start(struct cnx_mgr * cm_ctx,struct cm_req * cm_req)262  static QDF_STATUS cm_host_roam_start(struct cnx_mgr *cm_ctx,
263  				     struct cm_req *cm_req)
264  {
265  	cm_req->roam_req.cur_candidate = NULL;
266  
267  	return cm_host_roam_preauth_start(cm_ctx, cm_req);
268  }
269  
270  static
cm_host_roam_start_fail(struct cnx_mgr * cm_ctx,struct cm_req * cm_req,enum wlan_cm_connect_fail_reason reason)271  QDF_STATUS cm_host_roam_start_fail(struct cnx_mgr *cm_ctx,
272  				   struct cm_req *cm_req,
273  				   enum wlan_cm_connect_fail_reason reason)
274  {
275  	cm_send_preauth_start_fail(cm_ctx, cm_req->cm_id, reason);
276  
277  	return QDF_STATUS_SUCCESS;
278  }
279  #else
cm_host_roam_start(struct cnx_mgr * cm_ctx,struct cm_req * cm_req)280  static QDF_STATUS cm_host_roam_start(struct cnx_mgr *cm_ctx,
281  				     struct cm_req *cm_req)
282  {
283  	return cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_START_REASSOC,
284  					sizeof(cm_req->roam_req),
285  					&cm_req->roam_req);
286  }
287  
288  static
cm_host_roam_start_fail(struct cnx_mgr * cm_ctx,struct cm_req * cm_req,enum wlan_cm_connect_fail_reason reason)289  QDF_STATUS cm_host_roam_start_fail(struct cnx_mgr *cm_ctx,
290  				   struct cm_req *cm_req,
291  				   enum wlan_cm_connect_fail_reason reason)
292  {
293  	return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true);
294  }
295  #endif
296  
cm_host_roam_start_req(struct cnx_mgr * cm_ctx,struct cm_req * cm_req)297  QDF_STATUS cm_host_roam_start_req(struct cnx_mgr *cm_ctx,
298  				  struct cm_req *cm_req)
299  {
300  	QDF_STATUS status;
301  	struct wlan_objmgr_pdev *pdev;
302  	enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE;
303  
304  	mlme_cm_roam_start_ind(cm_ctx->vdev, &cm_req->roam_req.req);
305  
306  	pdev = wlan_vdev_get_pdev(cm_ctx->vdev);
307  	if (!pdev) {
308  		reason = CM_GENERIC_FAILURE;
309  		goto roam_err;
310  	}
311  
312  	status = cm_roam_get_candidates(pdev, cm_ctx, &cm_req->roam_req);
313  	if (QDF_IS_STATUS_ERROR(status)) {
314  		reason = CM_NO_CANDIDATE_FOUND;
315  		goto roam_err;
316  	}
317  
318  	status = cm_host_roam_start(cm_ctx, cm_req);
319  	if (QDF_IS_STATUS_SUCCESS(status))
320  		return status;
321  
322  roam_err:
323  	return cm_host_roam_start_fail(cm_ctx, cm_req, reason);
324  }
325  
cm_roam_resp_cmid_match_list_head(struct cnx_mgr * cm_ctx,struct wlan_cm_connect_resp * resp)326  bool cm_roam_resp_cmid_match_list_head(struct cnx_mgr *cm_ctx,
327  				       struct wlan_cm_connect_resp *resp)
328  {
329  	return cm_check_cmid_match_list_head(cm_ctx, &resp->cm_id);
330  }
331  
cm_reassoc_active(struct cnx_mgr * cm_ctx,wlan_cm_id * cm_id)332  QDF_STATUS cm_reassoc_active(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id)
333  {
334  	struct cm_req *cm_req;
335  	struct wlan_cm_vdev_discon_req req;
336  	struct cm_disconnect_req *discon_req;
337  
338  	cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
339  	if (!cm_req)
340  		return QDF_STATUS_E_INVAL;
341  
342  	cm_ctx->active_cm_id = *cm_id;
343  
344  	qdf_mem_zero(&req, sizeof(req));
345  	req.cm_id = *cm_id;
346  	req.req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
347  	req.req.source = CM_ROAM_DISCONNECT;
348  	wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &req.req.bssid);
349  
350  	discon_req = qdf_mem_malloc(sizeof(*discon_req));
351  	if (!discon_req)
352  		return QDF_STATUS_E_NOMEM;
353  
354  	discon_req->cm_id = *cm_id;
355  	discon_req->req.vdev_id = req.req.vdev_id;
356  	qdf_copy_macaddr(&discon_req->req.bssid,
357  			 &req.req.bssid);
358  	cm_update_scan_mlme_on_disconnect(cm_ctx->vdev, discon_req);
359  	qdf_mem_free(discon_req);
360  
361  	return mlme_cm_disconnect_req(cm_ctx->vdev, &req);
362  }
363  
cm_reassoc_disconnect_complete(struct cnx_mgr * cm_ctx,struct wlan_cm_discon_rsp * resp)364  QDF_STATUS cm_reassoc_disconnect_complete(struct cnx_mgr *cm_ctx,
365  					  struct wlan_cm_discon_rsp *resp)
366  {
367  	QDF_STATUS status;
368  	struct cm_req *cm_req;
369  	struct qdf_mac_addr *bssid;
370  	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
371  	wlan_cm_id cm_id = cm_ctx->active_cm_id;
372  
373  	cm_req = cm_get_req_by_cm_id(cm_ctx, cm_id);
374  	if (!cm_req)
375  		return QDF_STATUS_E_INVAL;
376  
377  	mlme_cm_disconnect_complete_ind(cm_ctx->vdev, resp);
378  	bssid = &cm_req->roam_req.cur_candidate->entry->bssid;
379  
380  	status = mlme_cm_bss_peer_create_req(cm_ctx->vdev, bssid, NULL, false);
381  	if (QDF_IS_STATUS_ERROR(status)) {
382  		mlme_err(CM_PREFIX_FMT "Peer create request failed",
383  			 CM_PREFIX_REF(vdev_id, cm_id));
384  		status = cm_send_reassoc_start_fail(cm_ctx, cm_id,
385  						    CM_PEER_CREATE_FAILED,
386  						    true);
387  	}
388  
389  	return status;
390  }
391  
392  QDF_STATUS
cm_resume_reassoc_after_peer_create(struct cnx_mgr * cm_ctx,wlan_cm_id * cm_id)393  cm_resume_reassoc_after_peer_create(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id)
394  {
395  	struct wlan_cm_vdev_reassoc_req *req;
396  	struct cm_req *cm_req;
397  	QDF_STATUS status;
398  
399  	cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
400  	if (!cm_req)
401  		return QDF_STATUS_E_FAILURE;
402  
403  	req = qdf_mem_malloc(sizeof(*req));
404  	if (!req)
405  		return QDF_STATUS_E_NOMEM;
406  
407  	req->vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
408  	req->cm_id = *cm_id;
409  	req->bss = cm_req->roam_req.cur_candidate;
410  
411  	mlme_nofl_info(CM_PREFIX_FMT "Reassoc to " QDF_SSID_FMT " " QDF_MAC_ADDR_FMT " rssi: %d freq: %d source %d",
412  		       CM_PREFIX_REF(req->vdev_id, req->cm_id),
413  		       QDF_SSID_REF(req->bss->entry->ssid.length,
414  				    req->bss->entry->ssid.ssid),
415  		       QDF_MAC_ADDR_REF(req->bss->entry->bssid.bytes),
416  		       req->bss->entry->rssi_raw,
417  		       req->bss->entry->channel.chan_freq,
418  		       cm_req->roam_req.req.source);
419  
420  	status = mlme_cm_reassoc_req(cm_ctx->vdev, req);
421  	if (QDF_IS_STATUS_ERROR(status)) {
422  		mlme_err(CM_PREFIX_FMT "Reassoc request failed",
423  			 CM_PREFIX_REF(req->vdev_id, req->cm_id));
424  		mlme_cm_bss_peer_delete_req(cm_ctx->vdev);
425  		status = cm_send_reassoc_start_fail(cm_ctx, *cm_id,
426  						    CM_JOIN_FAILED, true);
427  	}
428  
429  	qdf_mem_free(req);
430  	return status;
431  }
432  
cm_reassoc_complete(struct cnx_mgr * cm_ctx,struct wlan_cm_connect_resp * resp)433  QDF_STATUS cm_reassoc_complete(struct cnx_mgr *cm_ctx,
434  			       struct wlan_cm_connect_resp *resp)
435  {
436  	/*
437  	 * If the entry is not present in the list, it must have been cleared
438  	 * already.
439  	 */
440  	if (!cm_get_req_by_cm_id(cm_ctx, resp->cm_id))
441  		return QDF_STATUS_SUCCESS;
442  
443  	resp->is_reassoc = true;
444  	cm_connect_complete(cm_ctx, resp);
445  	/*
446  	 * If roaming fails and conn_sm is in ROAMING state, then
447  	 * initiate disconnect to cleanup and move conn_sm to INIT state
448  	 */
449  	if (QDF_IS_STATUS_ERROR(resp->connect_status) &&
450  	    cm_get_state(cm_ctx) == WLAN_CM_S_ROAMING) {
451  		cm_reassoc_fail_disconnect(cm_ctx->vdev, CM_ROAM_DISCONNECT,
452  					   REASON_UNSPEC_FAILURE,
453  					   &resp->bssid);
454  	}
455  
456  	return QDF_STATUS_SUCCESS;
457  }
458  
459  static void
cm_reassoc_handle_event_post_fail(struct cnx_mgr * cm_ctx,wlan_cm_id cm_id)460  cm_reassoc_handle_event_post_fail(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id)
461  {
462  	struct wlan_cm_connect_resp *resp;
463  
464  	resp = qdf_mem_malloc(sizeof(*resp));
465  	if (!resp)
466  		return;
467  
468  	cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id,
469  					  CM_GENERIC_FAILURE);
470  	cm_reassoc_complete(cm_ctx, resp);
471  	qdf_mem_free(resp);
472  }
473  
cm_reassoc_cmd_timeout(struct cnx_mgr * cm_ctx,wlan_cm_id cm_id)474  static QDF_STATUS cm_reassoc_cmd_timeout(struct cnx_mgr *cm_ctx,
475  					 wlan_cm_id cm_id)
476  {
477  	struct wlan_cm_connect_resp *resp;
478  	QDF_STATUS status;
479  
480  	resp = qdf_mem_malloc(sizeof(*resp));
481  	if (!resp)
482  		return QDF_STATUS_E_NOMEM;
483  
484  	cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id, CM_SER_TIMEOUT);
485  	status = cm_sm_deliver_event(cm_ctx->vdev,
486  				     WLAN_CM_SM_EV_REASSOC_FAILURE,
487  				     sizeof(*resp), resp);
488  	if (QDF_IS_STATUS_ERROR(status))
489  		cm_reassoc_complete(cm_ctx, resp);
490  
491  	qdf_mem_free(resp);
492  
493  	return status;
494  }
495  
496  #ifdef WLAN_CM_USE_SPINLOCK
cm_activate_reassoc_req_sched_cb(struct scheduler_msg * msg)497  static QDF_STATUS cm_activate_reassoc_req_sched_cb(struct scheduler_msg *msg)
498  {
499  	struct wlan_serialization_command *cmd = msg->bodyptr;
500  	struct wlan_objmgr_vdev *vdev;
501  	struct cnx_mgr *cm_ctx;
502  	QDF_STATUS ret = QDF_STATUS_E_FAILURE;
503  
504  	if (!cmd || !cmd->vdev) {
505  		mlme_err("Invalid Input");
506  		return QDF_STATUS_E_INVAL;
507  	}
508  
509  	vdev = cmd->vdev;
510  	cm_ctx = cm_get_cm_ctx(vdev);
511  	if (!cm_ctx)
512  		return QDF_STATUS_E_INVAL;
513  
514  	ret = cm_sm_deliver_event(vdev,
515  				  WLAN_CM_SM_EV_REASSOC_ACTIVE,
516  				  sizeof(wlan_cm_id),
517  				  &cmd->cmd_id);
518  	/*
519  	 * Called from scheduler context hence posting failure
520  	 */
521  	if (QDF_IS_STATUS_ERROR(ret))
522  		cm_reassoc_handle_event_post_fail(cm_ctx, cmd->cmd_id);
523  
524  	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
525  	return ret;
526  }
527  
528  static QDF_STATUS
cm_activate_reassoc_req(struct wlan_serialization_command * cmd)529  cm_activate_reassoc_req(struct wlan_serialization_command *cmd)
530  {
531  	struct wlan_objmgr_vdev *vdev = cmd->vdev;
532  	struct scheduler_msg msg = {0};
533  	QDF_STATUS ret;
534  
535  	msg.bodyptr = cmd;
536  	msg.callback = cm_activate_reassoc_req_sched_cb;
537  	msg.flush_callback = cm_activate_cmd_req_flush_cb;
538  
539  	ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_MLME_CM_ID);
540  	if (QDF_IS_STATUS_ERROR(ret))
541  		return ret;
542  
543  	ret = scheduler_post_message(QDF_MODULE_ID_MLME,
544  				     QDF_MODULE_ID_MLME,
545  				     QDF_MODULE_ID_MLME, &msg);
546  
547  	if (QDF_IS_STATUS_ERROR(ret)) {
548  		mlme_err(CM_PREFIX_FMT "Failed to post scheduler_msg",
549  			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id));
550  		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
551  		return ret;
552  	}
553  
554  	return ret;
555  }
556  #else
557  static QDF_STATUS
cm_activate_reassoc_req(struct wlan_serialization_command * cmd)558  cm_activate_reassoc_req(struct wlan_serialization_command *cmd)
559  {
560  	return cm_sm_deliver_event(cmd->vdev,
561  				   WLAN_CM_SM_EV_REASSOC_ACTIVE,
562  				   sizeof(wlan_cm_id),
563  				   &cmd->cmd_id);
564  }
565  #endif
566  
567  static QDF_STATUS
cm_ser_reassoc_cb(struct wlan_serialization_command * cmd,enum wlan_serialization_cb_reason reason)568  cm_ser_reassoc_cb(struct wlan_serialization_command *cmd,
569  		  enum wlan_serialization_cb_reason reason)
570  {
571  	QDF_STATUS status = QDF_STATUS_SUCCESS;
572  	struct wlan_objmgr_vdev *vdev;
573  	struct cnx_mgr *cm_ctx;
574  	enum qdf_hang_reason hang_reason =
575  				QDF_VDEV_ACTIVE_SER_REASSOC_TIMEOUT;
576  
577  	if (!cmd) {
578  		mlme_err("cmd is NULL, reason: %d", reason);
579  		QDF_ASSERT(0);
580  		return QDF_STATUS_E_NULL_VALUE;
581  	}
582  
583  	vdev = cmd->vdev;
584  	cm_ctx = cm_get_cm_ctx(vdev);
585  	if (!cm_ctx)
586  		return QDF_STATUS_E_NULL_VALUE;
587  
588  	switch (reason) {
589  	case WLAN_SER_CB_ACTIVATE_CMD:
590  		/*
591  		 * For pending to active reason, use async api to take lock.
592  		 * For direct activation use sync api to avoid taking lock
593  		 * as lock is already acquired by the requester.
594  		 */
595  		if (cmd->activation_reason == SER_PENDING_TO_ACTIVE)
596  			status = cm_activate_reassoc_req(cmd);
597  		else
598  			status = cm_sm_deliver_event_sync(
599  					cm_ctx, WLAN_CM_SM_EV_REASSOC_ACTIVE,
600  					sizeof(wlan_cm_id), &cmd->cmd_id);
601  
602  		if (QDF_IS_STATUS_SUCCESS(status))
603  			break;
604  		/*
605  		 * Handle failure if posting fails, i.e. the SM state has
606  		 * changed or head cm_id doesn't match the active cm_id.
607  		 * connect active should be handled only in JOIN_PENDING. If
608  		 * new command has been received connect activation should be
609  		 * aborted from here with connect req cleanup.
610  		 */
611  		cm_reassoc_handle_event_post_fail(cm_ctx, cmd->cmd_id);
612  		break;
613  	case WLAN_SER_CB_CANCEL_CMD:
614  		/* command removed from pending list. */
615  		break;
616  	case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT:
617  		mlme_err(CM_PREFIX_FMT "Active command timeout",
618  			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id));
619  		cm_trigger_panic_on_cmd_timeout(cm_ctx->vdev, hang_reason);
620  		cm_reassoc_cmd_timeout(cm_ctx, cmd->cmd_id);
621  		break;
622  	case WLAN_SER_CB_RELEASE_MEM_CMD:
623  		cm_reset_active_cm_id(vdev, cmd->cmd_id);
624  		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
625  		break;
626  	default:
627  		QDF_ASSERT(0);
628  		status = QDF_STATUS_E_INVAL;
629  		break;
630  	}
631  
632  	return status;
633  }
634  
635  #define REASSOC_TIMEOUT	10000
cm_ser_reassoc_req(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)636  static QDF_STATUS cm_ser_reassoc_req(struct cnx_mgr *cm_ctx,
637  				     struct cm_roam_req *cm_req)
638  {
639  	struct wlan_serialization_command cmd = {0, };
640  	enum wlan_serialization_status ser_cmd_status;
641  	QDF_STATUS status;
642  	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
643  
644  	mlme_cm_osif_roam_sync_ind(cm_ctx->vdev);
645  
646  	status = wlan_objmgr_vdev_try_get_ref(cm_ctx->vdev, WLAN_MLME_CM_ID);
647  	if (QDF_IS_STATUS_ERROR(status)) {
648  		mlme_err(CM_PREFIX_FMT "unable to get reference",
649  			 CM_PREFIX_REF(vdev_id, cm_req->cm_id));
650  		return status;
651  	}
652  
653  	cmd.cmd_type = WLAN_SER_CMD_VDEV_ROAM;
654  	cmd.cmd_id = cm_req->cm_id;
655  	cmd.cmd_cb = cm_ser_reassoc_cb;
656  	cmd.source = WLAN_UMAC_COMP_MLME;
657  	cmd.is_high_priority = false;
658  	cmd.cmd_timeout_duration = REASSOC_TIMEOUT;
659  	cmd.vdev = cm_ctx->vdev;
660  	cmd.is_blocking = cm_ser_get_blocking_cmd();
661  
662  	ser_cmd_status = wlan_serialization_request(&cmd);
663  	switch (ser_cmd_status) {
664  	case WLAN_SER_CMD_PENDING:
665  		/* command moved to pending list.Do nothing */
666  		break;
667  	case WLAN_SER_CMD_ACTIVE:
668  		/* command moved to active list. Do nothing */
669  		break;
670  	default:
671  		mlme_err(CM_PREFIX_FMT "ser cmd status %d",
672  			 CM_PREFIX_REF(vdev_id, cm_req->cm_id), ser_cmd_status);
673  		wlan_objmgr_vdev_release_ref(cm_ctx->vdev, WLAN_MLME_CM_ID);
674  
675  		return QDF_STATUS_E_FAILURE;
676  	}
677  
678  	return QDF_STATUS_SUCCESS;
679  }
680  
681  #ifdef WLAN_POLICY_MGR_ENABLE
682  QDF_STATUS
cm_handle_reassoc_hw_mode_change(struct cnx_mgr * cm_ctx,wlan_cm_id * cm_id,enum wlan_cm_sm_evt event)683  cm_handle_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id,
684  				 enum wlan_cm_sm_evt event)
685  {
686  	struct cm_req *cm_req;
687  	enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE;
688  	struct wlan_objmgr_pdev *pdev;
689  	QDF_STATUS status;
690  
691  	if (!cm_id)
692  		return QDF_STATUS_E_FAILURE;
693  
694  	cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
695  	if (!cm_req)
696  		return QDF_STATUS_E_INVAL;
697  
698  	pdev = wlan_vdev_get_pdev(cm_ctx->vdev);
699  	if (!pdev) {
700  		mlme_err(CM_PREFIX_FMT "Failed to find pdev",
701  			 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
702  				       cm_req->cm_id));
703  		goto send_failure;
704  	}
705  
706  	if (event == WLAN_CM_SM_EV_HW_MODE_SUCCESS) {
707  		status = cm_ser_reassoc_req(cm_ctx, &cm_req->roam_req);
708  		if (QDF_IS_STATUS_ERROR(status)) {
709  			reason = CM_SER_FAILURE;
710  			goto send_failure;
711  		}
712  		return status;
713  	}
714  
715  	/* Set reason HW mode fail for event WLAN_CM_SM_EV_HW_MODE_FAILURE */
716  	reason = CM_HW_MODE_FAILURE;
717  
718  send_failure:
719  	return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true);
720  }
721  
cm_reassoc_hw_mode_change_resp(struct wlan_objmgr_pdev * pdev,uint8_t vdev_id,wlan_cm_id cm_id,QDF_STATUS status)722  void cm_reassoc_hw_mode_change_resp(struct wlan_objmgr_pdev *pdev,
723  				    uint8_t vdev_id, wlan_cm_id cm_id,
724  				    QDF_STATUS status)
725  {
726  	struct wlan_objmgr_vdev *vdev;
727  	QDF_STATUS qdf_status;
728  	enum wlan_cm_sm_evt event = WLAN_CM_SM_EV_HW_MODE_SUCCESS;
729  	struct cnx_mgr *cm_ctx;
730  
731  	mlme_debug(CM_PREFIX_FMT "Continue Reassoc after HW mode change, status %d",
732  		   CM_PREFIX_REF(vdev_id, cm_id), status);
733  
734  	vdev = wlan_objmgr_get_vdev_by_id_from_pdev(pdev, vdev_id,
735  						    WLAN_MLME_CM_ID);
736  	if (!vdev)
737  		return;
738  
739  	cm_ctx = cm_get_cm_ctx(vdev);
740  	if (!cm_ctx) {
741  		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
742  		return;
743  	}
744  
745  	if (QDF_IS_STATUS_ERROR(status))
746  		event = WLAN_CM_SM_EV_HW_MODE_FAILURE;
747  	qdf_status = cm_sm_deliver_event(vdev, event, sizeof(wlan_cm_id),
748  					 &cm_id);
749  
750  	/*
751  	 * Handle failure if posting fails, i.e. the SM state has
752  	 * changed or head cm_id doesn't match the active cm_id.
753  	 * hw mode change resp should be handled in REASSOC state. If
754  	 * new command has been received reassoc should be
755  	 * aborted from here with reassoc req cleanup.
756  	 */
757  	if (QDF_IS_STATUS_ERROR(qdf_status))
758  		cm_reassoc_handle_event_post_fail(cm_ctx, cm_id);
759  	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
760  }
761  
762  static QDF_STATUS
cm_check_for_reassoc_hw_mode_change(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)763  cm_check_for_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx,
764  				    struct cm_roam_req *cm_req)
765  {
766  	qdf_freq_t candidate_freq;
767  	struct wlan_objmgr_psoc *psoc;
768  	QDF_STATUS status;
769  
770  	psoc = wlan_vdev_get_psoc(cm_ctx->vdev);
771  	if (!psoc)
772  		return QDF_STATUS_E_INVAL;
773  
774  	if (!cm_req->cur_candidate)
775  		return QDF_STATUS_E_EMPTY;
776  
777  	candidate_freq = cm_req->cur_candidate->entry->channel.chan_freq;
778  	status = policy_mgr_handle_conc_multiport(
779  			psoc, cm_req->req.vdev_id,
780  			candidate_freq, POLICY_MGR_UPDATE_REASON_LFR2_ROAM,
781  			cm_req->cm_id);
782  	if (status == QDF_STATUS_E_NOSUPPORT)
783  		status = QDF_STATUS_E_ALREADY;
784  
785  	return status;
786  }
787  #else
788  static inline QDF_STATUS
cm_check_for_reassoc_hw_mode_change(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)789  cm_check_for_reassoc_hw_mode_change(struct cnx_mgr *cm_ctx,
790  				    struct cm_roam_req *cm_req)
791  {
792  	return QDF_STATUS_E_ALREADY;
793  }
794  #endif
795  
cm_reassoc_start(struct cnx_mgr * cm_ctx,struct cm_roam_req * cm_req)796  QDF_STATUS cm_reassoc_start(struct cnx_mgr *cm_ctx,
797  			    struct cm_roam_req *cm_req)
798  {
799  	QDF_STATUS status = QDF_STATUS_E_FAILURE;
800  	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
801  	enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE;
802  
803  	status = cm_check_for_reassoc_hw_mode_change(cm_ctx, cm_req);
804  	if (QDF_IS_STATUS_ERROR(status) && status != QDF_STATUS_E_ALREADY) {
805  		reason = CM_HW_MODE_FAILURE;
806  		mlme_err(CM_PREFIX_FMT "Failed to set HW mode change status %d",
807  			 CM_PREFIX_REF(vdev_id, cm_req->cm_id), status);
808  		goto err;
809  	} else if (QDF_IS_STATUS_SUCCESS(status)) {
810  		mlme_debug(CM_PREFIX_FMT "Reassoc will continue after HW mode change",
811  			   CM_PREFIX_REF(vdev_id, cm_req->cm_id));
812  		return QDF_STATUS_SUCCESS;
813  	}
814  
815  	status = cm_ser_reassoc_req(cm_ctx, cm_req);
816  	if (QDF_IS_STATUS_SUCCESS(status))
817  		return status;
818  
819  	reason = CM_SER_FAILURE;
820  	mlme_err(CM_PREFIX_FMT "Serialization of reassoc failed",
821  		 CM_PREFIX_REF(vdev_id, cm_req->cm_id));
822  err:
823  	return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason, true);
824  }
825  
cm_reassoc_rsp(struct wlan_objmgr_vdev * vdev,struct wlan_cm_connect_resp * resp)826  QDF_STATUS cm_reassoc_rsp(struct wlan_objmgr_vdev *vdev,
827  			  struct wlan_cm_connect_resp *resp)
828  {
829  	struct cnx_mgr *cm_ctx;
830  	QDF_STATUS qdf_status;
831  	wlan_cm_id cm_id;
832  	uint32_t prefix;
833  	enum wlan_cm_sm_evt event;
834  	struct qdf_mac_addr pmksa_mac = QDF_MAC_ADDR_ZERO_INIT;
835  
836  	cm_ctx = cm_get_cm_ctx(vdev);
837  	if (!cm_ctx)
838  		return QDF_STATUS_E_INVAL;
839  
840  	cm_id = cm_ctx->active_cm_id;
841  	prefix = CM_ID_GET_PREFIX(cm_id);
842  
843  	if (prefix != ROAM_REQ_PREFIX ||
844  	    cm_id != resp->cm_id) {
845  		mlme_err(CM_PREFIX_FMT " Active cm_id 0x%x is different",
846  			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->cm_id),
847  			 cm_id);
848  		qdf_status = QDF_STATUS_E_FAILURE;
849  		goto post_err;
850  	}
851  
852  	cm_connect_rsp_get_mld_addr_or_bssid(resp, &pmksa_mac);
853  
854  	if (QDF_IS_STATUS_SUCCESS(resp->connect_status)) {
855  		/*
856  		 * On successful connection to sae single pmk AP,
857  		 * clear all the single pmk AP.
858  		 */
859  		if (cm_is_cm_id_current_candidate_single_pmk(cm_ctx, cm_id))
860  			wlan_crypto_selective_clear_sae_single_pmk_entries(
861  					vdev, &pmksa_mac);
862  		event = WLAN_CM_SM_EV_REASSOC_DONE;
863  	} else {
864  		event = WLAN_CM_SM_EV_REASSOC_FAILURE;
865  	}
866  
867  	qdf_status = cm_sm_deliver_event(cm_ctx->vdev, event, sizeof(*resp),
868  					 resp);
869  	if (QDF_IS_STATUS_SUCCESS(qdf_status))
870  		return qdf_status;
871  post_err:
872  	cm_reassoc_complete(cm_ctx, resp);
873  
874  	return qdf_status;
875  }
876  
cm_roam_bss_peer_create_rsp(struct wlan_objmgr_vdev * vdev,QDF_STATUS status,struct qdf_mac_addr * peer_mac)877  QDF_STATUS cm_roam_bss_peer_create_rsp(struct wlan_objmgr_vdev *vdev,
878  				       QDF_STATUS status,
879  				       struct qdf_mac_addr *peer_mac)
880  {
881  	struct cnx_mgr *cm_ctx;
882  	QDF_STATUS qdf_status;
883  	wlan_cm_id cm_id;
884  	uint32_t prefix;
885  	struct cm_req *cm_req;
886  
887  	cm_ctx = cm_get_cm_ctx(vdev);
888  	if (!cm_ctx)
889  		return QDF_STATUS_E_INVAL;
890  
891  	cm_id = cm_ctx->active_cm_id;
892  	prefix = CM_ID_GET_PREFIX(cm_id);
893  
894  	if (prefix != ROAM_REQ_PREFIX) {
895  		mlme_err(CM_PREFIX_FMT "Active req is not roam req",
896  			 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), cm_id));
897  		mlme_cm_bss_peer_delete_req(vdev);
898  		return QDF_STATUS_E_INVAL;
899  	}
900  
901  	if (QDF_IS_STATUS_SUCCESS(status)) {
902  		qdf_status = cm_bss_peer_create_resp_mlo_attach(vdev, peer_mac);
903  		if (QDF_IS_STATUS_ERROR(qdf_status)) {
904  			mlme_cm_bss_peer_delete_req(vdev);
905  			goto send_err;
906  		}
907  
908  		qdf_status =
909  		    cm_sm_deliver_event(vdev,
910  					WLAN_CM_SM_EV_BSS_CREATE_PEER_SUCCESS,
911  					sizeof(wlan_cm_id), &cm_id);
912  
913  		if (QDF_IS_STATUS_ERROR(qdf_status)) {
914  			mlme_cm_bss_peer_delete_req(vdev);
915  			cm_reassoc_handle_event_post_fail(cm_ctx, cm_id);
916  		}
917  
918  		return qdf_status;
919  	}
920  
921  send_err:
922  	cm_req = cm_get_req_by_cm_id(cm_ctx, cm_id);
923  	if (!cm_req)
924  		return QDF_STATUS_E_INVAL;
925  
926  	return cm_send_reassoc_start_fail(cm_ctx, cm_id,
927  					  CM_PEER_CREATE_FAILED, false);
928  }
929  
cm_roam_disconnect_rsp(struct wlan_objmgr_vdev * vdev,struct wlan_cm_discon_rsp * resp)930  QDF_STATUS cm_roam_disconnect_rsp(struct wlan_objmgr_vdev *vdev,
931  				  struct wlan_cm_discon_rsp *resp)
932  {
933  	struct cnx_mgr *cm_ctx;
934  	QDF_STATUS qdf_status;
935  	wlan_cm_id cm_id;
936  	uint32_t prefix;
937  
938  	cm_ctx = cm_get_cm_ctx(vdev);
939  	if (!cm_ctx)
940  		return QDF_STATUS_E_INVAL;
941  
942  	cm_id = cm_ctx->active_cm_id;
943  	prefix = CM_ID_GET_PREFIX(cm_id);
944  
945  	if (prefix != ROAM_REQ_PREFIX || cm_id != resp->req.cm_id) {
946  		mlme_err(CM_PREFIX_FMT "Active cm_id 0x%x is different",
947  			 CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->req.cm_id),
948  			 cm_id);
949  		qdf_status = QDF_STATUS_E_FAILURE;
950  		goto disconnect_complete;
951  	}
952  	qdf_status =
953  		cm_sm_deliver_event(vdev,
954  				    WLAN_CM_SM_EV_HO_ROAM_DISCONNECT_DONE,
955  				    sizeof(*resp), resp);
956  	if (QDF_IS_STATUS_SUCCESS(qdf_status))
957  		return qdf_status;
958  
959  disconnect_complete:
960  	cm_reassoc_handle_event_post_fail(cm_ctx, cm_id);
961  	return qdf_status;
962  }
963