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