1 /* 2 * Copyright (c) 2012-2015,2020-2021 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 (PLATFORM_VALUE(10000, 50000)) 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->scan_id), 169 &cm_req->scan_id); 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 return QDF_STATUS_E_FAILURE; 193 194 cm_req = cm_get_req_by_scan_id(cm_ctx, *scan_id); 195 if (!cm_req) 196 return QDF_STATUS_E_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