1 /*
2 * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "wlan_ll_lt_sap_main.h"
18 #include "wlan_reg_services_api.h"
19 #include "wlan_ll_sap_public_structs.h"
20 #include "wlan_policy_mgr_i.h"
21 #include "wlan_mlme_vdev_mgr_interface.h"
22 #include "wlan_ll_sap_main.h"
23 #include "wlan_ll_lt_sap_bearer_switch.h"
24 #include "wlan_scan_api.h"
25 #include "target_if.h"
26
ll_lt_sap_is_supported(struct wlan_objmgr_psoc * psoc)27 bool ll_lt_sap_is_supported(struct wlan_objmgr_psoc *psoc)
28 {
29 struct wmi_unified *wmi_handle;
30
31 wmi_handle = get_wmi_unified_hdl_from_psoc(psoc);
32 if (!wmi_handle) {
33 ll_sap_err("Invalid WMI handle");
34 return false;
35 }
36
37 return wmi_service_enabled(wmi_handle, wmi_service_xpan_support);
38 }
39
40 /**
41 * ll_lt_sap_get_sorted_user_config_acs_ch_list() - API to get sorted user
42 * configured ACS channel list
43 * @psoc: Pointer to psoc object
44 * @vdev_id: Vdev Id
45 * @ch_info: Pointer to ch_info
46 *
47 * Return: None
48 */
49 static
ll_lt_sap_get_sorted_user_config_acs_ch_list(struct wlan_objmgr_psoc * psoc,uint8_t vdev_id,struct sap_sel_ch_info * ch_info)50 QDF_STATUS ll_lt_sap_get_sorted_user_config_acs_ch_list(
51 struct wlan_objmgr_psoc *psoc,
52 uint8_t vdev_id,
53 struct sap_sel_ch_info *ch_info)
54 {
55 struct scan_filter *filter;
56 qdf_list_t *list = NULL;
57 struct wlan_objmgr_pdev *pdev;
58 struct wlan_objmgr_vdev *vdev;
59 QDF_STATUS status = QDF_STATUS_E_FAILURE;
60
61 vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id,
62 WLAN_LL_SAP_ID);
63
64 if (!vdev) {
65 ll_sap_err("Invalid vdev for vdev_id %d", vdev_id);
66 return QDF_STATUS_E_FAILURE;
67 }
68
69 pdev = wlan_vdev_get_pdev(vdev);
70
71 filter = qdf_mem_malloc(sizeof(*filter));
72 if (!filter)
73 goto rel_ref;
74
75 wlan_sap_get_user_config_acs_ch_list(vdev_id, filter);
76
77 list = wlan_scan_get_result(pdev, filter);
78
79 qdf_mem_free(filter);
80
81 if (!list)
82 goto rel_ref;
83
84 status = wlan_ll_sap_sort_channel_list(vdev_id, list, ch_info);
85
86 wlan_scan_purge_results(list);
87
88 rel_ref:
89 wlan_objmgr_vdev_release_ref(vdev, WLAN_LL_SAP_ID);
90
91 return status;
92 }
93
ll_lt_sap_init(struct wlan_objmgr_vdev * vdev)94 QDF_STATUS ll_lt_sap_init(struct wlan_objmgr_vdev *vdev)
95 {
96 struct ll_sap_vdev_priv_obj *ll_sap_obj;
97 QDF_STATUS status;
98 uint8_t i, j;
99 struct bearer_switch_info *bs_ctx;
100
101 ll_sap_obj = ll_sap_get_vdev_priv_obj(vdev);
102
103 if (!ll_sap_obj) {
104 ll_sap_err("vdev %d ll_sap obj null",
105 wlan_vdev_get_id(vdev));
106 return QDF_STATUS_E_INVAL;
107 }
108
109 bs_ctx = qdf_mem_malloc(sizeof(struct bearer_switch_info));
110 if (!bs_ctx)
111 return QDF_STATUS_E_NOMEM;
112
113 ll_sap_obj->bearer_switch_ctx = bs_ctx;
114
115 qdf_atomic_init(&bs_ctx->request_id);
116
117 for (i = 0; i < WLAN_UMAC_PSOC_MAX_VDEVS; i++)
118 for (j = 0; j < BEARER_SWITCH_REQ_MAX; j++)
119 qdf_atomic_init(&bs_ctx->ref_count[i][j]);
120
121 qdf_atomic_init(&bs_ctx->fw_ref_count);
122 qdf_atomic_init(&bs_ctx->total_ref_count);
123
124 bs_ctx->vdev = vdev;
125
126 status = bs_sm_create(bs_ctx);
127
128 if (QDF_IS_STATUS_ERROR(status))
129 goto bs_sm_failed;
130
131 ll_sap_debug("vdev %d", wlan_vdev_get_id(vdev));
132
133 return QDF_STATUS_SUCCESS;
134
135 bs_sm_failed:
136 qdf_mem_free(ll_sap_obj->bearer_switch_ctx);
137 ll_sap_obj->bearer_switch_ctx = NULL;
138 return status;
139 }
140
ll_lt_sap_deinit(struct wlan_objmgr_vdev * vdev)141 QDF_STATUS ll_lt_sap_deinit(struct wlan_objmgr_vdev *vdev)
142 {
143 struct ll_sap_vdev_priv_obj *ll_sap_obj;
144
145 ll_sap_obj = ll_sap_get_vdev_priv_obj(vdev);
146
147 if (!ll_sap_obj) {
148 ll_sap_err("vdev %d ll_sap obj null",
149 wlan_vdev_get_id(vdev));
150 return QDF_STATUS_E_INVAL;
151 }
152
153 if (!ll_sap_obj->bearer_switch_ctx) {
154 ll_sap_debug("vdev %d Bearer switch context is NULL",
155 wlan_vdev_get_id(vdev));
156 return QDF_STATUS_E_INVAL;
157 }
158
159 bs_sm_destroy(ll_sap_obj->bearer_switch_ctx);
160 qdf_mem_free(ll_sap_obj->bearer_switch_ctx);
161 ll_sap_obj->bearer_switch_ctx = NULL;
162
163 ll_sap_debug("vdev %d", wlan_vdev_get_id(vdev));
164
165 return QDF_STATUS_SUCCESS;
166 }
167
168 static
ll_lt_sap_update_mac_freq(struct wlan_ll_lt_sap_mac_freq * freq_list,qdf_freq_t sbs_cut_off_freq,qdf_freq_t given_freq,uint32_t given_weight)169 void ll_lt_sap_update_mac_freq(struct wlan_ll_lt_sap_mac_freq *freq_list,
170 qdf_freq_t sbs_cut_off_freq,
171 qdf_freq_t given_freq, uint32_t given_weight)
172 {
173 if (WLAN_REG_IS_5GHZ_CH_FREQ(given_freq) &&
174 ((sbs_cut_off_freq && given_freq < sbs_cut_off_freq) ||
175 !sbs_cut_off_freq) && !freq_list->freq_5GHz_low) {
176 freq_list->freq_5GHz_low = given_freq;
177 freq_list->weight_5GHz_low = given_weight;
178
179 /*
180 * Update same freq in 5GHz high freq if sbs_cut_off_freq
181 * is not present
182 */
183 if (!sbs_cut_off_freq) {
184 freq_list->freq_5GHz_high = given_freq;
185 freq_list->weight_5GHz_high = given_weight;
186 }
187 } else if (WLAN_REG_IS_5GHZ_CH_FREQ(given_freq) &&
188 ((sbs_cut_off_freq && given_freq > sbs_cut_off_freq) ||
189 !sbs_cut_off_freq) && !freq_list->freq_5GHz_high) {
190 freq_list->freq_5GHz_high = given_freq;
191 freq_list->weight_5GHz_high = given_weight;
192
193 /*
194 * Update same freq for 5GHz low freq if sbs_cut_off_freq
195 * is not present
196 */
197 if (!sbs_cut_off_freq) {
198 freq_list->freq_5GHz_low = given_freq;
199 freq_list->weight_5GHz_low = given_weight;
200 }
201 } else if (WLAN_REG_IS_6GHZ_CHAN_FREQ(given_freq) &&
202 !freq_list->freq_6GHz) {
203 freq_list->freq_6GHz = given_freq;
204 freq_list->weight_6GHz = given_weight;
205 }
206 }
207
208 /* Threshold value of channel weight */
209 #define CHANNEL_WEIGHT_THRESHOLD_VALUE 20000
210 static
ll_lt_sap_update_freq_list(struct wlan_objmgr_psoc * psoc,struct sap_sel_ch_info * ch_param,struct wlan_ll_lt_sap_freq_list * freq_list,struct policy_mgr_pcl_list * pcl,struct connection_info * info,uint8_t connection_count,uint8_t vdev_id)211 void ll_lt_sap_update_freq_list(struct wlan_objmgr_psoc *psoc,
212 struct sap_sel_ch_info *ch_param,
213 struct wlan_ll_lt_sap_freq_list *freq_list,
214 struct policy_mgr_pcl_list *pcl,
215 struct connection_info *info,
216 uint8_t connection_count,
217 uint8_t vdev_id)
218 {
219 qdf_freq_t freq, sbs_cut_off_freq;
220 qdf_freq_t same_mac_freq, standalone_mac_freq;
221 uint8_t i = 0, count;
222 uint32_t weight;
223 enum policy_mgr_con_mode con_mode = PM_MAX_NUM_OF_MODE;
224
225 sbs_cut_off_freq = policy_mgr_get_sbs_cut_off_freq(psoc);
226
227 for (i = 0; i < ch_param->num_ch; i++) {
228 if (!ch_param->ch_info[i].valid)
229 continue;
230
231 freq = ch_param->ch_info[i].chan_freq;
232 weight = ch_param->ch_info[i].weight;
233
234 /*
235 * Do not select same channel where LL_LT_SAP was
236 * present earlier
237 */
238 if (freq_list->prev_freq == freq)
239 continue;
240
241 /* Check if channel is present in PCL or not */
242 if (!wlan_ll_sap_freq_present_in_pcl(pcl, freq))
243 continue;
244
245 /*
246 * Store first valid best channel from ACS final list
247 * This will be used if there is no valid freq present
248 * within threshold value or no concurrency present
249 */
250 if (!freq_list->best_freq) {
251 freq_list->best_freq = freq;
252 freq_list->weight_best_freq = weight;
253 }
254
255 if (!connection_count)
256 break;
257
258 /*
259 * Instead of selecting random channel, select those
260 * channel whose weight is less than
261 * CHANNEL_WEIGHT_THRESHOLD_VALUE value. This will help
262 * to get the best channel compartively and avoid multiple
263 * times of channel switch.
264 */
265 if (weight > CHANNEL_WEIGHT_THRESHOLD_VALUE)
266 continue;
267
268 same_mac_freq = 0;
269 standalone_mac_freq = 0;
270
271 /*
272 * Loop through all existing connections before updating
273 * channels for LL_LT_SAP.
274 */
275 for (count = 0; count < connection_count; count++) {
276 /* Do not select SCC channel to update freq_list */
277 if (freq == info[count].ch_freq) {
278 same_mac_freq = 0;
279 standalone_mac_freq = 0;
280 break;
281 }
282 /*
283 * Check whether ch_param frequency is in same mac
284 * or not.
285 */
286 if (policy_mgr_2_freq_always_on_same_mac(
287 psoc, freq,
288 info[count].ch_freq)) {
289 con_mode = policy_mgr_get_mode_by_vdev_id(
290 psoc, info[count].vdev_id);
291 /*
292 * Check whether SAP is present in same mac or
293 * not. If yes then do not select that frequency
294 * because two beacon entity can't be in same
295 * mac.
296 * Also, do not fill same_mac_freq if it's
297 * already filled i.e two existing connection
298 * are present on same mac.
299 */
300 if (con_mode == PM_SAP_MODE || same_mac_freq) {
301 same_mac_freq = 0;
302 standalone_mac_freq = 0;
303 break;
304 }
305 same_mac_freq = freq;
306 } else if (!standalone_mac_freq) {
307 /*
308 * Fill standalone_mac_freq only if it's not
309 * filled
310 */
311 standalone_mac_freq = freq;
312 }
313 }
314
315 /*
316 * Scenario: Let say two concurrent connection(other than
317 * LL_LT_SAP) are present in both mac and one channel from
318 * ch_param structure needs to select to fill in freq_list for
319 * LL_LT_SAP.
320 * Since, there is an existing connection present in both mac
321 * then there is chance that channel from ch_param structure
322 * may get select for both same_mac_freq and standalone_mac_freq
323 *
324 * But ideally standalone_mac_freq is not free, some existing
325 * connection is already present in it.
326 * So, instead of giving priority to fill standalone_mac_freq
327 * first, fill same_mac_freq first.
328 *
329 * Example: Let say 2 concurrent interface present in channel
330 * 36 and 149. Now channel 48 from ch_param structure needs to
331 * be validate and fill based on existing interface.
332 * With respect to channel 36, it will fit to same_mac_freq
333 * and with respect to channel 149, it will fit to
334 * standalone_mac_freq. But in standalone_mac_freq, there is
335 * already existing interface present. So, give priority to
336 * same_mac_freq to fill the freq list.
337 */
338 if (same_mac_freq)
339 ll_lt_sap_update_mac_freq(&freq_list->shared_mac,
340 sbs_cut_off_freq,
341 same_mac_freq,
342 weight);
343 else if (standalone_mac_freq)
344 ll_lt_sap_update_mac_freq(&freq_list->standalone_mac,
345 sbs_cut_off_freq,
346 standalone_mac_freq,
347 weight);
348
349 if (freq_list->shared_mac.freq_5GHz_low &&
350 freq_list->shared_mac.freq_5GHz_high &&
351 freq_list->shared_mac.freq_6GHz &&
352 freq_list->standalone_mac.freq_5GHz_low &&
353 freq_list->standalone_mac.freq_5GHz_high &&
354 freq_list->standalone_mac.freq_6GHz)
355 break;
356 }
357
358 ll_sap_debug("vdev %d, best %d[%d] Shared: low %d[%d] high %d[%d] 6Ghz %d[%d], Standalone: low %d[%d] high %d[%d] 6Ghz %d[%d], prev %d, existing connection cnt %d",
359 vdev_id, freq_list->best_freq,
360 freq_list->weight_best_freq,
361 freq_list->shared_mac.freq_5GHz_low,
362 freq_list->shared_mac.weight_5GHz_low,
363 freq_list->shared_mac.freq_5GHz_high,
364 freq_list->shared_mac.weight_5GHz_high,
365 freq_list->shared_mac.freq_6GHz,
366 freq_list->shared_mac.weight_6GHz,
367 freq_list->standalone_mac.freq_5GHz_low,
368 freq_list->standalone_mac.weight_5GHz_low,
369 freq_list->standalone_mac.freq_5GHz_high,
370 freq_list->standalone_mac.weight_5GHz_high,
371 freq_list->standalone_mac.freq_6GHz,
372 freq_list->standalone_mac.weight_6GHz,
373 freq_list->prev_freq, connection_count);
374 }
375
376 static
ll_lt_sap_get_allowed_freq_list(struct wlan_objmgr_psoc * psoc,uint8_t vdev_id,struct sap_sel_ch_info * ch_param,struct wlan_ll_lt_sap_freq_list * freq_list)377 QDF_STATUS ll_lt_sap_get_allowed_freq_list(
378 struct wlan_objmgr_psoc *psoc,
379 uint8_t vdev_id,
380 struct sap_sel_ch_info *ch_param,
381 struct wlan_ll_lt_sap_freq_list *freq_list)
382 {
383 struct connection_info conn_info[MAX_NUMBER_OF_CONC_CONNECTIONS];
384 uint8_t count;
385 struct policy_mgr_pcl_list *pcl;
386 QDF_STATUS status;
387
388 pcl = qdf_mem_malloc(sizeof(*pcl));
389 if (!pcl)
390 return QDF_STATUS_E_FAILURE;
391
392 status = policy_mgr_get_pcl_ch_list_for_ll_sap(psoc, pcl, vdev_id,
393 conn_info, &count);
394 if (QDF_IS_STATUS_ERROR(status))
395 goto end;
396
397 ll_lt_sap_update_freq_list(psoc, ch_param, freq_list, pcl,
398 conn_info, count, vdev_id);
399
400 status = QDF_STATUS_SUCCESS;
401 end:
402 qdf_mem_free(pcl);
403 return status;
404 }
405
ll_lt_sap_get_freq_list(struct wlan_objmgr_psoc * psoc,struct wlan_ll_lt_sap_freq_list * freq_list,uint8_t vdev_id)406 QDF_STATUS ll_lt_sap_get_freq_list(struct wlan_objmgr_psoc *psoc,
407 struct wlan_ll_lt_sap_freq_list *freq_list,
408 uint8_t vdev_id)
409 {
410 struct sap_sel_ch_info ch_param = { NULL, 0 };
411 QDF_STATUS status;
412
413 /*
414 * This memory will be allocated in sap_chan_sel_init() as part
415 * of sap_sort_channel_list(). But the caller has to free up the
416 * allocated memory
417 */
418
419 status = ll_lt_sap_get_sorted_user_config_acs_ch_list(psoc, vdev_id,
420 &ch_param);
421
422 if (!ch_param.num_ch || QDF_IS_STATUS_ERROR(status))
423 goto release_mem;
424
425 status = ll_lt_sap_get_allowed_freq_list(psoc, vdev_id, &ch_param,
426 freq_list);
427
428 release_mem:
429 wlan_ll_sap_free_chan_info(&ch_param);
430 return status;
431 }
432
ll_lt_sap_get_valid_freq(struct wlan_objmgr_psoc * psoc,uint8_t vdev_id)433 qdf_freq_t ll_lt_sap_get_valid_freq(struct wlan_objmgr_psoc *psoc,
434 uint8_t vdev_id)
435 {
436 struct wlan_ll_lt_sap_freq_list freq_list;
437
438 qdf_mem_zero(&freq_list, sizeof(freq_list));
439
440 ll_lt_sap_get_freq_list(psoc, &freq_list, vdev_id);
441
442 if (freq_list.standalone_mac.freq_5GHz_low)
443 return freq_list.standalone_mac.freq_5GHz_low;
444 if (freq_list.shared_mac.freq_5GHz_low)
445 return freq_list.shared_mac.freq_5GHz_low;
446 if (freq_list.standalone_mac.freq_6GHz)
447 return freq_list.standalone_mac.freq_6GHz;
448 if (freq_list.standalone_mac.freq_5GHz_high)
449 return freq_list.standalone_mac.freq_5GHz_high;
450 if (freq_list.shared_mac.freq_6GHz)
451 return freq_list.shared_mac.freq_6GHz;
452 if (freq_list.shared_mac.freq_5GHz_high)
453 return freq_list.shared_mac.freq_5GHz_high;
454
455 return freq_list.best_freq;
456 }
457