1 /* 2 * Copyright (c) 2017-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 6 * any purpose with or without fee is hereby granted, provided that the 7 * above copyright notice and this permission notice appear in all 8 * copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* 21 * DOC: contains 6ghz scan manager functionality 22 */ 23 24 #include "wlan_scan_main.h" 25 #include "wlan_utility.h" 26 #include <wlan_reg_services_api.h> 27 #include "wlan_scan_manager.h" 28 29 /* Beacon/probe weightage multiplier */ 30 #define BCN_PROBE_WEIGHTAGE 5 31 32 /* maximum number of 6ghz hints can be sent per scan request */ 33 #define MAX_HINTS_PER_SCAN_REQ 15 34 35 /* Saved profile weightage multiplier */ 36 #define SAVED_PROFILE_WEIGHTAGE 10 37 38 #ifdef FEATURE_6G_SCAN_CHAN_SORT_ALGO 39 40 /** 41 * scm_sort_6ghz_channel_list() - Sort the 6ghz channels based on weightage 42 * @vdev: vdev on which scan request is issued 43 * @chan_list: channel info of the scan request 44 * 45 * Calculate weightage of each channel based on beacon weightage and saved 46 * profile weightage. Sort the channels based on this weight in descending order 47 * to scan the most preferred channels first compared other 6ghz channels. 48 * 49 * Return: None 50 */ 51 static void 52 scm_sort_6ghz_channel_list(struct wlan_objmgr_vdev *vdev, 53 struct chan_list *chan_list) 54 { 55 uint8_t i, j = 0, max, tmp_list_count; 56 struct meta_rnr_channel *channel; 57 struct chan_info temp_list[MAX_6GHZ_CHANNEL]; 58 struct rnr_chan_weight *rnr_chan_info, temp; 59 uint32_t weight; 60 struct wlan_objmgr_psoc *psoc; 61 62 psoc = wlan_vdev_get_psoc(vdev); 63 if (!psoc) { 64 scm_err("Psoc is NULL"); 65 return; 66 } 67 68 for (i = 0; i < chan_list->num_chan; i++) 69 if (WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_list->chan[i].freq)) 70 temp_list[j++] = chan_list->chan[i]; 71 72 tmp_list_count = j; 73 scm_debug("Total 6ghz channels %d", tmp_list_count); 74 75 /* No Need to sort if the 6ghz channels are less than or one */ 76 if (tmp_list_count <= 1) 77 return; 78 79 rnr_chan_info = qdf_mem_malloc(sizeof(*rnr_chan_info) * tmp_list_count); 80 if (!rnr_chan_info) 81 return; 82 83 /* compute the weightage */ 84 for (i = 0, j = 0; i < tmp_list_count; i++) { 85 channel = scm_get_chan_meta(psoc, temp_list[i].freq); 86 if (!channel) 87 continue; 88 weight = channel->bss_beacon_probe_count * BCN_PROBE_WEIGHTAGE + 89 channel->saved_profile_count * SAVED_PROFILE_WEIGHTAGE; 90 rnr_chan_info[j].weight = weight; 91 rnr_chan_info[j].chan_freq = temp_list[i].freq; 92 rnr_chan_info[j].phymode = temp_list[i].phymode; 93 rnr_chan_info[j].flags = temp_list[i].flags; 94 j++; 95 /* 96 * Log the info only if weight or bss_beacon_probe_count are 97 * non-zero to avoid excessive logging. 98 */ 99 if (weight || channel->bss_beacon_probe_count) 100 scm_debug("Freq %d weight %d bcn_cnt %d", 101 temp_list[i].freq, weight, 102 channel->bss_beacon_probe_count); 103 } 104 105 /* Sort the channel using selection sort - descending order */ 106 for (i = 0; i < tmp_list_count - 1; i++) { 107 max = i; 108 for (j = i + 1; j < tmp_list_count; j++) { 109 if (rnr_chan_info[j].weight > 110 rnr_chan_info[max].weight) 111 max = j; 112 } 113 if (max != i) { 114 qdf_mem_copy(&temp, &rnr_chan_info[max], 115 sizeof(*rnr_chan_info)); 116 qdf_mem_copy(&rnr_chan_info[max], &rnr_chan_info[i], 117 sizeof(*rnr_chan_info)); 118 qdf_mem_copy(&rnr_chan_info[i], &temp, 119 sizeof(*rnr_chan_info)); 120 } 121 } 122 123 /* update the 6g list based on the weightage */ 124 for (i = 0, j = 0; (i < NUM_CHANNELS && j < tmp_list_count); i++) 125 if (wlan_reg_is_6ghz_chan_freq(chan_list->chan[i].freq)) { 126 chan_list->chan[i].freq = rnr_chan_info[j].chan_freq; 127 chan_list->chan[i].flags = rnr_chan_info[j].flags; 128 chan_list->chan[i].phymode = rnr_chan_info[j++].phymode; 129 } 130 131 qdf_mem_free(rnr_chan_info); 132 } 133 134 static void scm_update_rnr_info(struct wlan_objmgr_psoc *psoc, 135 struct scan_start_request *req) 136 { 137 uint8_t i, num_bssid = 0, num_ssid = 0; 138 uint8_t total_count = MAX_HINTS_PER_SCAN_REQ; 139 uint32_t freq; 140 struct meta_rnr_channel *chan; 141 qdf_list_node_t *cur_node, *next_node = NULL; 142 struct scan_rnr_node *rnr_node; 143 struct chan_list *chan_list; 144 QDF_STATUS status; 145 146 if (!req) 147 return; 148 149 chan_list = &req->scan_req.chan_list; 150 for (i = 0; i < chan_list->num_chan; i++) { 151 freq = chan_list->chan[i].freq; 152 153 chan = scm_get_chan_meta(psoc, freq); 154 if (!chan || qdf_list_empty(&chan->rnr_list)) 155 continue; 156 157 qdf_list_peek_front(&chan->rnr_list, &cur_node); 158 while (cur_node && total_count) { 159 rnr_node = qdf_container_of(cur_node, 160 struct scan_rnr_node, 161 node); 162 if (!qdf_is_macaddr_zero(&rnr_node->entry.bssid) && 163 req->scan_req.num_hint_bssid < 164 WLAN_SCAN_MAX_HINT_BSSID) { 165 qdf_mem_copy(&req->scan_req.hint_bssid[ 166 num_bssid++].bssid, 167 &rnr_node->entry.bssid, 168 QDF_MAC_ADDR_SIZE); 169 req->scan_req.num_hint_bssid++; 170 total_count--; 171 } else if (rnr_node->entry.short_ssid && 172 req->scan_req.num_hint_s_ssid < 173 WLAN_SCAN_MAX_HINT_S_SSID) { 174 req->scan_req.hint_s_ssid[ 175 num_ssid++].short_ssid = 176 rnr_node->entry.short_ssid; 177 req->scan_req.num_hint_s_ssid++; 178 total_count--; 179 } 180 status = qdf_list_peek_next(&chan->rnr_list, cur_node, 181 &next_node); 182 if (QDF_IS_STATUS_ERROR(status)) 183 break; 184 cur_node = next_node; 185 next_node = NULL; 186 } 187 } 188 } 189 190 /** 191 * scm_add_rnr_info() - Add the cached RNR info to scan request 192 * @vdev: vdev on which scan request is issued 193 * @req: Scan start request 194 * 195 * Fetch the cached RNR info from scan db and update it to the scan request to 196 * include RNR channels in the scan request. 197 * 198 * Return: None 199 */ 200 static void scm_add_rnr_info(struct wlan_objmgr_pdev *pdev, 201 struct scan_start_request *req) 202 { 203 struct wlan_objmgr_psoc *psoc; 204 struct channel_list_db *rnr_db; 205 206 psoc = wlan_pdev_get_psoc(pdev); 207 if (!psoc) 208 return; 209 rnr_db = scm_get_rnr_channel_db(psoc); 210 if (!rnr_db) 211 return; 212 213 rnr_db->scan_count++; 214 if (rnr_db->scan_count >= RNR_UPDATE_SCAN_CNT_THRESHOLD) { 215 rnr_db->scan_count = 0; 216 scm_rnr_db_flush(psoc); 217 scm_update_rnr_from_scan_cache(pdev); 218 } 219 220 scm_update_rnr_info(psoc, req); 221 } 222 #else 223 static void 224 scm_sort_6ghz_channel_list(struct wlan_objmgr_vdev *vdev, 225 struct chan_list *chan_list) 226 { 227 } 228 229 static void scm_add_rnr_info(struct wlan_objmgr_pdev *pdev, 230 struct scan_start_request *req) 231 { 232 } 233 #endif 234 235 static inline bool 236 scm_is_full_scan_by_userspace(struct chan_list *chan_list) 237 { 238 return (chan_list->num_chan >= FULL_SCAN_CH_COUNT_MIN_BY_USERSPACE); 239 } 240 241 static inline bool 242 scm_is_scan_type_exempted_from_optimization(struct scan_start_request *req) 243 { 244 /* Dont modify the channel list for RRM type*/ 245 return (req->scan_req.scan_type == SCAN_TYPE_RRM); 246 } 247 248 /** 249 * scm_add_all_valid_6g_channels() - Add all valid 6g channels to scan request 250 * @vdev: vdev on which scan request is issued 251 * @req: Scan start request 252 * @num_scan_ch: Total number of scan channels 253 * @is_colocated_6ghz_scan_enabled: colocated 6ghz scan flag enabled in scan req 254 * 255 * If colocated 6ghz scan flag present in host scan request or at least one 6G 256 * channel is present in the host scan request, then this API 257 * fills all remaining (other than channel(s) resent in host scan req) valid 258 * 6 GHz channel(s) to scan requests channel list and set the flag 259 * FLAG_SCAN_ONLY_IF_RNR_FOUND for each of those added channels. 260 * By this driver allows Firmware to scan 6G channels based on RNR IEs only. 261 * 262 * Return: None 263 */ 264 void scm_add_all_valid_6g_channels(struct wlan_objmgr_pdev *pdev, 265 struct chan_list *chan_list, 266 uint8_t *num_scan_ch, 267 bool is_colocated_6ghz_scan_enabled) 268 { 269 uint8_t i, j; 270 enum channel_enum freq_idx; 271 struct regulatory_channel *cur_chan_list; 272 bool is_6g_ch_present = false, found; 273 QDF_STATUS status; 274 uint8_t temp_num_chan = 0; 275 276 for (i = 0; i < chan_list->num_chan; i++) { 277 if (wlan_reg_is_6ghz_chan_freq(chan_list->chan[i].freq)) { 278 scm_debug("At least one 6G chan present in scan req:%d", 279 chan_list->chan[i].freq); 280 is_6g_ch_present = true; 281 break; 282 } 283 } 284 285 if (!is_6g_ch_present && !is_colocated_6ghz_scan_enabled) { 286 scm_debug("Neither 6G chan present nor flag set in scan req"); 287 return; 288 } 289 290 cur_chan_list = qdf_mem_malloc(NUM_CHANNELS * 291 sizeof(struct regulatory_channel)); 292 if (!cur_chan_list) 293 return; 294 295 status = wlan_reg_get_current_chan_list(pdev, cur_chan_list); 296 if (QDF_IS_STATUS_ERROR(status)) { 297 qdf_mem_free(cur_chan_list); 298 scm_debug("Failed to get cur_chan list"); 299 return; 300 } 301 302 freq_idx = 303 wlan_reg_get_chan_enum_for_freq(wlan_reg_min_6ghz_chan_freq()); 304 if (freq_idx == INVALID_CHANNEL) 305 return; 306 307 scm_debug("freq_idx:%d", freq_idx); 308 temp_num_chan = chan_list->num_chan; 309 for (i = freq_idx; i < NUM_CHANNELS; i++) { 310 found = false; 311 for (j = 0; j < temp_num_chan; j++) { 312 if (cur_chan_list[i].center_freq == 313 chan_list->chan[j].freq) { 314 found = true; 315 break; 316 } 317 } 318 if (!found && cur_chan_list[i].state != CHANNEL_STATE_DISABLE && 319 cur_chan_list[i].state != CHANNEL_STATE_INVALID) { 320 chan_list->chan[chan_list->num_chan].freq = 321 cur_chan_list[i].center_freq; 322 chan_list->chan[chan_list->num_chan].flags = 323 FLAG_SCAN_ONLY_IF_RNR_FOUND; 324 chan_list->num_chan++; 325 } 326 } 327 328 scm_debug("prev num_chan:%d, current num_chan:%d", temp_num_chan, 329 chan_list->num_chan); 330 *num_scan_ch = chan_list->num_chan; 331 qdf_mem_free(cur_chan_list); 332 } 333 334 static void 335 scm_copy_valid_channels(struct wlan_objmgr_psoc *psoc, 336 enum scan_mode_6ghz scan_mode, 337 struct scan_start_request *req, 338 uint8_t *num_scan_ch) 339 { 340 uint8_t i, num_ch = *num_scan_ch; 341 struct chan_list *chan_list = &req->scan_req.chan_list; 342 qdf_freq_t freq; 343 344 switch (scan_mode) { 345 case SCAN_MODE_6G_NO_CHANNEL: 346 /* Don't add any 6g channels */ 347 for (i = 0; i < chan_list->num_chan; i++) 348 if (!wlan_reg_is_6ghz_chan_freq( 349 chan_list->chan[i].freq)) 350 chan_list->chan[num_ch++] = 351 chan_list->chan[i]; 352 break; 353 case SCAN_MODE_6G_PSC_CHANNEL: 354 case SCAN_MODE_6G_PSC_DUTY_CYCLE: 355 /* 356 * Filter out non-PSC 6g channels if firmware doesn't 357 * supports RNR_ONLY scan flag/feature and the scan type is 358 * allowed to be optimized. 359 */ 360 if (!scm_is_6ghz_scan_optimization_supported(psoc) && 361 !scm_is_scan_type_exempted_from_optimization(req)) { 362 for (i = 0; i < chan_list->num_chan; i++) { 363 freq = chan_list->chan[i].freq; 364 if (!wlan_reg_is_6ghz_chan_freq(freq) || 365 (wlan_reg_is_6ghz_chan_freq(freq) && 366 wlan_reg_is_6ghz_psc_chan_freq(freq))) 367 chan_list->chan[num_ch++] = 368 chan_list->chan[i]; 369 } 370 break; 371 } 372 /* 373 * Consider the complete channel list if firmware supports 374 * RNR_ONLY scan flag/feature. 375 */ 376 fallthrough; 377 default: 378 /* 379 * Allow all 2g/5g/6g channels. Below are also covered in this 380 * 1. SCAN_MODE_6G_ALL_CHANNEL: Copy all channels and RNR flag 381 * won't be set for any channel. 382 * 2. SCAN_MODE_6G_PSC_CHANNEL: Copy all channels and RNR flag 383 * will be set for non-PSC. 384 * 3. SCAN_MODE_6G_PSC_DUTY_CYCLE: Copy all channels and RNR 385 * flag will be set for non-PSC for all scans and RNR flag 386 * will be set for PSC channels only for duty cycle scan. 387 */ 388 num_ch = chan_list->num_chan; 389 } 390 391 *num_scan_ch = num_ch; 392 } 393 394 static inline void 395 scm_set_rnr_flag_non_psc_6g_ch(struct chan_info *chan, uint8_t num_chan) 396 { 397 uint8_t i; 398 399 for (i = 0; i < num_chan; i++) 400 if (wlan_reg_is_6ghz_chan_freq(chan[i].freq) && 401 !wlan_reg_is_6ghz_psc_chan_freq(chan[i].freq)) 402 chan[i].flags = FLAG_SCAN_ONLY_IF_RNR_FOUND; 403 } 404 405 static inline void 406 scm_set_rnr_flag_all_6g_ch(struct chan_info *chan, uint8_t num_chan) 407 { 408 uint8_t i; 409 410 for (i = 0; i < num_chan; i++) 411 if (wlan_reg_is_6ghz_chan_freq(chan[i].freq)) 412 chan[i].flags = FLAG_SCAN_ONLY_IF_RNR_FOUND; 413 } 414 415 static bool scm_is_duty_cycle_scan(struct wlan_scan_obj *scan_obj) 416 { 417 bool duty_cycle = false; 418 419 scan_obj->duty_cycle_cnt_6ghz++; 420 if (scan_obj->duty_cycle_cnt_6ghz == 1) 421 duty_cycle = true; 422 if (scan_obj->scan_def.duty_cycle_6ghz == scan_obj->duty_cycle_cnt_6ghz) 423 scan_obj->duty_cycle_cnt_6ghz = 0; 424 425 return duty_cycle; 426 } 427 428 inline bool 429 scm_is_6ghz_scan_optimization_supported(struct wlan_objmgr_psoc *psoc) 430 { 431 return wlan_psoc_nif_fw_ext_cap_get(psoc, 432 WLAN_SOC_CEXT_SCAN_PER_CH_CONFIG); 433 } 434 435 void scm_add_channel_flags(struct wlan_objmgr_vdev *vdev, 436 struct chan_list *chan_list, 437 uint8_t *num_chan, 438 bool is_colocated_6ghz_scan_enabled, 439 bool is_pno_scan) 440 { 441 struct wlan_scan_obj *scan_obj; 442 enum scan_mode_6ghz scan_mode; 443 struct wlan_objmgr_pdev *pdev; 444 uint8_t num_scan_chan = *num_chan; 445 446 pdev = wlan_vdev_get_pdev(vdev); 447 if (!pdev) 448 return; 449 scan_obj = wlan_vdev_get_scan_obj(vdev); 450 if (!scan_obj) { 451 scm_err("scan_obj is NULL"); 452 return; 453 } 454 455 scan_mode = scan_obj->scan_def.scan_mode_6g; 456 457 switch (scan_mode) { 458 case SCAN_MODE_6G_RNR_ONLY: 459 /* 460 * When the ini is set to SCAN_MODE_6G_RNR_ONLY 461 * always set RNR flag for all(PSC and non-PSC) channels. 462 */ 463 scm_set_rnr_flag_all_6g_ch(&chan_list->chan[0], num_scan_chan); 464 break; 465 case SCAN_MODE_6G_PSC_CHANNEL: 466 /* 467 * When the ini is set to SCAN_MODE_6G_PSC_CHANNEL, 468 * always set RNR flag for non-PSC channels. 469 */ 470 scm_set_rnr_flag_non_psc_6g_ch(&chan_list->chan[0], 471 num_scan_chan); 472 break; 473 case SCAN_MODE_6G_PSC_DUTY_CYCLE: 474 case SCAN_MODE_6G_ALL_DUTY_CYCLE: 475 if (!is_pno_scan && !scm_is_duty_cycle_scan(scan_obj)) 476 scm_set_rnr_flag_all_6g_ch(&chan_list->chan[0], 477 num_scan_chan); 478 else if (scan_mode == SCAN_MODE_6G_PSC_DUTY_CYCLE) { 479 if (is_pno_scan) 480 scm_debug("Duty cycle scan not supported in pno"); 481 scm_set_rnr_flag_non_psc_6g_ch(&chan_list->chan[0], 482 num_scan_chan); 483 } 484 break; 485 case SCAN_MODE_6G_ALL_CHANNEL: 486 /* 487 * When the ini is set to SCAN_MODE_6G_ALL_CHANNEL, 488 * Host fills all remaining (other than channel(s) present in 489 * host scan req) valid 6 GHz channel(s) to scan requests and 490 * set the flag FLAG_SCAN_ONLY_IF_RNR_FOUND for each remaining 491 * channels. 492 */ 493 scm_add_all_valid_6g_channels(pdev, chan_list, num_chan, 494 is_colocated_6ghz_scan_enabled); 495 break; 496 default: 497 /* 498 * Don't set the RNR flag for SCAN_MODE_6G_NO_CHANNEL/ 499 * SCAN_MODE_6G_RNR_ONLY 500 */ 501 break; 502 } 503 } 504 505 void 506 scm_update_6ghz_channel_list(struct scan_start_request *req, 507 struct wlan_scan_obj *scan_obj) 508 { 509 struct wlan_objmgr_vdev *vdev = req->vdev; 510 struct wlan_objmgr_pdev *pdev; 511 struct chan_list *chan_list = &req->scan_req.chan_list; 512 enum scan_mode_6ghz scan_mode; 513 uint8_t num_scan_ch = 0; 514 enum QDF_OPMODE op_mode; 515 struct wlan_objmgr_psoc *psoc; 516 517 pdev = wlan_vdev_get_pdev(vdev); 518 if (!pdev) 519 return; 520 psoc = wlan_pdev_get_psoc(pdev); 521 522 /* Dont update the channel list for not STA mode */ 523 op_mode = wlan_vdev_mlme_get_opmode(req->vdev); 524 if (op_mode == QDF_SAP_MODE || 525 op_mode == QDF_P2P_DEVICE_MODE || 526 op_mode == QDF_P2P_CLIENT_MODE || 527 op_mode == QDF_P2P_GO_MODE) 528 return; 529 530 scan_mode = scan_obj->scan_def.scan_mode_6g; 531 scm_debug("6g scan mode %d", scan_mode); 532 533 /* 534 * Host has learned RNR info/channels from previous scan. Add them to 535 * the scan request and don't set RNR_ONLY flag to scan them without 536 * optimization. Don't add RNR info if the scan type is exempted from 537 * optimization. 538 */ 539 if (scan_mode != SCAN_MODE_6G_NO_CHANNEL && 540 scm_is_full_scan_by_userspace(chan_list) && 541 !scm_is_scan_type_exempted_from_optimization(req)) 542 scm_add_rnr_info(pdev, req); 543 544 /* copy all the channels given by userspace */ 545 scm_copy_valid_channels(psoc, scan_mode, req, &num_scan_ch); 546 547 /* No more optimizations are needed in the below cases */ 548 if (!scm_is_full_scan_by_userspace(chan_list) || 549 !scm_is_6ghz_scan_optimization_supported(psoc) || 550 scm_is_scan_type_exempted_from_optimization(req)) 551 goto end; 552 553 scm_add_channel_flags(vdev, chan_list, &num_scan_ch, 554 req->scan_req.scan_policy_colocated_6ghz, false); 555 556 end: 557 chan_list->num_chan = num_scan_ch; 558 559 scm_sort_6ghz_channel_list(req->vdev, &req->scan_req.chan_list); 560 } 561