From: Viktor Barna <viktor.barna@xxxxxxxxxx> (Part of the split. Please, take a look at the cover letter for more details). Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx> --- drivers/net/wireless/celeno/cl8k/dfs/dfs.c | 977 +++++++++++++++++++++ 1 file changed, 977 insertions(+) create mode 100644 drivers/net/wireless/celeno/cl8k/dfs/dfs.c diff --git a/drivers/net/wireless/celeno/cl8k/dfs/dfs.c b/drivers/net/wireless/celeno/cl8k/dfs/dfs.c new file mode 100644 index 000000000000..b2717a8be26a --- /dev/null +++ b/drivers/net/wireless/celeno/cl8k/dfs/dfs.c @@ -0,0 +1,977 @@ +// SPDX-License-Identifier: MIT +/* Copyright(c) 2019-2021, Celeno Communications Ltd. */ + +#include "dfs/dfs.h" +#include "dfs/dfs_db.h" +#include "dfs/radar.h" +#include "chip.h" +#include "channel.h" +#include "chan_info.h" +#include "env_det.h" +#include "debug.h" +#include "utils/timer.h" +#include "tx/tx.h" +#include "temperature.h" +#include "utils/math.h" +#include "calib.h" +#include "traffic.h" +#include "utils/utils.h" +#include "reg/reg_riu.h" +#include "reg/reg_mac_hw.h" +#include "band.h" +#include "chandef.h" +#include "utils/string.h" +#include "utils/file.h" +#include "config.h" + +#define dfs_pr(cl_hw, level, ...) \ + do { \ + if ((level) <= (cl_hw)->dfs_db.dbg_lvl) \ + pr_debug(__VA_ARGS__); \ + } while (0) + +#define dfs_pr_verbose(cl_hw, ...) dfs_pr((cl_hw), DBG_LVL_VERBOSE, ##__VA_ARGS__) +#define dfs_pr_err(cl_hw, ...) dfs_pr((cl_hw), DBG_LVL_ERROR, ##__VA_ARGS__) +#define dfs_pr_warn(cl_hw, ...) dfs_pr((cl_hw), DBG_LVL_WARNING, ##__VA_ARGS__) +#define dfs_pr_trace(cl_hw, ...) dfs_pr((cl_hw), DBG_LVL_TRACE, ##__VA_ARGS__) +#define dfs_pr_info(cl_hw, ...) dfs_pr((cl_hw), DBG_LVL_INFO, ##__VA_ARGS__) + +#define COUNTRY_CODE_LEN 2 + +/* + * ID Min Max Tol Min Max Tol Tol MIN PPB Trig Type + * Width Width Width PRI PRI PRI FREQ Burst Count + */ + +/* ETSI Radar Types v1.8.2 */ +static struct cl_radar_type radar_type_etsi[] = { + + {0, 1, 1, 1, 1428, 1428, 1, 1, 1, 18, 10, RADAR_WAVEFORM_SHORT}, + {1, 1, 5, 1, 1000, 5000, 1, 1, 1, 10, 5, RADAR_WAVEFORM_SHORT}, + {2, 1, 15, 1, 625, 5000, 1, 1, 1, 15, 8, RADAR_WAVEFORM_SHORT}, + {3, 1, 15, 1, 250, 435, 1, 1, 1, 25, 10, RADAR_WAVEFORM_SHORT}, + {4, 10, 30, 1, 250, 500, 1, 1, 1, 20, 10, RADAR_WAVEFORM_SHORT}, + {5, 1, 5, 1, 2500, 3334, 1, 1, 2, 10, 5, RADAR_WAVEFORM_STAGGERED}, + {6, 1, 5, 1, 833, 2500, 1, 1, 2, 15, 8, RADAR_WAVEFORM_STAGGERED}, +}; + +/* FCC Radar Types 8/14 */ +static struct cl_radar_type radar_type_fcc[] = { + {0, 1, 1, 0, 1428, 1428, 1, 1, 1, 18, 10, RADAR_WAVEFORM_SHORT}, + {1, 1, 5, 3, 518, 3066, 3, 1, 1, 18, 10, RADAR_WAVEFORM_SHORT}, + {2, 1, 5, 3, 150, 230, 3, 1, 1, 23, 10, RADAR_WAVEFORM_SHORT}, + {3, 3, 10, 3, 200, 500, 3, 1, 1, 16, 6, RADAR_WAVEFORM_SHORT}, + {4, 6, 20, 3, 200, 500, 3, 1, 1, 12, 6, RADAR_WAVEFORM_SHORT}, + {5, 50, 100, 50, 1000, 2000, 1, 1, 2, 10, 5, RADAR_WAVEFORM_LONG}, + {6, 1, 1, 0, 333, 333, 1, 1, 2, 30, 10, RADAR_WAVEFORM_LONG}, +}; + +static void cl_dfs_fw_en(struct cl_hw *cl_hw, u8 dfs_en) +{ + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + struct cl_tcv_conf *conf = cl_hw->conf; + + cl_msg_tx_set_dfs(cl_hw, dfs_en, dfs_db->dfs_standard, + conf->ci_dfs_initial_gain, conf->ci_dfs_agc_cd_th); +} + +static int cl_dfs_print_tbl(struct cl_hw *cl_hw) +{ + int i; + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + char *buf = NULL; + ssize_t buf_size; + int err = 0; + int len = 0; + + cl_snprintf(&buf, &len, &buf_size, + "-------------------------------------------------------------------------" + "---------------\n" + "| | Min | Max | Tol | Min | Max | Tol | Tol | Min | |" + " Trig | |\n" + "| ID | Width | Width | Width | PRI | PRI | PRI | FREQ | Burst | PPB |" + " Count | Type |\n" + "-------------------------------------------------------------------------" + "---------------\n"); + + for (i = 0; i < dfs_db->radar_type_cnt; i++) { + cl_snprintf(&buf, &len, &buf_size, + "| %2u | %5d | %5d | %5d | %5d | %5d | %3d | %4d | %5u |" + " %3u | %5u | %4d |\n", + dfs_db->radar_type[i].id, + dfs_db->radar_type[i].min_width, + dfs_db->radar_type[i].max_width, + dfs_db->radar_type[i].tol_width, + dfs_db->radar_type[i].min_pri, + dfs_db->radar_type[i].max_pri, + dfs_db->radar_type[i].tol_pri, + dfs_db->radar_type[i].tol_freq, + dfs_db->radar_type[i].min_burst, + dfs_db->radar_type[i].ppb, + dfs_db->radar_type[i].trig_count, + dfs_db->radar_type[i].waveform); + cl_snprintf(&buf, &len, &buf_size, + "-----------------------------------------------------------------" + "-----------------------\n"); + } + + err = cl_vendor_reply(cl_hw, buf, len); + kfree(buf); + + return err; +} + +static bool cl_dfs_create_detection_buffer(struct cl_hw *cl_hw, struct cl_dfs_db *dfs_db, + struct cl_dfs_pulse *pulse_buffer, u8 *samples_cnt, + unsigned long time) +{ + u8 i; + u8 pulse_idx; + /* Init First index to last */ + u8 first_pulse_idx = (dfs_db->buf_idx - 1 + CL_DFS_PULSE_BUF_SIZE) & CL_DFS_PULSE_BUF_MASK; + + /* Find Start Pulse indexes */ + for (i = 0; i < CL_DFS_PULSE_BUF_SIZE; i++) { + pulse_idx = (i + dfs_db->buf_idx) & CL_DFS_PULSE_BUF_MASK; + + if ((time - dfs_db->dfs_pulse[pulse_idx].time) < dfs_db->search_window) { + first_pulse_idx = pulse_idx; + break; + } + } + + dfs_pr_info(cl_hw, "DFS: First pulse idx = %u, Last pulse idx = %u\n", + first_pulse_idx, dfs_db->buf_idx - 1); + + if (dfs_db->buf_idx - 1 >= first_pulse_idx) + if ((dfs_db->buf_idx - first_pulse_idx - 1) < (dfs_db->min_pulse_eeq - 1)) { + /* Return if buffer don't hold enough valid samples */ + dfs_pr_warn(cl_hw, "DFS: Not enough pulses in buffer\n"); + + return false; + } + + /* Copy the processed samples to local Buf to avoid index castings */ + for (i = 0; pulse_idx != ((dfs_db->buf_idx - 1 + CL_DFS_PULSE_BUF_SIZE) + & CL_DFS_PULSE_BUF_MASK); i++) { + pulse_idx = (i + first_pulse_idx) & CL_DFS_PULSE_BUF_MASK; + memcpy(&pulse_buffer[i], &dfs_db->dfs_pulse[pulse_idx], sizeof(pulse_buffer[i])); + } + *samples_cnt = i + 1; + + return true; +} + +static void cl_dfs_add_pulses_to_global_buffer(struct cl_hw *cl_hw, struct cl_dfs_db *dfs_db, + struct cl_radar_pulse *pulse, u8 pulse_cnt, + unsigned long time) +{ + int i; + + for (i = 0; i < pulse_cnt; i++) + dfs_pr_info(cl_hw, "Pulse=%d, Width=%u, PRI=%u, FREQ=%d, Time=%lu, FOM=%x\n", + i, pulse[i].len, pulse[i].rep, pulse[i].freq, time, pulse[i].fom); + + /* Maintain cyclic pulse buffer */ + for (i = 0; i < pulse_cnt; i++) { + dfs_db->dfs_pulse[dfs_db->buf_idx].freq = pulse[i].freq; + dfs_db->dfs_pulse[dfs_db->buf_idx].width = pulse[i].len; + dfs_db->dfs_pulse[dfs_db->buf_idx].pri = pulse[i].rep; + dfs_db->dfs_pulse[dfs_db->buf_idx].occ = 0; /* occ temp disabled. */ + dfs_db->dfs_pulse[dfs_db->buf_idx].time = time; + + dfs_db->buf_idx++; + dfs_db->buf_idx &= CL_DFS_PULSE_BUF_MASK; + } +} + +static bool cl_dfs_buf_maintain(struct cl_hw *cl_hw, struct cl_radar_pulse *pulse, + struct cl_dfs_pulse *pulse_buffer, u8 pulse_cnt, + unsigned long time, u8 *samples_cnt, struct cl_dfs_db *dfs_db) +{ + int i; + + cl_dfs_add_pulses_to_global_buffer(cl_hw, dfs_db, pulse, pulse_cnt, time); + if (!cl_dfs_create_detection_buffer(cl_hw, dfs_db, pulse_buffer, samples_cnt, time)) + return false; + + for (i = 0; i < *samples_cnt; i++) + dfs_pr_info(cl_hw, "DFS: pulse[%d]: width=%u, pri=%u, freq=%d\n", + i, pulse_buffer[i].width, pulse_buffer[i].pri, pulse_buffer[i].freq); + + return true; +} + +static inline bool cl_dfs_pulse_match(s32 pulse_val, s32 spec_min_val, + s32 spec_max_val, s32 spec_tol) +{ + return ((pulse_val >= (spec_min_val - spec_tol)) && + (pulse_val <= (spec_max_val + spec_tol))); +} + +static u8 cl_dfs_is_stag_pulse(struct cl_hw *cl_hw, struct cl_dfs_db *dfs_db, + struct cl_dfs_pulse *pulse) +{ + int i; + struct cl_radar_type *radar_type; + + for (i = 0; i < dfs_db->radar_type_cnt; i++) { + radar_type = &dfs_db->radar_type[i]; + + if (radar_type->waveform != RADAR_WAVEFORM_STAGGERED) + continue; + + if (cl_dfs_pulse_match((s32)pulse->width, radar_type->min_width, + radar_type->max_width, radar_type->tol_width) && + cl_dfs_pulse_match((s32)pulse->pri, radar_type->min_pri, + radar_type->max_pri, radar_type->tol_pri)) { + /* Search for the second burst */ + if (abs(pulse[0].pri - pulse[2].pri) <= dfs_db->radar_type[i].tol_pri && + abs(pulse[1].pri - pulse[3].pri) <= radar_type->tol_pri && + abs(pulse[0].pri - pulse[1].pri) > radar_type->tol_pri && + abs(pulse[2].pri - pulse[3].pri) > radar_type->tol_pri) { + dfs_pr_info(cl_hw, "DFS: Found match type %d\n", i); + return (i + 1); + } else if (abs(pulse[0].pri - pulse[3].pri) <= radar_type->tol_pri && + abs(pulse[1].pri - pulse[4].pri) <= radar_type->tol_pri && + abs(pulse[0].pri - pulse[1].pri) > radar_type->tol_pri && + abs(pulse[3].pri - pulse[4].pri) > radar_type->tol_pri) { + dfs_pr_info(cl_hw, "DFS: Found match radar %d\n", i); + return (i + 1); + } + } + } + + return 0; +} + +static u8 cl_dfs_is_non_stag_pulse(struct cl_hw *cl_hw, struct cl_dfs_db *dfs_db, + struct cl_dfs_pulse *pulse) +{ + int i; + struct cl_radar_type *radar_type; + + for (i = 0; i < dfs_db->radar_type_cnt; i++) { + radar_type = &dfs_db->radar_type[i]; + + if (radar_type->waveform == RADAR_WAVEFORM_STAGGERED) + continue; + + if (cl_dfs_pulse_match((s32)pulse->width, radar_type->min_width, + radar_type->max_width, radar_type->tol_width) && + cl_dfs_pulse_match((s32)pulse->pri, radar_type->min_pri, + radar_type->max_pri, radar_type->tol_pri)) { + dfs_pr_info(cl_hw, "DFS: Found match type %d\n", i); + return (i + 1); + } + } + + dfs_pr_warn(cl_hw, "DFS: Match not found\n"); + + return 0; +} + +static u8 cl_dfs_get_pulse_type(struct cl_hw *cl_hw, struct cl_dfs_pulse *pulse, + bool stag_candidate) +{ + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + + if (stag_candidate) { + u8 pulse_type = cl_dfs_is_stag_pulse(cl_hw, dfs_db, pulse); + + if (pulse_type) + return pulse_type; + } + + return cl_dfs_is_non_stag_pulse(cl_hw, dfs_db, pulse); +} + +static bool cl_dfs_compare_cand(struct cl_hw *cl_hw, struct cl_dfs_db *dfs_db, u8 pulse_type, + struct cl_dfs_pulse radar_cand, u8 *match, int idx, + u8 *occ_ch_cand) +{ + int i; + + if (!(abs(dfs_db->pulse_buffer[idx].width - radar_cand.width) <= + dfs_db->radar_type[pulse_type].tol_width)) + goto end; + + if (!(abs(dfs_db->pulse_buffer[idx].freq - radar_cand.freq) <= + dfs_db->radar_type[pulse_type].tol_freq)) + goto end; + + for (i = 1; i < CL_DFS_CONCEAL_CNT; i++) + if (abs(dfs_db->pulse_buffer[idx].pri - i * radar_cand.pri) <= + dfs_db->radar_type[pulse_type].tol_pri) + break; + + if (i == CL_DFS_CONCEAL_CNT) + goto end; + + (*match)++; + (*occ_ch_cand) += dfs_db->pulse_buffer[i].occ; + +end: + dfs_pr_info(cl_hw, "DFS: compared pulse - width=%u, pri=%u, freq=%u match: %u " + "trig cnt: %u\n", + dfs_db->pulse_buffer[idx].width, dfs_db->pulse_buffer[idx].pri, + dfs_db->pulse_buffer[idx].freq, *match, + dfs_db->radar_type[pulse_type].trig_count); + + if (*match < dfs_db->radar_type[pulse_type].trig_count) + return false; + + return true; +} + +static bool cl_dfs_check_cand(struct cl_hw *cl_hw, struct cl_dfs_db *dfs_db, u8 pulse_type, + struct cl_dfs_pulse radar_cand, u8 samples_cnt) +{ + u8 occ_ch_cand = 0; + u8 match = 0; + int i; + + dfs_pr_info(cl_hw, "DFS: candidate pulse - width=%u, pri=%u, freq=%u\n", + radar_cand.width, radar_cand.pri, radar_cand.freq); + + for (i = 0; i < samples_cnt; i++) { + if (!cl_dfs_compare_cand(cl_hw, dfs_db, pulse_type, radar_cand, &match, i, + &occ_ch_cand)) + continue; + + dfs_pr_verbose(cl_hw, "DFS: Radar detected - type %u\n", pulse_type); + return true; + } + + return false; +} + +static bool cl_dfs_short_pulse_search(struct cl_hw *cl_hw, struct cl_radar_pulse *pulse, + u8 pulse_cnt, unsigned long time, struct cl_dfs_db *dfs_db) +{ + int i; + bool stag_candidate; + u8 samples_cnt = 0; + u8 pulse_type; + + /* Return if not enough pulses in the buffer */ + if (!cl_dfs_buf_maintain(cl_hw, pulse, dfs_db->pulse_buffer, pulse_cnt, time, + &samples_cnt, dfs_db)) + return false; + + for (i = 0; i < samples_cnt; i++) { + struct cl_dfs_pulse radar_cand; + + stag_candidate = false; + + /* Make sure there is enough samples to staggered check */ + if (dfs_db->dfs_standard == CL_STANDARD_ETSI && + (samples_cnt - i) > CL_DFS_STAGGERED_CHEC_LEN) + stag_candidate = true; + + pulse_type = cl_dfs_get_pulse_type(cl_hw, &dfs_db->pulse_buffer[i], stag_candidate); + + if (!pulse_type) + continue; + + radar_cand.width = dfs_db->pulse_buffer[i].width; + radar_cand.pri = dfs_db->pulse_buffer[i].pri; + radar_cand.freq = dfs_db->pulse_buffer[i].freq; + + if (cl_dfs_check_cand(cl_hw, dfs_db, pulse_type - 1, radar_cand, samples_cnt)) + return true; + } + + return false; +} + +static bool cl_dfs_long_pulse_search(struct cl_hw *cl_hw, struct cl_radar_pulse *pulse, + u8 pulse_cnt, unsigned long time) +{ + u32 prev_pulse_time_diff; + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + struct cl_tcv_conf *conf = cl_hw->conf; + int i; + + for (i = 0; i < pulse_cnt; i++) { + if (pulse[i].len > CL_DFS_LONG_MIN_WIDTH) { + prev_pulse_time_diff = time - dfs_db->last_long_pulse_ts; + + if (pulse[i].rep >= dfs_db->radar_type[5].min_pri && + pulse[i].rep <= dfs_db->radar_type[5].max_pri) + dfs_db->long_pri_match_count += 1; + + dfs_pr_info(cl_hw, "DFS: Long pulse search: width = %u, delta_time = %u\n", + pulse[i].len, prev_pulse_time_diff); + + if (dfs_db->long_pulse_count == 0 || + (prev_pulse_time_diff >= conf->ci_dfs_long_pulse_min && + prev_pulse_time_diff <= conf->ci_dfs_long_pulse_max)) { + dfs_db->long_pulse_count += 1; + } else if (prev_pulse_time_diff > min(dfs_db->max_interrupt_diff, + conf->ci_dfs_long_pulse_min)) { + dfs_db->long_pulse_count = 0; + dfs_db->short_pulse_count = 0; + dfs_db->long_pri_match_count = 0; + } + dfs_db->last_long_pulse_ts = time; + } else if (pulse[i].len < CL_DFS_LONG_FALSE_WIDTH) { + dfs_db->short_pulse_count++; + + if (dfs_db->short_pulse_count > CL_DFS_LONG_FALSE_IND) { + dfs_db->long_pulse_count = 0; + dfs_db->short_pulse_count = 0; + dfs_db->long_pri_match_count = 0; + + dfs_pr_warn(cl_hw, "DFS: Restart long sequence search\n"); + } + } + } + + if (dfs_db->long_pulse_count >= dfs_db->radar_type[5].trig_count && + dfs_db->long_pri_match_count >= (dfs_db->radar_type[5].trig_count - 1)) { + dfs_db->short_pulse_count = 0; + dfs_db->long_pulse_count = 0; + dfs_db->long_pri_match_count = 0; + return true; + } else { + return false; + } +} + +static bool cl_dfs_post_detection(struct cl_hw *cl_hw) +{ + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + struct cl_tcv_conf *conf = cl_hw->conf; + + /* Make sure firmware sets the DFS registers */ + cl_radar_flush(cl_hw); + cl_msg_tx_set_dfs(cl_hw, false, dfs_db->dfs_standard, + conf->ci_dfs_initial_gain, conf->ci_dfs_agc_cd_th); + + ieee80211_radar_detected(cl_hw->hw); + + return true; +} + +bool cl_dfs_pulse_process(struct cl_hw *cl_hw, struct cl_radar_pulse *pulse, u8 pulse_cnt, + unsigned long time) +{ + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + + dfs_db->pulse_cnt += pulse_cnt; + + if (dfs_db->dfs_standard == CL_STANDARD_FCC && + cl_dfs_long_pulse_search(cl_hw, pulse, pulse_cnt, time)) { + dfs_pr_verbose(cl_hw, "DFS: Radar detected - long\n"); + return cl_dfs_post_detection(cl_hw); + } else if (cl_dfs_short_pulse_search(cl_hw, pulse, pulse_cnt, time, dfs_db)) { + dfs_pr_verbose(cl_hw, "DFS: Radar detected - short\n"); + return cl_dfs_post_detection(cl_hw); + } + + return false; +} + +static void cl_dfs_set_min_pulse(struct cl_hw *cl_hw) +{ + int i; + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + + dfs_db->min_pulse_eeq = U8_MAX; + + for (i = 0; i < dfs_db->radar_type_cnt; i++) { + if (dfs_db->radar_type[i].trig_count < dfs_db->min_pulse_eeq) + dfs_db->min_pulse_eeq = dfs_db->radar_type[i].trig_count; + } + dfs_db->min_pulse_eeq = max(dfs_db->min_pulse_eeq, (u8)CL_DFS_MIN_PULSE_TRIG); +} + +static void cl_dfs_set_region(struct cl_hw *cl_hw, enum cl_reg_standard std) +{ + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + + dfs_db->dfs_standard = std; + + if (dfs_db->dfs_standard == CL_STANDARD_FCC) { + dfs_db->csa_cnt = CL_DFS_FCC_CSA_CNT; + dfs_db->radar_type = radar_type_fcc; + dfs_db->radar_type_cnt = sizeof(radar_type_fcc) / sizeof(struct cl_radar_type); + } else { + dfs_db->csa_cnt = CL_DFS_CE_CSA_CNT; + dfs_db->radar_type = radar_type_etsi; + dfs_db->radar_type_cnt = sizeof(radar_type_etsi) / sizeof(struct cl_radar_type); + } +} + +static void cl_dfs_start_cac(struct cl_dfs_db *db) +{ + db->cac.started = true; +} + +static void cl_dfs_end_cac(struct cl_dfs_db *db) +{ + db->cac.started = false; +} + +void cl_dfs_radar_listen_start(struct cl_hw *cl_hw) +{ + set_bit(CL_DEV_RADAR_LISTEN, &cl_hw->drv_flags); + + cl_dfs_fw_en(cl_hw, true); + + dfs_pr_verbose(cl_hw, "DFS: Started radar listening\n"); +} + +void cl_dfs_radar_listen_end(struct cl_hw *cl_hw) +{ + clear_bit(CL_DEV_RADAR_LISTEN, &cl_hw->drv_flags); + + cl_dfs_fw_en(cl_hw, false); + + dfs_pr_verbose(cl_hw, "DFS: Ended radar listening\n"); +} + +void cl_dfs_force_cac_start(struct cl_hw *cl_hw) +{ + bool is_listening = test_bit(CL_DEV_RADAR_LISTEN, &cl_hw->drv_flags); + + cl_dfs_start_cac(&cl_hw->dfs_db); + + /* Reset request state upon completion */ + cl_dfs_request_cac(cl_hw, false); + + /* Disable all the TX flow - be silent */ + cl_tx_en(cl_hw, CL_TX_EN_DFS, false); + + /* If for some reason we are still not listening radar, do it */ + if (unlikely(!is_listening && cl_hw->hw->conf.radar_enabled)) + cl_dfs_radar_listen_start(cl_hw); + + dfs_pr_verbose(cl_hw, "DFS: CAC started\n"); +} + +void cl_dfs_force_cac_end(struct cl_hw *cl_hw) +{ + bool is_listening = test_bit(CL_DEV_RADAR_LISTEN, &cl_hw->drv_flags); + + /* Enable all the TX flow */ + cl_tx_en(cl_hw, CL_TX_EN_DFS, true); + + /* + * If for some reason we are still listening and mac80211 does not + * require to listen radar - disable it + */ + if (unlikely(is_listening && !cl_hw->hw->conf.radar_enabled)) + cl_dfs_radar_listen_end(cl_hw); + + cl_dfs_end_cac(&cl_hw->dfs_db); + + dfs_pr_verbose(cl_hw, "DFS: CAC ended\n"); +} + +static u16 cl_dfs_get_remain_cac_time(struct cl_hw *cl_hw) +{ + struct cl_vif *cl_vif = cl_vif_get_first_ap(cl_hw); + struct wireless_dev *wdev = cl_vif ? ieee80211_vif_to_wdev(cl_vif->vif) : NULL; + + if (wdev && wdev->cac_started) + return (jiffies_to_msecs(jiffies - wdev->cac_start_time) / 1000U); + + return 0; +} + +bool __must_check cl_dfs_is_en(struct cl_hw *cl_hw) +{ + return cl_hw->dfs_db.en; +} + +bool __must_check cl_dfs_is_in_cac(struct cl_hw *cl_hw) +{ + return cl_hw->dfs_db.cac.started; +} + +bool __must_check cl_dfs_radar_listening(struct cl_hw *cl_hw) +{ + return test_bit(CL_DEV_RADAR_LISTEN, &cl_hw->drv_flags); +} + +bool __must_check cl_dfs_requested_cac(struct cl_hw *cl_hw) +{ + return cl_hw->dfs_db.cac.requested; +} + +void cl_dfs_request_cac(struct cl_hw *cl_hw, bool should_do) +{ + cl_hw->dfs_db.cac.requested = should_do; +} + +static void cl_dfs_edit_tbl(struct cl_hw *cl_hw, u8 row, u8 line, s16 val) +{ + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + + if (row > dfs_db->radar_type_cnt) { + dfs_pr_err(cl_hw, "Invalid row number (%u) [0 - %u]\n", line, + dfs_db->radar_type_cnt - 1); + return; + } + + if (line == 0 || line > CL_DFS_MAX_TBL_LINE) { + dfs_pr_err(cl_hw, "Invalid line number (%u) [1 - %u]\n", line, + CL_DFS_MAX_TBL_LINE - 1); + return; + } + + if (line == 1) + dfs_db->radar_type[row].min_width = (s32)val; + else if (line == 2) + dfs_db->radar_type[row].max_width = (s32)val; + else if (line == 3) + dfs_db->radar_type[row].tol_width = (s32)val; + else if (line == 4) + dfs_db->radar_type[row].min_pri = (s32)val; + else if (line == 5) + dfs_db->radar_type[row].max_pri = (s32)val; + else if (line == 6) + dfs_db->radar_type[row].tol_pri = (s32)val; + else if (line == 7) + dfs_db->radar_type[row].tol_freq = (s32)val; + else if (line == 8) + dfs_db->radar_type[row].min_burst = (u8)val; + else if (line == 9) + dfs_db->radar_type[row].ppb = (u8)val; + else if (line == 10) + dfs_db->radar_type[row].trig_count = (u8)val; + else if (line == 11) + dfs_db->radar_type[row].waveform = (enum cl_radar_waveform)val; + + /* Verify if min_pulse_eeq was changed */ + cl_dfs_set_min_pulse(cl_hw); +} + +static void cl_dfs_tbl_overwrite_set(struct cl_hw *cl_hw) +{ + s8 *tok = NULL, *saveptr = NULL; + u8 param1 = 0; + u8 param2 = 0; + s16 param3 = 0; + char str[64]; + + if (strlen(cl_hw->conf->ce_dfs_tbl_overwrite) == 0) + return; + + snprintf(str, sizeof(str), cl_hw->conf->ce_dfs_tbl_overwrite); + + tok = cl_strtok_r(str, ",", &saveptr); + while (tok) { + if (sscanf(tok, "%hhd,%hhd,%hd", ¶m1, ¶m2, ¶m3) == 3) + cl_dfs_edit_tbl(cl_hw, param1, param2, param3); + tok = cl_strtok_r(NULL, ",", &saveptr); + } +} + +void cl_dfs_close(struct cl_hw *cl_hw) +{ + if (!cl_band_is_5g(cl_hw)) + return; + + cl_hw->dfs_db.en = false; +} + +void cl_dfs_init(struct cl_hw *cl_hw) +{ + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + struct cl_tcv_conf *conf = cl_hw->conf; + + if (!cl_band_is_5g(cl_hw)) + return; + + dfs_db->en = conf->ci_ieee80211h; + + /* + * Setting min window size to avoid the case where the second interrupt + * within the burst is setting the counter to 0. the max is between jiffies + * unit and max PRI in ms. + */ + dfs_db->max_interrupt_diff = max(1000 / HZ, 2); + dfs_db->search_window = CL_DFS_PULSE_WINDOW; + + cl_dfs_set_region(cl_hw, cl_hw->channel_info.standard); + cl_dfs_set_min_pulse(cl_hw); + cl_dfs_tbl_overwrite_set(cl_hw); +} + +void cl_dfs_recovery(struct cl_hw *cl_hw) +{ + /* Re-enable DFS after recovery */ + if (cl_dfs_is_in_cac(cl_hw)) { + cl_dfs_fw_en(cl_hw, true); + + /* If recovery happened during CAC make sure to disable beacon backup */ + cl_tx_en(cl_hw, CL_TX_EN_DFS, false); + } +} + +static int cl_dfs_print_pulse_buffer(struct cl_hw *cl_hw, bool clear_buf) +{ + int i; + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + char *buf = NULL; + ssize_t buf_size; + int err = 0; + int len = 0; + + cl_snprintf(&buf, &len, &buf_size, + "DFS Pulse Count = %u\n", dfs_db->pulse_cnt); + + for (i = 0; i < ARRAY_SIZE(dfs_db->dfs_pulse); i++) { + cl_snprintf(&buf, &len, &buf_size, + "Pulse Buffer: i=%d, Width=%u, PRI=%u, FREQ=%d, OCC=%u, Time=%lu\n", + i, dfs_db->dfs_pulse[i].width, + dfs_db->dfs_pulse[i].pri, + dfs_db->dfs_pulse[i].freq, + dfs_db->dfs_pulse[i].occ, + dfs_db->dfs_pulse[i].time); + } + + if (clear_buf) { + dfs_db->pulse_cnt = 0; + memset(dfs_db->dfs_pulse, 0, sizeof(dfs_db->dfs_pulse)); + } + + err = cl_vendor_reply(cl_hw, buf, len); + kfree(buf); + + return err; +} + +static void cl_dfs_cli_force_detection(struct cl_hw *cl_hw, struct cl_dfs_db *dfs_db) +{ + dfs_pr_verbose(cl_hw, "DFS: Force Radar Detection\n"); + cl_dfs_post_detection(cl_hw); +} + +static void cl_dfs_cli_set_cac(struct cl_hw *cl_hw, bool start) +{ + if (start) + cl_dfs_force_cac_start(cl_hw); + else + cl_dfs_force_cac_end(cl_hw); +} + +static int cl_dfs_cli_help(struct cl_hw *cl_hw) +{ + char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + int err = 0; + + if (!buf) + return -ENOMEM; + + snprintf(buf, PAGE_SIZE, + "dfs usage:\n" + "-b : Print pulse buffer [(0)Read / (1)Read and clear]\n" + "-c : Stop/Start CAC[(0)Stop / (1)Start]\n" + "-d : DFS debug mode[0: Verbose 1: Error, 2: Warning, 3: Trace, 4: Info]\n" + "-e : Enable/Disable DFS [(0)Disable DFS / (1)Enable DFS]\n" + "-f : Force radar detection\n" + "-i : Set initial gain\n" + "-j : Set agc cd threshold\n" + "-k : return remaining cac time (in seconds)\n" + "-m : Set Min/Max Windows for Long radar types\n" + "-p : Print radar tables [0:Detection Table, 1:Channel info]\n" + "-q : Print jumpable channel list\n" + "-s : Simulate radar detection table " + "[width][pri][freq][time]\n" + "-t : Edit radar detection table [row][line][value]\n" + "-w : Set search window size\n"); + + err = cl_vendor_reply(cl_hw, buf, strlen(buf)); + kfree(buf); + + return err; +} + +int cl_dfs_cli(struct cl_hw *cl_hw, struct cli_params *cli_params, u8 *ret_buf, u16 *ret_buf_len) +{ + s32 *params = cli_params->params; + u32 expected_params = 0; + struct cl_dfs_db *dfs_db = &cl_hw->dfs_db; + struct cl_tcv_conf *conf = cl_hw->conf; + bool print_buf = false; + bool dbg_lvl = false; + bool en = false; + bool force = false; + bool print_tbl = false; + bool return_remain_cac_time = false; + bool radar_sim = false; + bool edit_tbl = false; + bool win_set = false; + bool cac = false; + bool long_prms = false; + bool init_gain = false; + bool agc_cd_hh = false; + + switch (cli_params->option) { + case 'b': + print_buf = true; + expected_params = 1; + break; + case 'c': + cac = true; + expected_params = 1; + break; + case 'd': + dbg_lvl = true; + expected_params = 1; + break; + case 'e': + en = true; + expected_params = 1; + break; + case 'f': + force = true; + expected_params = 0; + break; + case 'i': + init_gain = true; + expected_params = 1; + break; + case 'j': + agc_cd_hh = true; + expected_params = 1; + break; + case 'k': + return_remain_cac_time = true; + expected_params = 0; + break; + case 'm': + long_prms = true; + expected_params = 2; + break; + case 'p': + print_tbl = true; + expected_params = 1; + break; + case 's': + radar_sim = true; + expected_params = 4; + break; + case 't': + edit_tbl = true; + expected_params = 3; + break; + case 'w': + win_set = true; + expected_params = 1; + break; + case '?': + return cl_dfs_cli_help(cl_hw); + default: + cl_dbg_err(cl_hw, "Illegal option (%c) - try '?' for help\n", cli_params->option); + goto out_err; + } + + if (expected_params != cli_params->num_params) { + cl_dbg_err(cl_hw, "Wrong number of arguments (expected %u) (actual %u)\n", + expected_params, cli_params->num_params); + goto out_err; + } + + if (cac) { + bool start = (bool)params[0]; + + cl_dfs_cli_set_cac(cl_hw, start); + return 0; + } + + if (print_buf) + return cl_dfs_print_pulse_buffer(cl_hw, (bool)params[0]); + + if (dbg_lvl) { + s32 dbg_lvl = params[0]; + + if (dbg_lvl > 0 && dbg_lvl < DBG_LVL_MAX) { + dfs_db->dbg_lvl = dbg_lvl; + dfs_pr_verbose(cl_hw, "Debug level = %d\n", dbg_lvl); + } else { + dfs_pr_err(cl_hw, "Invalid debug level (%d)\n", dbg_lvl); + } + + return 0; + } + + if (en) { + dfs_db->en = (bool)(params[0]); + dfs_pr_verbose(cl_hw, "DFS = %s\n", dfs_db->en ? "Enabled" : "Disabled"); + cl_dfs_fw_en(cl_hw, dfs_db->en); + return 0; + } + + if (force) { + cl_dfs_cli_force_detection(cl_hw, dfs_db); + return 0; + } + + if (print_tbl) { + u8 table = (u8)params[0]; + + if (table == 0) + return cl_dfs_print_tbl(cl_hw); + return 0; + } + + if (return_remain_cac_time) { + u16 cac_time = cl_dfs_get_remain_cac_time(cl_hw); + + snprintf(ret_buf, PAGE_SIZE, "%u", cac_time); + *ret_buf_len = strlen(ret_buf); + return 1; + } + + if (radar_sim) { + struct cl_radar_pulse pulse[CL_DFS_MAX_PULSE]; + unsigned long time; + + pulse[0].len = (u32)params[0]; + pulse[0].rep = (u32)params[1]; + pulse[0].freq = (s32)params[2]; + time = (unsigned long)params[3]; + cl_dfs_pulse_process(cl_hw, pulse, 1, time); + return 0; + } + + if (edit_tbl) { + cl_dfs_edit_tbl(cl_hw, (u8)params[0], (u8)params[1], (s16)params[2]); + return 0; + } + + if (win_set) { + dfs_db->search_window = (u16)params[0]; + dfs_pr_verbose(cl_hw, "Search window size = %u\n", dfs_db->search_window); + return 0; + } + + if (long_prms) { + conf->ci_dfs_long_pulse_min = (u16)params[0]; + conf->ci_dfs_long_pulse_max = (u16)params[1]; + dfs_pr_verbose(cl_hw, "Long pulse min = %u\n", conf->ci_dfs_long_pulse_min); + dfs_pr_verbose(cl_hw, "Long pulse max = %u\n", conf->ci_dfs_long_pulse_max); + return 0; + } + + if (init_gain) { + conf->ci_dfs_initial_gain = (u8)params[0]; + dfs_pr_verbose(cl_hw, "Initial gain = %u\n", conf->ci_dfs_initial_gain); + return 0; + } + + if (agc_cd_hh) { + conf->ci_dfs_agc_cd_th = (u8)params[0]; + dfs_pr_verbose(cl_hw, "AGC CD threshold = %u\n", conf->ci_dfs_agc_cd_th); + return 0; + } + +out_err: + return -EIO; +} + -- 2.30.0 ________________________________ The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any retransmission, dissemination, copying or other use of, or taking of any action in reliance upon this information is prohibited. If you received this in error, please contact the sender and delete the material from any computer. Nothing contained herein shall be deemed as a representation, warranty or a commitment by Celeno. No warranties are expressed or implied, including, but not limited to, any implied warranties of non-infringement, merchantability and fitness for a particular purpose. ________________________________