xref: /wlan-dirver/qca-wifi-host-cmn/umac/mlme/connection_mgr/core/src/wlan_cm_connect_scan.c (revision 901120c066e139c7f8a2c8e4820561fdd83c67ef)
1 /*
2  * Copyright (c) 2012-2015,2020-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 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 connect scan (scan for ssid) specific apis of
20  * connection manager
21  */
22 
23 #include "wlan_cm_main_api.h"
24 #include "wlan_scan_api.h"
25 
26 /* Scan for ssid timeout set to 10 seconds
27  * Calculation for timeout:
28  * 8 sec(time to complete scan on all channels) + 2 sec(buffer)
29  */
30 #define SCAN_FOR_SSID_TIMEOUT       (PLATFORM_VALUE(10000, 50000))
31 
32 static QDF_STATUS cm_fill_scan_req(struct cnx_mgr *cm_ctx,
33 				   struct cm_connect_req *cm_req,
34 				   struct scan_start_request *req)
35 {
36 	struct wlan_objmgr_pdev *pdev;
37 	struct wlan_objmgr_psoc *psoc;
38 	uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
39 	QDF_STATUS status = QDF_STATUS_E_INVAL;
40 	enum channel_state state;
41 	qdf_freq_t ch_freq;
42 
43 	pdev = wlan_vdev_get_pdev(cm_ctx->vdev);
44 	if (!pdev) {
45 		mlme_err(CM_PREFIX_FMT "Failed to find pdev",
46 			 CM_PREFIX_REF(vdev_id, cm_req->cm_id));
47 		return status;
48 	}
49 
50 	psoc = wlan_pdev_get_psoc(pdev);
51 	if (!psoc) {
52 		mlme_err(CM_PREFIX_FMT "Failed to find psoc",
53 			 CM_PREFIX_REF(vdev_id, cm_req->cm_id));
54 		return status;
55 	}
56 
57 	cm_req->scan_id = wlan_scan_get_scan_id(psoc);
58 	status = wlan_scan_init_default_params(cm_ctx->vdev, req);
59 	if (QDF_IS_STATUS_ERROR(status))
60 		return status;
61 
62 	req->scan_req.scan_type = SCAN_TYPE_SCAN_FOR_CONNECT;
63 	req->scan_req.scan_id = cm_req->scan_id;
64 	req->scan_req.scan_req_id = cm_ctx->scan_requester_id;
65 	req->scan_req.scan_f_passive = false;
66 	req->scan_req.scan_f_bcast_probe = false;
67 
68 	if (cm_req->req.scan_ie.len) {
69 		req->scan_req.extraie.ptr =
70 			qdf_mem_malloc(cm_req->req.scan_ie.len);
71 
72 		if (!req->scan_req.extraie.ptr) {
73 			status = QDF_STATUS_E_NOMEM;
74 			return status;
75 		}
76 
77 		qdf_mem_copy(req->scan_req.extraie.ptr,
78 			     cm_req->req.scan_ie.ptr,
79 			     cm_req->req.scan_ie.len);
80 		req->scan_req.extraie.len = cm_req->req.scan_ie.len;
81 	}
82 
83 	if (wlan_vdev_mlme_get_opmode(cm_ctx->vdev) == QDF_P2P_CLIENT_MODE)
84 		req->scan_req.scan_priority = SCAN_PRIORITY_HIGH;
85 
86 	ch_freq = cm_req->req.chan_freq;
87 	if (ch_freq) {
88 		state = wlan_reg_get_channel_state_for_pwrmode(
89 							pdev,
90 							ch_freq,
91 							REG_BEST_PWR_MODE);
92 
93 		if (state == CHANNEL_STATE_DISABLE ||
94 		    state == CHANNEL_STATE_INVALID) {
95 			mlme_err(CM_PREFIX_FMT "Invalid channel frequency",
96 				 CM_PREFIX_REF(vdev_id, cm_req->cm_id));
97 			status = QDF_STATUS_E_INVAL;
98 			return status;
99 		}
100 		req->scan_req.chan_list.chan[0].freq = ch_freq;
101 		req->scan_req.chan_list.num_chan = 1;
102 	}
103 
104 	if (cm_req->req.ssid.length > WLAN_SSID_MAX_LEN) {
105 		mlme_debug(CM_PREFIX_FMT "Wrong ssid length %d",
106 			   CM_PREFIX_REF(vdev_id, cm_req->cm_id),
107 			   cm_req->req.ssid.length);
108 
109 		status = QDF_STATUS_E_INVAL;
110 		return status;
111 	}
112 	req->scan_req.num_ssids = 1;
113 	qdf_mem_copy(&req->scan_req.ssid[0].ssid,
114 		     &cm_req->req.ssid.ssid,
115 		     cm_req->req.ssid.length);
116 
117 	req->scan_req.ssid[0].length = cm_req->req.ssid.length;
118 	mlme_debug(CM_PREFIX_FMT "Connect scan for " QDF_SSID_FMT,
119 		   CM_PREFIX_REF(vdev_id, cm_req->cm_id),
120 		   QDF_SSID_REF(req->scan_req.ssid[0].length,
121 				req->scan_req.ssid[0].ssid));
122 
123 	req->scan_req.num_bssid = 1;
124 	if (qdf_is_macaddr_zero(&cm_req->req.bssid))
125 		qdf_set_macaddr_broadcast(&req->scan_req.bssid_list[0]);
126 	else
127 		qdf_copy_macaddr(&req->scan_req.bssid_list[0],
128 				 &cm_req->req.bssid);
129 
130 	/* max_scan_time set to 10sec, at timeout scan is aborted */
131 	req->scan_req.max_scan_time = SCAN_FOR_SSID_TIMEOUT;
132 
133 	return status;
134 }
135 
136 QDF_STATUS cm_connect_scan_start(struct cnx_mgr *cm_ctx,
137 				 struct cm_connect_req *cm_req)
138 {
139 	QDF_STATUS status = QDF_STATUS_E_INVAL;
140 	struct scan_start_request *scan_req;
141 
142 	scan_req = qdf_mem_malloc(sizeof(*scan_req));
143 	if (!scan_req) {
144 		status = QDF_STATUS_E_NOMEM;
145 		goto scan_err;
146 	}
147 
148 	status = cm_fill_scan_req(cm_ctx, cm_req, scan_req);
149 
150 	if (QDF_IS_STATUS_ERROR(status)) {
151 		if (scan_req->scan_req.extraie.ptr) {
152 			qdf_mem_free(scan_req->scan_req.extraie.ptr);
153 			scan_req->scan_req.extraie.len = 0;
154 			scan_req->scan_req.extraie.ptr = NULL;
155 		}
156 		qdf_mem_free(scan_req);
157 		goto scan_err;
158 	}
159 
160 	/* scan_req will be freed by wlan_scan_start */
161 	status = wlan_scan_start(scan_req);
162 
163 scan_err:
164 	if (QDF_IS_STATUS_ERROR(status)) {
165 		mlme_err(CM_PREFIX_FMT "Failed to initiate scan with status: %d",
166 			 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
167 				       cm_req->cm_id), status);
168 
169 		status = cm_sm_deliver_event_sync(cm_ctx,
170 						  WLAN_CM_SM_EV_SCAN_FAILURE,
171 						  sizeof(cm_req->scan_id),
172 						  &cm_req->scan_id);
173 		/*
174 		 * Handle failure if posting fails, i.e. the SM state has
175 		 * changed or head cm_id doesn't match the active cm_id.
176 		 * scan start failure should be handled only in SS_SCAN. If
177 		 * new command has been received connect procedure should be
178 		 * aborted from here with connect req cleanup.
179 		 */
180 		if (QDF_IS_STATUS_ERROR(status))
181 			cm_connect_handle_event_post_fail(cm_ctx,
182 							  cm_req->cm_id);
183 	}
184 
185 	return status;
186 }
187 
188 QDF_STATUS cm_connect_scan_resp(struct cnx_mgr *cm_ctx, wlan_scan_id *scan_id,
189 				QDF_STATUS status)
190 {
191 	struct cm_req *cm_req = NULL;
192 	enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE;
193 
194 	if (!*scan_id)
195 		return QDF_STATUS_E_FAILURE;
196 
197 	cm_req = cm_get_req_by_scan_id(cm_ctx, *scan_id);
198 	if (!cm_req)
199 		return QDF_STATUS_E_FAILURE;
200 
201 	if (QDF_IS_STATUS_ERROR(status)) {
202 		reason = CM_NO_CANDIDATE_FOUND;
203 		goto scan_failure;
204 	}
205 	cm_connect_start(cm_ctx, &cm_req->connect_req);
206 
207 	return QDF_STATUS_SUCCESS;
208 scan_failure:
209 	return cm_send_connect_start_fail(cm_ctx, &cm_req->connect_req,
210 					  reason);
211 }
212 
213 void wlan_cm_scan_cb(struct wlan_objmgr_vdev *vdev,
214 		     struct scan_event *event, void *arg)
215 {
216 	struct cnx_mgr *cm_ctx = (struct cnx_mgr *)arg;
217 	wlan_cm_id cm_id = CM_ID_INVALID;
218 	bool success = false;
219 	QDF_STATUS status;
220 
221 	if (!util_is_scan_completed(event, &success))
222 		return;
223 
224 	status = cm_sm_deliver_event(vdev,
225 				     WLAN_CM_SM_EV_SCAN_SUCCESS,
226 				     sizeof(event->scan_id),
227 				     &event->scan_id);
228 	/*
229 	 * Handle failure if posting fails, i.e. the SM state has
230 	 * changed or head cm_id doesn't match the active cm_id.
231 	 * scan cb should be handled only in SS_SCAN. If
232 	 * new command has been received connect procedure should be
233 	 * aborted from here with connect req cleanup.
234 	 */
235 	if (QDF_IS_STATUS_ERROR(status)) {
236 		cm_id = cm_get_cm_id_by_scan_id(cm_ctx, event->scan_id);
237 		if (cm_id != CM_ID_INVALID)
238 			cm_connect_handle_event_post_fail(cm_ctx,
239 							  cm_id);
240 	}
241 
242 }
243 
244