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