1 /* 2 * Copyright (c) 2012-2015, 2020-2021, The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /** 18 * DOC: Implements init/deinit specific apis of connection manager 19 */ 20 21 #include "wlan_cm_main.h" 22 #include "wlan_cm_roam.h" 23 #include "wlan_cm_main_api.h" 24 #include "wlan_scan_api.h" 25 26 #ifdef WLAN_CM_USE_SPINLOCK 27 /** 28 * cm_req_lock_create - Create CM req SM mutex/spinlock 29 * @cm_ctx: connection manager ctx 30 * 31 * Creates CM SM mutex/spinlock 32 * 33 * Return: void 34 */ 35 static inline void 36 cm_req_lock_create(struct cnx_mgr *cm_ctx) 37 { 38 qdf_spinlock_create(&cm_ctx->cm_req_lock); 39 } 40 41 /** 42 * cm_req_lock_destroy - Destroy CM SM mutex/spinlock 43 * @cm_ctx: connection manager ctx 44 * 45 * Destroy CM SM mutex/spinlock 46 * 47 * Return: void 48 */ 49 static inline void 50 cm_req_lock_destroy(struct cnx_mgr *cm_ctx) 51 { 52 qdf_spinlock_destroy(&cm_ctx->cm_req_lock); 53 } 54 #else 55 static inline void 56 cm_req_lock_create(struct cnx_mgr *cm_ctx) 57 { 58 qdf_mutex_create(&cm_ctx->cm_req_lock); 59 } 60 61 static inline void 62 cm_req_lock_destroy(struct cnx_mgr *cm_ctx) 63 { 64 qdf_mutex_destroy(&cm_ctx->cm_req_lock); 65 } 66 #endif /* WLAN_CM_USE_SPINLOCK */ 67 68 QDF_STATUS wlan_cm_init(struct vdev_mlme_obj *vdev_mlme) 69 { 70 struct wlan_objmgr_vdev *vdev = vdev_mlme->vdev; 71 enum QDF_OPMODE op_mode = wlan_vdev_mlme_get_opmode(vdev); 72 struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(vdev); 73 QDF_STATUS status; 74 75 if (op_mode != QDF_STA_MODE && op_mode != QDF_P2P_CLIENT_MODE) 76 return QDF_STATUS_SUCCESS; 77 78 vdev_mlme->cnx_mgr_ctx = qdf_mem_malloc(sizeof(struct cnx_mgr)); 79 if (!vdev_mlme->cnx_mgr_ctx) 80 return QDF_STATUS_E_NOMEM; 81 82 vdev_mlme->cnx_mgr_ctx->vdev = vdev; 83 status = mlme_cm_ext_hdl_create(vdev, 84 &vdev_mlme->cnx_mgr_ctx->ext_cm_ptr); 85 if (QDF_IS_STATUS_ERROR(status)) { 86 qdf_mem_free(vdev_mlme->cnx_mgr_ctx); 87 vdev_mlme->cnx_mgr_ctx = NULL; 88 return status; 89 } 90 91 status = cm_sm_create(vdev_mlme->cnx_mgr_ctx); 92 if (QDF_IS_STATUS_ERROR(status)) { 93 mlme_cm_ext_hdl_destroy(vdev, 94 vdev_mlme->cnx_mgr_ctx->ext_cm_ptr); 95 vdev_mlme->cnx_mgr_ctx->ext_cm_ptr = NULL; 96 qdf_mem_free(vdev_mlme->cnx_mgr_ctx); 97 vdev_mlme->cnx_mgr_ctx = NULL; 98 return status; 99 } 100 vdev_mlme->cnx_mgr_ctx->max_connect_attempts = 101 CM_MAX_CONNECT_ATTEMPTS; 102 vdev_mlme->cnx_mgr_ctx->connect_timeout = 103 CM_MAX_PER_CANDIDATE_CONNECT_TIMEOUT; 104 qdf_list_create(&vdev_mlme->cnx_mgr_ctx->req_list, CM_MAX_REQ); 105 cm_req_lock_create(vdev_mlme->cnx_mgr_ctx); 106 107 vdev_mlme->cnx_mgr_ctx->scan_requester_id = 108 wlan_scan_register_requester(psoc, 109 "CM", 110 wlan_cm_scan_cb, 111 vdev_mlme->cnx_mgr_ctx); 112 qdf_event_create(&vdev_mlme->cnx_mgr_ctx->disconnect_complete); 113 cm_req_history_init(vdev_mlme->cnx_mgr_ctx); 114 115 return QDF_STATUS_SUCCESS; 116 } 117 118 static void cm_deinit_req_list(struct cnx_mgr *cm_ctx) 119 { 120 uint32_t prefix; 121 qdf_list_node_t *cur_node = NULL, *next_node = NULL; 122 struct cm_req *cm_req = NULL; 123 124 /* 125 * flush unhandled req from the list, this should not happen if SM is 126 * handled properly, but in cases of active command timeout 127 * (which needs be debugged and avoided anyway) if VDEV/PEER SM 128 * is not able to handle the req it may send out of sync command and 129 * thus resulting in a unhandled request. Thus to avoid memleak flush 130 * all unhandled req before destroying the list. 131 */ 132 cm_req_lock_acquire(cm_ctx); 133 qdf_list_peek_front(&cm_ctx->req_list, &cur_node); 134 while (cur_node) { 135 qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node); 136 137 cm_req = qdf_container_of(cur_node, struct cm_req, node); 138 prefix = CM_ID_GET_PREFIX(cm_req->cm_id); 139 qdf_list_remove_node(&cm_ctx->req_list, &cm_req->node); 140 mlme_info(CM_PREFIX_FMT "flush prefix %x", 141 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), 142 cm_req->cm_id), prefix); 143 if (prefix == CONNECT_REQ_PREFIX) { 144 cm_ctx->connect_count--; 145 cm_free_connect_req_mem(&cm_req->connect_req); 146 } else if (prefix == ROAM_REQ_PREFIX) { 147 cm_free_roam_req_mem(&cm_req->roam_req); 148 } else if (prefix == DISCONNECT_REQ_PREFIX) { 149 cm_ctx->disconnect_count--; 150 } 151 qdf_mem_free(cm_req); 152 153 cur_node = next_node; 154 next_node = NULL; 155 cm_req = NULL; 156 } 157 cm_req_lock_release(cm_ctx); 158 159 cm_req_lock_destroy(cm_ctx); 160 qdf_list_destroy(&cm_ctx->req_list); 161 } 162 163 QDF_STATUS wlan_cm_deinit(struct vdev_mlme_obj *vdev_mlme) 164 { 165 struct wlan_objmgr_vdev *vdev = vdev_mlme->vdev; 166 enum QDF_OPMODE op_mode = wlan_vdev_mlme_get_opmode(vdev); 167 struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(vdev); 168 wlan_scan_requester scan_requester_id; 169 struct cnx_mgr *cm_ctx; 170 171 if (op_mode != QDF_STA_MODE && op_mode != QDF_P2P_CLIENT_MODE) 172 return QDF_STATUS_SUCCESS; 173 174 cm_ctx = vdev_mlme->cnx_mgr_ctx; 175 cm_req_history_deinit(cm_ctx); 176 qdf_event_destroy(&cm_ctx->disconnect_complete); 177 scan_requester_id = cm_ctx->scan_requester_id; 178 wlan_scan_unregister_requester(psoc, scan_requester_id); 179 180 cm_deinit_req_list(cm_ctx); 181 cm_sm_destroy(cm_ctx); 182 mlme_cm_ext_hdl_destroy(vdev, cm_ctx->ext_cm_ptr); 183 cm_ctx->ext_cm_ptr = NULL; 184 qdf_mem_free(cm_ctx); 185 vdev_mlme->cnx_mgr_ctx = NULL; 186 187 return QDF_STATUS_SUCCESS; 188 } 189