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