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