xref: /wlan-driver/qca-wifi-host-cmn/umac/mlme/connection_mgr/core/src/wlan_cm_connect_scan.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1 /*
2  * Copyright (c) 2012-2015,2020-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-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 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 
cm_fill_scan_req(struct cnx_mgr * cm_ctx,struct cm_connect_req * cm_req,struct scan_start_request * req)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 	/* Try using freq hint to scan if chan freq is not set */
88 	if (!ch_freq)
89 		ch_freq = cm_req->req.chan_freq_hint;
90 	if (ch_freq) {
91 		state = wlan_reg_get_channel_state_for_pwrmode(
92 							pdev,
93 							ch_freq,
94 							REG_BEST_PWR_MODE);
95 
96 		if (state == CHANNEL_STATE_DISABLE ||
97 		    state == CHANNEL_STATE_INVALID) {
98 			mlme_err(CM_PREFIX_FMT "Invalid channel frequency",
99 				 CM_PREFIX_REF(vdev_id, cm_req->cm_id));
100 			status = QDF_STATUS_E_INVAL;
101 			return status;
102 		}
103 		req->scan_req.chan_list.chan[0].freq = ch_freq;
104 		req->scan_req.chan_list.num_chan = 1;
105 	}
106 
107 	if (cm_req->req.ssid.length > WLAN_SSID_MAX_LEN) {
108 		mlme_debug(CM_PREFIX_FMT "Wrong ssid length %d",
109 			   CM_PREFIX_REF(vdev_id, cm_req->cm_id),
110 			   cm_req->req.ssid.length);
111 
112 		status = QDF_STATUS_E_INVAL;
113 		return status;
114 	}
115 	req->scan_req.num_ssids = 1;
116 	qdf_mem_copy(&req->scan_req.ssid[0].ssid,
117 		     &cm_req->req.ssid.ssid,
118 		     cm_req->req.ssid.length);
119 
120 	req->scan_req.ssid[0].length = cm_req->req.ssid.length;
121 	mlme_debug(CM_PREFIX_FMT "Connect scan for " QDF_SSID_FMT,
122 		   CM_PREFIX_REF(vdev_id, cm_req->cm_id),
123 		   QDF_SSID_REF(req->scan_req.ssid[0].length,
124 				req->scan_req.ssid[0].ssid));
125 
126 	req->scan_req.num_bssid = 1;
127 	if (qdf_is_macaddr_zero(&cm_req->req.bssid))
128 		qdf_set_macaddr_broadcast(&req->scan_req.bssid_list[0]);
129 	else
130 		qdf_copy_macaddr(&req->scan_req.bssid_list[0],
131 				 &cm_req->req.bssid);
132 
133 	/* max_scan_time set to 10sec, at timeout scan is aborted */
134 	req->scan_req.max_scan_time = SCAN_FOR_SSID_TIMEOUT;
135 
136 	return status;
137 }
138 
cm_connect_scan_start(struct cnx_mgr * cm_ctx,struct cm_connect_req * cm_req)139 QDF_STATUS cm_connect_scan_start(struct cnx_mgr *cm_ctx,
140 				 struct cm_connect_req *cm_req)
141 {
142 	QDF_STATUS status = QDF_STATUS_E_INVAL;
143 	struct scan_start_request *scan_req;
144 
145 	scan_req = qdf_mem_malloc(sizeof(*scan_req));
146 	if (!scan_req) {
147 		status = QDF_STATUS_E_NOMEM;
148 		goto scan_err;
149 	}
150 
151 	status = cm_fill_scan_req(cm_ctx, cm_req, scan_req);
152 
153 	if (QDF_IS_STATUS_ERROR(status)) {
154 		if (scan_req->scan_req.extraie.ptr) {
155 			qdf_mem_free(scan_req->scan_req.extraie.ptr);
156 			scan_req->scan_req.extraie.len = 0;
157 			scan_req->scan_req.extraie.ptr = NULL;
158 		}
159 		qdf_mem_free(scan_req);
160 		goto scan_err;
161 	}
162 
163 	/* scan_req will be freed by wlan_scan_start */
164 	status = wlan_scan_start(scan_req);
165 
166 scan_err:
167 	if (QDF_IS_STATUS_ERROR(status)) {
168 		mlme_err(CM_PREFIX_FMT "Failed to initiate scan with status: %d",
169 			 CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
170 				       cm_req->cm_id), status);
171 
172 		status = cm_sm_deliver_event_sync(cm_ctx,
173 						  WLAN_CM_SM_EV_SCAN_FAILURE,
174 						  sizeof(cm_req->scan_id),
175 						  &cm_req->scan_id);
176 		/*
177 		 * Handle failure if posting fails, i.e. the SM state has
178 		 * changed or head cm_id doesn't match the active cm_id.
179 		 * scan start failure should be handled only in SS_SCAN. If
180 		 * new command has been received connect procedure should be
181 		 * aborted from here with connect req cleanup.
182 		 */
183 		if (QDF_IS_STATUS_ERROR(status))
184 			cm_connect_handle_event_post_fail(cm_ctx,
185 							  cm_req->cm_id);
186 	}
187 
188 	return status;
189 }
190 
cm_connect_scan_resp(struct cnx_mgr * cm_ctx,wlan_scan_id * scan_id,QDF_STATUS status)191 QDF_STATUS cm_connect_scan_resp(struct cnx_mgr *cm_ctx, wlan_scan_id *scan_id,
192 				QDF_STATUS status)
193 {
194 	struct cm_req *cm_req = NULL;
195 	enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE;
196 
197 	if (!*scan_id)
198 		return QDF_STATUS_E_FAILURE;
199 
200 	cm_req = cm_get_req_by_scan_id(cm_ctx, *scan_id);
201 	if (!cm_req)
202 		return QDF_STATUS_E_FAILURE;
203 
204 	if (QDF_IS_STATUS_ERROR(status)) {
205 		reason = CM_NO_CANDIDATE_FOUND;
206 		goto scan_failure;
207 	}
208 	cm_connect_start(cm_ctx, &cm_req->connect_req);
209 
210 	return QDF_STATUS_SUCCESS;
211 scan_failure:
212 	return cm_send_connect_start_fail(cm_ctx, &cm_req->connect_req,
213 					  reason);
214 }
215 
wlan_cm_scan_cb(struct wlan_objmgr_vdev * vdev,struct scan_event * event,void * arg)216 void wlan_cm_scan_cb(struct wlan_objmgr_vdev *vdev,
217 		     struct scan_event *event, void *arg)
218 {
219 	struct cnx_mgr *cm_ctx = (struct cnx_mgr *)arg;
220 	wlan_cm_id cm_id = CM_ID_INVALID;
221 	bool success = false;
222 	QDF_STATUS status;
223 
224 	if (!util_is_scan_completed(event, &success))
225 		return;
226 
227 	status = cm_sm_deliver_event(vdev,
228 				     WLAN_CM_SM_EV_SCAN_SUCCESS,
229 				     sizeof(event->scan_id),
230 				     &event->scan_id);
231 	/*
232 	 * Handle failure if posting fails, i.e. the SM state has
233 	 * changed or head cm_id doesn't match the active cm_id.
234 	 * scan cb should be handled only in SS_SCAN. If
235 	 * new command has been received connect procedure should be
236 	 * aborted from here with connect req cleanup.
237 	 */
238 	if (QDF_IS_STATUS_ERROR(status)) {
239 		cm_id = cm_get_cm_id_by_scan_id(cm_ctx, event->scan_id);
240 		if (cm_id != CM_ID_INVALID)
241 			cm_connect_handle_event_post_fail(cm_ctx,
242 							  cm_id);
243 	}
244 
245 }
246 
247