xref: /wlan-dirver/qca-wifi-host-cmn/umac/mlme/connection_mgr/core/src/wlan_cm_connect.c (revision 45a38684b07295822dc8eba39e293408f203eec8)
1 /*
2  * Copyright (c) 2012-2015, 2020, 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 connect specific APIs of connection manager
19  */
20 
21 #include "wlan_cm_main_api.h"
22 #include "wlan_cm_bss_score_param.h"
23 #include "wlan_scan_api.h"
24 #include "wlan_cm_sm.h"
25 #ifdef WLAN_POLICY_MGR_ENABLE
26 #include "wlan_policy_mgr_api.h"
27 #endif
28 #include <wlan_serialization_api.h>
29 
30 #ifdef WLAN_POLICY_MGR_ENABLE
31 static void
32 cm_get_pcl_chan_weigtage_for_sta(struct wlan_objmgr_pdev *pdev,
33 				 struct pcl_freq_weight_list *pcl_lst)
34 {
35 	enum QDF_OPMODE opmode = QDF_STA_MODE;
36 	enum policy_mgr_con_mode pm_mode;
37 	uint32_t num_entries = 0;
38 	QDF_STATUS status;
39 
40 	if (!pcl_lst)
41 		return;
42 
43 	if (policy_mgr_map_concurrency_mode(&opmode, &pm_mode)) {
44 		status = policy_mgr_get_pcl(wlan_pdev_get_psoc(pdev), pm_mode,
45 					    pcl_lst->pcl_freq_list,
46 					    &num_entries,
47 					    pcl_lst->pcl_weight_list,
48 					    NUM_CHANNELS);
49 		if (QDF_IS_STATUS_ERROR(status))
50 			return;
51 		pcl_lst->num_of_pcl_channels = num_entries;
52 	}
53 }
54 
55 static void cm_calculate_scores(struct wlan_objmgr_pdev *pdev,
56 				struct scan_filter *filter, qdf_list_t *list)
57 {
58 	struct pcl_freq_weight_list *pcl_lst = NULL;
59 
60 	if (!filter->num_of_bssid) {
61 		pcl_lst = qdf_mem_malloc(sizeof(*pcl_lst));
62 		cm_get_pcl_chan_weigtage_for_sta(pdev, pcl_lst);
63 		if (pcl_lst && !pcl_lst->num_of_pcl_channels) {
64 			qdf_mem_free(pcl_lst);
65 			pcl_lst = NULL;
66 		}
67 	}
68 	wlan_cm_calculate_bss_score(pdev, pcl_lst, list, &filter->bssid_hint);
69 	if (pcl_lst)
70 		qdf_mem_free(pcl_lst);
71 }
72 #else
73 static inline
74 void cm_calculate_scores(struct wlan_objmgr_pdev *pdev,
75 			 struct scan_filter *filter, qdf_list_t *list)
76 {
77 	wlan_cm_calculate_bss_score(pdev, NULL, list, &filter->bssid_hint);
78 }
79 #endif
80 
81 #ifdef WLAN_FEATURE_11W
82 static inline void
83 cm_set_pmf_caps(struct cm_connect_req *cm_req, struct scan_filter *filter)
84 {
85 	filter->pmf_cap = cm_req->req.crypto.pmf_cap;
86 }
87 #else
88 static inline void
89 cm_set_pmf_caps(struct cm_connect_req *cm_req, struct scan_filter *filter)
90 {}
91 #endif
92 
93 static void cm_connect_prepare_scan_fliter(struct cnx_mgr *cm_ctx,
94 					   struct cm_connect_req *cm_req,
95 					   struct scan_filter *filter)
96 {
97 	if (!qdf_is_macaddr_zero(&cm_req->req.bssid)) {
98 		filter->num_of_bssid = 1;
99 		qdf_copy_macaddr(&filter->bssid_list[0], &cm_req->req.bssid);
100 	}
101 
102 	qdf_copy_macaddr(&filter->bssid_hint, &cm_req->req.bssid_hint);
103 	filter->num_of_ssid = 1;
104 	qdf_mem_copy(&filter->ssid_list[0], &cm_req->req.ssid,
105 		     sizeof(struct wlan_ssid));
106 
107 	if (cm_req->req.chan_freq) {
108 		filter->num_of_channels = 1;
109 		filter->chan_freq_list[0] = cm_req->req.chan_freq;
110 	}
111 
112 	/* Fill band (STA+STA) */
113 	/* RSN OVERRIDE */
114 
115 	filter->authmodeset = cm_req->req.crypto.auth_type;
116 	filter->ucastcipherset = cm_req->req.crypto.ciphers_pairwise;
117 	filter->key_mgmt = cm_req->req.crypto.akm_suites;
118 	filter->mcastcipherset = cm_req->req.crypto.group_cipher;
119 	filter->mgmtcipherset = cm_req->req.crypto.mgmt_ciphers;
120 
121 	cm_set_pmf_caps(cm_req, filter);
122 
123 	/* FOR WPS/OSEN set ignore auth */
124 	/* SET mobility domain */
125 	/* Fill fils info */
126 }
127 
128 static QDF_STATUS
129 cm_send_connect_start_fail(struct cnx_mgr *cm_ctx,
130 			   struct cm_connect_req *cm_req,
131 			   enum wlan_cm_connect_fail_reason reason)
132 {
133 	struct wlan_cm_connect_rsp *resp;
134 	QDF_STATUS status;
135 
136 	resp = qdf_mem_malloc(sizeof(*resp));
137 	if (!resp)
138 		return QDF_STATUS_E_NOMEM;
139 
140 	status = cm_sm_deliver_event(cm_ctx, WLAN_CM_SM_EV_CONNECT_FAILURE,
141 				     sizeof(*resp), resp);
142 	qdf_mem_free(resp);
143 
144 	return status;
145 }
146 
147 static QDF_STATUS cm_connect_get_candidates(struct wlan_objmgr_pdev *pdev,
148 					    struct cnx_mgr *cm_ctx,
149 					    struct cm_connect_req *cm_req)
150 {
151 	struct scan_filter *filter;
152 	uint32_t num_bss = 0;
153 	enum QDF_OPMODE op_mode;
154 	qdf_list_t *candidate_list;
155 
156 	filter = qdf_mem_malloc(sizeof(*filter));
157 	if (!filter)
158 		return QDF_STATUS_E_NOMEM;
159 
160 	cm_connect_prepare_scan_fliter(cm_ctx, cm_req, filter);
161 
162 	candidate_list = wlan_scan_get_result(pdev, filter);
163 	if (candidate_list) {
164 		num_bss = qdf_list_size(candidate_list);
165 		mlme_debug("num_entries found %d", num_bss);
166 	}
167 
168 	op_mode = wlan_vdev_mlme_get_opmode(cm_ctx->vdev);
169 	if (num_bss && op_mode == QDF_STA_MODE)
170 		cm_calculate_scores(pdev, filter, candidate_list);
171 	qdf_mem_free(filter);
172 
173 	if (!candidate_list || !qdf_list_size(candidate_list)) {
174 		if (candidate_list)
175 			wlan_scan_purge_results(candidate_list);
176 		mlme_info("No valid candidate found num_bss %d", num_bss);
177 		/*
178 		 * Do connect scan only of no candidates were found
179 		 * if candidates were found and were removed due to invalid
180 		 * return failure
181 		 */
182 		if (!num_bss)
183 			return cm_sm_deliver_event(cm_ctx, WLAN_CM_SM_EV_SCAN,
184 						   sizeof(*cm_req), cm_req);
185 
186 		return QDF_STATUS_E_EMPTY;
187 	}
188 	cm_req->candidate_list = candidate_list;
189 
190 	return QDF_STATUS_SUCCESS;
191 }
192 
193 static QDF_STATUS
194 cm_ser_connect_cb(struct wlan_serialization_command *cmd,
195 		  enum wlan_serialization_cb_reason reason)
196 {
197 	QDF_STATUS status = QDF_STATUS_SUCCESS;
198 	struct wlan_objmgr_vdev *vdev;
199 
200 	if (!cmd) {
201 		mlme_err("cmd is NULL, reason: %d", reason);
202 		QDF_ASSERT(0);
203 		return QDF_STATUS_E_NULL_VALUE;
204 	}
205 
206 	vdev = cmd->vdev;
207 
208 	switch (reason) {
209 	case WLAN_SER_CB_ACTIVATE_CMD:
210 		/* Post event to move CM SM to join active */
211 		break;
212 
213 	case WLAN_SER_CB_CANCEL_CMD:
214 		/* command removed from pending list. */
215 		break;
216 
217 	case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT:
218 		mlme_err("Active command timeout cm_id %d", cmd->cmd_id);
219 		QDF_ASSERT(0);
220 		break;
221 
222 	case WLAN_SER_CB_RELEASE_MEM_CMD:
223 		/* command completed. Release reference of vdev */
224 		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
225 		break;
226 
227 	default:
228 		QDF_ASSERT(0);
229 		status = QDF_STATUS_E_INVAL;
230 		break;
231 	}
232 
233 	return status;
234 }
235 
236 #define CONNECT_TIMEOUT       30000
237 
238 static QDF_STATUS cm_ser_connect_req(struct wlan_objmgr_pdev *pdev,
239 				     struct cnx_mgr *cm_ctx,
240 				     struct cm_connect_req *cm_req)
241 {
242 	struct wlan_serialization_command cmd = {0, };
243 	enum wlan_serialization_status ser_cmd_status;
244 	QDF_STATUS status;
245 
246 	status = wlan_objmgr_vdev_try_get_ref(cm_ctx->vdev, WLAN_MLME_CM_ID);
247 	if (QDF_IS_STATUS_ERROR(status)) {
248 		mlme_err("unable to get reference");
249 		return status;
250 	}
251 
252 	cmd.cmd_type = WLAN_SER_CMD_VDEV_CONNECT;
253 	cmd.cmd_id = cm_req->cm_id;
254 	cmd.cmd_cb = cm_ser_connect_cb;
255 	cmd.source = WLAN_UMAC_COMP_MLME;
256 	cmd.is_high_priority = false;
257 	cmd.cmd_timeout_duration = CONNECT_TIMEOUT;
258 	cmd.vdev = cm_ctx->vdev;
259 
260 	ser_cmd_status = wlan_serialization_request(&cmd);
261 	switch (ser_cmd_status) {
262 	case WLAN_SER_CMD_PENDING:
263 		/* command moved to pending list.Do nothing */
264 		break;
265 	case WLAN_SER_CMD_ACTIVE:
266 		/* command moved to active list. Do nothing */
267 		break;
268 	default:
269 		mlme_err("ser cmd status %d", ser_cmd_status);
270 		wlan_objmgr_vdev_release_ref(cm_ctx->vdev, WLAN_MLME_CM_ID);
271 
272 		return QDF_STATUS_E_FAILURE;
273 	}
274 
275 	return QDF_STATUS_SUCCESS;
276 }
277 
278 QDF_STATUS cm_connect_start(struct cnx_mgr *cm_ctx,
279 			    struct cm_connect_req *cm_req)
280 {
281 	struct wlan_objmgr_pdev *pdev;
282 	enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE;
283 	QDF_STATUS status;
284 
285 	/* Interface event */
286 	pdev = wlan_vdev_get_pdev(cm_ctx->vdev);
287 	if (!pdev) {
288 		mlme_err("Failed to find pdev from vdev %d",
289 			 wlan_vdev_get_id(cm_ctx->vdev));
290 		goto connect_err;
291 	}
292 
293 	status = cm_connect_get_candidates(pdev, cm_ctx, cm_req);
294 	if (QDF_IS_STATUS_ERROR(status)) {
295 		reason = CM_NO_CANDIDATE_FOUND;
296 		goto connect_err;
297 	}
298 
299 	/*  Do HW mode change */
300 
301 	status = cm_ser_connect_req(pdev, cm_ctx, cm_req);
302 	if (QDF_IS_STATUS_ERROR(status)) {
303 		reason = CM_SER_FAILURE;
304 		goto connect_err;
305 	}
306 
307 	return QDF_STATUS_SUCCESS;
308 
309 connect_err:
310 	return cm_send_connect_start_fail(cm_ctx, cm_req, reason);
311 }
312 
313 QDF_STATUS cm_connect_active(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id)
314 {
315 	/*
316 	 * get first valid candidate, create bss peer.
317 	 * fill vdev crypto for the peer.
318 	 * call vdev sm to start connect for the candidate.
319 	 */
320 	return QDF_STATUS_SUCCESS;
321 }
322 
323 QDF_STATUS cm_try_next_candidate(struct cnx_mgr *cm_ctx,
324 				 struct wlan_cm_connect_rsp *resp)
325 {
326 	/*
327 	 * get next valid candidate, if no candidate left, post
328 	 * WLAN_CM_SM_EV_CONNECT_FAILURE to SM, inform osif about failure for
329 	 * the candidate if its not last one. and initiate the connect for
330 	 * next candidate.
331 	 */
332 	return QDF_STATUS_SUCCESS;
333 }
334 
335 QDF_STATUS cm_connect_cmd_timeout(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id)
336 {
337 	/*
338 	 * get the connect req from connect list and post
339 	 * WLAN_CM_SM_EV_CONNECT_FAILURE.
340 	 */
341 	return QDF_STATUS_SUCCESS;
342 }
343 
344 QDF_STATUS cm_connect_complete(struct cnx_mgr *cm_ctx,
345 			       struct wlan_cm_connect_rsp *resp)
346 {
347 	/*
348 	 * inform osif about success/failure, inform interface manager
349 	 * update fils/wep key and inform legacy, update bcn filter and scan
350 	 * entry mlme info, blm action and remove from serialization at the end.
351 	 */
352 	return QDF_STATUS_SUCCESS;
353 }
354 
355 QDF_STATUS cm_connect_start_req(struct wlan_objmgr_vdev *vdev,
356 				struct wlan_cm_connect_req *req)
357 {
358 	struct cnx_mgr *cm_ctx = NULL;
359 	struct cm_connect_req *cm_req = NULL;
360 
361 	/*
362 	 * Get WAPI/WPA/RSN IE and refill crypto params of req.
363 	 * Prepare cm_connect_req cm_req, get cm id and inform it to OSIF.
364 	 * store connect req to the cm ctx req_list
365 	 */
366 
367 	return cm_sm_deliver_event(cm_ctx, WLAN_CM_SM_EV_CONNECT_REQ,
368 				   sizeof(*cm_req), cm_req);
369 }
370