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