1 /*
2 * Copyright (c) 2011-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 Roam utils for connection manager
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 #include <wlan_scan_api.h>
28
cm_free_roam_req_mem(struct cm_roam_req * roam_req)29 void cm_free_roam_req_mem(struct cm_roam_req *roam_req)
30 {
31 if (roam_req->candidate_list)
32 wlan_scan_purge_results(roam_req->candidate_list);
33 }
34
35 #ifndef CONN_MGR_ADV_FEATURE
cm_fill_roam_vdev_crypto_params(struct cnx_mgr * cm_ctx,struct wlan_cm_connect_req * req)36 static void cm_fill_roam_vdev_crypto_params(struct cnx_mgr *cm_ctx,
37 struct wlan_cm_connect_req *req)
38 {
39 cm_fill_vdev_crypto_params(cm_ctx, req);
40 }
41 #else
cm_fill_roam_vdev_crypto_params(struct cnx_mgr * cm_ctx,struct wlan_cm_connect_req * req)42 static void cm_fill_roam_vdev_crypto_params(struct cnx_mgr *cm_ctx,
43 struct wlan_cm_connect_req *req)
44 {
45 }
46 #endif /* CONN_MGR_ADV_FEATURE */
47
48 #if defined(WLAN_FEATURE_HOST_ROAM) || defined(WLAN_FEATURE_ROAM_OFFLOAD)
cm_check_and_prepare_roam_req(struct cnx_mgr * cm_ctx,struct cm_connect_req * connect_req,struct cm_req ** roam_req)49 QDF_STATUS cm_check_and_prepare_roam_req(struct cnx_mgr *cm_ctx,
50 struct cm_connect_req *connect_req,
51 struct cm_req **roam_req)
52 {
53 QDF_STATUS status;
54 struct wlan_cm_connect_req *req;
55 struct qdf_mac_addr bssid;
56 struct qdf_mac_addr bss_mld_addr = {0};
57 struct wlan_ssid ssid;
58 struct cm_req *cm_req, *req_ptr;
59 qdf_freq_t freq = 0;
60
61 /* Handle only if roam is enabled */
62 if (!cm_is_roam_enabled(wlan_vdev_get_psoc(cm_ctx->vdev)))
63 return QDF_STATUS_E_NOSUPPORT;
64
65 cm_req = qdf_container_of(connect_req, struct cm_req, connect_req);
66 req = &connect_req->req;
67
68 if (req->chan_freq)
69 freq = req->chan_freq;
70 else if (req->chan_freq_hint)
71 freq = req->chan_freq_hint;
72 /*
73 * Reject re-assoc unless freq along with prev bssid and one
74 * of bssid or bssid hint is present.
75 */
76 if (!cm_is_connect_req_reassoc(req))
77 return QDF_STATUS_E_FAILURE;
78
79 wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &bssid);
80 /* Reject re-assoc unless prev_bssid matches the current BSSID. */
81 if (!qdf_is_macaddr_equal(&req->prev_bssid, &bssid)) {
82 mlme_debug("BSSID didn't matched: bssid: "QDF_MAC_ADDR_FMT " prev bssid: " QDF_MAC_ADDR_FMT,
83 QDF_MAC_ADDR_REF(bssid.bytes),
84 QDF_MAC_ADDR_REF(req->prev_bssid.bytes));
85 status = wlan_vdev_get_bss_peer_mld_mac(cm_ctx->vdev,
86 &bss_mld_addr);
87 if (!(QDF_IS_STATUS_SUCCESS(status) &&
88 qdf_is_macaddr_equal(&req->prev_bssid, &bss_mld_addr))) {
89 mlme_debug("BSSID didn't matched: bss mld: "QDF_MAC_ADDR_FMT " prev bssid: " QDF_MAC_ADDR_FMT,
90 QDF_MAC_ADDR_REF(bss_mld_addr.bytes),
91 QDF_MAC_ADDR_REF(req->prev_bssid.bytes));
92 return QDF_STATUS_E_FAILURE;
93 }
94 }
95
96 status = wlan_vdev_mlme_get_ssid(cm_ctx->vdev, ssid.ssid, &ssid.length);
97 if (QDF_IS_STATUS_ERROR(status)) {
98 mlme_err("failed to get ssid");
99 return QDF_STATUS_E_FAILURE;
100 }
101
102 /* Reject re-assoc unless ssid matches. */
103 if (ssid.length != req->ssid.length ||
104 qdf_mem_cmp(ssid.ssid, req->ssid.ssid, ssid.length)) {
105 mlme_debug("SSID didn't matched: self ssid: \"" QDF_SSID_FMT "\", ssid in req: \"" QDF_SSID_FMT "\"",
106 QDF_SSID_REF(ssid.length, ssid.ssid),
107 QDF_SSID_REF(req->ssid.length, req->ssid.ssid));
108 return QDF_STATUS_E_FAILURE;
109 }
110
111 /* fill roam_req for roaming and free cm_req */
112 *roam_req = qdf_mem_malloc(sizeof(**roam_req));
113 if (!*roam_req)
114 return QDF_STATUS_E_NOMEM;
115
116 req_ptr = *roam_req;
117 if (!qdf_is_macaddr_zero(&req->bssid))
118 qdf_copy_macaddr(&req_ptr->roam_req.req.bssid, &req->bssid);
119 else
120 qdf_copy_macaddr(&req_ptr->roam_req.req.bssid,
121 &req->bssid_hint);
122
123 qdf_copy_macaddr(&req_ptr->roam_req.req.prev_bssid, &req->prev_bssid);
124 cm_fill_roam_vdev_crypto_params(cm_ctx, &connect_req->req);
125 req_ptr->roam_req.req.chan_freq = freq;
126 req_ptr->roam_req.req.source = CM_ROAMING_HOST;
127
128 /* Free the connect req, as reassoc is tried */
129 cm_free_connect_req_mem(connect_req);
130 qdf_mem_free(cm_req);
131
132 return QDF_STATUS_SUCCESS;
133 }
134
cm_add_roam_req_to_list(struct cnx_mgr * cm_ctx,struct cm_req * cm_req)135 QDF_STATUS cm_add_roam_req_to_list(struct cnx_mgr *cm_ctx,
136 struct cm_req *cm_req)
137 {
138 QDF_STATUS status;
139
140 cm_req->roam_req.cm_id =
141 cm_get_cm_id(cm_ctx, cm_req->roam_req.req.source);
142 cm_req->cm_id = cm_req->roam_req.cm_id;
143 cm_req->roam_req.req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
144 status =
145 cm_add_req_to_list_and_indicate_osif(cm_ctx, cm_req,
146 cm_req->roam_req.req.source);
147
148 return status;
149 }
150
151 QDF_STATUS
cm_fill_bss_info_in_roam_rsp_by_cm_id(struct cnx_mgr * cm_ctx,wlan_cm_id cm_id,struct wlan_cm_connect_resp * resp)152 cm_fill_bss_info_in_roam_rsp_by_cm_id(struct cnx_mgr *cm_ctx,
153 wlan_cm_id cm_id,
154 struct wlan_cm_connect_resp *resp)
155 {
156 qdf_list_node_t *cur_node = NULL, *next_node = NULL;
157 struct cm_req *cm_req;
158 uint32_t prefix = CM_ID_GET_PREFIX(cm_id);
159 struct wlan_cm_roam_req *req;
160 QDF_STATUS status = QDF_STATUS_E_FAILURE;
161 struct scan_cache_node *candidate;
162 struct scan_cache_entry *entry;
163
164 if (prefix != ROAM_REQ_PREFIX)
165 return QDF_STATUS_E_INVAL;
166
167 cm_req_lock_acquire(cm_ctx);
168 qdf_list_peek_front(&cm_ctx->req_list, &cur_node);
169 while (cur_node) {
170 qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node);
171 cm_req = qdf_container_of(cur_node, struct cm_req, node);
172
173 if (cm_req->cm_id != cm_id) {
174 cur_node = next_node;
175 next_node = NULL;
176 continue;
177 }
178
179 status = QDF_STATUS_SUCCESS;
180
181 req = &cm_req->roam_req.req;
182 resp->freq = req->chan_freq;
183 wlan_vdev_mlme_get_ssid(cm_ctx->vdev, resp->ssid.ssid,
184 &resp->ssid.length);
185
186 if (qdf_is_macaddr_zero(&req->bssid))
187 break;
188
189 candidate = cm_req->roam_req.cur_candidate;
190 qdf_copy_macaddr(&resp->bssid, &req->bssid);
191 if (candidate) {
192 entry = candidate->entry;
193 cm_connect_resp_fill_mld_addr_from_candidate(cm_ctx->vdev,
194 entry, resp);
195 }
196 break;
197 }
198 cm_req_lock_release(cm_ctx);
199
200 return status;
201 }
202
cm_is_roam_enabled(struct wlan_objmgr_psoc * psoc)203 bool cm_is_roam_enabled(struct wlan_objmgr_psoc *psoc)
204 {
205 if (cm_roam_offload_enabled(psoc) || cm_is_host_roam_enabled())
206 return true;
207
208 mlme_rl_debug("All roam mode (offload %d, host %d) are disabled",
209 cm_roam_offload_enabled(psoc), cm_is_host_roam_enabled());
210
211 return false;
212 }
213 #endif
214
215 #ifdef WLAN_FEATURE_HOST_ROAM
cm_get_active_reassoc_req(struct wlan_objmgr_vdev * vdev,struct wlan_cm_vdev_reassoc_req * req)216 bool cm_get_active_reassoc_req(struct wlan_objmgr_vdev *vdev,
217 struct wlan_cm_vdev_reassoc_req *req)
218 {
219 struct cnx_mgr *cm_ctx;
220 qdf_list_node_t *cur_node = NULL, *next_node = NULL;
221 struct cm_req *cm_req = NULL;
222 bool status = false;
223 uint32_t cm_id_prefix;
224
225 cm_ctx = cm_get_cm_ctx(vdev);
226 if (!cm_ctx)
227 return status;
228
229 cm_req_lock_acquire(cm_ctx);
230 qdf_list_peek_front(&cm_ctx->req_list, &cur_node);
231 while (cur_node) {
232 qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node);
233
234 cm_req = qdf_container_of(cur_node, struct cm_req, node);
235 cm_id_prefix = CM_ID_GET_PREFIX((cm_req->cm_id));
236
237 if (cm_req->cm_id == cm_ctx->active_cm_id &&
238 cm_id_prefix == ROAM_REQ_PREFIX) {
239 req->vdev_id = wlan_vdev_get_id(vdev);
240 req->cm_id = cm_req->roam_req.cm_id;
241 qdf_copy_macaddr(&req->prev_bssid,
242 &cm_req->roam_req.req.prev_bssid);
243 req->bss = cm_req->roam_req.cur_candidate;
244 status = true;
245 cm_req_lock_release(cm_ctx);
246 return status;
247 }
248
249 cur_node = next_node;
250 next_node = NULL;
251 }
252 cm_req_lock_release(cm_ctx);
253
254 return status;
255 }
256 #endif
257 #ifdef WLAN_FEATURE_ROAM_OFFLOAD
cm_get_first_roam_command(struct wlan_objmgr_vdev * vdev)258 struct cm_roam_req *cm_get_first_roam_command(struct wlan_objmgr_vdev *vdev)
259 {
260 struct cnx_mgr *cm_ctx;
261 qdf_list_node_t *cur_node = NULL, *next_node = NULL;
262 struct cm_req *cm_req = NULL;
263 uint32_t cm_id_prefix;
264
265 cm_ctx = cm_get_cm_ctx(vdev);
266 if (!cm_ctx)
267 return NULL;
268
269 cm_req_lock_acquire(cm_ctx);
270 qdf_list_peek_front(&cm_ctx->req_list, &cur_node);
271 while (cur_node) {
272 qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node);
273
274 cm_req = qdf_container_of(cur_node, struct cm_req, node);
275 cm_id_prefix = CM_ID_GET_PREFIX((cm_req->cm_id));
276
277 if (cm_id_prefix == ROAM_REQ_PREFIX) {
278 cm_req_lock_release(cm_ctx);
279 return &cm_req->roam_req;
280 }
281
282 cur_node = next_node;
283 next_node = NULL;
284 }
285 cm_req_lock_release(cm_ctx);
286
287 return NULL;
288 }
289 #endif
290