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