1 /*
2 * Copyright (c) 2012-2015,2020-2021 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2021-2023 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 /**
19 * DOC: Implements general SM framework for connection manager roaming sm
20 */
21
22 #include "wlan_cm_main.h"
23 #include "wlan_cm_roam_sm.h"
24 #include "wlan_cm_sm.h"
25 #include "wlan_cm_main_api.h"
26 #include "wlan_cm_roam.h"
27 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
28 #include "wlan_mlo_mgr_roam.h"
29 #endif
30
31 #if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD)
cm_state_roaming_entry(void * ctx)32 void cm_state_roaming_entry(void *ctx)
33 {
34 struct cnx_mgr *cm_ctx = ctx;
35
36 cm_sm_state_update(cm_ctx, WLAN_CM_S_ROAMING, WLAN_CM_SS_IDLE);
37 }
38
cm_state_roaming_exit(void * ctx)39 void cm_state_roaming_exit(void *ctx)
40 {
41 }
42
43 #ifdef WLAN_FEATURE_ROAM_OFFLOAD
44 static
cm_handle_fw_roaming_event(struct cnx_mgr * cm_ctx,uint16_t event,uint16_t data_len,void * data)45 bool cm_handle_fw_roaming_event(struct cnx_mgr *cm_ctx, uint16_t event,
46 uint16_t data_len, void *data)
47 {
48 bool event_handled = true;
49 QDF_STATUS status;
50
51 switch (event) {
52 case WLAN_CM_SM_EV_ROAM_INVOKE:
53 status = cm_add_fw_roam_cmd_to_list_n_ser(cm_ctx, data);
54 if (QDF_IS_STATUS_ERROR(status)) {
55 event_handled = false;
56 break;
57 }
58 cm_sm_transition_to(cm_ctx, WLAN_CM_SS_ROAM_STARTED);
59 cm_sm_deliver_event_sync(cm_ctx,
60 WLAN_CM_SM_EV_ROAM_INVOKE,
61 data_len, data);
62 break;
63 case WLAN_CM_SM_EV_ROAM_START:
64 status = cm_add_fw_roam_cmd_to_list_n_ser(cm_ctx, data);
65 if (QDF_IS_STATUS_ERROR(status)) {
66 event_handled = false;
67 break;
68 }
69 cm_sm_transition_to(cm_ctx, WLAN_CM_SS_ROAM_STARTED);
70 cm_sm_deliver_event_sync(cm_ctx,
71 WLAN_CM_SM_EV_ROAM_START,
72 0, NULL);
73 break;
74 case WLAN_CM_SM_EV_ROAM_ABORT:
75 cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTED);
76 cm_sm_deliver_event_sync(cm_ctx, event,
77 data_len, data);
78 break;
79 case WLAN_CM_SM_EV_ROAM_SYNC:
80 cm_sm_transition_to(cm_ctx, WLAN_CM_SS_ROAM_SYNC);
81 status = cm_sm_deliver_event_sync(cm_ctx, event,
82 data_len, data);
83 if (QDF_IS_STATUS_ERROR(status))
84 event_handled = false;
85 break;
86 default:
87 event_handled = false;
88 break;
89 }
90
91 return event_handled;
92 }
93 #else
94 static inline
cm_handle_fw_roaming_event(struct cnx_mgr * cm_ctx,uint16_t event,uint16_t data_len,void * data)95 bool cm_handle_fw_roaming_event(struct cnx_mgr *cm_ctx, uint16_t event,
96 uint16_t data_len, void *data)
97 {
98 return false;
99 }
100 #endif
101
cm_state_roaming_event(void * ctx,uint16_t event,uint16_t data_len,void * data)102 bool cm_state_roaming_event(void *ctx, uint16_t event,
103 uint16_t data_len, void *data)
104 {
105 struct cnx_mgr *cm_ctx = ctx;
106 bool event_handled = true;
107 struct wlan_objmgr_psoc *psoc;
108
109 switch (event) {
110 case WLAN_CM_SM_EV_ROAM_REQ:
111 psoc = wlan_vdev_get_psoc(cm_ctx->vdev);
112 if (!psoc) {
113 event_handled = false;
114 break;
115 }
116 if (cm_roam_offload_enabled(psoc)) {
117 cm_sm_deliver_event_sync(cm_ctx,
118 WLAN_CM_SM_EV_ROAM_INVOKE,
119 data_len, data);
120 } else {
121 cm_add_roam_req_to_list(cm_ctx, data);
122 cm_sm_transition_to(cm_ctx, WLAN_CM_SS_PREAUTH);
123 cm_sm_deliver_event_sync(cm_ctx,
124 WLAN_CM_SM_EV_ROAM_START,
125 data_len, data);
126 }
127 break;
128 default:
129 event_handled = cm_handle_fw_roaming_event(cm_ctx, event,
130 data_len, data);
131 break;
132 }
133
134 return event_handled;
135 }
136
cm_handle_connect_disconnect_in_roam(struct cnx_mgr * cm_ctx,uint16_t event,uint16_t data_len,void * data)137 static bool cm_handle_connect_disconnect_in_roam(struct cnx_mgr *cm_ctx,
138 uint16_t event,
139 uint16_t data_len, void *data)
140 {
141 QDF_STATUS status;
142
143 switch (event) {
144 case WLAN_CM_SM_EV_CONNECT_REQ:
145 status = cm_handle_connect_req_in_non_init_state(cm_ctx, data,
146 WLAN_CM_S_ROAMING);
147 if (QDF_IS_STATUS_ERROR(status))
148 return false;
149 cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTING);
150 cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_CONNECT_START,
151 data_len, data);
152 break;
153 case WLAN_CM_SM_EV_DISCONNECT_REQ:
154 status = cm_handle_discon_req_in_non_connected_state(cm_ctx,
155 data, WLAN_CM_S_ROAMING);
156 if (QDF_IS_STATUS_ERROR(status))
157 return false;
158 cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
159 cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_DISCONNECT_START,
160 data_len, data);
161 break;
162 case WLAN_CM_SM_EV_DISCONNECT_ACTIVE:
163 cm_disconnect_active(cm_ctx, data);
164 break;
165 default:
166 return false;
167 break;
168 }
169
170 return true;
171 }
172 #endif
173
174 #ifdef WLAN_FEATURE_HOST_ROAM
cm_subst_preauth_entry(void * ctx)175 void cm_subst_preauth_entry(void *ctx)
176 {
177 struct cnx_mgr *cm_ctx = ctx;
178
179 if (cm_get_state(cm_ctx) != WLAN_CM_S_ROAMING)
180 QDF_BUG(0);
181
182 cm_set_substate(cm_ctx, WLAN_CM_SS_PREAUTH);
183 /* set preauth to true when we enter preauth state */
184 cm_ctx->preauth_in_progress = true;
185 }
186
cm_subst_preauth_exit(void * ctx)187 void cm_subst_preauth_exit(void *ctx)
188 {
189 }
190
191 #ifdef WLAN_FEATURE_PREAUTH_ENABLE
192 static bool
cm_handle_preauth_event(struct cnx_mgr * cm_ctx,uint16_t event,uint16_t data_len,void * data)193 cm_handle_preauth_event(struct cnx_mgr *cm_ctx, uint16_t event,
194 uint16_t data_len, void *data)
195 {
196 bool event_handled = true;
197
198 switch (event) {
199 case WLAN_CM_SM_EV_PREAUTH_ACTIVE:
200 if (!cm_check_cmid_match_list_head(cm_ctx, data)) {
201 event_handled = false;
202 break;
203 }
204 cm_preauth_active(cm_ctx, data);
205 break;
206 case WLAN_CM_SM_EV_PREAUTH_RESP:
207 cm_preauth_done_resp(cm_ctx, data);
208 break;
209 case WLAN_CM_SM_EV_PREAUTH_DONE:
210 cm_sm_transition_to(cm_ctx, WLAN_CM_SS_REASSOC);
211 cm_preauth_success(cm_ctx, data);
212 break;
213 case WLAN_CM_SM_EV_PREAUTH_FAIL:
214 cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTED);
215 cm_preauth_fail(cm_ctx, data);
216 break;
217 default:
218 event_handled = false;
219 }
220
221 return event_handled;
222 }
223 #else
224 static inline bool
cm_handle_preauth_event(struct cnx_mgr * cm_ctx,uint16_t event,uint16_t data_len,void * data)225 cm_handle_preauth_event(struct cnx_mgr *cm_ctx, uint16_t event,
226 uint16_t data_len, void *data)
227 {
228 return false;
229 }
230 #endif
231
cm_subst_preauth_event(void * ctx,uint16_t event,uint16_t data_len,void * data)232 bool cm_subst_preauth_event(void *ctx, uint16_t event,
233 uint16_t data_len, void *data)
234 {
235 struct cnx_mgr *cm_ctx = ctx;
236 bool event_handled = true;
237
238 switch (event) {
239 case WLAN_CM_SM_EV_CONNECT_REQ:
240 case WLAN_CM_SM_EV_DISCONNECT_REQ:
241 case WLAN_CM_SM_EV_DISCONNECT_ACTIVE:
242 event_handled =
243 cm_handle_connect_disconnect_in_roam(cm_ctx, event,
244 data_len, data);
245 break;
246 case WLAN_CM_SM_EV_ROAM_START:
247 cm_host_roam_start_req(cm_ctx, data);
248 break;
249 case WLAN_CM_SM_EV_START_REASSOC:
250 cm_sm_transition_to(cm_ctx, WLAN_CM_SS_REASSOC);
251 cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
252 break;
253 case WLAN_CM_SM_EV_REASSOC_FAILURE:
254 cm_reassoc_complete(cm_ctx, data);
255 break;
256 default:
257 event_handled = cm_handle_preauth_event(cm_ctx, event,
258 data_len, data);
259 break;
260 }
261
262 return event_handled;
263 }
264
cm_subst_reassoc_entry(void * ctx)265 void cm_subst_reassoc_entry(void *ctx)
266 {
267 struct cnx_mgr *cm_ctx = ctx;
268
269 if (cm_get_state(cm_ctx) != WLAN_CM_S_ROAMING)
270 QDF_BUG(0);
271
272 cm_set_substate(cm_ctx, WLAN_CM_SS_REASSOC);
273 /* set preauth to false as soon as we move to reassoc state */
274 cm_ctx->preauth_in_progress = false;
275 }
276
cm_subst_reassoc_exit(void * ctx)277 void cm_subst_reassoc_exit(void *ctx)
278 {
279 }
280
281 #ifdef WLAN_FEATURE_PREAUTH_ENABLE
282 static bool
cm_handle_reassoc_event(struct cnx_mgr * cm_ctx,uint16_t event,uint16_t data_len,void * data)283 cm_handle_reassoc_event(struct cnx_mgr *cm_ctx, uint16_t event,
284 uint16_t data_len, void *data)
285 {
286 bool event_handled = true;
287 QDF_STATUS status;
288
289 switch (event) {
290 case WLAN_CM_SM_EV_REASSOC_TIMER:
291 status = cm_handle_reassoc_timer(cm_ctx, data);
292 if (QDF_IS_STATUS_ERROR(status))
293 event_handled = false;
294 break;
295 default:
296 event_handled = false;
297 }
298
299 return event_handled;
300 }
301 #else
302 static inline bool
cm_handle_reassoc_event(struct cnx_mgr * cm_ctx,uint16_t event,uint16_t data_len,void * data)303 cm_handle_reassoc_event(struct cnx_mgr *cm_ctx, uint16_t event,
304 uint16_t data_len, void *data)
305 {
306 return false;
307 }
308 #endif
309
cm_subst_reassoc_event(void * ctx,uint16_t event,uint16_t data_len,void * data)310 bool cm_subst_reassoc_event(void *ctx, uint16_t event,
311 uint16_t data_len, void *data)
312 {
313 struct cnx_mgr *cm_ctx = ctx;
314 bool event_handled = true;
315
316 switch (event) {
317 case WLAN_CM_SM_EV_CONNECT_REQ:
318 case WLAN_CM_SM_EV_DISCONNECT_REQ:
319 case WLAN_CM_SM_EV_DISCONNECT_ACTIVE:
320 event_handled =
321 cm_handle_connect_disconnect_in_roam(cm_ctx, event,
322 data_len, data);
323 break;
324 case WLAN_CM_SM_EV_START_REASSOC:
325 cm_reassoc_start(cm_ctx, data);
326 break;
327 case WLAN_CM_SM_EV_REASSOC_ACTIVE:
328 if (!cm_check_cmid_match_list_head(cm_ctx, data)) {
329 event_handled = false;
330 break;
331 }
332 cm_reassoc_active(cm_ctx, data);
333 break;
334 case WLAN_CM_SM_EV_HO_ROAM_DISCONNECT_DONE:
335 cm_reassoc_disconnect_complete(cm_ctx, data);
336 break;
337 case WLAN_CM_SM_EV_BSS_CREATE_PEER_SUCCESS:
338 if (!cm_check_cmid_match_list_head(cm_ctx, data)) {
339 event_handled = false;
340 break;
341 }
342 cm_resume_reassoc_after_peer_create(cm_ctx, data);
343 break;
344 case WLAN_CM_SM_EV_REASSOC_DONE:
345 if (!cm_roam_resp_cmid_match_list_head(cm_ctx, data)) {
346 event_handled = false;
347 break;
348 }
349 cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTED);
350 cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
351 break;
352 case WLAN_CM_SM_EV_REASSOC_FAILURE:
353 cm_reassoc_complete(cm_ctx, data);
354 break;
355 case WLAN_CM_SM_EV_HW_MODE_SUCCESS:
356 case WLAN_CM_SM_EV_HW_MODE_FAILURE:
357 /* check if cm id is valid for the current req */
358 if (!cm_check_cmid_match_list_head(cm_ctx, data)) {
359 event_handled = false;
360 break;
361 }
362 cm_handle_reassoc_hw_mode_change(cm_ctx, data, event);
363 break;
364 default:
365 event_handled = cm_handle_reassoc_event(cm_ctx, event,
366 data_len, data);
367 break;
368 }
369
370 return event_handled;
371 }
372
373 #endif /* WLAN_FEATURE_HOST_ROAM */
374
375 #ifdef WLAN_FEATURE_ROAM_OFFLOAD
cm_subst_roam_start_entry(void * ctx)376 void cm_subst_roam_start_entry(void *ctx)
377 {
378 struct cnx_mgr *cm_ctx = ctx;
379
380 if (cm_get_state(cm_ctx) != WLAN_CM_S_ROAMING)
381 QDF_BUG(0);
382
383 cm_set_substate(cm_ctx, WLAN_CM_SS_ROAM_STARTED);
384 }
385
cm_subst_roam_start_exit(void * ctx)386 void cm_subst_roam_start_exit(void *ctx)
387 {
388 }
389
cm_subst_roam_start_event(void * ctx,uint16_t event,uint16_t data_len,void * data)390 bool cm_subst_roam_start_event(void *ctx, uint16_t event,
391 uint16_t data_len, void *data)
392 {
393 bool event_handled = true;
394 struct cnx_mgr *cm_ctx = ctx;
395 QDF_STATUS status;
396
397 switch (event) {
398 case WLAN_CM_SM_EV_CONNECT_REQ:
399 case WLAN_CM_SM_EV_DISCONNECT_REQ:
400 case WLAN_CM_SM_EV_DISCONNECT_ACTIVE:
401 event_handled =
402 cm_handle_connect_disconnect_in_roam(cm_ctx, event,
403 data_len, data);
404 break;
405 case WLAN_CM_SM_EV_ROAM_START:
406 cm_fw_roam_start(ctx);
407 break;
408 case WLAN_CM_SM_EV_ROAM_INVOKE:
409 cm_send_roam_invoke_req(cm_ctx, data);
410 break;
411 case WLAN_CM_SM_EV_ROAM_ABORT:
412 case WLAN_CM_SM_EV_ROAM_INVOKE_FAIL:
413 case WLAN_CM_SM_EV_ROAM_HO_FAIL:
414 cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTED);
415 cm_sm_deliver_event_sync(cm_ctx, event,
416 data_len, data);
417 break;
418 case WLAN_CM_SM_EV_ROAM_SYNC:
419 cm_sm_transition_to(cm_ctx, WLAN_CM_SS_ROAM_SYNC);
420 status = cm_sm_deliver_event_sync(cm_ctx, event,
421 data_len, data);
422 if (QDF_IS_STATUS_ERROR(status))
423 event_handled = false;
424 break;
425 default:
426 event_handled = false;
427 break;
428 }
429
430 return event_handled;
431 }
432
cm_subst_roam_sync_entry(void * ctx)433 void cm_subst_roam_sync_entry(void *ctx)
434 {
435 struct cnx_mgr *cm_ctx = ctx;
436
437 if (cm_get_state(cm_ctx) != WLAN_CM_S_ROAMING)
438 QDF_BUG(0);
439
440 cm_set_substate(cm_ctx, WLAN_CM_SS_ROAM_SYNC);
441 }
442
cm_subst_roam_sync_exit(void * ctx)443 void cm_subst_roam_sync_exit(void *ctx)
444 {
445 }
446
cm_subst_roam_sync_event(void * ctx,uint16_t event,uint16_t data_len,void * data)447 bool cm_subst_roam_sync_event(void *ctx, uint16_t event,
448 uint16_t data_len, void *data)
449 {
450 bool event_handled = true;
451 struct cnx_mgr *cm_ctx = ctx;
452 QDF_STATUS status;
453
454 switch (event) {
455 case WLAN_CM_SM_EV_CONNECT_REQ:
456 case WLAN_CM_SM_EV_DISCONNECT_REQ:
457 case WLAN_CM_SM_EV_DISCONNECT_ACTIVE:
458 event_handled =
459 cm_handle_connect_disconnect_in_roam(cm_ctx, event,
460 data_len, data);
461 break;
462 case WLAN_CM_SM_EV_ROAM_SYNC:
463 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
464 status = mlo_cm_roam_sync_cb(cm_ctx->vdev, data, data_len);
465 if (QDF_IS_STATUS_ERROR(status)) {
466 event_handled = false;
467 break;
468 }
469 #endif
470 status = cm_fw_send_vdev_roam_event(cm_ctx, data_len, data);
471 if (QDF_IS_STATUS_ERROR(status))
472 event_handled = false;
473 break;
474 case WLAN_CM_SM_EV_ROAM_DONE:
475 cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTED);
476 cm_sm_deliver_event_sync(cm_ctx, event,
477 data_len, data);
478 break;
479 case WLAN_CM_SM_EV_ROAM_ABORT:
480 case WLAN_CM_SM_EV_ROAM_HO_FAIL:
481 cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTED);
482 cm_sm_deliver_event_sync(cm_ctx, event,
483 data_len, data);
484 break;
485 default:
486 event_handled = false;
487 break;
488 }
489
490 return event_handled;
491 }
492 #endif /* WLAN_FEATURE_ROAM_OFFLOAD */
493