1 /*
2 * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
3 * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 /**
19 * DOC: wlan_hdd_dcs.c
20 *
21 * DCS implementation.
22 *
23 */
24
25 #include <wlan_hdd_hostapd.h>
26 #include <wlan_hdd_dcs.h>
27 #include <wlan_hdd_includes.h>
28 #include <wlan_dcs_ucfg_api.h>
29 #include <wlan_dlm_ucfg_api.h>
30 #include <wlan_osif_priv.h>
31 #include <wlan_objmgr_vdev_obj.h>
32 #include <wlan_dcs_ucfg_api.h>
33
34 /* Time(in milliseconds) before which the AP doesn't expect a connection */
35 #define HDD_DCS_AWGN_BSS_RETRY_DELAY (5 * 60 * 1000)
36
37 /**
38 * hdd_dcs_add_bssid_to_reject_list() - add bssid to reject list
39 * @pdev: pdev ptr
40 * @bssid: bssid to be added
41 *
42 * Return: QDF_STATUS
43 */
44 static QDF_STATUS
hdd_dcs_add_bssid_to_reject_list(struct wlan_objmgr_pdev * pdev,struct qdf_mac_addr * bssid)45 hdd_dcs_add_bssid_to_reject_list(struct wlan_objmgr_pdev *pdev,
46 struct qdf_mac_addr *bssid)
47 {
48 struct reject_ap_info ap_info;
49
50 qdf_mem_zero(&ap_info, sizeof(struct reject_ap_info));
51 qdf_copy_macaddr(&ap_info.bssid, bssid);
52 /* set retry_delay to reject new connect requests */
53 ap_info.rssi_reject_params.retry_delay =
54 HDD_DCS_AWGN_BSS_RETRY_DELAY;
55 ap_info.reject_ap_type = DRIVER_RSSI_REJECT_TYPE;
56 ap_info.reject_reason = REASON_STA_KICKOUT;
57 ap_info.source = ADDED_BY_DRIVER;
58 return ucfg_dlm_add_bssid_to_reject_list(pdev, &ap_info);
59 }
60
61 /**
62 * hdd_dcs_switch_chan_cb() - hdd dcs switch channel callback
63 * @vdev: vdev ptr
64 * @tgt_freq: target channel frequency
65 * @tgt_width: target channel width
66 *
67 * This callback is registered with dcs component to trigger channel switch.
68 *
69 * Return: QDF_STATUS
70 */
hdd_dcs_switch_chan_cb(struct wlan_objmgr_vdev * vdev,qdf_freq_t tgt_freq,enum phy_ch_width tgt_width)71 static QDF_STATUS hdd_dcs_switch_chan_cb(struct wlan_objmgr_vdev *vdev,
72 qdf_freq_t tgt_freq,
73 enum phy_ch_width tgt_width)
74 {
75 struct hdd_adapter *adapter;
76 struct wlan_hdd_link_info *link_info;
77 mac_handle_t mac_handle;
78 struct qdf_mac_addr *bssid;
79 int ret;
80 QDF_STATUS status = QDF_STATUS_SUCCESS;
81 struct wlan_objmgr_pdev *pdev;
82 struct wlan_objmgr_psoc *psoc;
83
84 link_info = wlan_hdd_get_link_info_from_objmgr(vdev);
85 if (!link_info) {
86 hdd_err("Invalid vdev %d", wlan_vdev_get_id(vdev));
87 return QDF_STATUS_E_INVAL;
88 }
89
90 adapter = link_info->adapter;
91 switch (adapter->device_mode) {
92 case QDF_STA_MODE:
93 if (!hdd_cm_is_vdev_associated(link_info))
94 return QDF_STATUS_E_INVAL;
95
96 bssid = &link_info->session.station.conn_info.bssid;
97
98 /* disconnect if got invalid freq or width */
99 if (tgt_freq == 0 || tgt_width == CH_WIDTH_INVALID) {
100 pdev = wlan_vdev_get_pdev(vdev);
101 if (!pdev)
102 return QDF_STATUS_E_INVAL;
103
104 hdd_dcs_add_bssid_to_reject_list(pdev, bssid);
105 wlan_hdd_cm_issue_disconnect(link_info,
106 REASON_UNSPEC_FAILURE,
107 true);
108 return QDF_STATUS_SUCCESS;
109 }
110
111 mac_handle = hdd_context_get_mac_handle(adapter->hdd_ctx);
112 if (!mac_handle)
113 return QDF_STATUS_E_INVAL;
114
115 status = sme_switch_channel(mac_handle, bssid,
116 tgt_freq, tgt_width);
117 break;
118 case QDF_SAP_MODE:
119 if (!test_bit(SOFTAP_BSS_STARTED, &link_info->link_flags))
120 return QDF_STATUS_E_INVAL;
121
122 /* stop sap if got invalid freq or width */
123 if (tgt_freq == 0 || tgt_width == CH_WIDTH_INVALID) {
124 schedule_work(&adapter->sap_stop_bss_work);
125 return QDF_STATUS_SUCCESS;
126 }
127
128 psoc = wlan_vdev_get_psoc(vdev);
129 if (!psoc)
130 return QDF_STATUS_E_INVAL;
131
132 wlan_hdd_set_sap_csa_reason(psoc, link_info->vdev_id,
133 CSA_REASON_DCS);
134 ret = hdd_softap_set_channel_change(adapter->dev, tgt_freq,
135 tgt_width, true);
136 status = qdf_status_from_os_return(ret);
137 break;
138 default:
139 hdd_err("OP mode %d not supported", adapter->device_mode);
140 break;
141 }
142
143 return status;
144 }
145
146 #ifdef WLAN_FEATURE_SAP_ACS_OPTIMIZE
147 /**
148 * hdd_get_bw_for_freq - get BW for provided freq
149 * @res_msg: resp msg with freq info
150 * @freq: freq for which BW is required
151 * @total_chan: total no of channels
152 *
153 * Return: bandwidth
154 */
155 static enum phy_ch_width
hdd_get_bw_for_freq(struct get_usable_chan_res_params * res_msg,uint16_t freq,uint16_t total_chan)156 hdd_get_bw_for_freq(struct get_usable_chan_res_params *res_msg,
157 uint16_t freq, uint16_t total_chan)
158 {
159 uint16_t i;
160
161 for (i = 0; i < total_chan; i++) {
162 if (res_msg[i].freq == freq)
163 return res_msg[i].bw;
164 }
165 return CH_WIDTH_INVALID;
166 }
167
168 /**
169 * hdd_dcs_select_random_chan: To select random 6G channel
170 * for CSA
171 * @pdev: pdev object
172 * @vdev: vdevobject
173 *
174 * Return: success/failure
175 */
176 static QDF_STATUS
hdd_dcs_select_random_chan(struct wlan_objmgr_pdev * pdev,struct wlan_objmgr_vdev * vdev)177 hdd_dcs_select_random_chan(struct wlan_objmgr_pdev *pdev,
178 struct wlan_objmgr_vdev *vdev)
179 {
180 struct get_usable_chan_req_params req_msg;
181 struct get_usable_chan_res_params *res_msg;
182 enum phy_ch_width tgt_width;
183 uint16_t final_lst[NUM_CHANNELS] = {0};
184 uint16_t intf_ch_freq = 0;
185 uint32_t count;
186 uint32_t i;
187 QDF_STATUS status = QDF_STATUS_E_EMPTY;
188
189 res_msg = qdf_mem_malloc(NUM_CHANNELS *
190 sizeof(*res_msg));
191
192 if (!res_msg) {
193 hdd_err("res_msg invalid");
194 return QDF_STATUS_E_NOMEM;
195 }
196 req_msg.band_mask = BIT(REG_BAND_6G);
197 req_msg.iface_mode_mask = BIT(NL80211_IFTYPE_AP);
198 req_msg.filter_mask = 0;
199 status = wlan_reg_get_usable_channel(pdev, req_msg, res_msg, &count,
200 REG_CURRENT_PWR_MODE);
201 if (QDF_STATUS_SUCCESS != status) {
202 hdd_err("get usable channel failed %d", status);
203 qdf_mem_free(res_msg);
204 return QDF_STATUS_E_INVAL;
205 }
206 hdd_debug("channel count %d for band %d", count, REG_BAND_6G);
207 for (i = 0; i < count; i++)
208 final_lst[i] = res_msg[i].freq;
209
210 intf_ch_freq = wlan_get_rand_from_lst_for_freq(final_lst, count);
211 if (!intf_ch_freq || intf_ch_freq > wlan_reg_max_6ghz_chan_freq()) {
212 hdd_debug("ch freq gt max 6g freq %d",
213 wlan_reg_max_6ghz_chan_freq());
214 qdf_mem_free(res_msg);
215 return QDF_STATUS_E_INVAL;
216 }
217 tgt_width = hdd_get_bw_for_freq(res_msg, intf_ch_freq, count);
218 if (tgt_width >= CH_WIDTH_INVALID) {
219 qdf_mem_free(res_msg);
220 return QDF_STATUS_E_INVAL;
221 }
222 if (tgt_width > CH_WIDTH_160MHZ) {
223 hdd_debug("restrict max bw to 160");
224 tgt_width = CH_WIDTH_160MHZ;
225 }
226 qdf_mem_free(res_msg);
227 return ucfg_dcs_switch_chan(vdev, intf_ch_freq,
228 tgt_width);
229 }
230 #else
231 static inline QDF_STATUS
hdd_dcs_select_random_chan(struct wlan_objmgr_pdev * pdev,struct wlan_objmgr_vdev * vdev)232 hdd_dcs_select_random_chan(struct wlan_objmgr_pdev *pdev,
233 struct wlan_objmgr_vdev *vdev)
234 {
235 return QDF_STATUS_E_NOSUPPORT;
236 }
237 #endif
238
239 /**
240 * hdd_dcs_cb() - hdd dcs specific callback
241 * @psoc: psoc
242 * @mac_id: mac_id
243 * @interference_type: wlan or continuous wave interference type
244 * @arg: List of arguments
245 *
246 * This callback is registered with dcs component to start acs operation
247 *
248 * Return: None
249 */
hdd_dcs_cb(struct wlan_objmgr_psoc * psoc,uint8_t mac_id,uint8_t interference_type,void * arg)250 static void hdd_dcs_cb(struct wlan_objmgr_psoc *psoc, uint8_t mac_id,
251 uint8_t interference_type, void *arg)
252 {
253 struct hdd_context *hdd_ctx = (struct hdd_context *)arg;
254 struct wlan_hdd_link_info *link_info;
255 struct sap_context *sap_ctx;
256 uint32_t count;
257 uint32_t list[MAX_NUMBER_OF_CONC_CONNECTIONS];
258 uint32_t index;
259 QDF_STATUS status;
260
261 /*
262 * so far CAP_DCS_CWIM interference mitigation is not supported
263 */
264 if (interference_type == WLAN_HOST_DCS_CWIM) {
265 hdd_debug("CW interference mitigation is not supported");
266 return;
267 }
268
269 if (policy_mgr_is_force_scc(psoc) &&
270 policy_mgr_is_sta_gc_active_on_mac(psoc, mac_id)) {
271 ucfg_config_dcs_event_data(psoc, mac_id, true);
272
273 hdd_debug("force scc %d, mac id %d sta gc count %d",
274 policy_mgr_is_force_scc(psoc), mac_id,
275 policy_mgr_is_sta_gc_active_on_mac(psoc, mac_id));
276 return;
277 }
278
279 count = policy_mgr_get_sap_go_count_on_mac(psoc, list, mac_id);
280 for (index = 0; index < count; index++) {
281 link_info = hdd_get_link_info_by_vdev(hdd_ctx, list[index]);
282 if (!link_info) {
283 hdd_err("vdev_id %u does not exist with host",
284 list[index]);
285 return;
286 }
287
288 sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(link_info);
289 if (!wlansap_dcs_is_wlan_interference_mitigation_enabled(sap_ctx))
290 continue;
291
292 hdd_debug("DCS triggers ACS on vdev_id=%u, mac_id=%u",
293 list[index], mac_id);
294 /*
295 * Select Random channel for low latency sap as
296 * ACS can't select channel of same MAC from which
297 * CSA is triggered because same MAC frequencies
298 * will not be present in scan list and results and
299 * selecting freq of other MAC may cause MCC with
300 * other modes if present.
301 */
302 if (wlan_mlme_get_ap_policy(link_info->vdev) !=
303 HOST_CONCURRENT_AP_POLICY_UNSPECIFIED) {
304 status = hdd_dcs_select_random_chan(hdd_ctx->pdev,
305 link_info->vdev);
306 if (QDF_IS_STATUS_SUCCESS(status))
307 return;
308 }
309 wlan_hdd_cfg80211_start_acs(link_info);
310 return;
311 }
312 }
313
314 #ifdef CONFIG_AFC_SUPPORT
315 /**
316 * hdd_dcs_afc_sel_chan_cb() - Callback to select best SAP channel/bandwidth
317 * after channel state update by AFC
318 * @arg: argument
319 * @vdev_id: vdev id of SAP
320 * @cur_freq: SAP current channel frequency
321 * @cur_bw: SAP current channel bandwidth
322 * @pref_bw: pointer to channel bandwidth prefer to set as input and output
323 * as target bandwidth can set
324 *
325 * Return: Target home channel frequency selected
326 */
hdd_dcs_afc_sel_chan_cb(void * arg,uint32_t vdev_id,qdf_freq_t cur_freq,enum phy_ch_width cur_bw,enum phy_ch_width * pref_bw)327 static qdf_freq_t hdd_dcs_afc_sel_chan_cb(void *arg,
328 uint32_t vdev_id,
329 qdf_freq_t cur_freq,
330 enum phy_ch_width cur_bw,
331 enum phy_ch_width *pref_bw)
332 {
333 struct hdd_context *hdd_ctx = (struct hdd_context *)arg;
334 struct wlan_hdd_link_info *link_info;
335 struct sap_context *sap_ctx;
336 qdf_freq_t target_freq;
337
338 if (!hdd_ctx)
339 return 0;
340
341 link_info = hdd_get_link_info_by_vdev(hdd_ctx, vdev_id);
342 if (!link_info)
343 return 0;
344
345 sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(link_info);
346 if (!sap_ctx)
347 return 0;
348
349 target_freq = sap_afc_dcs_sel_chan(sap_ctx, cur_freq, cur_bw, pref_bw);
350
351 return target_freq;
352 }
353 #else
hdd_dcs_afc_sel_chan_cb(void * arg,uint32_t vdev_id,qdf_freq_t cur_freq,enum phy_ch_width cur_bw,enum phy_ch_width * pref_bw)354 static inline qdf_freq_t hdd_dcs_afc_sel_chan_cb(void *arg,
355 uint32_t vdev_id,
356 qdf_freq_t cur_freq,
357 enum phy_ch_width cur_bw,
358 enum phy_ch_width *pref_bw)
359 {
360 return 0;
361 }
362 #endif
363
hdd_dcs_register_cb(struct hdd_context * hdd_ctx)364 void hdd_dcs_register_cb(struct hdd_context *hdd_ctx)
365 {
366 ucfg_dcs_register_cb(hdd_ctx->psoc, hdd_dcs_cb, hdd_ctx);
367 ucfg_dcs_register_awgn_cb(hdd_ctx->psoc, hdd_dcs_switch_chan_cb);
368 ucfg_dcs_register_afc_sel_chan_cb(hdd_ctx->psoc,
369 hdd_dcs_afc_sel_chan_cb,
370 hdd_ctx);
371 }
372
hdd_dcs_hostapd_set_chan(struct hdd_context * hdd_ctx,uint8_t vdev_id,qdf_freq_t dcs_ch_freq)373 QDF_STATUS hdd_dcs_hostapd_set_chan(struct hdd_context *hdd_ctx,
374 uint8_t vdev_id,
375 qdf_freq_t dcs_ch_freq)
376 {
377 struct hdd_ap_ctx *ap_ctx;
378 struct sap_context *sap_ctx;
379 QDF_STATUS status;
380 uint8_t mac_id;
381 uint32_t list[MAX_NUMBER_OF_CONC_CONNECTIONS];
382 uint32_t conn_idx, count;
383 struct wlan_hdd_link_info *link_info;
384 uint32_t dcs_ch = wlan_reg_freq_to_chan(hdd_ctx->pdev, dcs_ch_freq);
385
386 status = policy_mgr_get_mac_id_by_session_id(hdd_ctx->psoc, vdev_id,
387 &mac_id);
388
389 if (QDF_IS_STATUS_ERROR(status)) {
390 hdd_err("get mac id failed");
391 return QDF_STATUS_E_INVAL;
392 }
393 count = policy_mgr_get_sap_go_count_on_mac(hdd_ctx->psoc, list, mac_id);
394
395 /*
396 * Dcs can only be enabled after all vdev finish csa.
397 * Set vdev starting for every vdev before doing csa.
398 * The CSA triggered by DCS will be done in serial.
399 */
400 for (conn_idx = 0; conn_idx < count; conn_idx++) {
401 link_info = hdd_get_link_info_by_vdev(hdd_ctx, list[conn_idx]);
402 if (!link_info) {
403 hdd_err("vdev_id %u does not exist with host",
404 list[conn_idx]);
405 return QDF_STATUS_E_INVAL;
406 }
407
408 ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(link_info);
409 sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(link_info);
410 if (ap_ctx->operating_chan_freq != dcs_ch_freq)
411 wlansap_dcs_set_vdev_starting(sap_ctx, true);
412 else
413 wlansap_dcs_set_vdev_starting(sap_ctx, false);
414 }
415 for (conn_idx = 0; conn_idx < count; conn_idx++) {
416 link_info = hdd_get_link_info_by_vdev(hdd_ctx, list[conn_idx]);
417 if (!link_info) {
418 hdd_err("vdev_id %u does not exist with host",
419 list[conn_idx]);
420 return QDF_STATUS_E_INVAL;
421 }
422
423 ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(link_info);
424 if (ap_ctx->operating_chan_freq == dcs_ch_freq)
425 continue;
426
427 hdd_ctx->acs_policy.acs_chan_freq = AUTO_CHANNEL_SELECT;
428 hdd_debug("dcs triggers old ch:%d new ch:%d",
429 ap_ctx->operating_chan_freq, dcs_ch_freq);
430 wlan_hdd_set_sap_csa_reason(hdd_ctx->psoc,
431 link_info->vdev_id, CSA_REASON_DCS);
432 status = hdd_switch_sap_channel(link_info, dcs_ch, true);
433 if (status == QDF_STATUS_SUCCESS)
434 status = QDF_STATUS_E_PENDING;
435 return status;
436 }
437
438 return QDF_STATUS_SUCCESS;
439 }
440
441 /**
442 * hdd_dcs_hostapd_enable_wlan_interference_mitigation() - enable wlan
443 * interference mitigation
444 * @hdd_ctx: hdd ctx
445 * @vdev_id: vdev id
446 *
447 * This function is used to enable wlan interference mitigation through
448 * send dcs command.
449 *
450 * Return: None
451 */
hdd_dcs_hostapd_enable_wlan_interference_mitigation(struct hdd_context * hdd_ctx,uint8_t vdev_id)452 static void hdd_dcs_hostapd_enable_wlan_interference_mitigation(
453 struct hdd_context *hdd_ctx,
454 uint8_t vdev_id)
455 {
456 QDF_STATUS status;
457 uint8_t mac_id;
458 struct wlan_hdd_link_info *link_info;
459 struct hdd_ap_ctx *ap_ctx;
460 struct sap_context *sap_ctx;
461
462 status = policy_mgr_get_mac_id_by_session_id(hdd_ctx->psoc, vdev_id,
463 &mac_id);
464 if (QDF_IS_STATUS_ERROR(status)) {
465 hdd_err("get mac id failed");
466 return;
467 }
468
469 link_info = hdd_get_link_info_by_vdev(hdd_ctx, vdev_id);
470 if (!link_info) {
471 hdd_err("vdev_id %u does not exist with host", vdev_id);
472 return;
473 }
474
475 ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(link_info);
476 sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(link_info);
477 if (wlansap_dcs_is_wlan_interference_mitigation_enabled(sap_ctx) &&
478 !WLAN_REG_IS_24GHZ_CH_FREQ(ap_ctx->operating_chan_freq))
479 ucfg_config_dcs_event_data(hdd_ctx->psoc, mac_id, true);
480 }
481
hdd_dcs_chan_select_complete(struct hdd_adapter * adapter)482 void hdd_dcs_chan_select_complete(struct hdd_adapter *adapter)
483 {
484 qdf_freq_t dcs_freq;
485 struct hdd_context *hdd_ctx;
486 uint32_t chan_freq;
487 struct hdd_ap_ctx *ap_ctx;
488
489 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
490 if (!hdd_ctx) {
491 hdd_err("Invalid HDD context pointer");
492 return;
493 }
494
495 ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter->deflink);
496 dcs_freq = wlansap_dcs_get_freq(ap_ctx->sap_context);
497 chan_freq = ap_ctx->operating_chan_freq;
498 if (dcs_freq && dcs_freq != chan_freq)
499 hdd_dcs_hostapd_set_chan(hdd_ctx, adapter->deflink->vdev_id,
500 dcs_freq);
501 else
502 hdd_dcs_hostapd_enable_wlan_interference_mitigation(
503 hdd_ctx, adapter->deflink->vdev_id);
504
505 qdf_atomic_set(&ap_ctx->acs_in_progress, 0);
506 }
507
hdd_dcs_clear(struct hdd_adapter * adapter)508 void hdd_dcs_clear(struct hdd_adapter *adapter)
509 {
510 QDF_STATUS status;
511 uint8_t mac_id;
512 struct hdd_context *hdd_ctx;
513 struct wlan_objmgr_psoc *psoc;
514 uint32_t list[MAX_NUMBER_OF_CONC_CONNECTIONS];
515 struct sap_context *sap_ctx;
516
517 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
518 if (!hdd_ctx) {
519 hdd_err("Invalid HDD context pointer");
520 return;
521 }
522
523 psoc = hdd_ctx->psoc;
524
525 status = policy_mgr_get_mac_id_by_session_id(psoc,
526 adapter->deflink->vdev_id,
527 &mac_id);
528 if (QDF_IS_STATUS_ERROR(status)) {
529 hdd_err("get mac id failed");
530 return;
531 }
532
533 sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(adapter->deflink);
534 if (policy_mgr_get_sap_go_count_on_mac(psoc, list, mac_id) <= 1) {
535 ucfg_config_dcs_disable(psoc, mac_id, WLAN_HOST_DCS_WLANIM);
536 ucfg_wlan_dcs_cmd(psoc, mac_id, true);
537 if (wlansap_dcs_is_wlan_interference_mitigation_enabled(sap_ctx))
538 ucfg_dcs_clear(psoc, mac_id);
539 }
540
541 wlansap_dcs_set_vdev_wlan_interference_mitigation(sap_ctx, false);
542 wlansap_dcs_set_vdev_starting(sap_ctx, false);
543 }
544