1 /* 2 * Copyright (c) 2012-2015, 2020, 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 specific APIs of connection manager 19 */ 20 21 #include "wlan_cm_main_api.h" 22 #include "wlan_cm_bss_score_param.h" 23 #include "wlan_scan_api.h" 24 #include "wlan_cm_sm.h" 25 #ifdef WLAN_POLICY_MGR_ENABLE 26 #include "wlan_policy_mgr_api.h" 27 #endif 28 #include <wlan_serialization_api.h> 29 30 #ifdef WLAN_POLICY_MGR_ENABLE 31 static void 32 cm_get_pcl_chan_weigtage_for_sta(struct wlan_objmgr_pdev *pdev, 33 struct pcl_freq_weight_list *pcl_lst) 34 { 35 enum QDF_OPMODE opmode = QDF_STA_MODE; 36 enum policy_mgr_con_mode pm_mode; 37 uint32_t num_entries = 0; 38 QDF_STATUS status; 39 40 if (!pcl_lst) 41 return; 42 43 if (policy_mgr_map_concurrency_mode(&opmode, &pm_mode)) { 44 status = policy_mgr_get_pcl(wlan_pdev_get_psoc(pdev), pm_mode, 45 pcl_lst->pcl_freq_list, 46 &num_entries, 47 pcl_lst->pcl_weight_list, 48 NUM_CHANNELS); 49 if (QDF_IS_STATUS_ERROR(status)) 50 return; 51 pcl_lst->num_of_pcl_channels = num_entries; 52 } 53 } 54 55 static void cm_calculate_scores(struct wlan_objmgr_pdev *pdev, 56 struct scan_filter *filter, qdf_list_t *list) 57 { 58 struct pcl_freq_weight_list *pcl_lst = NULL; 59 60 if (!filter->num_of_bssid) { 61 pcl_lst = qdf_mem_malloc(sizeof(*pcl_lst)); 62 cm_get_pcl_chan_weigtage_for_sta(pdev, pcl_lst); 63 if (pcl_lst && !pcl_lst->num_of_pcl_channels) { 64 qdf_mem_free(pcl_lst); 65 pcl_lst = NULL; 66 } 67 } 68 wlan_cm_calculate_bss_score(pdev, pcl_lst, list, &filter->bssid_hint); 69 if (pcl_lst) 70 qdf_mem_free(pcl_lst); 71 } 72 #else 73 static inline 74 void cm_calculate_scores(struct wlan_objmgr_pdev *pdev, 75 struct scan_filter *filter, qdf_list_t *list) 76 { 77 wlan_cm_calculate_bss_score(pdev, NULL, list, &filter->bssid_hint); 78 } 79 #endif 80 81 #ifdef WLAN_FEATURE_11W 82 static inline void 83 cm_set_pmf_caps(struct cm_connect_req *cm_req, struct scan_filter *filter) 84 { 85 filter->pmf_cap = cm_req->req.crypto.pmf_cap; 86 } 87 #else 88 static inline void 89 cm_set_pmf_caps(struct cm_connect_req *cm_req, struct scan_filter *filter) 90 {} 91 #endif 92 93 static void cm_connect_prepare_scan_fliter(struct cnx_mgr *cm_ctx, 94 struct cm_connect_req *cm_req, 95 struct scan_filter *filter) 96 { 97 if (!qdf_is_macaddr_zero(&cm_req->req.bssid)) { 98 filter->num_of_bssid = 1; 99 qdf_copy_macaddr(&filter->bssid_list[0], &cm_req->req.bssid); 100 } 101 102 qdf_copy_macaddr(&filter->bssid_hint, &cm_req->req.bssid_hint); 103 filter->num_of_ssid = 1; 104 qdf_mem_copy(&filter->ssid_list[0], &cm_req->req.ssid, 105 sizeof(struct wlan_ssid)); 106 107 if (cm_req->req.chan_freq) { 108 filter->num_of_channels = 1; 109 filter->chan_freq_list[0] = cm_req->req.chan_freq; 110 } 111 112 /* Fill band (STA+STA) */ 113 /* RSN OVERRIDE */ 114 115 filter->authmodeset = cm_req->req.crypto.auth_type; 116 filter->ucastcipherset = cm_req->req.crypto.ciphers_pairwise; 117 filter->key_mgmt = cm_req->req.crypto.akm_suites; 118 filter->mcastcipherset = cm_req->req.crypto.group_cipher; 119 filter->mgmtcipherset = cm_req->req.crypto.mgmt_ciphers; 120 121 cm_set_pmf_caps(cm_req, filter); 122 123 /* FOR WPS/OSEN set ignore auth */ 124 /* SET mobility domain */ 125 /* Fill fils info */ 126 } 127 128 static QDF_STATUS 129 cm_send_connect_start_fail(struct cnx_mgr *cm_ctx, 130 struct cm_connect_req *cm_req, 131 enum wlan_cm_connect_fail_reason reason) 132 { 133 struct wlan_cm_connect_rsp *resp; 134 QDF_STATUS status; 135 136 resp = qdf_mem_malloc(sizeof(*resp)); 137 if (!resp) 138 return QDF_STATUS_E_NOMEM; 139 140 status = cm_sm_deliver_event(cm_ctx, WLAN_CM_SM_EV_CONNECT_FAILURE, 141 sizeof(*resp), resp); 142 qdf_mem_free(resp); 143 144 return status; 145 } 146 147 static QDF_STATUS cm_connect_get_candidates(struct wlan_objmgr_pdev *pdev, 148 struct cnx_mgr *cm_ctx, 149 struct cm_connect_req *cm_req) 150 { 151 struct scan_filter *filter; 152 uint32_t num_bss = 0; 153 enum QDF_OPMODE op_mode; 154 qdf_list_t *candidate_list; 155 156 filter = qdf_mem_malloc(sizeof(*filter)); 157 if (!filter) 158 return QDF_STATUS_E_NOMEM; 159 160 cm_connect_prepare_scan_fliter(cm_ctx, cm_req, filter); 161 162 candidate_list = wlan_scan_get_result(pdev, filter); 163 if (candidate_list) { 164 num_bss = qdf_list_size(candidate_list); 165 mlme_debug("num_entries found %d", num_bss); 166 } 167 168 op_mode = wlan_vdev_mlme_get_opmode(cm_ctx->vdev); 169 if (num_bss && op_mode == QDF_STA_MODE) 170 cm_calculate_scores(pdev, filter, candidate_list); 171 qdf_mem_free(filter); 172 173 if (!candidate_list || !qdf_list_size(candidate_list)) { 174 if (candidate_list) 175 wlan_scan_purge_results(candidate_list); 176 mlme_info("No valid candidate found num_bss %d", num_bss); 177 /* 178 * Do connect scan only of no candidates were found 179 * if candidates were found and were removed due to invalid 180 * return failure 181 */ 182 if (!num_bss) 183 return cm_sm_deliver_event(cm_ctx, WLAN_CM_SM_EV_SCAN, 184 sizeof(*cm_req), cm_req); 185 186 return QDF_STATUS_E_EMPTY; 187 } 188 cm_req->candidate_list = candidate_list; 189 190 return QDF_STATUS_SUCCESS; 191 } 192 193 static QDF_STATUS 194 cm_ser_connect_cb(struct wlan_serialization_command *cmd, 195 enum wlan_serialization_cb_reason reason) 196 { 197 QDF_STATUS status = QDF_STATUS_SUCCESS; 198 struct wlan_objmgr_vdev *vdev; 199 200 if (!cmd) { 201 mlme_err("cmd is NULL, reason: %d", reason); 202 QDF_ASSERT(0); 203 return QDF_STATUS_E_NULL_VALUE; 204 } 205 206 vdev = cmd->vdev; 207 208 switch (reason) { 209 case WLAN_SER_CB_ACTIVATE_CMD: 210 /* Post event to move CM SM to join active */ 211 break; 212 213 case WLAN_SER_CB_CANCEL_CMD: 214 /* command removed from pending list. */ 215 break; 216 217 case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: 218 mlme_err("Active command timeout cm_id %d", cmd->cmd_id); 219 QDF_ASSERT(0); 220 break; 221 222 case WLAN_SER_CB_RELEASE_MEM_CMD: 223 /* command completed. Release reference of vdev */ 224 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID); 225 break; 226 227 default: 228 QDF_ASSERT(0); 229 status = QDF_STATUS_E_INVAL; 230 break; 231 } 232 233 return status; 234 } 235 236 #define CONNECT_TIMEOUT 30000 237 238 static QDF_STATUS cm_ser_connect_req(struct wlan_objmgr_pdev *pdev, 239 struct cnx_mgr *cm_ctx, 240 struct cm_connect_req *cm_req) 241 { 242 struct wlan_serialization_command cmd = {0, }; 243 enum wlan_serialization_status ser_cmd_status; 244 QDF_STATUS status; 245 246 status = wlan_objmgr_vdev_try_get_ref(cm_ctx->vdev, WLAN_MLME_CM_ID); 247 if (QDF_IS_STATUS_ERROR(status)) { 248 mlme_err("unable to get reference"); 249 return status; 250 } 251 252 cmd.cmd_type = WLAN_SER_CMD_VDEV_CONNECT; 253 cmd.cmd_id = cm_req->cm_id; 254 cmd.cmd_cb = cm_ser_connect_cb; 255 cmd.source = WLAN_UMAC_COMP_MLME; 256 cmd.is_high_priority = false; 257 cmd.cmd_timeout_duration = CONNECT_TIMEOUT; 258 cmd.vdev = cm_ctx->vdev; 259 260 ser_cmd_status = wlan_serialization_request(&cmd); 261 switch (ser_cmd_status) { 262 case WLAN_SER_CMD_PENDING: 263 /* command moved to pending list.Do nothing */ 264 break; 265 case WLAN_SER_CMD_ACTIVE: 266 /* command moved to active list. Do nothing */ 267 break; 268 default: 269 mlme_err("ser cmd status %d", ser_cmd_status); 270 wlan_objmgr_vdev_release_ref(cm_ctx->vdev, WLAN_MLME_CM_ID); 271 272 return QDF_STATUS_E_FAILURE; 273 } 274 275 return QDF_STATUS_SUCCESS; 276 } 277 278 QDF_STATUS cm_connect_start(struct cnx_mgr *cm_ctx, 279 struct cm_connect_req *cm_req) 280 { 281 struct wlan_objmgr_pdev *pdev; 282 enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE; 283 QDF_STATUS status; 284 285 /* Interface event */ 286 pdev = wlan_vdev_get_pdev(cm_ctx->vdev); 287 if (!pdev) { 288 mlme_err("Failed to find pdev from vdev %d", 289 wlan_vdev_get_id(cm_ctx->vdev)); 290 goto connect_err; 291 } 292 293 status = cm_connect_get_candidates(pdev, cm_ctx, cm_req); 294 if (QDF_IS_STATUS_ERROR(status)) { 295 reason = CM_NO_CANDIDATE_FOUND; 296 goto connect_err; 297 } 298 299 /* Do HW mode change */ 300 301 status = cm_ser_connect_req(pdev, cm_ctx, cm_req); 302 if (QDF_IS_STATUS_ERROR(status)) { 303 reason = CM_SER_FAILURE; 304 goto connect_err; 305 } 306 307 return QDF_STATUS_SUCCESS; 308 309 connect_err: 310 return cm_send_connect_start_fail(cm_ctx, cm_req, reason); 311 } 312 313 QDF_STATUS cm_connect_active(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id) 314 { 315 /* 316 * get first valid candidate, create bss peer. 317 * fill vdev crypto for the peer. 318 * call vdev sm to start connect for the candidate. 319 */ 320 return QDF_STATUS_SUCCESS; 321 } 322 323 QDF_STATUS cm_try_next_candidate(struct cnx_mgr *cm_ctx, 324 struct wlan_cm_connect_rsp *resp) 325 { 326 /* 327 * get next valid candidate, if no candidate left, post 328 * WLAN_CM_SM_EV_CONNECT_FAILURE to SM, inform osif about failure for 329 * the candidate if its not last one. and initiate the connect for 330 * next candidate. 331 */ 332 return QDF_STATUS_SUCCESS; 333 } 334 335 QDF_STATUS cm_connect_cmd_timeout(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id) 336 { 337 /* 338 * get the connect req from connect list and post 339 * WLAN_CM_SM_EV_CONNECT_FAILURE. 340 */ 341 return QDF_STATUS_SUCCESS; 342 } 343 344 QDF_STATUS cm_connect_complete(struct cnx_mgr *cm_ctx, 345 struct wlan_cm_connect_rsp *resp) 346 { 347 /* 348 * inform osif about success/failure, inform interface manager 349 * update fils/wep key and inform legacy, update bcn filter and scan 350 * entry mlme info, blm action and remove from serialization at the end. 351 */ 352 return QDF_STATUS_SUCCESS; 353 } 354 355 QDF_STATUS cm_connect_start_req(struct wlan_objmgr_vdev *vdev, 356 struct wlan_cm_connect_req *req) 357 { 358 struct cnx_mgr *cm_ctx = NULL; 359 struct cm_connect_req *cm_req = NULL; 360 361 /* 362 * Get WAPI/WPA/RSN IE and refill crypto params of req. 363 * Prepare cm_connect_req cm_req, get cm id and inform it to OSIF. 364 * store connect req to the cm ctx req_list 365 */ 366 367 return cm_sm_deliver_event(cm_ctx, WLAN_CM_SM_EV_CONNECT_REQ, 368 sizeof(*cm_req), cm_req); 369 } 370