xref: /wlan-dirver/qca-wifi-host-cmn/umac/scan/core/src/wlan_scan_manager_6ghz.c (revision ff97c457a24e42f296651977e24b8030ca80dffb)
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