xref: /wlan-dirver/qca-wifi-host-cmn/umac/mlme/connection_mgr/core/src/wlan_cm_roam_util.c (revision 901120c066e139c7f8a2c8e4820561fdd83c67ef)
1 /*
2  * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2021-2022 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 
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
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
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)
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 wlan_ssid ssid;
57 	struct cm_req *cm_req, *req_ptr;
58 	qdf_freq_t freq = 0;
59 
60 	/* Handle only if roam is enabled */
61 	if (!cm_is_roam_enabled(wlan_vdev_get_psoc(cm_ctx->vdev)))
62 		return QDF_STATUS_E_NOSUPPORT;
63 
64 	cm_req = qdf_container_of(connect_req, struct cm_req, connect_req);
65 	req = &connect_req->req;
66 
67 	if (req->chan_freq)
68 		freq = req->chan_freq;
69 	else if (req->chan_freq_hint)
70 		freq = req->chan_freq_hint;
71 	/*
72 	 * Reject re-assoc unless freq along with prev bssid and one
73 	 * of bssid or bssid hint is present.
74 	 */
75 	if (!cm_is_connect_req_reassoc(req))
76 		return QDF_STATUS_E_FAILURE;
77 
78 	wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &bssid);
79 
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 		return QDF_STATUS_E_FAILURE;
86 	}
87 
88 	status = wlan_vdev_mlme_get_ssid(cm_ctx->vdev, ssid.ssid, &ssid.length);
89 	if (QDF_IS_STATUS_ERROR(status)) {
90 		mlme_err("failed to get ssid");
91 		return QDF_STATUS_E_FAILURE;
92 	}
93 
94 	/* Reject re-assoc unless ssid matches. */
95 	if (ssid.length != req->ssid.length ||
96 	    qdf_mem_cmp(ssid.ssid, req->ssid.ssid, ssid.length)) {
97 		mlme_debug("SSID didn't matched: self ssid: \"" QDF_SSID_FMT "\", ssid in req: \"" QDF_SSID_FMT "\"",
98 			   QDF_SSID_REF(ssid.length, ssid.ssid),
99 			   QDF_SSID_REF(req->ssid.length, req->ssid.ssid));
100 		return QDF_STATUS_E_FAILURE;
101 	}
102 
103 	/* fill roam_req for roaming and free cm_req */
104 	*roam_req = qdf_mem_malloc(sizeof(**roam_req));
105 	if (!*roam_req)
106 		return QDF_STATUS_E_NOMEM;
107 
108 	req_ptr = *roam_req;
109 	if (!qdf_is_macaddr_zero(&req->bssid))
110 		qdf_copy_macaddr(&req_ptr->roam_req.req.bssid, &req->bssid);
111 	else
112 		qdf_copy_macaddr(&req_ptr->roam_req.req.bssid,
113 				 &req->bssid_hint);
114 
115 	qdf_copy_macaddr(&req_ptr->roam_req.req.prev_bssid, &req->prev_bssid);
116 	cm_fill_roam_vdev_crypto_params(cm_ctx, &connect_req->req);
117 	req_ptr->roam_req.req.chan_freq = freq;
118 	req_ptr->roam_req.req.source = CM_ROAMING_HOST;
119 
120 	/* Free the connect req, as reassoc is tried */
121 	cm_free_connect_req_mem(connect_req);
122 	qdf_mem_free(cm_req);
123 
124 	return QDF_STATUS_SUCCESS;
125 }
126 
127 QDF_STATUS cm_add_roam_req_to_list(struct cnx_mgr *cm_ctx,
128 				   struct cm_req *cm_req)
129 {
130 	QDF_STATUS status;
131 
132 	cm_req->roam_req.cm_id =
133 			cm_get_cm_id(cm_ctx, cm_req->roam_req.req.source);
134 	cm_req->cm_id = cm_req->roam_req.cm_id;
135 	cm_req->roam_req.req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
136 	status =
137 	    cm_add_req_to_list_and_indicate_osif(cm_ctx, cm_req,
138 						 cm_req->roam_req.req.source);
139 
140 	return status;
141 }
142 
143 QDF_STATUS
144 cm_fill_bss_info_in_roam_rsp_by_cm_id(struct cnx_mgr *cm_ctx,
145 				      wlan_cm_id cm_id,
146 				      struct wlan_cm_connect_resp *resp)
147 {
148 	qdf_list_node_t *cur_node = NULL, *next_node = NULL;
149 	struct cm_req *cm_req;
150 	uint32_t prefix = CM_ID_GET_PREFIX(cm_id);
151 	struct wlan_cm_roam_req *req;
152 
153 	if (prefix != ROAM_REQ_PREFIX)
154 		return QDF_STATUS_E_INVAL;
155 
156 	cm_req_lock_acquire(cm_ctx);
157 	qdf_list_peek_front(&cm_ctx->req_list, &cur_node);
158 	while (cur_node) {
159 		qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node);
160 		cm_req = qdf_container_of(cur_node, struct cm_req, node);
161 
162 		if (cm_req->cm_id == cm_id) {
163 			req = &cm_req->roam_req.req;
164 			resp->freq = req->chan_freq;
165 			wlan_vdev_mlme_get_ssid(cm_ctx->vdev, resp->ssid.ssid,
166 						&resp->ssid.length);
167 
168 			if (!qdf_is_macaddr_zero(&req->bssid))
169 				qdf_copy_macaddr(&resp->bssid, &req->bssid);
170 
171 			cm_req_lock_release(cm_ctx);
172 			return QDF_STATUS_SUCCESS;
173 		}
174 
175 		cur_node = next_node;
176 		next_node = NULL;
177 	}
178 	cm_req_lock_release(cm_ctx);
179 
180 	return QDF_STATUS_E_FAILURE;
181 }
182 
183 bool cm_is_roam_enabled(struct wlan_objmgr_psoc *psoc)
184 {
185 	if (cm_roam_offload_enabled(psoc) || cm_is_host_roam_enabled())
186 		return true;
187 
188 	mlme_rl_debug("All roam mode (offload %d, host %d) are disabled",
189 		      cm_roam_offload_enabled(psoc), cm_is_host_roam_enabled());
190 
191 	return false;
192 }
193 #endif
194 
195 #ifdef WLAN_FEATURE_HOST_ROAM
196 bool cm_get_active_reassoc_req(struct wlan_objmgr_vdev *vdev,
197 			       struct wlan_cm_vdev_reassoc_req *req)
198 {
199 	struct cnx_mgr *cm_ctx;
200 	qdf_list_node_t *cur_node = NULL, *next_node = NULL;
201 	struct cm_req *cm_req = NULL;
202 	bool status = false;
203 	uint32_t cm_id_prefix;
204 
205 	cm_ctx = cm_get_cm_ctx(vdev);
206 	if (!cm_ctx)
207 		return status;
208 
209 	cm_req_lock_acquire(cm_ctx);
210 	qdf_list_peek_front(&cm_ctx->req_list, &cur_node);
211 	while (cur_node) {
212 		qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node);
213 
214 		cm_req = qdf_container_of(cur_node, struct cm_req, node);
215 		cm_id_prefix = CM_ID_GET_PREFIX((cm_req->cm_id));
216 
217 		if (cm_req->cm_id == cm_ctx->active_cm_id &&
218 		    cm_id_prefix == ROAM_REQ_PREFIX) {
219 			req->vdev_id = wlan_vdev_get_id(vdev);
220 			req->cm_id = cm_req->roam_req.cm_id;
221 			qdf_copy_macaddr(&req->prev_bssid,
222 					 &cm_req->roam_req.req.prev_bssid);
223 			req->bss = cm_req->roam_req.cur_candidate;
224 			status = true;
225 			cm_req_lock_release(cm_ctx);
226 			return status;
227 		}
228 
229 		cur_node = next_node;
230 		next_node = NULL;
231 	}
232 	cm_req_lock_release(cm_ctx);
233 
234 	return status;
235 }
236 #endif
237 #ifdef WLAN_FEATURE_ROAM_OFFLOAD
238 struct cm_roam_req *cm_get_first_roam_command(struct wlan_objmgr_vdev *vdev)
239 {
240 	struct cnx_mgr *cm_ctx;
241 	qdf_list_node_t *cur_node = NULL, *next_node = NULL;
242 	struct cm_req *cm_req = NULL;
243 	uint32_t cm_id_prefix;
244 
245 	cm_ctx = cm_get_cm_ctx(vdev);
246 	if (!cm_ctx)
247 		return NULL;
248 
249 	cm_req_lock_acquire(cm_ctx);
250 	qdf_list_peek_front(&cm_ctx->req_list, &cur_node);
251 	while (cur_node) {
252 		qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node);
253 
254 		cm_req = qdf_container_of(cur_node, struct cm_req, node);
255 		cm_id_prefix = CM_ID_GET_PREFIX((cm_req->cm_id));
256 
257 		if (cm_id_prefix == ROAM_REQ_PREFIX) {
258 			cm_req_lock_release(cm_ctx);
259 			return &cm_req->roam_req;
260 		}
261 
262 		cur_node = next_node;
263 		next_node = NULL;
264 	}
265 	cm_req_lock_release(cm_ctx);
266 
267 	return NULL;
268 }
269 #endif
270