1 /* 2 * Copyright (c) 2012-2015, 2020-2021, The Linux Foundation. All rights reserved. 3 * Copyright (c) 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 init/deinit specific apis of connection manager 20 */ 21 22 #include "wlan_cm_main.h" 23 #include "wlan_cm_roam.h" 24 #include "wlan_cm_main_api.h" 25 #include "wlan_scan_api.h" 26 #include "wlan_mlo_mgr_link_switch.h" 27 28 #ifdef WLAN_CM_USE_SPINLOCK 29 /** 30 * cm_req_lock_create - Create CM req SM mutex/spinlock 31 * @cm_ctx: connection manager ctx 32 * 33 * Creates CM SM mutex/spinlock 34 * 35 * Return: void 36 */ 37 static inline void cm_req_lock_create(struct cnx_mgr * cm_ctx)38 cm_req_lock_create(struct cnx_mgr *cm_ctx) 39 { 40 qdf_spinlock_create(&cm_ctx->cm_req_lock); 41 } 42 43 /** 44 * cm_req_lock_destroy - Destroy CM SM mutex/spinlock 45 * @cm_ctx: connection manager ctx 46 * 47 * Destroy CM SM mutex/spinlock 48 * 49 * Return: void 50 */ 51 static inline void cm_req_lock_destroy(struct cnx_mgr * cm_ctx)52 cm_req_lock_destroy(struct cnx_mgr *cm_ctx) 53 { 54 qdf_spinlock_destroy(&cm_ctx->cm_req_lock); 55 } 56 #else 57 static inline void cm_req_lock_create(struct cnx_mgr * cm_ctx)58 cm_req_lock_create(struct cnx_mgr *cm_ctx) 59 { 60 qdf_mutex_create(&cm_ctx->cm_req_lock); 61 } 62 63 static inline void cm_req_lock_destroy(struct cnx_mgr * cm_ctx)64 cm_req_lock_destroy(struct cnx_mgr *cm_ctx) 65 { 66 qdf_mutex_destroy(&cm_ctx->cm_req_lock); 67 } 68 #endif /* WLAN_CM_USE_SPINLOCK */ 69 wlan_cm_init(struct vdev_mlme_obj * vdev_mlme)70 QDF_STATUS wlan_cm_init(struct vdev_mlme_obj *vdev_mlme) 71 { 72 struct wlan_objmgr_vdev *vdev = vdev_mlme->vdev; 73 enum QDF_OPMODE op_mode = wlan_vdev_mlme_get_opmode(vdev); 74 struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(vdev); 75 QDF_STATUS status; 76 77 if (op_mode != QDF_STA_MODE && op_mode != QDF_P2P_CLIENT_MODE) 78 return QDF_STATUS_SUCCESS; 79 80 vdev_mlme->cnx_mgr_ctx = qdf_mem_malloc(sizeof(struct cnx_mgr)); 81 if (!vdev_mlme->cnx_mgr_ctx) 82 return QDF_STATUS_E_NOMEM; 83 84 vdev_mlme->cnx_mgr_ctx->vdev = vdev; 85 status = mlme_cm_ext_hdl_create(vdev, 86 &vdev_mlme->cnx_mgr_ctx->ext_cm_ptr); 87 if (QDF_IS_STATUS_ERROR(status)) { 88 qdf_mem_free(vdev_mlme->cnx_mgr_ctx); 89 vdev_mlme->cnx_mgr_ctx = NULL; 90 return status; 91 } 92 93 status = cm_sm_create(vdev_mlme->cnx_mgr_ctx); 94 if (QDF_IS_STATUS_ERROR(status)) { 95 mlme_cm_ext_hdl_destroy(vdev, 96 vdev_mlme->cnx_mgr_ctx->ext_cm_ptr); 97 vdev_mlme->cnx_mgr_ctx->ext_cm_ptr = NULL; 98 qdf_mem_free(vdev_mlme->cnx_mgr_ctx); 99 vdev_mlme->cnx_mgr_ctx = NULL; 100 return status; 101 } 102 vdev_mlme->cnx_mgr_ctx->max_connect_attempts = 103 CM_MAX_CONNECT_ATTEMPTS; 104 vdev_mlme->cnx_mgr_ctx->connect_timeout = 105 CM_MAX_PER_CANDIDATE_CONNECT_TIMEOUT; 106 qdf_list_create(&vdev_mlme->cnx_mgr_ctx->req_list, CM_MAX_REQ); 107 cm_req_lock_create(vdev_mlme->cnx_mgr_ctx); 108 109 vdev_mlme->cnx_mgr_ctx->scan_requester_id = 110 wlan_scan_register_requester(psoc, 111 "CM", 112 wlan_cm_scan_cb, 113 vdev_mlme->cnx_mgr_ctx); 114 qdf_event_create(&vdev_mlme->cnx_mgr_ctx->disconnect_complete); 115 cm_req_history_init(vdev_mlme->cnx_mgr_ctx); 116 117 return QDF_STATUS_SUCCESS; 118 } 119 cm_deinit_req_list(struct cnx_mgr * cm_ctx)120 static void cm_deinit_req_list(struct cnx_mgr *cm_ctx) 121 { 122 uint32_t prefix; 123 qdf_list_node_t *cur_node = NULL, *next_node = NULL; 124 struct cm_req *cm_req = NULL; 125 126 /* 127 * flush unhandled req from the list, this should not happen if SM is 128 * handled properly, but in cases of active command timeout 129 * (which needs be debugged and avoided anyway) if VDEV/PEER SM 130 * is not able to handle the req it may send out of sync command and 131 * thus resulting in a unhandled request. Thus to avoid memleak flush 132 * all unhandled req before destroying the list. 133 */ 134 cm_req_lock_acquire(cm_ctx); 135 qdf_list_peek_front(&cm_ctx->req_list, &cur_node); 136 while (cur_node) { 137 qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node); 138 139 cm_req = qdf_container_of(cur_node, struct cm_req, node); 140 prefix = CM_ID_GET_PREFIX(cm_req->cm_id); 141 qdf_list_remove_node(&cm_ctx->req_list, &cm_req->node); 142 mlme_info(CM_PREFIX_FMT "flush prefix %x", 143 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), 144 cm_req->cm_id), prefix); 145 if (prefix == CONNECT_REQ_PREFIX) { 146 cm_ctx->connect_count--; 147 cm_free_connect_req_mem(&cm_req->connect_req); 148 } else if (prefix == ROAM_REQ_PREFIX) { 149 cm_free_roam_req_mem(&cm_req->roam_req); 150 } else if (prefix == DISCONNECT_REQ_PREFIX) { 151 cm_ctx->disconnect_count--; 152 } 153 qdf_mem_free(cm_req); 154 155 cur_node = next_node; 156 next_node = NULL; 157 cm_req = NULL; 158 } 159 cm_req_lock_release(cm_ctx); 160 161 cm_req_lock_destroy(cm_ctx); 162 qdf_list_destroy(&cm_ctx->req_list); 163 } 164 wlan_cm_deinit(struct vdev_mlme_obj * vdev_mlme)165 QDF_STATUS wlan_cm_deinit(struct vdev_mlme_obj *vdev_mlme) 166 { 167 struct wlan_objmgr_vdev *vdev = vdev_mlme->vdev; 168 enum QDF_OPMODE op_mode = wlan_vdev_mlme_get_opmode(vdev); 169 struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(vdev); 170 wlan_scan_requester scan_requester_id; 171 struct cnx_mgr *cm_ctx; 172 173 if (op_mode != QDF_STA_MODE && op_mode != QDF_P2P_CLIENT_MODE) 174 return QDF_STATUS_SUCCESS; 175 176 cm_ctx = vdev_mlme->cnx_mgr_ctx; 177 cm_req_history_deinit(cm_ctx); 178 qdf_event_destroy(&cm_ctx->disconnect_complete); 179 scan_requester_id = cm_ctx->scan_requester_id; 180 wlan_scan_unregister_requester(psoc, scan_requester_id); 181 182 cm_deinit_req_list(cm_ctx); 183 cm_sm_destroy(cm_ctx); 184 mlme_cm_ext_hdl_destroy(vdev, cm_ctx->ext_cm_ptr); 185 cm_ctx->ext_cm_ptr = NULL; 186 qdf_mem_free(cm_ctx); 187 vdev_mlme->cnx_mgr_ctx = NULL; 188 189 return QDF_STATUS_SUCCESS; 190 } 191 192 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE cm_standby_link_update_mlme_by_bssid(struct wlan_objmgr_vdev * vdev,uint32_t assoc_state,struct wlan_ssid ssid)193 void cm_standby_link_update_mlme_by_bssid(struct wlan_objmgr_vdev *vdev, 194 uint32_t assoc_state, 195 struct wlan_ssid ssid) 196 { 197 struct mlo_link_info *link_info; 198 uint8_t link_info_iter; 199 struct mlme_info mlme_info; 200 struct bss_info bss_info; 201 202 if (!wlan_vdev_mlme_is_assoc_sta_vdev(vdev)) 203 return; 204 205 link_info = mlo_mgr_get_ap_link(vdev); 206 if (!link_info) 207 return; 208 209 for (link_info_iter = 0; link_info_iter < 3; link_info_iter++) { 210 if (qdf_is_macaddr_zero(&link_info->ap_link_addr)) 211 break; 212 213 if (link_info->vdev_id == WLAN_INVALID_VDEV_ID) { 214 mlme_info.assoc_state = assoc_state; 215 qdf_copy_macaddr(&bss_info.bssid, 216 &link_info->ap_link_addr); 217 bss_info.freq = link_info->link_chan_info->ch_freq; 218 bss_info.ssid.length = ssid.length; 219 qdf_mem_copy(&bss_info.ssid.ssid, ssid.ssid, 220 bss_info.ssid.length); 221 222 wlan_scan_update_mlme_by_bssinfo(wlan_vdev_get_pdev(vdev), 223 &bss_info, &mlme_info); 224 } 225 226 link_info++; 227 } 228 } 229 #endif 230