xref: /wlan-dirver/qca-wifi-host-cmn/umac/scan/core/src/wlan_scan_manager_6ghz.c (revision 97f44cd39e4ff816eaa1710279d28cf6b9e65ad9)
1 /*
2  * Copyright (c) 2017-2020 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++].freq = chan_list->chan[i].freq;
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 		j++;
92 		/*
93 		 * Log the info only if weight or bss_beacon_probe_count are
94 		 * non-zero to avoid excessive logging.
95 		 */
96 		if (weight || channel->bss_beacon_probe_count)
97 			scm_debug("Freq %d weight %d bcn_cnt %d",
98 				  temp_list[i].freq, weight,
99 				  channel->bss_beacon_probe_count);
100 	}
101 
102 	/* Sort the channel using selection sort - descending order */
103 	for (i = 0; i < tmp_list_count - 1; i++) {
104 		max = i;
105 		for (j = i + 1; j < tmp_list_count; j++) {
106 			if (rnr_chan_info[j].weight >
107 			    rnr_chan_info[max].weight)
108 				max = j;
109 		}
110 		if (max != i) {
111 			qdf_mem_copy(&temp, &rnr_chan_info[max],
112 				     sizeof(*rnr_chan_info));
113 			qdf_mem_copy(&rnr_chan_info[max], &rnr_chan_info[i],
114 				     sizeof(*rnr_chan_info));
115 			qdf_mem_copy(&rnr_chan_info[i], &temp,
116 				     sizeof(*rnr_chan_info));
117 		}
118 	}
119 
120 	/* update the 6g list based on the weightage */
121 	for (i = 0, j = 0; (i < NUM_CHANNELS && j < tmp_list_count); i++)
122 		if (wlan_reg_is_6ghz_chan_freq(chan_list->chan[i].freq))
123 			chan_list->chan[i].freq = rnr_chan_info[j++].chan_freq;
124 
125 	qdf_mem_free(rnr_chan_info);
126 }
127 
128 static void scm_update_rnr_info(struct wlan_objmgr_psoc *psoc,
129 				struct scan_start_request *req)
130 {
131 	uint8_t i, num_bssid = 0, num_ssid = 0;
132 	uint8_t total_count = MAX_HINTS_PER_SCAN_REQ;
133 	uint32_t freq;
134 	struct meta_rnr_channel *chan;
135 	qdf_list_node_t *cur_node, *next_node = NULL;
136 	struct scan_rnr_node *rnr_node;
137 	struct chan_list *chan_list;
138 	QDF_STATUS status;
139 
140 	if (!req)
141 		return;
142 
143 	chan_list = &req->scan_req.chan_list;
144 	for (i = 0; i < chan_list->num_chan; i++) {
145 		freq = chan_list->chan[i].freq;
146 
147 		chan = scm_get_chan_meta(psoc, freq);
148 		if (!chan || qdf_list_empty(&chan->rnr_list))
149 			continue;
150 
151 		qdf_list_peek_front(&chan->rnr_list, &cur_node);
152 		while (cur_node && total_count) {
153 			rnr_node = qdf_container_of(cur_node,
154 						    struct scan_rnr_node,
155 						    node);
156 			if (!qdf_is_macaddr_zero(&rnr_node->entry.bssid) &&
157 			    req->scan_req.num_hint_bssid <
158 			    WLAN_SCAN_MAX_HINT_BSSID) {
159 				qdf_mem_copy(&req->scan_req.hint_bssid[
160 							num_bssid++].bssid,
161 					     &rnr_node->entry.bssid,
162 					     QDF_MAC_ADDR_SIZE);
163 				req->scan_req.num_hint_bssid++;
164 				total_count--;
165 			} else if (rnr_node->entry.short_ssid &&
166 				   req->scan_req.num_hint_s_ssid <
167 				   WLAN_SCAN_MAX_HINT_S_SSID) {
168 				req->scan_req.hint_s_ssid[
169 					num_ssid++].short_ssid =
170 						rnr_node->entry.short_ssid;
171 				req->scan_req.num_hint_s_ssid++;
172 				total_count--;
173 			}
174 			status = qdf_list_peek_next(&chan->rnr_list, cur_node,
175 						    &next_node);
176 			if (QDF_IS_STATUS_ERROR(status))
177 				break;
178 			cur_node = next_node;
179 			next_node = NULL;
180 		}
181 	}
182 }
183 
184 /**
185  * scm_add_rnr_info() - Add the cached RNR info to scan request
186  * @vdev: vdev on which scan request is issued
187  * @req: Scan start request
188  *
189  * Fetch the cached RNR info from scan db and update it to the scan request to
190  * include RNR channels in the scan request.
191  *
192  * Return: None
193  */
194 static void scm_add_rnr_info(struct wlan_objmgr_pdev *pdev,
195 			     struct scan_start_request *req)
196 {
197 	struct wlan_objmgr_psoc *psoc;
198 	struct channel_list_db *rnr_db;
199 
200 	psoc = wlan_pdev_get_psoc(pdev);
201 	if (!psoc)
202 		return;
203 	rnr_db = scm_get_rnr_channel_db(psoc);
204 	if (!rnr_db)
205 		return;
206 
207 	rnr_db->scan_count++;
208 	if (rnr_db->scan_count >= RNR_UPDATE_SCAN_CNT_THRESHOLD) {
209 		rnr_db->scan_count = 0;
210 		scm_rnr_db_flush(psoc);
211 		scm_update_rnr_from_scan_cache(pdev);
212 	}
213 
214 	scm_update_rnr_info(psoc, req);
215 }
216 #else
217 static void
218 scm_sort_6ghz_channel_list(struct wlan_objmgr_vdev *vdev,
219 			   struct chan_list *chan_list)
220 {
221 }
222 
223 static void scm_add_rnr_info(struct wlan_objmgr_pdev *pdev,
224 			     struct scan_start_request *req)
225 {
226 }
227 #endif
228 
229 static inline bool
230 scm_is_full_scan_by_userspace(struct chan_list *chan_list)
231 {
232 	return (chan_list->num_chan >= FULL_SCAN_CH_COUNT_MIN_BY_USERSPACE);
233 }
234 
235 static void
236 scm_copy_valid_channels(struct wlan_objmgr_psoc *psoc,
237 			enum scan_mode_6ghz scan_mode,
238 			struct chan_list *chan_list,
239 			uint8_t *num_scan_ch)
240 {
241 	uint8_t i, num_ch = *num_scan_ch;
242 	qdf_freq_t freq;
243 
244 	switch (scan_mode) {
245 	case SCAN_MODE_6G_NO_CHANNEL:
246 		/* Don't add any 6g channels */
247 		for (i = 0; i < chan_list->num_chan; i++)
248 			if (!wlan_reg_is_6ghz_chan_freq(
249 					chan_list->chan[i].freq))
250 				chan_list->chan[num_ch++] =
251 					chan_list->chan[i];
252 		break;
253 	case SCAN_MODE_6G_PSC_CHANNEL:
254 	case SCAN_MODE_6G_PSC_DUTY_CYCLE:
255 		/*
256 		 * Don't add non-PSC 6g channels if firmware doesn't
257 		 * support RNR_ONLY scan flag/feature.
258 		 */
259 		if (!scm_is_6ghz_scan_optimization_supported(psoc)) {
260 			for (i = 0; i < chan_list->num_chan; i++) {
261 				freq = chan_list->chan[i].freq;
262 				if (!wlan_reg_is_6ghz_chan_freq(freq) ||
263 				    (wlan_reg_is_6ghz_chan_freq(freq) &&
264 				     wlan_reg_is_6ghz_psc_chan_freq(freq)))
265 					chan_list->chan[num_ch++] =
266 						chan_list->chan[i];
267 			}
268 			break;
269 		}
270 		/*
271 		 * Consider the complete channel list if firmware supports
272 		 * RNR_ONLY scan flag/feature.
273 		 */
274 		/* fallthrough */
275 	default:
276 		/*
277 		 * Allow all 2g/5g/6g channels. Below are also covered in this
278 		 * 1. SCAN_MODE_6G_ALL_CHANNEL: Copy all channels and RNR flag
279 		 *    won't be set for any channel.
280 		 * 2. SCAN_MODE_6G_PSC_CHANNEL: Copy all channels and RNR flag
281 		 *    will be set for non-PSC.
282 		 * 3. SCAN_MODE_6G_PSC_DUTY_CYCLE: Copy all channels and RNR
283 		 *    flag will be set for non-PSC for all scans and RNR flag
284 		 *    will be set for PSC channels only for duty cycle scan.
285 		 */
286 		num_ch = chan_list->num_chan;
287 	}
288 
289 	*num_scan_ch = num_ch;
290 }
291 
292 static inline void
293 scm_set_rnr_flag_non_psc_6g_ch(struct chan_info *chan, uint8_t num_chan)
294 {
295 	uint8_t i;
296 
297 	for (i = 0; i < num_chan; i++)
298 		if (wlan_reg_is_6ghz_chan_freq(chan[i].freq) &&
299 		    !wlan_reg_is_6ghz_psc_chan_freq(chan[i].freq))
300 			chan[i].flags = FLAG_SCAN_ONLY_IF_RNR_FOUND;
301 }
302 
303 static inline void
304 scm_set_rnr_flag_all_6g_ch(struct chan_info *chan, uint8_t num_chan)
305 {
306 	uint8_t i;
307 
308 	for (i = 0; i < num_chan; i++)
309 		if (wlan_reg_is_6ghz_chan_freq(chan[i].freq))
310 			chan[i].flags = FLAG_SCAN_ONLY_IF_RNR_FOUND;
311 }
312 
313 static bool scm_is_duty_cycle_scan(struct wlan_scan_obj *scan_obj)
314 {
315 	bool duty_cycle = false;
316 
317 	scan_obj->duty_cycle_cnt_6ghz++;
318 	if (scan_obj->duty_cycle_cnt_6ghz ==
319 		scan_obj->scan_def.duty_cycle_6ghz) {
320 		duty_cycle = true;
321 		scan_obj->duty_cycle_cnt_6ghz = 0;
322 	}
323 
324 	return duty_cycle;
325 }
326 
327 inline bool
328 scm_is_6ghz_scan_optimization_supported(struct wlan_objmgr_psoc *psoc)
329 {
330 	return wlan_psoc_nif_fw_ext_cap_get(psoc,
331 					    WLAN_SOC_CEXT_SCAN_PER_CH_CONFIG);
332 }
333 
334 void
335 scm_update_6ghz_channel_list(struct scan_start_request *req,
336 			     struct wlan_scan_obj *scan_obj)
337 {
338 	struct wlan_objmgr_vdev *vdev = req->vdev;
339 	struct wlan_objmgr_pdev *pdev;
340 	struct chan_list *chan_list = &req->scan_req.chan_list;
341 	enum scan_mode_6ghz scan_mode;
342 	uint8_t num_scan_ch = 0;
343 	enum QDF_OPMODE op_mode;
344 
345 	pdev = wlan_vdev_get_pdev(vdev);
346 	if (!pdev)
347 		return;
348 
349 	/* Dont update the channel list for not STA mode */
350 	op_mode = wlan_vdev_mlme_get_opmode(req->vdev);
351 	if (op_mode == QDF_SAP_MODE ||
352 	    op_mode == QDF_P2P_DEVICE_MODE ||
353 	    op_mode == QDF_P2P_CLIENT_MODE ||
354 	    op_mode == QDF_P2P_GO_MODE)
355 		return;
356 
357 	scan_mode = scan_obj->scan_def.scan_mode_6g;
358 	scm_debug("6g scan mode %d", scan_mode);
359 
360 	/*
361 	 * Host has learned RNR info/channels from previous scan. Add them to
362 	 * the scan request and don't set RNR_ONLY flag to scan them without
363 	 * optimization.
364 	 */
365 	if (scan_mode != SCAN_MODE_6G_NO_CHANNEL &&
366 	    scm_is_full_scan_by_userspace(chan_list))
367 		scm_add_rnr_info(pdev, req);
368 
369 	/* copy all the channels given by userspace */
370 	scm_copy_valid_channels(wlan_pdev_get_psoc(pdev), scan_mode, chan_list,
371 				&num_scan_ch);
372 
373 	/* No more optimizations are needed in the below cases */
374 	if (!scm_is_full_scan_by_userspace(chan_list) ||
375 	    !scm_is_6ghz_scan_optimization_supported(
376 				wlan_pdev_get_psoc(pdev)))
377 		goto end;
378 
379 	switch (scan_mode) {
380 	case SCAN_MODE_6G_RNR_ONLY:
381 		/*
382 		 * When the ini is set to SCAN_MODE_6G_RNR_ONLY,
383 		 * always set RNR flag for all(PSC and non-PSC) channels.
384 		 */
385 		scm_set_rnr_flag_all_6g_ch(&chan_list->chan[0], num_scan_ch);
386 		break;
387 	case SCAN_MODE_6G_PSC_CHANNEL:
388 		/*
389 		 * When the ini is set to SCAN_MODE_6G_PSC_CHANNEL,
390 		 * always set RNR flag for non-PSC channels.
391 		 */
392 		scm_set_rnr_flag_non_psc_6g_ch(&chan_list->chan[0],
393 					       num_scan_ch);
394 		break;
395 	case SCAN_MODE_6G_PSC_DUTY_CYCLE:
396 	case SCAN_MODE_6G_ALL_DUTY_CYCLE:
397 		if (!scm_is_duty_cycle_scan(scan_obj))
398 			scm_set_rnr_flag_all_6g_ch(&chan_list->chan[0],
399 						   num_scan_ch);
400 		else if (scan_mode == SCAN_MODE_6G_PSC_DUTY_CYCLE)
401 			scm_set_rnr_flag_non_psc_6g_ch(&chan_list->chan[0],
402 						       num_scan_ch);
403 		break;
404 	default:
405 		/*
406 		 * Don't set the RNR flag for SCAN_MODE_6G_NO_CHANNEL/
407 		 * SCAN_MODE_6G_RNR_ONLY
408 		 */
409 		break;
410 	}
411 
412 end:
413 	chan_list->num_chan = num_scan_ch;
414 
415 	scm_sort_6ghz_channel_list(req->vdev, &req->scan_req.chan_list);
416 }
417