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