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