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