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) 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 39 void cm_state_roaming_exit(void *ctx) 40 { 41 } 42 43 #ifdef WLAN_FEATURE_ROAM_OFFLOAD 44 static 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 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 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 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 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 187 void cm_subst_preauth_exit(void *ctx) 188 { 189 } 190 191 #ifdef WLAN_FEATURE_PREAUTH_ENABLE 192 static bool 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 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 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 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 277 void cm_subst_reassoc_exit(void *ctx) 278 { 279 } 280 281 #ifdef WLAN_FEATURE_PREAUTH_ENABLE 282 static bool 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 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 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 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 386 void cm_subst_roam_start_exit(void *ctx) 387 { 388 } 389 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 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 443 void cm_subst_roam_sync_exit(void *ctx) 444 { 445 } 446 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 #endif 468 status = cm_fw_send_vdev_roam_event(cm_ctx, data_len, data); 469 if (QDF_IS_STATUS_ERROR(status)) 470 event_handled = false; 471 break; 472 case WLAN_CM_SM_EV_ROAM_DONE: 473 cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTED); 474 cm_sm_deliver_event_sync(cm_ctx, event, 475 data_len, data); 476 break; 477 case WLAN_CM_SM_EV_ROAM_ABORT: 478 case WLAN_CM_SM_EV_ROAM_HO_FAIL: 479 cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTED); 480 cm_sm_deliver_event_sync(cm_ctx, event, 481 data_len, data); 482 break; 483 default: 484 event_handled = false; 485 break; 486 } 487 488 return event_handled; 489 } 490 #endif /* WLAN_FEATURE_ROAM_OFFLOAD */ 491