/* * Copyright (c) 2016-2021 The Linux Foundation. All rights reserved. * Copyright (c) 2002-2006, Atheros Communications Inc. * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * DOC: This file contains the dfs_attach() and dfs_detach() functions as well * as the dfs_control() function which is used to process ioctls related to DFS. * For Linux/Mac, "radartool" is the command line tool that can be used to call * various ioctls to set and get radar detection thresholds. */ #include "../dfs_zero_cac.h" #include "wlan_dfs_lmac_api.h" #include "wlan_dfs_mlme_api.h" #include "wlan_dfs_tgt_api.h" #include "../dfs_internal.h" #include "../dfs_filter_init.h" #include #include "wlan_dfs_utils_api.h" #include "../dfs_process_radar_found_ind.h" #include "../dfs_partial_offload_radar.h" /* Disable NOL in FW. */ #define DISABLE_NOL_FW 0 #ifndef WLAN_DFS_STATIC_MEM_ALLOC /** * dfs_alloc_wlan_dfs() - allocate wlan_dfs buffer * * Return: buffer, null on failure. */ static inline struct wlan_dfs *dfs_alloc_wlan_dfs(void) { return qdf_mem_malloc(sizeof(struct wlan_dfs)); } /** * dfs_free_wlan_dfs() - Free wlan_dfs buffer * @dfs: wlan_dfs buffer pointer * * Return: None */ static inline void dfs_free_wlan_dfs(struct wlan_dfs *dfs) { qdf_mem_free(dfs); } /** * dfs_alloc_dfs_curchan() - allocate dfs_channel buffer * * Return: buffer, null on failure. */ static inline struct dfs_channel *dfs_alloc_dfs_curchan(void) { return qdf_mem_malloc(sizeof(struct dfs_channel)); } static inline struct dfs_channel *dfs_alloc_dfs_prevchan(void) { return qdf_mem_malloc(sizeof(struct dfs_channel)); } /** * dfs_free_dfs_chan() - Free dfs_channel buffer * @dfs_chan: dfs_channel buffer pointer * * Return: None */ static inline void dfs_free_dfs_chan(struct dfs_channel *dfs_chan) { qdf_mem_free(dfs_chan); } #else /* Static buffers for DFS objects */ static struct wlan_dfs global_dfs; static struct dfs_channel global_dfs_curchan; static struct dfs_channel global_dfs_prevchan; static inline struct wlan_dfs *dfs_alloc_wlan_dfs(void) { return &global_dfs; } static inline void dfs_free_wlan_dfs(struct wlan_dfs *dfs) { } static inline struct dfs_channel *dfs_alloc_dfs_curchan(void) { return &global_dfs_curchan; } static inline struct dfs_channel *dfs_alloc_dfs_prevchan(void) { return &global_dfs_prevchan; } static inline void dfs_free_dfs_chan(struct dfs_channel *dfs_chan) { } #endif /* * dfs_testtimer_task() - Sends CSA in the current channel. * * When the user sets usenol to 0 and inject the RADAR, AP does not mark the * channel as RADAR and does not add the channel to NOL. It sends the CSA in * the current channel. * * NB: not using kernel-doc format since the kernel-doc script doesn't * handle the os_timer_func() macro */ #ifdef CONFIG_CHAN_FREQ_API static os_timer_func(dfs_testtimer_task) { struct wlan_dfs *dfs = NULL; OS_GET_TIMER_ARG(dfs, struct wlan_dfs *); dfs->wlan_dfstest = 0; /* * Flip the channel back to the original channel. * Make sure this is done properly with a CSA. */ dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "go back to channel %d", dfs->wlan_dfstest_ieeechan); dfs_mlme_start_csa_for_freq(dfs->dfs_pdev_obj, dfs->wlan_dfstest_ieeechan, dfs->dfs_curchan->dfs_ch_freq, dfs->dfs_curchan->dfs_ch_mhz_freq_seg2, dfs->dfs_curchan->dfs_ch_flags); } #endif int dfs_get_debug_info(struct wlan_dfs *dfs, void *data) { if (data) *(uint32_t *)data = dfs->dfs_proc_phyerr; return (int)dfs->dfs_proc_phyerr; } void dfs_main_task_testtimer_init(struct wlan_dfs *dfs) { qdf_timer_init(NULL, &(dfs->wlan_dfstesttimer), dfs_testtimer_task, (void *)dfs, QDF_TIMER_TYPE_WAKE_APPS); } int dfs_create_object(struct wlan_dfs **dfs) { *dfs = dfs_alloc_wlan_dfs(); if (!(*dfs)) return 1; qdf_mem_zero(*dfs, sizeof(**dfs)); (*dfs)->dfs_curchan = dfs_alloc_dfs_curchan(); if (!((*dfs)->dfs_curchan)) { dfs_free_wlan_dfs(*dfs); return 1; } (*dfs)->dfs_prevchan = dfs_alloc_dfs_prevchan(); if (!((*dfs)->dfs_prevchan)) { dfs_free_wlan_dfs(*dfs); return 1; } qdf_mem_zero((*dfs)->dfs_prevchan, sizeof(struct dfs_channel)); return 0; } #if defined(QCA_DFS_BW_PUNCTURE) #if defined(CONFIG_REG_CLIENT) static void dfs_puncture_init(struct wlan_dfs *dfs) { /* * Enable sub chan DFS type if QCA_DFS_BW_PUNCTURE defined, or all * bonded operation freq will be affected and disabled for nol, * puncture can't work, always need to switch freq. */ dfs_set_nol_subchannel_marking(dfs, true); dfs->dfs_use_puncture = true; } #else static void dfs_puncture_init(struct wlan_dfs *dfs) { uint8_t i; struct dfs_punc_obj *dfs_punc_obj; for (i = 0 ; i < N_MAX_PUNC_SM; i++) { dfs_punc_obj = &dfs->dfs_punc_lst.dfs_punc_arr[i]; dfs_punc_cac_timer_attach(dfs, dfs_punc_obj); } } #endif #else static inline void dfs_puncture_init(struct wlan_dfs *dfs) { } #endif int dfs_attach(struct wlan_dfs *dfs) { int ret; if (!dfs->dfs_is_offload_enabled) { ret = dfs_main_attach(dfs); /* * For full offload we have a wmi handler registered to process * a radar event from firmware in the event of a radar detect. * So, init of timer, dfs_task is not required for * full-offload. dfs_task timer is called in * dfs_main_timer_init within dfs_main_attach for * partial-offload in the event of radar detect. */ if (ret) { dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs_main_attach failed"); return ret; } } dfs_cac_timer_attach(dfs); dfs_zero_cac_attach(dfs); dfs_nol_attach(dfs); dfs_postnol_attach(dfs); /* * Init of timer ,dfs_testtimer_task is required by both partial * and full offload, indicating test mode timer initialization for both. */ dfs_main_task_testtimer_init(dfs); dfs_puncture_init(dfs); return 0; } void dfs_stop(struct wlan_dfs *dfs) { dfs_nol_timer_cleanup(dfs); dfs_nol_workqueue_cleanup(dfs); dfs_clear_nolhistory(dfs); } void dfs_task_testtimer_reset(struct wlan_dfs *dfs) { if (dfs->wlan_dfstest) { qdf_timer_sync_cancel(&dfs->wlan_dfstesttimer); dfs->wlan_dfstest = 0; } } void dfs_task_testtimer_detach(struct wlan_dfs *dfs) { qdf_timer_free(&dfs->wlan_dfstesttimer); dfs->wlan_dfstest = 0; } void dfs_reset(struct wlan_dfs *dfs) { if (!dfs) { dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL"); return; } dfs_cac_timer_reset(dfs); dfs_zero_cac_reset(dfs); if (!dfs->dfs_is_offload_enabled) { dfs_main_timer_reset(dfs); dfs_host_wait_timer_reset(dfs); dfs_false_radarfound_reset_vars(dfs); } dfs_task_testtimer_reset(dfs); } void dfs_timer_detach(struct wlan_dfs *dfs) { dfs_cac_timer_detach(dfs); dfs_puncture_cac_timer_detach(dfs); dfs_zero_cac_timer_detach(dfs->dfs_soc_obj); if (!dfs->dfs_is_offload_enabled) { dfs_main_timer_detach(dfs); dfs_host_wait_timer_detach(dfs); } dfs_task_testtimer_detach(dfs); } void dfs_detach(struct wlan_dfs *dfs) { dfs_timer_detach(dfs); if (!dfs->dfs_is_offload_enabled) dfs_main_detach(dfs); dfs_zero_cac_detach(dfs); dfs_nol_detach(dfs); } #ifndef WLAN_DFS_STATIC_MEM_ALLOC void dfs_destroy_object(struct wlan_dfs *dfs) { dfs_free_dfs_chan(dfs->dfs_prevchan); dfs_free_dfs_chan(dfs->dfs_curchan); dfs_free_wlan_dfs(dfs); } #else void dfs_destroy_object(struct wlan_dfs *dfs) { } #endif /* dfs_set_disable_radar_marking()- Set the flag to mark/unmark a radar flag * on NOL channel. * @dfs: Pointer to wlan_dfs structure. * @disable_radar_marking: Flag to enable/disable marking channel as radar. */ #if defined(WLAN_DFS_FULL_OFFLOAD) && defined(QCA_DFS_NOL_OFFLOAD) static void dfs_set_disable_radar_marking(struct wlan_dfs *dfs, bool disable_radar_marking) { dfs->dfs_disable_radar_marking = disable_radar_marking; } #else static inline void dfs_set_disable_radar_marking(struct wlan_dfs *dfs, bool disable_radar_marking) { } #endif #if defined(WLAN_DFS_FULL_OFFLOAD) && defined(QCA_DFS_NOL_OFFLOAD) bool dfs_get_disable_radar_marking(struct wlan_dfs *dfs) { return dfs->dfs_disable_radar_marking; } #endif int dfs_control(struct wlan_dfs *dfs, u_int id, void *indata, uint32_t insize, void *outdata, uint32_t *outsize) { struct wlan_dfs_phyerr_param peout; struct dfs_ioctl_params *dfsparams; int error = 0; uint32_t val = 0; struct dfsreq_nolinfo *nol; uint32_t *data = NULL; int i; int usenol_pdev_param; if (!dfs) { dfs_err(NULL, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL"); goto bad; } switch (id) { case DFS_SET_THRESH: if (insize < sizeof(struct dfs_ioctl_params) || !indata) { dfs_debug(dfs, WLAN_DEBUG_DFS1, "insize = %d, expected = %zu bytes, indata = %pK", insize, sizeof(struct dfs_ioctl_params), indata); error = -EINVAL; break; } dfsparams = (struct dfs_ioctl_params *)indata; if (!dfs_set_thresholds(dfs, DFS_PARAM_FIRPWR, dfsparams->dfs_firpwr)) error = -EINVAL; if (!dfs_set_thresholds(dfs, DFS_PARAM_RRSSI, dfsparams->dfs_rrssi)) error = -EINVAL; if (!dfs_set_thresholds(dfs, DFS_PARAM_HEIGHT, dfsparams->dfs_height)) error = -EINVAL; if (!dfs_set_thresholds(dfs, DFS_PARAM_PRSSI, dfsparams->dfs_prssi)) error = -EINVAL; if (!dfs_set_thresholds(dfs, DFS_PARAM_INBAND, dfsparams->dfs_inband)) error = -EINVAL; /* 5413 speicfic. */ if (!dfs_set_thresholds(dfs, DFS_PARAM_RELPWR, dfsparams->dfs_relpwr)) error = -EINVAL; if (!dfs_set_thresholds(dfs, DFS_PARAM_RELSTEP, dfsparams->dfs_relstep)) error = -EINVAL; if (!dfs_set_thresholds(dfs, DFS_PARAM_MAXLEN, dfsparams->dfs_maxlen)) error = -EINVAL; break; case DFS_BANGRADAR: error = dfs_bang_radar(dfs, indata, insize); break; case DFS_GET_THRESH: if (!outdata || !outsize || *outsize < sizeof(struct dfs_ioctl_params)) { error = -EINVAL; break; } *outsize = sizeof(struct dfs_ioctl_params); dfsparams = (struct dfs_ioctl_params *) outdata; qdf_mem_zero(&peout, sizeof(struct wlan_dfs_phyerr_param)); /* Fetch the DFS thresholds using the internal representation */ (void) dfs_get_thresholds(dfs, &peout); /* Convert them to the dfs IOCTL representation. */ wlan_dfs_dfsparam_to_ioctlparam(&peout, dfsparams); break; case DFS_RADARDETECTS: if (!outdata || !outsize || *outsize < sizeof(uint32_t)) { error = -EINVAL; break; } *outsize = sizeof(uint32_t); *((uint32_t *)outdata) = dfs->wlan_dfs_stats.num_radar_detects; break; case DFS_DISABLE_DETECT: dfs->dfs_proc_phyerr &= ~DFS_RADAR_EN; dfs->dfs_proc_phyerr &= ~DFS_SECOND_SEGMENT_RADAR_EN; dfs->dfs_ignore_dfs = 1; dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "enable detects, ignore_dfs %d", dfs->dfs_ignore_dfs ? 1 : 0); break; case DFS_ENABLE_DETECT: dfs->dfs_proc_phyerr |= DFS_RADAR_EN; dfs->dfs_proc_phyerr |= DFS_SECOND_SEGMENT_RADAR_EN; dfs->dfs_ignore_dfs = 0; dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "enable detects, ignore_dfs %d", dfs->dfs_ignore_dfs ? 1 : 0); break; case DFS_DISABLE_FFT: dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "TODO disable FFT val=0x%x", val); break; case DFS_ENABLE_FFT: dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "TODO enable FFT val=0x%x", val); break; case DFS_SET_DEBUG_LEVEL: if (insize < sizeof(uint32_t) || !indata) { error = -EINVAL; break; } dfs->dfs_debug_mask = *(uint32_t *)indata; /* Do not allow user to set the ALWAYS/MAX bit. * It will be used internally by dfs print macro(s) * to print messages when dfs is NULL. */ dfs->dfs_debug_mask &= ~(WLAN_DEBUG_DFS_ALWAYS); dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "debug level now = 0x%x", dfs->dfs_debug_mask); if (dfs->dfs_debug_mask & WLAN_DEBUG_DFS3) { /* Enable debug Radar Event */ dfs->dfs_event_log_on = 1; } else if ((utils_get_dfsdomain(dfs->dfs_pdev_obj) == DFS_FCC_DOMAIN) && lmac_is_host_dfs_check_support_enabled(dfs->dfs_pdev_obj)) { dfs->dfs_event_log_on = 1; } else { dfs->dfs_event_log_on = 0; } break; case DFS_SET_FALSE_RSSI_THRES: if (insize < sizeof(uint32_t) || !indata) { error = -EINVAL; break; } dfs->wlan_dfs_false_rssi_thres = *(uint32_t *)indata; dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "false RSSI threshold now = 0x%x", dfs->wlan_dfs_false_rssi_thres); break; case DFS_SET_PEAK_MAG: if (insize < sizeof(uint32_t) || !indata) { error = -EINVAL; break; } dfs->wlan_dfs_peak_mag = *(uint32_t *)indata; dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "peak_mag now = 0x%x", dfs->wlan_dfs_peak_mag); break; case DFS_GET_CAC_VALID_TIME: if (!outdata || !outsize || *outsize < sizeof(uint32_t)) { error = -EINVAL; break; } *outsize = sizeof(uint32_t); *((uint32_t *)outdata) = dfs->dfs_cac_valid_time; break; case DFS_SET_CAC_VALID_TIME: if (insize < sizeof(uint32_t) || !indata) { error = -EINVAL; break; } dfs->dfs_cac_valid_time = *(uint32_t *)indata; dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs timeout = %d", dfs->dfs_cac_valid_time); break; case DFS_IGNORE_CAC: if (insize < sizeof(uint32_t) || !indata) { error = -EINVAL; break; } if (*(uint32_t *)indata) dfs->dfs_ignore_cac = 1; else dfs->dfs_ignore_cac = 0; dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "ignore cac = 0x%x", dfs->dfs_ignore_cac); break; case DFS_SET_NOL_TIMEOUT: if (insize < sizeof(uint32_t) || !indata) { error = -EINVAL; break; } if (*(int *)indata) dfs->wlan_dfs_nol_timeout = *(int *)indata; else dfs->wlan_dfs_nol_timeout = DFS_NOL_TIMEOUT_S; dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "nol timeout = %d sec", dfs->wlan_dfs_nol_timeout); break; case DFS_MUTE_TIME: if (insize < sizeof(uint32_t) || !indata) { error = -EINVAL; break; } data = (uint32_t *) indata; dfs->wlan_dfstesttime = *data; dfs->wlan_dfstesttime *= (1000); /* convert sec into ms */ break; case DFS_GET_USENOL: if (!outdata || !outsize || *outsize < sizeof(uint32_t)) { error = -EINVAL; break; } *outsize = sizeof(uint32_t); *((uint32_t *)outdata) = dfs->dfs_use_nol; dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "#Phyerr=%d, #false detect=%d, #queued=%d", dfs->dfs_phyerr_count, dfs->dfs_phyerr_reject_count, dfs->dfs_phyerr_queued_count); dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs_phyerr_freq_min=%d, dfs_phyerr_freq_max=%d", dfs->dfs_phyerr_freq_min, dfs->dfs_phyerr_freq_max); dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "Total radar events detected=%d, entries in the radar queue follows:", dfs->dfs_event_log_count); for (i = 0; (i < DFS_EVENT_LOG_SIZE) && (i < dfs->dfs_event_log_count); i++) { #define FREQ_OFFSET1 ((int)dfs->radar_log[i].freq_offset_khz / 1000) #define FREQ_OFFSET2 ((int)abs(dfs->radar_log[i].freq_offset_khz) % 1000) dfs_debug(dfs, WLAN_DEBUG_DFS, "ts=%llu diff_ts=%u rssi=%u dur=%u, is_chirp=%d, seg_id=%d, sidx=%d, freq_offset=%d.%dMHz, peak_mag=%d, total_gain=%d, mb_gain=%d, relpwr_db=%d, delta_diff=%d, delta_peak=%d, psidx_diff=%d\n", dfs->radar_log[i].ts, dfs->radar_log[i].diff_ts, dfs->radar_log[i].rssi, dfs->radar_log[i].dur, dfs->radar_log[i].is_chirp, dfs->radar_log[i].seg_id, dfs->radar_log[i].sidx, FREQ_OFFSET1, FREQ_OFFSET2, dfs->radar_log[i].peak_mag, dfs->radar_log[i].total_gain, dfs->radar_log[i].mb_gain, dfs->radar_log[i].relpwr_db, dfs->radar_log[i].delta_diff, dfs->radar_log[i].delta_peak, dfs->radar_log[i].psidx_diff); } dfs->dfs_event_log_count = 0; dfs->dfs_phyerr_count = 0; dfs->dfs_phyerr_reject_count = 0; dfs->dfs_phyerr_queued_count = 0; dfs->dfs_phyerr_freq_min = 0x7fffffff; dfs->dfs_phyerr_freq_max = 0; break; case DFS_SET_USENOL: if (insize < sizeof(uint32_t) || !indata) { error = -EINVAL; break; } dfs->dfs_use_nol = *(uint32_t *)indata; usenol_pdev_param = dfs->dfs_use_nol; if (dfs->dfs_is_offload_enabled) { if (dfs->dfs_use_nol == USENOL_ENABLE_NOL_HOST_DISABLE_NOL_FW) usenol_pdev_param = DISABLE_NOL_FW; tgt_dfs_send_usenol_pdev_param(dfs->dfs_pdev_obj, usenol_pdev_param); } break; case DFS_SET_DISABLE_RADAR_MARKING: if (dfs->dfs_is_offload_enabled && (utils_get_dfsdomain(dfs->dfs_pdev_obj) == DFS_FCC_DOMAIN)) { if (insize < sizeof(uint32_t) || !indata) { error = -EINVAL; break; } dfs_set_disable_radar_marking(dfs, *(uint8_t *)indata); } break; case DFS_GET_DISABLE_RADAR_MARKING: if (!outdata || !outsize || *outsize < sizeof(uint8_t)) { error = -EINVAL; break; } if (dfs->dfs_is_offload_enabled) { *outsize = sizeof(uint8_t); *((uint8_t *)outdata) = dfs_get_disable_radar_marking(dfs); } break; case DFS_GET_NOL: if (!outdata || !outsize || *outsize < sizeof(struct dfsreq_nolinfo)) { error = -EINVAL; break; } *outsize = sizeof(struct dfsreq_nolinfo); nol = (struct dfsreq_nolinfo *)outdata; DFS_GET_NOL_LOCKED(dfs, (struct dfsreq_nolelem *)nol->dfs_nol, &nol->dfs_ch_nchans); DFS_PRINT_NOL_LOCKED(dfs); break; case DFS_SET_NOL: if (insize < sizeof(struct dfsreq_nolinfo) || !indata) { error = -EINVAL; break; } nol = (struct dfsreq_nolinfo *) indata; dfs_set_nol(dfs, (struct dfsreq_nolelem *)nol->dfs_nol, nol->dfs_ch_nchans); break; case DFS_SHOW_NOL: DFS_PRINT_NOL_LOCKED(dfs); break; case DFS_SHOW_NOLHISTORY: dfs_print_nolhistory(dfs); break; case DFS_SHOW_PRECAC_LISTS: dfs_print_precaclists(dfs); break; case DFS_RESET_PRECAC_LISTS: dfs_reset_precac_lists(dfs); break; case DFS_INJECT_SEQUENCE: error = dfs_inject_synthetic_pulse_sequence(dfs, indata); if (error) dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "Not injected Synthetic pulse"); break; case DFS_ALLOW_HW_PULSES: if (insize < sizeof(u_int8_t) || !indata) { error = -EINVAL; break; } dfs_allow_hw_pulses(dfs, !!(*(u_int8_t *)indata)); break; case DFS_SET_PRI_MULTIPILER: dfs->dfs_pri_multiplier = *(int *)indata; dfs_debug(dfs, WLAN_DEBUG_DFS_ALWAYS, "Set dfs pri multiplier to %d, dfsdomain %d", dfs->dfs_pri_multiplier, dfs->dfsdomain); break; default: error = -EINVAL; } bad: return error; } #ifdef WLAN_FEATURE_11BE static inline bool dfs_is_chan_punc_same_as_given_punc(struct dfs_channel *dfs_curchan, uint16_t dfs_chan_punc_pattern) { return (dfs_curchan->dfs_ch_punc_pattern == dfs_chan_punc_pattern); } #else static inline bool dfs_is_chan_punc_same_as_given_punc(struct dfs_channel *dfs_curchan, uint16_t dfs_chan_punc_pattern) { return true; } #endif /** * dfs_is_curchan_same_as_given_chan() - Find if dfs_curchan has the same * channel parameters provided. * @dfs_curchan: Pointer to DFS current channel structure. * @dfs_ch_freq: New curchan's primary frequency. * @dfs_ch_flags: New curchan's channel flags. * @dfs_ch_flagext: New curchan's channel flags extension. * @dfs_ch_vhtop_ch_freq_seg1: New curchan's primary centre IEEE. * @dfs_ch_vhtop_ch_freq_seg2: New curchan's secondary centre IEEE. * @dfs_chan_punc_pattern: Channel puncture pattern * * Return: True if curchan has the same channel parameters of the given channel, * else false. */ static bool dfs_is_curchan_same_as_given_chan(struct dfs_channel *dfs_curchan, uint16_t dfs_ch_freq, uint64_t dfs_ch_flags, uint16_t dfs_ch_flagext, uint8_t dfs_ch_vhtop_ch_freq_seg1, uint8_t dfs_ch_vhtop_ch_freq_seg2, uint16_t dfs_chan_punc_pattern) { if ((dfs_curchan->dfs_ch_freq == dfs_ch_freq) && (dfs_curchan->dfs_ch_flags == dfs_ch_flags) && (dfs_curchan->dfs_ch_flagext == dfs_ch_flagext) && (dfs_curchan->dfs_ch_vhtop_ch_freq_seg1 == dfs_ch_vhtop_ch_freq_seg1) && (dfs_curchan->dfs_ch_vhtop_ch_freq_seg2 == dfs_ch_vhtop_ch_freq_seg2) && (dfs_is_chan_punc_same_as_given_punc(dfs_curchan, dfs_chan_punc_pattern))) return true; return false; } #ifdef WLAN_FEATURE_11BE static inline void dfs_set_cur_chan_punc_pattern(struct wlan_dfs *dfs, uint16_t dfs_ch_punc_pattern) { dfs->dfs_curchan->dfs_ch_punc_pattern = dfs_ch_punc_pattern; } #else static inline void dfs_set_cur_chan_punc_pattern(struct wlan_dfs *dfs, uint16_t dfs_ch_punc_pattern) { } #endif #ifdef CONFIG_CHAN_FREQ_API void dfs_set_current_channel_for_freq(struct wlan_dfs *dfs, uint16_t dfs_chan_freq, uint64_t dfs_chan_flags, uint16_t dfs_chan_flagext, uint8_t dfs_chan_ieee, uint8_t dfs_chan_vhtop_freq_seg1, uint8_t dfs_chan_vhtop_freq_seg2, uint16_t dfs_chan_mhz_freq_seg1, uint16_t dfs_chan_mhz_freq_seg2, uint16_t dfs_ch_punc_pattern, bool *is_channel_updated) { if (is_channel_updated) *is_channel_updated = false; if (!dfs) { dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs is NULL"); return; } /* Check if the input parameters are the same as that of dfs_curchan */ if (dfs_is_curchan_same_as_given_chan(dfs->dfs_curchan, dfs_chan_freq, dfs_chan_flags, dfs_chan_flagext, dfs_chan_vhtop_freq_seg1, dfs_chan_vhtop_freq_seg2, dfs_ch_punc_pattern)) { dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "dfs_curchan already updated"); return; } /* Update dfs previous channel with the old dfs_curchan, if it exists */ if (dfs->dfs_curchan->dfs_ch_freq) qdf_mem_copy(dfs->dfs_prevchan, dfs->dfs_curchan, sizeof(struct dfs_channel)); dfs->dfs_curchan->dfs_ch_freq = dfs_chan_freq; dfs->dfs_curchan->dfs_ch_flags = dfs_chan_flags; dfs->dfs_curchan->dfs_ch_flagext = dfs_chan_flagext; dfs->dfs_curchan->dfs_ch_ieee = dfs_chan_ieee; dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg1 = dfs_chan_vhtop_freq_seg1; dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg2 = dfs_chan_vhtop_freq_seg2; dfs->dfs_curchan->dfs_ch_mhz_freq_seg1 = dfs_chan_mhz_freq_seg1; dfs->dfs_curchan->dfs_ch_mhz_freq_seg2 = dfs_chan_mhz_freq_seg2; dfs_set_cur_chan_punc_pattern(dfs, dfs_ch_punc_pattern); if (is_channel_updated) *is_channel_updated = true; if (dfs->dfs_use_puncture) dfs_handle_dfs_puncture_unpuncture(dfs); } #endif void dfs_update_cur_chan_flags(struct wlan_dfs *dfs, uint64_t flags, uint16_t flagext) { dfs->dfs_curchan->dfs_ch_flags = flags; dfs->dfs_curchan->dfs_ch_flagext = flagext; } int dfs_reinit_timers(struct wlan_dfs *dfs) { dfs_cac_timer_attach(dfs); dfs_zero_cac_timer_init(dfs->dfs_soc_obj); dfs_main_task_testtimer_init(dfs); return 0; } void dfs_reset_dfs_prevchan(struct wlan_dfs *dfs) { qdf_mem_zero(dfs->dfs_prevchan, sizeof(struct dfs_channel)); } #ifdef WLAN_DFS_TRUE_160MHZ_SUPPORT bool dfs_is_true_160mhz_supported(struct wlan_dfs *dfs) { struct wlan_objmgr_psoc *psoc = dfs->dfs_soc_obj->psoc; struct wlan_lmac_if_target_tx_ops *tgt_tx_ops; struct wlan_lmac_if_tx_ops *tx_ops; uint32_t target_type; tx_ops = wlan_psoc_get_lmac_if_txops(psoc); if (!tx_ops) { dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "tx_ops is NULL"); return false; } target_type = lmac_get_target_type(dfs->dfs_pdev_obj); tgt_tx_ops = &tx_ops->target_tx_ops; if (tgt_tx_ops->tgt_is_tgt_type_qcn9000 && tgt_tx_ops->tgt_is_tgt_type_qcn9000(target_type)) return true; if (tgt_tx_ops->tgt_is_tgt_type_qcn6122 && tgt_tx_ops->tgt_is_tgt_type_qcn6122(target_type)) return true; if (tgt_tx_ops->tgt_is_tgt_type_qcn9160 && tgt_tx_ops->tgt_is_tgt_type_qcn9160(target_type)) return true; if (tgt_tx_ops->tgt_is_tgt_type_qcn6432 && tgt_tx_ops->tgt_is_tgt_type_qcn6432(target_type)) return true; return false; } bool dfs_is_restricted_80p80mhz_supported(struct wlan_dfs *dfs) { return wlan_psoc_nif_fw_ext_cap_get(dfs->dfs_soc_obj->psoc, WLAN_SOC_RESTRICTED_80P80_SUPPORT); } #endif #ifdef QCA_SUPPORT_AGILE_DFS uint8_t dfs_get_agile_detector_id(struct wlan_dfs *dfs) { return dfs->dfs_agile_detector_id; } #endif /** * dfs_chan_to_ch_width() - Outputs the channel width in MHz of the given input * dfs_channel. * @chan: Pointer to the input dfs_channel structure. * * Return: Channel width in MHz. BW_INVALID(0MHz) on invalid channel. */ uint16_t dfs_chan_to_ch_width(struct dfs_channel *chan) { uint16_t ch_width; if (!chan) return BW_INVALID; if (WLAN_IS_CHAN_MODE_320(chan)) ch_width = BW_320; else if (WLAN_IS_CHAN_MODE_160(chan)) ch_width = BW_160; else if (WLAN_IS_CHAN_MODE_80(chan)) ch_width = BW_80; else if (WLAN_IS_CHAN_MODE_40(chan)) ch_width = BW_40; else if (WLAN_IS_CHAN_MODE_20(chan)) ch_width = BW_20; else ch_width = BW_INVALID; return ch_width; }