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