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