summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/eswin/ecrnx_radar.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/eswin/ecrnx_radar.c')
-rw-r--r--drivers/net/wireless/eswin/ecrnx_radar.c1647
1 files changed, 1647 insertions, 0 deletions
diff --git a/drivers/net/wireless/eswin/ecrnx_radar.c b/drivers/net/wireless/eswin/ecrnx_radar.c
new file mode 100644
index 000000000000..fca6ce8afa19
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_radar.c
@@ -0,0 +1,1647 @@
+/**
+******************************************************************************
+ *
+ * @file ecrnx_radar.c
+ *
+ * @brief Functions to handle radar detection
+ * Radar detection is copied (and adapted) from ath driver source code.
+ *
+ * Copyright (c) 2012 Neratec Solutions AG
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <net/mac80211.h>
+
+#include "ecrnx_radar.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_events.h"
+#include "ecrnx_compat.h"
+
+/*
+ * tolerated deviation of radar time stamp in usecs on both sides
+ * TODO: this might need to be HW-dependent
+ */
+#define PRI_TOLERANCE 16
+
+/**
+ * struct radar_types - contains array of patterns defined for one DFS domain
+ * @domain: DFS regulatory domain
+ * @num_radar_types: number of radar types to follow
+ * @radar_types: radar types array
+ */
+struct radar_types {
+ enum nl80211_dfs_regions region;
+ u32 num_radar_types;
+ const struct radar_detector_specs *spec_riu;
+ const struct radar_detector_specs *spec_fcu;
+};
+
+/**
+ * Type of radar waveform:
+ * RADAR_WAVEFORM_SHORT : waveform defined by
+ * - pulse width
+ * - pulse interval in a burst (pri)
+ * - number of pulses in a burst (ppb)
+ *
+ * RADAR_WAVEFORM_WEATHER :
+ * same than SHORT except that ppb is dependent of pri
+ *
+ * RADAR_WAVEFORM_INTERLEAVED :
+ * same than SHORT except there are several value of pri (interleaved)
+ *
+ * RADAR_WAVEFORM_LONG :
+ *
+ */
+enum radar_waveform_type {
+ RADAR_WAVEFORM_SHORT,
+ RADAR_WAVEFORM_WEATHER,
+ RADAR_WAVEFORM_INTERLEAVED,
+ RADAR_WAVEFORM_LONG
+};
+
+/**
+ * struct radar_detector_specs - detector specs for a radar pattern type
+ * @type_id: pattern type, as defined by regulatory
+ * @width_min: minimum radar pulse width in [us]
+ * @width_max: maximum radar pulse width in [us]
+ * @pri_min: minimum pulse repetition interval in [us] (including tolerance)
+ * @pri_max: minimum pri in [us] (including tolerance)
+ * @num_pri: maximum number of different pri for this type
+ * @ppb: pulses per bursts for this type
+ * @ppb_thresh: number of pulses required to trigger detection
+ * @max_pri_tolerance: pulse time stamp tolerance on both sides [us]
+ * @type: Type of radar waveform
+ */
+struct radar_detector_specs {
+ u8 type_id;
+ u8 width_min;
+ u8 width_max;
+ u16 pri_min;
+ u16 pri_max;
+ u8 num_pri;
+ u8 ppb;
+ u8 ppb_thresh;
+ u8 max_pri_tolerance;
+ enum radar_waveform_type type;
+};
+
+
+/* percentage on ppb threshold to trigger detection */
+#define MIN_PPB_THRESH 50
+#define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100)
+#define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF)
+
+/* width tolerance */
+#define WIDTH_TOLERANCE 2
+#define WIDTH_LOWER(X) (X)
+#define WIDTH_UPPER(X) (X)
+
+#define ETSI_PATTERN_SHORT(ID, WMIN, WMAX, PMIN, PMAX, PPB) \
+ { \
+ ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \
+ (PRF2PRI(PMAX) - PRI_TOLERANCE), \
+ (PRF2PRI(PMIN) + PRI_TOLERANCE), 1, PPB, \
+ PPB_THRESH(PPB), PRI_TOLERANCE, RADAR_WAVEFORM_SHORT \
+ }
+
+#define ETSI_PATTERN_INTERLEAVED(ID, WMIN, WMAX, PMIN, PMAX, PRFMIN, PRFMAX, PPB) \
+ { \
+ ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \
+ (PRF2PRI(PMAX) * PRFMIN- PRI_TOLERANCE), \
+ (PRF2PRI(PMIN) * PRFMAX + PRI_TOLERANCE), \
+ PRFMAX, PPB * PRFMAX, \
+ PPB_THRESH(PPB), PRI_TOLERANCE, RADAR_WAVEFORM_INTERLEAVED \
+ }
+
+/* radar types as defined by ETSI EN-301-893 v1.7.1 */
+static const struct radar_detector_specs etsi_radar_ref_types_v17_riu[] = {
+ ETSI_PATTERN_SHORT(0, 0, 8, 700, 700, 18),
+ ETSI_PATTERN_SHORT(1, 0, 10, 200, 1000, 10),
+ ETSI_PATTERN_SHORT(2, 0, 22, 200, 1600, 15),
+ ETSI_PATTERN_SHORT(3, 0, 22, 2300, 4000, 25),
+ ETSI_PATTERN_SHORT(4, 20, 38, 2000, 4000, 20),
+ ETSI_PATTERN_INTERLEAVED(5, 0, 8, 300, 400, 2, 3, 10),
+ ETSI_PATTERN_INTERLEAVED(6, 0, 8, 400, 1200, 2, 3, 15),
+};
+
+static const struct radar_detector_specs etsi_radar_ref_types_v17_fcu[] = {
+ ETSI_PATTERN_SHORT(0, 0, 8, 700, 700, 18),
+ ETSI_PATTERN_SHORT(1, 0, 8, 200, 1000, 10),
+ ETSI_PATTERN_SHORT(2, 0, 16, 200, 1600, 15),
+ ETSI_PATTERN_SHORT(3, 0, 16, 2300, 4000, 25),
+ ETSI_PATTERN_SHORT(4, 20, 34, 2000, 4000, 20),
+ ETSI_PATTERN_INTERLEAVED(5, 0, 8, 300, 400, 2, 3, 10),
+ ETSI_PATTERN_INTERLEAVED(6, 0, 8, 400, 1200, 2, 3, 15),
+};
+
+static const struct radar_types etsi_radar_types_v17 = {
+ .region = NL80211_DFS_ETSI,
+ .num_radar_types = ARRAY_SIZE(etsi_radar_ref_types_v17_riu),
+ .spec_riu = etsi_radar_ref_types_v17_riu,
+ .spec_fcu = etsi_radar_ref_types_v17_fcu,
+};
+
+#define FCC_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB, TYPE) \
+ { \
+ ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \
+ PMIN - PRI_TOLERANCE, \
+ PMAX * PRF + PRI_TOLERANCE, PRF, PPB * PRF, \
+ PPB_THRESH(PPB), PRI_TOLERANCE, TYPE \
+ }
+
+static const struct radar_detector_specs fcc_radar_ref_types_riu[] = {
+ FCC_PATTERN(0, 0, 8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(1, 0, 8, 518, 3066, 1, 102, RADAR_WAVEFORM_WEATHER),
+ FCC_PATTERN(2, 0, 8, 150, 230, 1, 23, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(3, 6, 20, 200, 500, 1, 16, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(4, 10, 28, 200, 500, 1, 12, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(5, 50, 110, 1000, 2000, 1, 8, RADAR_WAVEFORM_LONG),
+ FCC_PATTERN(6, 0, 8, 333, 333, 1, 9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_detector_specs fcc_radar_ref_types_fcu[] = {
+ FCC_PATTERN(0, 0, 8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(1, 0, 8, 518, 3066, 1, 102, RADAR_WAVEFORM_WEATHER),
+ FCC_PATTERN(2, 0, 8, 150, 230, 1, 23, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(3, 6, 12, 200, 500, 1, 16, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(4, 10, 22, 200, 500, 1, 12, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(5, 50, 104, 1000, 2000, 1, 8, RADAR_WAVEFORM_LONG),
+ FCC_PATTERN(6, 0, 8, 333, 333, 1, 9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_types fcc_radar_types = {
+ .region = NL80211_DFS_FCC,
+ .num_radar_types = ARRAY_SIZE(fcc_radar_ref_types_riu),
+ .spec_riu = fcc_radar_ref_types_riu,
+ .spec_fcu = fcc_radar_ref_types_fcu,
+};
+
+#define JP_PATTERN FCC_PATTERN
+static const struct radar_detector_specs jp_radar_ref_types_riu[] = {
+ JP_PATTERN(0, 0, 8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(1, 2, 8, 3846, 3846, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(2, 0, 8, 1388, 1388, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(3, 0, 8, 4000, 4000, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(4, 0, 8, 150, 230, 1, 23, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(5, 6, 20, 200, 500, 1, 16, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(6, 10, 28, 200, 500, 1, 12, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(7, 50, 110, 1000, 2000, 1, 8, RADAR_WAVEFORM_LONG),
+ JP_PATTERN(8, 0, 8, 333, 333, 1, 9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_detector_specs jp_radar_ref_types_fcu[] = {
+ JP_PATTERN(0, 0, 8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(1, 2, 6, 3846, 3846, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(2, 0, 8, 1388, 1388, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(3, 2, 2, 4000, 4000, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(4, 0, 8, 150, 230, 1, 23, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(5, 6, 12, 200, 500, 1, 16, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(6, 10, 22, 200, 500, 1, 12, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(7, 50, 104, 1000, 2000, 1, 8, RADAR_WAVEFORM_LONG),
+ JP_PATTERN(8, 0, 8, 333, 333, 1, 9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_types jp_radar_types = {
+ .region = NL80211_DFS_JP,
+ .num_radar_types = ARRAY_SIZE(jp_radar_ref_types_riu),
+ .spec_riu = jp_radar_ref_types_riu,
+ .spec_fcu = jp_radar_ref_types_fcu,
+};
+
+static const struct radar_types *dfs_domains[] = {
+ &etsi_radar_types_v17,
+ &fcc_radar_types,
+ &jp_radar_types,
+};
+
+
+/**
+ * struct pri_sequence - sequence of pulses matching one PRI
+ * @head: list_head
+ * @pri: pulse repetition interval (PRI) in usecs
+ * @dur: duration of sequence in usecs
+ * @count: number of pulses in this sequence
+ * @count_falses: number of not matching pulses in this sequence
+ * @first_ts: time stamp of first pulse in usecs
+ * @last_ts: time stamp of last pulse in usecs
+ * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur)
+ * @ppb_thresh: Number of pulses to validate detection
+ * (need for weather radar whose value depends of pri)
+ */
+struct pri_sequence {
+ struct list_head head;
+ u32 pri;
+ u32 dur;
+ u32 count;
+ u32 count_falses;
+ u64 first_ts;
+ u64 last_ts;
+ u64 deadline_ts;
+ u8 ppb_thresh;
+};
+
+
+/**
+ * struct pulse_elem - elements in pulse queue
+ * @ts: time stamp in usecs
+ */
+struct pulse_elem {
+ struct list_head head;
+ u64 ts;
+};
+
+/**
+ * struct pri_detector - PRI detector element for a dedicated radar type
+ * @head:
+ * @rs: detector specs for this detector element
+ * @last_ts: last pulse time stamp considered for this element in usecs
+ * @sequences: list_head holding potential pulse sequences
+ * @pulses: list connecting pulse_elem objects
+ * @count: number of pulses in queue
+ * @max_count: maximum number of pulses to be queued
+ * @window_size: window size back from newest pulse time stamp in usecs
+ * @freq:
+ */
+struct pri_detector {
+ struct list_head head;
+ const struct radar_detector_specs *rs;
+ u64 last_ts;
+ struct list_head sequences;
+ struct list_head pulses;
+ u32 count;
+ u32 max_count;
+ u32 window_size;
+ struct pri_detector_ops *ops;
+ u16 freq;
+};
+
+/**
+ * struct pri_detector_ops - PRI detector ops (dependent of waveform type)
+ * @init : Initialize pri_detector structure
+ * @add_pulse : Add a pulse to the pri-detector
+ * @reset_on_pri_overflow : Should the pri_detector be resetted when pri overflow
+ */
+struct pri_detector_ops {
+ void (*init)(struct pri_detector *pde);
+ struct pri_sequence * (*add_pulse)(struct pri_detector *pde, u16 len, u64 ts, u16 pri);
+ int reset_on_pri_overflow;
+};
+
+
+/******************************************************************************
+ * PRI (pulse repetition interval) sequence detection
+ *****************************************************************************/
+/**
+ * Singleton Pulse and Sequence Pools
+ *
+ * Instances of pri_sequence and pulse_elem are kept in singleton pools to
+ * reduce the number of dynamic allocations. They are shared between all
+ * instances and grow up to the peak number of simultaneously used objects.
+ *
+ * Memory is freed after all references to the pools are released.
+ */
+static u32 singleton_pool_references;
+static LIST_HEAD(pulse_pool);
+static LIST_HEAD(pseq_pool);
+static DEFINE_SPINLOCK(pool_lock);
+
+static void pool_register_ref(void)
+{
+ spin_lock_bh(&pool_lock);
+ singleton_pool_references++;
+ spin_unlock_bh(&pool_lock);
+}
+
+static void pool_deregister_ref(void)
+{
+ spin_lock_bh(&pool_lock);
+ singleton_pool_references--;
+ if (singleton_pool_references == 0) {
+ /* free singleton pools with no references left */
+ struct pri_sequence *ps, *ps0;
+ struct pulse_elem *p, *p0;
+
+ list_for_each_entry_safe(p, p0, &pulse_pool, head) {
+ list_del(&p->head);
+ kfree(p);
+ }
+ list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
+ list_del(&ps->head);
+ kfree(ps);
+ }
+ }
+ spin_unlock_bh(&pool_lock);
+}
+
+static void pool_put_pulse_elem(struct pulse_elem *pe)
+{
+ spin_lock_bh(&pool_lock);
+ list_add(&pe->head, &pulse_pool);
+ spin_unlock_bh(&pool_lock);
+}
+
+static void pool_put_pseq_elem(struct pri_sequence *pse)
+{
+ spin_lock_bh(&pool_lock);
+ list_add(&pse->head, &pseq_pool);
+ spin_unlock_bh(&pool_lock);
+}
+
+static struct pri_sequence *pool_get_pseq_elem(void)
+{
+ struct pri_sequence *pse = NULL;
+ spin_lock_bh(&pool_lock);
+ if (!list_empty(&pseq_pool)) {
+ pse = list_first_entry(&pseq_pool, struct pri_sequence, head);
+ list_del(&pse->head);
+ }
+ spin_unlock_bh(&pool_lock);
+
+ if (pse == NULL) {
+ pse = kmalloc(sizeof(*pse), GFP_ATOMIC);
+ }
+
+ return pse;
+}
+
+static struct pulse_elem *pool_get_pulse_elem(void)
+{
+ struct pulse_elem *pe = NULL;
+ spin_lock_bh(&pool_lock);
+ if (!list_empty(&pulse_pool)) {
+ pe = list_first_entry(&pulse_pool, struct pulse_elem, head);
+ list_del(&pe->head);
+ }
+ spin_unlock_bh(&pool_lock);
+ return pe;
+}
+
+static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde)
+{
+ struct list_head *l = &pde->pulses;
+ if (list_empty(l))
+ return NULL;
+ return list_entry(l->prev, struct pulse_elem, head);
+}
+
+static bool pulse_queue_dequeue(struct pri_detector *pde)
+{
+ struct pulse_elem *p = pulse_queue_get_tail(pde);
+ if (p != NULL) {
+ list_del_init(&p->head);
+ pde->count--;
+ /* give it back to pool */
+ pool_put_pulse_elem(p);
+ }
+ return (pde->count > 0);
+}
+
+/**
+ * pulse_queue_check_window - remove pulses older than window
+ * @pde: pointer on pri_detector
+ *
+ * dequeue pulse that are too old.
+ */
+static
+void pulse_queue_check_window(struct pri_detector *pde)
+{
+ u64 min_valid_ts;
+ struct pulse_elem *p;
+
+ /* there is no delta time with less than 2 pulses */
+ if (pde->count < 2)
+ return;
+
+ if (pde->last_ts <= pde->window_size)
+ return;
+
+ min_valid_ts = pde->last_ts - pde->window_size;
+ while ((p = pulse_queue_get_tail(pde)) != NULL) {
+ if (p->ts >= min_valid_ts)
+ return;
+ pulse_queue_dequeue(pde);
+ }
+}
+
+/**
+ * pulse_queue_enqueue - Queue one pulse
+ * @pde: pointer on pri_detector
+ *
+ * Add one pulse to the list. If the maximum number of pulses
+ * if reached, remove oldest one.
+ */
+static
+bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts)
+{
+ struct pulse_elem *p = pool_get_pulse_elem();
+ if (p == NULL) {
+ p = kmalloc(sizeof(*p), GFP_ATOMIC);
+ if (p == NULL) {
+ return false;
+ }
+ }
+ INIT_LIST_HEAD(&p->head);
+ p->ts = ts;
+ list_add(&p->head, &pde->pulses);
+ pde->count++;
+ pde->last_ts = ts;
+ pulse_queue_check_window(pde);
+ if (pde->count >= pde->max_count)
+ pulse_queue_dequeue(pde);
+
+ return true;
+}
+
+
+/***************************************************************************
+ * Short waveform
+ **************************************************************************/
+/**
+ * pde_get_multiple() - get number of multiples considering a given tolerance
+ * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise
+ */
+static
+u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance)
+{
+ u32 remainder;
+ u32 factor;
+ u32 delta;
+
+ if (fraction == 0)
+ return 0;
+
+ delta = (val < fraction) ? (fraction - val) : (val - fraction);
+
+ if (delta <= tolerance)
+ /* val and fraction are within tolerance */
+ return 1;
+
+ factor = val / fraction;
+ remainder = val % fraction;
+ if (remainder > tolerance) {
+ /* no exact match */
+ if ((fraction - remainder) <= tolerance)
+ /* remainder is within tolerance */
+ factor++;
+ else
+ factor = 0;
+ }
+ return factor;
+}
+
+/**
+ * pde_short_create_sequences - create_sequences function for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ * @ts: timestamp of the pulse
+ * @min_count: Minimum number of pulse to be present in the sequence.
+ * (With this pulse there is already a sequence with @min_count
+ * pulse, so if we can't create a sequence with more pulse don't
+ * create it)
+ * @return: false if an error occured (memory allocation) true otherwise
+ *
+ * For each pulses queued check if we can create a sequence with
+ * pri = (ts - pulse_queued.ts) which contains more than @min_count pulses.
+ *
+ */
+static
+bool pde_short_create_sequences(struct pri_detector *pde,
+ u64 ts, u32 min_count)
+{
+ struct pulse_elem *p;
+ u16 pulse_idx = 0;
+
+ list_for_each_entry(p, &pde->pulses, head) {
+ struct pri_sequence ps, *new_ps;
+ struct pulse_elem *p2;
+ u32 tmp_false_count;
+ u64 min_valid_ts;
+ u32 delta_ts = ts - p->ts;
+ pulse_idx++;
+
+ if (delta_ts < pde->rs->pri_min)
+ /* ignore too small pri */
+ continue;
+
+ if (delta_ts > pde->rs->pri_max)
+ /* stop on too large pri (sorted list) */
+ break;
+
+ /* build a new sequence with new potential pri */
+ ps.count = 2;
+ ps.count_falses = pulse_idx - 1;
+ ps.first_ts = p->ts;
+ ps.last_ts = ts;
+ ps.pri = ts - p->ts;
+ ps.dur = ps.pri * (pde->rs->ppb - 1)
+ + 2 * pde->rs->max_pri_tolerance;
+
+ p2 = p;
+ tmp_false_count = 0;
+ if (ps.dur > ts)
+ min_valid_ts = 0;
+ else
+ min_valid_ts = ts - ps.dur;
+ /* check which past pulses are candidates for new sequence */
+ list_for_each_entry_continue(p2, &pde->pulses, head) {
+ u32 factor;
+ if (p2->ts < min_valid_ts)
+ /* stop on crossing window border */
+ break;
+ /* check if pulse match (multi)PRI */
+ factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri,
+ pde->rs->max_pri_tolerance);
+ if (factor > 0) {
+ ps.count++;
+ ps.first_ts = p2->ts;
+ /*
+ * on match, add the intermediate falses
+ * and reset counter
+ */
+ ps.count_falses += tmp_false_count;
+ tmp_false_count = 0;
+ } else {
+ /* this is a potential false one */
+ tmp_false_count++;
+ }
+ }
+ if (ps.count <= min_count) {
+ /* did not reach minimum count, drop sequence */
+ continue;
+ }
+ /* this is a valid one, add it */
+ ps.deadline_ts = ps.first_ts + ps.dur;
+ if (pde->rs->type == RADAR_WAVEFORM_WEATHER) {
+ ps.ppb_thresh = 19000000 / (360 * ps.pri);
+ ps.ppb_thresh = PPB_THRESH(ps.ppb_thresh);
+ } else {
+ ps.ppb_thresh = pde->rs->ppb_thresh;
+ }
+
+ new_ps = pool_get_pseq_elem();
+ if (new_ps == NULL) {
+ return false;
+ }
+ memcpy(new_ps, &ps, sizeof(ps));
+ INIT_LIST_HEAD(&new_ps->head);
+ list_add(&new_ps->head, &pde->sequences);
+ }
+ return true;
+}
+
+/**
+ * pde_short_add_to_existing_seqs - add_to_existing_seqs function for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ * @ts: timestamp of the pulse
+ *
+ * Check all sequemces created for this pde.
+ * - If the sequence is too old delete it.
+ * - Else if the delta with the previous pulse match the pri of the sequence
+ * add the pulse to this sequence. If the pulse cannot be added it is added
+ * to the false pulses for this sequence
+ *
+ * @return the length of the longest sequence in which the pulse has been added
+ */
+static
+u32 pde_short_add_to_existing_seqs(struct pri_detector *pde, u64 ts)
+{
+ u32 max_count = 0;
+ struct pri_sequence *ps, *ps2;
+ list_for_each_entry_safe(ps, ps2, &pde->sequences, head) {
+ u32 delta_ts;
+ u32 factor;
+
+ /* first ensure that sequence is within window */
+ if (ts > ps->deadline_ts) {
+ list_del_init(&ps->head);
+ pool_put_pseq_elem(ps);
+ continue;
+ }
+
+ delta_ts = ts - ps->last_ts;
+ factor = pde_get_multiple(delta_ts, ps->pri,
+ pde->rs->max_pri_tolerance);
+
+ if (factor > 0) {
+ ps->last_ts = ts;
+ ps->count++;
+
+ if (max_count < ps->count)
+ max_count = ps->count;
+ } else {
+ ps->count_falses++;
+ }
+ }
+ return max_count;
+}
+
+
+/**
+ * pde_short_check_detection - check_detection function for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Check all sequemces created for this pde.
+ * - If a sequence contains more pulses than the threshold and more matching
+ * that false pulses.
+ *
+ * @return The first complete sequence, and NULL if no sequence is complete.
+ */
+static
+struct pri_sequence * pde_short_check_detection(struct pri_detector *pde)
+{
+ struct pri_sequence *ps;
+
+ if (list_empty(&pde->sequences))
+ return NULL;
+
+ list_for_each_entry(ps, &pde->sequences, head) {
+ /*
+ * we assume to have enough matching confidence if we
+ * 1) have enough pulses
+ * 2) have more matching than false pulses
+ */
+ if ((ps->count >= ps->ppb_thresh) &&
+ (ps->count * pde->rs->num_pri > ps->count_falses)) {
+ return ps;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * pde_short_init - init function for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Initialize pri_detector window size to the maximun size of one burst
+ * for the radar specification associated.
+ */
+static
+void pde_short_init(struct pri_detector *pde)
+{
+ pde->window_size = pde->rs->pri_max * pde->rs->ppb * pde->rs->num_pri;
+ pde->max_count = pde->rs->ppb * 2;
+}
+
+static void pri_detector_reset(struct pri_detector *pde, u64 ts);
+/**
+ * pde_short_add_pulse - Add pulse to a pri_detector for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ *
+ * @pde : pointer on pri_detector
+ * @len : width of the pulse
+ * @ts : timestamp of the pulse received
+ * @pri : Delta in us with the previous pulse.
+ * (0 means that delta in bigger than 65535 us)
+ *
+ * Process on pulse within this pri_detector
+ * - First try to add it to existing sequence
+ * - Then try to create a new and longest sequence
+ * - Check if this pulse complete a sequence
+ * - If not save this pulse in the list
+ */
+static
+struct pri_sequence *pde_short_add_pulse(struct pri_detector *pde,
+ u16 len, u64 ts, u16 pri)
+{
+ u32 max_updated_seq;
+ struct pri_sequence *ps;
+ const struct radar_detector_specs *rs = pde->rs;
+
+ if (pde->count == 0) {
+ /* This is the first pulse after reset, no need to check sequences */
+ pulse_queue_enqueue(pde, ts);
+ return NULL;
+ }
+
+ if ((ts - pde->last_ts) < rs->max_pri_tolerance) {
+ /* if delta to last pulse is too short, don't use this pulse */
+ return NULL;
+ }
+
+ max_updated_seq = pde_short_add_to_existing_seqs(pde, ts);
+
+ if (!pde_short_create_sequences(pde, ts, max_updated_seq)) {
+ pri_detector_reset(pde, ts);
+ return NULL;
+ }
+
+ ps = pde_short_check_detection(pde);
+
+ if (ps == NULL)
+ pulse_queue_enqueue(pde, ts);
+
+ return ps;
+}
+
+
+
+/**
+ * pri detector ops to detect short radar waveform
+ * A Short waveform is defined by :
+ * The width of pulses.
+ * The interval between two pulses inside a burst (called pri)
+ * (some waveform may have or 2/3 interleaved pri)
+ * The number of pulses per burst (ppb)
+ */
+static struct pri_detector_ops pri_detector_short = {
+ .init = pde_short_init,
+ .add_pulse = pde_short_add_pulse,
+ .reset_on_pri_overflow = 1,
+};
+
+
+/***************************************************************************
+ * Long waveform
+ **************************************************************************/
+#define LONG_RADAR_DURATION 12000000
+#define LONG_RADAR_BURST_MIN_DURATION (12000000 / 20)
+#define LONG_RADAR_MAX_BURST 20
+
+/**
+ * pde_long_init - init function for LONG radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Initialize pri_detector window size to the long waveform radar
+ * waveform (ie. 12s) and max_count
+ */
+static
+void pde_long_init(struct pri_detector *pde)
+{
+ pde->window_size = LONG_RADAR_DURATION;
+ pde->max_count = LONG_RADAR_MAX_BURST; /* only count burst not pulses */
+}
+
+
+/**
+ * pde_long_add_pulse - Add pulse to a pri_detector for
+ * LONG radar waveform
+ *
+ * @pde : pointer on pri_detector
+ * @len : width of the pulse
+ * @ts : timestamp of the pulse received
+ * @pri : Delta in us with the previous pulse.
+ *
+ *
+ * For long pulse we only handle one sequence. Since each burst
+ * have a different set of parameters (number of pulse, pri) than
+ * the previous one we only use pulse width to add the pulse in the
+ * sequence.
+ * We only queue one pulse per burst and valid the radar when enough burst
+ * has been detected.
+ */
+static
+struct pri_sequence *pde_long_add_pulse(struct pri_detector *pde,
+ u16 len, u64 ts, u16 pri)
+{
+ struct pri_sequence *ps;
+ const struct radar_detector_specs *rs = pde->rs;
+
+ if (list_empty(&pde->sequences)) {
+ /* First pulse, create a new sequence */
+ ps = pool_get_pseq_elem();
+ if (ps == NULL) {
+ return NULL;
+ }
+
+ /*For long waveform, "count" represents the number of burst detected */
+ ps->count = 1;
+ /*"count_false" represents the number of pulse in the current burst */
+ ps->count_falses = 1;
+ ps->first_ts = ts;
+ ps->last_ts = ts;
+ ps->deadline_ts = ts + pde->window_size;
+ ps->pri = 0;
+ INIT_LIST_HEAD(&ps->head);
+ list_add(&ps->head, &pde->sequences);
+ pulse_queue_enqueue(pde, ts);
+ } else {
+ u32 delta_ts;
+
+ ps = (struct pri_sequence *)pde->sequences.next;
+
+ delta_ts = ts - ps->last_ts;
+ ps->last_ts = ts;
+
+ if (delta_ts < rs->pri_max) {
+ /* ignore pulse too close from previous one */
+ } else if ((delta_ts >= rs->pri_min) &&
+ (delta_ts <= rs->pri_max)) {
+ /* this is a new pulse in the current burst, ignore it
+ (i.e don't queue it) */
+ ps->count_falses++;
+ } else if ((ps->count > 2) &&
+ (ps->dur + delta_ts) < LONG_RADAR_BURST_MIN_DURATION) {
+ /* not enough time between burst, ignore pulse */
+ } else {
+ /* a new burst */
+ ps->count++;
+ ps->count_falses = 1;
+
+ /* reset the start of the sequence if deadline reached */
+ if (ts > ps->deadline_ts) {
+ struct pulse_elem *p;
+ u64 min_valid_ts;
+
+ min_valid_ts = ts - pde->window_size;
+ while ((p = pulse_queue_get_tail(pde)) != NULL) {
+ if (p->ts >= min_valid_ts) {
+ ps->first_ts = p->ts;
+ ps->deadline_ts = p->ts + pde->window_size;
+ break;
+ }
+ pulse_queue_dequeue(pde);
+ ps->count--;
+ }
+ }
+
+ /* valid radar if enough burst detected and delta with first burst
+ is at least duration/2 */
+ if (ps->count > pde->rs->ppb_thresh &&
+ (ts - ps->first_ts) > (pde->window_size / 2)) {
+ return ps;
+ } else {
+ pulse_queue_enqueue(pde, ts);
+ ps->dur = delta_ts;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * pri detector ops to detect long radar waveform
+ */
+static struct pri_detector_ops pri_detector_long = {
+ .init = pde_long_init,
+ .add_pulse = pde_long_add_pulse,
+ .reset_on_pri_overflow = 0,
+};
+
+
+/***************************************************************************
+ * PRI detector init/reset/exit/get
+ **************************************************************************/
+/**
+ * pri_detector_init- Create a new pri_detector
+ *
+ * @dpd: dfs_pattern_detector instance pointer
+ * @radar_type: index of radar pattern
+ * @freq: Frequency of the pri detector
+ */
+struct pri_detector *pri_detector_init(struct dfs_pattern_detector *dpd,
+ u16 radar_type, u16 freq)
+{
+ struct pri_detector *pde;
+
+ pde = kzalloc(sizeof(*pde), GFP_ATOMIC);
+ if (pde == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&pde->sequences);
+ INIT_LIST_HEAD(&pde->pulses);
+ INIT_LIST_HEAD(&pde->head);
+ list_add(&pde->head, &dpd->detectors[radar_type]);
+
+ pde->rs = &dpd->radar_spec[radar_type];
+ pde->freq = freq;
+
+ if (pde->rs->type == RADAR_WAVEFORM_LONG) {
+ /* for LONG WAVEFORM */
+ pde->ops = &pri_detector_long;
+ } else {
+ /* for SHORT, WEATHER and INTERLEAVED */
+ pde->ops = &pri_detector_short;
+ }
+
+ /* Init dependent of specs */
+ pde->ops->init(pde);
+
+ pool_register_ref();
+ return pde;
+}
+
+/**
+ * pri_detector_reset - Reset pri_detector
+ *
+ * @pde: pointer on pri_detector
+ * @ts: New ts reference for the pri_detector
+ *
+ * free pulse queue and sequences list and give objects back to pools
+ */
+static
+void pri_detector_reset(struct pri_detector *pde, u64 ts)
+{
+ struct pri_sequence *ps, *ps0;
+ struct pulse_elem *p, *p0;
+ list_for_each_entry_safe(ps, ps0, &pde->sequences, head) {
+ list_del_init(&ps->head);
+ pool_put_pseq_elem(ps);
+ }
+ list_for_each_entry_safe(p, p0, &pde->pulses, head) {
+ list_del_init(&p->head);
+ pool_put_pulse_elem(p);
+ }
+ pde->count = 0;
+ pde->last_ts = ts;
+}
+
+/**
+ * pri_detector_exit - Delete pri_detector
+ *
+ * @pde: pointer on pri_detector
+ */
+static
+void pri_detector_exit(struct pri_detector *pde)
+{
+ pri_detector_reset(pde, 0);
+ pool_deregister_ref();
+ list_del(&pde->head);
+ kfree(pde);
+}
+
+/**
+ * pri_detector_get() - get pri detector for a given frequency and type
+ * @dpd: dfs_pattern_detector instance pointer
+ * @freq: frequency in MHz
+ * @radar_type: index of radar pattern
+ * @return pointer to pri detector on success, NULL otherwise
+ *
+ * Return existing pri detector for the given frequency or return a
+ * newly create one.
+ * Pri detector are "merged" by frequency so that if a pri detector for a freq
+ * of +/- 2Mhz already exists don't create a new one.
+ *
+ * Maybe will need to adapt frequency merge for pattern with chirp.
+ */
+static struct pri_detector *
+pri_detector_get(struct dfs_pattern_detector *dpd, u16 freq, u16 radar_type)
+{
+ struct pri_detector *pde, *cur = NULL;
+ list_for_each_entry(pde, &dpd->detectors[radar_type], head) {
+ if (pde->freq == freq) {
+ if (pde->count)
+ return pde;
+ else
+ cur = pde;
+ } else if (pde->freq - 2 == freq && pde->count) {
+ return pde;
+ } else if (pde->freq + 2 == freq && pde->count) {
+ return pde;
+ }
+ }
+
+ if (cur)
+ return cur;
+ else
+ return pri_detector_init(dpd, radar_type, freq);
+}
+
+
+/******************************************************************************
+ * DFS Pattern Detector
+ *****************************************************************************/
+/**
+ * dfs_pattern_detector_reset() - reset all channel detectors
+ *
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_reset(struct dfs_pattern_detector *dpd)
+{
+ struct pri_detector *pde;
+ int i;
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ if (!list_empty(&dpd->detectors[i]))
+ list_for_each_entry(pde, &dpd->detectors[i], head)
+ pri_detector_reset(pde, dpd->last_pulse_ts);
+ }
+
+ dpd->last_pulse_ts = 0;
+ dpd->prev_jiffies = jiffies;
+}
+
+/**
+ * dfs_pattern_detector_reset() - delete all channel detectors
+ *
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_exit(struct dfs_pattern_detector *dpd)
+{
+ struct pri_detector *pde, *pde0;
+ int i;
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ if (!list_empty(&dpd->detectors[i]))
+ list_for_each_entry_safe(pde, pde0, &dpd->detectors[i], head)
+ pri_detector_exit(pde);
+ }
+
+ kfree(dpd);
+}
+
+/**
+ * dfs_pattern_detector_pri_overflow - reset all channel detectors on pri
+ * overflow
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_pri_overflow(struct dfs_pattern_detector *dpd)
+{
+ struct pri_detector *pde;
+ int i;
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ if (!list_empty(&dpd->detectors[i]))
+ list_for_each_entry(pde, &dpd->detectors[i], head)
+ if (pde->ops->reset_on_pri_overflow)
+ pri_detector_reset(pde, dpd->last_pulse_ts);
+ }
+}
+
+/**
+ * dfs_pattern_detector_add_pulse - Process one pulse
+ *
+ * @dpd: dfs_pattern_detector
+ * @chain: Chain that correspond to this pattern_detector (only for debug)
+ * @freq: frequency of the pulse
+ * @pri: Delta with previous pulse. (0 if delta is too big for u16)
+ * @len: width of the pulse
+ * @now: jiffies value when pulse was received
+ *
+ * Get (or create) the channel_detector for this frequency. Then add the pulse
+ * in each pri_detector created in this channel_detector.
+ *
+ *
+ * @return True is the pulse complete a radar pattern, false otherwise
+ */
+static bool dfs_pattern_detector_add_pulse(struct dfs_pattern_detector *dpd,
+ enum ecrnx_radar_chain chain,
+ u16 freq, u16 pri, u16 len, u32 now)
+{
+ u32 i;
+
+ /*
+ * pulses received for a non-supported or un-initialized
+ * domain are treated as detected radars for fail-safety
+ */
+ if (dpd->region == NL80211_DFS_UNSET)
+ return true;
+
+ /* Compute pulse time stamp */
+ if (pri == 0) {
+ u32 delta_jiffie;
+ if (unlikely(now < dpd->prev_jiffies)) {
+ delta_jiffie = 0xffffffff - dpd->prev_jiffies + now;
+ } else {
+ delta_jiffie = now - dpd->prev_jiffies;
+ }
+ dpd->last_pulse_ts += jiffies_to_usecs(delta_jiffie);
+ dpd->prev_jiffies = now;
+ dfs_pattern_detector_pri_overflow(dpd);
+ } else {
+ dpd->last_pulse_ts += pri;
+ }
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ struct pri_sequence *ps;
+ struct pri_detector *pde;
+ const struct radar_detector_specs *rs = &dpd->radar_spec[i];
+
+ /* no need to look up for pde if len is not within range */
+ if ((rs->width_min > len) ||
+ (rs->width_max < len)) {
+ continue;
+ }
+
+ pde = pri_detector_get(dpd, freq, i);
+ ps = pde->ops->add_pulse(pde, len, dpd->last_pulse_ts, pri);
+
+ if (ps != NULL) {
+ trace_radar_detected(chain, dpd->region, pde->freq, i, ps->pri);
+ // reset everything instead of just the channel detector
+ dfs_pattern_detector_reset(dpd);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * get_dfs_domain_radar_types() - get radar types for a given DFS domain
+ * @param domain DFS domain
+ * @return radar_types ptr on success, NULL if DFS domain is not supported
+ */
+static const struct radar_types *
+get_dfs_domain_radar_types(enum nl80211_dfs_regions region)
+{
+ u32 i;
+ for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
+ if (dfs_domains[i]->region == region)
+ return dfs_domains[i];
+ }
+ return NULL;
+}
+
+/**
+ * get_dfs_max_radar_types() - get maximum radar types for all supported domain
+ * @return the maximum number of radar pattern supported by on region
+ */
+static u16 get_dfs_max_radar_types(void)
+{
+ u32 i;
+ u16 max = 0;
+ for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
+ if (dfs_domains[i]->num_radar_types > max)
+ max = dfs_domains[i]->num_radar_types;
+ }
+ return max;
+}
+
+/**
+ * dfs_pattern_detector_set_domain - set DFS domain
+ *
+ * @dpd: dfs_pattern_detector
+ * @region: DFS region
+ *
+ * set DFS domain, resets detector lines upon domain changes
+ */
+static
+bool dfs_pattern_detector_set_domain(struct dfs_pattern_detector *dpd,
+ enum nl80211_dfs_regions region, u8 chain)
+{
+ const struct radar_types *rt;
+ struct pri_detector *pde, *pde0;
+ int i;
+
+ if (dpd->region == region)
+ return true;
+
+ dpd->region = NL80211_DFS_UNSET;
+
+ rt = get_dfs_domain_radar_types(region);
+ if (rt == NULL)
+ return false;
+
+ /* delete all pri detectors for previous DFS domain */
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ if (!list_empty(&dpd->detectors[i]))
+ list_for_each_entry_safe(pde, pde0, &dpd->detectors[i], head)
+ pri_detector_exit(pde);
+ }
+
+ if (chain == ECRNX_RADAR_RIU)
+ dpd->radar_spec = rt->spec_riu;
+ else
+ dpd->radar_spec = rt->spec_fcu;
+ dpd->num_radar_types = rt->num_radar_types;
+
+ dpd->region = region;
+ return true;
+}
+
+/**
+ * dfs_pattern_detector_init - Initialize dfs_pattern_detector
+ *
+ * @region: DFS region
+ * @return: pointer on dfs_pattern_detector
+ *
+ */
+static struct dfs_pattern_detector *
+dfs_pattern_detector_init(enum nl80211_dfs_regions region, u8 chain)
+{
+ struct dfs_pattern_detector *dpd;
+ u16 i, max_radar_type = get_dfs_max_radar_types();
+
+ dpd = kmalloc(sizeof(*dpd) + max_radar_type * sizeof(dpd->detectors[0]),
+ GFP_KERNEL);
+ if (dpd == NULL)
+ return NULL;
+
+ dpd->region = NL80211_DFS_UNSET;
+ dpd->enabled = ECRNX_RADAR_DETECT_DISABLE;
+ dpd->last_pulse_ts = 0;
+ dpd->prev_jiffies = jiffies;
+ dpd->num_radar_types = 0;
+ for (i = 0; i < max_radar_type; i++)
+ INIT_LIST_HEAD(&dpd->detectors[i]);
+
+ if (dfs_pattern_detector_set_domain(dpd, region, chain))
+ return dpd;
+
+ kfree(dpd);
+ return NULL;
+}
+
+
+/******************************************************************************
+ * driver interface
+ *****************************************************************************/
+static u16 ecrnx_radar_get_center_freq(struct ecrnx_hw *ecrnx_hw, u8 chain)
+{
+ if (chain == ECRNX_RADAR_FCU)
+ return ecrnx_hw->phy.sec_chan.center1_freq;
+
+ if (chain == ECRNX_RADAR_RIU) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+ return ecrnx_hw->cur_freq;
+#else
+ if (!ecrnx_chanctx_valid(ecrnx_hw, ecrnx_hw->cur_chanctx)) {
+ WARN(1, "Radar pulse without channel information");
+ } else
+ return ecrnx_hw->chanctx_table[ecrnx_hw->cur_chanctx].chan_def.center_freq1;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+ }
+
+ return 0;
+}
+
+static void ecrnx_radar_detected(struct ecrnx_hw *ecrnx_hw)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+ ieee80211_radar_detected(ecrnx_hw->hw);
+#else
+ struct cfg80211_chan_def chan_def;
+
+ if (!ecrnx_chanctx_valid(ecrnx_hw, ecrnx_hw->cur_chanctx)) {
+ WARN(1, "Radar detected without channel information");
+ return;
+ }
+
+ /*
+ recopy chan_def in local variable because ecrnx_radar_cancel_cac may
+ clean the variable (if in CAC and it's the only vif using this context)
+ and CAC should be aborted before reporting the radar.
+ */
+ chan_def = ecrnx_hw->chanctx_table[ecrnx_hw->cur_chanctx].chan_def;
+
+ ecrnx_radar_cancel_cac(&ecrnx_hw->radar);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ cfg80211_radar_event(ecrnx_hw->wiphy, &chan_def, GFP_KERNEL);
+#endif
+#endif /* CONFIG_ECRNX_SOFTMAC */
+}
+
+static void ecrnx_radar_process_pulse(struct work_struct *ws)
+{
+ struct ecrnx_radar *radar = container_of(ws, struct ecrnx_radar,
+ detection_work);
+ struct ecrnx_hw *ecrnx_hw = container_of(radar, struct ecrnx_hw, radar);
+ int chain;
+ u32 pulses[ECRNX_RADAR_LAST][ECRNX_RADAR_PULSE_MAX];
+ u16 pulses_count[ECRNX_RADAR_LAST];
+ u32 now = jiffies; /* would be better to store jiffies value in IT handler */
+
+ /* recopy pulses locally to avoid too long spin_lock */
+ spin_lock_bh(&radar->lock);
+ for (chain = ECRNX_RADAR_RIU; chain < ECRNX_RADAR_LAST; chain++) {
+ int start, count;
+
+ count = radar->pulses[chain].count;
+ start = radar->pulses[chain].index - count;
+ if (start < 0)
+ start += ECRNX_RADAR_PULSE_MAX;
+
+ pulses_count[chain] = count;
+ if (count == 0)
+ continue;
+
+ if ((start + count) > ECRNX_RADAR_PULSE_MAX) {
+ u16 count1 = (ECRNX_RADAR_PULSE_MAX - start);
+ memcpy(&(pulses[chain][0]),
+ &(radar->pulses[chain].buffer[start]),
+ count1 * sizeof(struct radar_pulse));
+ memcpy(&(pulses[chain][count1]),
+ &(radar->pulses[chain].buffer[0]),
+ (count - count1) * sizeof(struct radar_pulse));
+ } else {
+ memcpy(&(pulses[chain][0]),
+ &(radar->pulses[chain].buffer[start]),
+ count * sizeof(struct radar_pulse));
+ }
+ radar->pulses[chain].count = 0;
+ }
+ spin_unlock_bh(&radar->lock);
+
+
+ /* now process pulses */
+ for (chain = ECRNX_RADAR_RIU; chain < ECRNX_RADAR_LAST; chain++) {
+ int i;
+ u16 freq;
+
+ if (pulses_count[chain] == 0)
+ continue;
+
+ freq = ecrnx_radar_get_center_freq(ecrnx_hw, chain);
+
+ for (i = 0; i < pulses_count[chain] ; i++) {
+ struct radar_pulse *p = (struct radar_pulse *)&pulses[chain][i];
+ trace_radar_pulse(chain, p);
+ if (dfs_pattern_detector_add_pulse(radar->dpd[chain], chain,
+ (s16)freq + (2 * p->freq),
+ p->rep, (p->len * 2), now)) {
+ u16 idx = radar->detected[chain].index;
+
+ if (chain == ECRNX_RADAR_RIU) {
+ /* operating chain, inform upper layer to change channel */
+ if (radar->dpd[chain]->enabled == ECRNX_RADAR_DETECT_REPORT) {
+ ecrnx_radar_detected(ecrnx_hw);
+ /* no need to report new radar until upper layer set a
+ new channel. This prevent warning if a new radar is
+ detected while mac80211 is changing channel */
+ ecrnx_radar_detection_enable(radar,
+ ECRNX_RADAR_DETECT_DISABLE,
+ chain);
+ /* purge any event received since the beginning of the
+ function (we are sure not to interfer with tasklet
+ as we disable detection just before) */
+ radar->pulses[chain].count = 0;
+ }
+ } else {
+ /* secondary radar detection chain, simply report info in
+ debugfs for now */
+ }
+
+ radar->detected[chain].freq[idx] = (s16)freq + (2 * p->freq);
+ radar->detected[chain].time[idx] = ktime_get_real_seconds();
+ radar->detected[chain].index = ((idx + 1 ) %
+ NX_NB_RADAR_DETECTED);
+ radar->detected[chain].count++;
+ /* no need to process next pulses for this chain */
+ break;
+ }
+ }
+ }
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+static void ecrnx_radar_cac_work(struct work_struct *ws)
+{
+ struct delayed_work *dw = container_of(ws, struct delayed_work, work);
+ struct ecrnx_radar *radar = container_of(dw, struct ecrnx_radar, cac_work);
+ struct ecrnx_hw *ecrnx_hw = container_of(radar, struct ecrnx_hw, radar);
+ struct ecrnx_chanctx *ctxt;
+
+ if (radar->cac_vif == NULL) {
+ WARN(1, "CAC finished but no vif set");
+ return;
+ }
+
+ ctxt = &ecrnx_hw->chanctx_table[radar->cac_vif->ch_index];
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ cfg80211_cac_event(radar->cac_vif->ndev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ &ctxt->chan_def,
+#endif
+ NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+#endif
+ ecrnx_send_apm_stop_cac_req(ecrnx_hw, radar->cac_vif);
+ ecrnx_chanctx_unlink(radar->cac_vif);
+
+ radar->cac_vif = NULL;
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+bool ecrnx_radar_detection_init(struct ecrnx_radar *radar)
+{
+ spin_lock_init(&radar->lock);
+
+ radar->dpd[ECRNX_RADAR_RIU] = dfs_pattern_detector_init(NL80211_DFS_UNSET,
+ ECRNX_RADAR_RIU);
+ if (radar->dpd[ECRNX_RADAR_RIU] == NULL)
+ return false;
+
+ radar->dpd[ECRNX_RADAR_FCU] = dfs_pattern_detector_init(NL80211_DFS_UNSET,
+ ECRNX_RADAR_FCU);
+ if (radar->dpd[ECRNX_RADAR_FCU] == NULL) {
+ ecrnx_radar_detection_deinit(radar);
+ return false;
+ }
+
+ INIT_WORK(&radar->detection_work, ecrnx_radar_process_pulse);
+#ifdef CONFIG_ECRNX_FULLMAC
+ INIT_DELAYED_WORK(&radar->cac_work, ecrnx_radar_cac_work);
+ radar->cac_vif = NULL;
+#endif /* CONFIG_ECRNX_FULLMAC */
+ return true;
+}
+
+void ecrnx_radar_detection_deinit(struct ecrnx_radar *radar)
+{
+ if (radar->dpd[ECRNX_RADAR_RIU]) {
+ dfs_pattern_detector_exit(radar->dpd[ECRNX_RADAR_RIU]);
+ radar->dpd[ECRNX_RADAR_RIU] = NULL;
+ }
+ if (radar->dpd[ECRNX_RADAR_FCU]) {
+ dfs_pattern_detector_exit(radar->dpd[ECRNX_RADAR_FCU]);
+ radar->dpd[ECRNX_RADAR_FCU] = NULL;
+ }
+}
+
+bool ecrnx_radar_set_domain(struct ecrnx_radar *radar,
+ enum nl80211_dfs_regions region)
+{
+ if (radar->dpd[0] == NULL)
+ return false;
+
+ trace_radar_set_region(region);
+
+ return (dfs_pattern_detector_set_domain(radar->dpd[ECRNX_RADAR_RIU],
+ region, ECRNX_RADAR_RIU) &&
+ dfs_pattern_detector_set_domain(radar->dpd[ECRNX_RADAR_FCU],
+ region, ECRNX_RADAR_FCU));
+}
+
+void ecrnx_radar_detection_enable(struct ecrnx_radar *radar, u8 enable, u8 chain)
+{
+ if (chain < ECRNX_RADAR_LAST ) {
+ trace_radar_enable_detection(radar->dpd[chain]->region, enable, chain);
+ spin_lock_bh(&radar->lock);
+ radar->dpd[chain]->enabled = enable;
+ spin_unlock_bh(&radar->lock);
+ }
+}
+
+bool ecrnx_radar_detection_is_enable(struct ecrnx_radar *radar, u8 chain)
+{
+ return radar->dpd[chain]->enabled != ECRNX_RADAR_DETECT_DISABLE;
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+void ecrnx_radar_start_cac(struct ecrnx_radar *radar, u32 cac_time_ms,
+ struct ecrnx_vif *vif)
+{
+ WARN(radar->cac_vif != NULL, "CAC already in progress");
+ radar->cac_vif = vif;
+ schedule_delayed_work(&radar->cac_work, msecs_to_jiffies(cac_time_ms));
+}
+
+void ecrnx_radar_cancel_cac(struct ecrnx_radar *radar)
+{
+ struct ecrnx_hw *ecrnx_hw = container_of(radar, struct ecrnx_hw, radar);
+
+ if (radar->cac_vif == NULL) {
+ return;
+ }
+
+ if (cancel_delayed_work(&radar->cac_work)) {
+ struct ecrnx_chanctx *ctxt;
+ ctxt = &ecrnx_hw->chanctx_table[radar->cac_vif->ch_index];
+ ecrnx_send_apm_stop_cac_req(ecrnx_hw, radar->cac_vif);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ cfg80211_cac_event(radar->cac_vif->ndev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ &ctxt->chan_def,
+#endif
+ NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
+#endif
+ ecrnx_chanctx_unlink(radar->cac_vif);
+ }
+
+ radar->cac_vif = NULL;
+}
+
+void ecrnx_radar_detection_enable_on_cur_channel(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_chanctx *ctxt;
+
+ /* If no information on current channel do nothing */
+ if (!ecrnx_chanctx_valid(ecrnx_hw, ecrnx_hw->cur_chanctx))
+ return;
+
+ ctxt = &ecrnx_hw->chanctx_table[ecrnx_hw->cur_chanctx];
+ if (ctxt->chan_def.chan->flags & IEEE80211_CHAN_RADAR) {
+ ecrnx_radar_detection_enable(&ecrnx_hw->radar,
+ ECRNX_RADAR_DETECT_REPORT,
+ ECRNX_RADAR_RIU);
+ } else {
+ ecrnx_radar_detection_enable(&ecrnx_hw->radar,
+ ECRNX_RADAR_DETECT_DISABLE,
+ ECRNX_RADAR_RIU);
+ }
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/*****************************************************************************
+ * Debug functions
+ *****************************************************************************/
+static
+int ecrnx_radar_dump_pri_detector(char *buf, size_t len,
+ struct pri_detector *pde)
+{
+ char freq_info[] = "Freq = %3.dMhz\n";
+ char seq_info[] = " pri | count | false \n";
+ struct pri_sequence *seq;
+ int res, write = 0;
+
+ if (list_empty(&pde->sequences)) {
+ return 0;
+ }
+
+ if (buf == NULL) {
+ int nb_seq = 1;
+ list_for_each_entry(seq, &pde->sequences, head) {
+ nb_seq++;
+ }
+
+ return (sizeof(freq_info) + nb_seq * sizeof(seq_info));
+ }
+
+ res = scnprintf(buf, len, freq_info, pde->freq);
+ write += res;
+ len -= res;
+
+ res = scnprintf(&buf[write], len, "%s", seq_info);
+ write += res;
+ len -= res;
+
+ list_for_each_entry(seq, &pde->sequences, head) {
+ res = scnprintf(&buf[write], len, " %6.d | %2.d | %.2d \n",
+ seq->pri, seq->count, seq->count_falses);
+ write += res;
+ len -= res;
+ }
+
+ return write;
+}
+
+int ecrnx_radar_dump_pattern_detector(char *buf, size_t len,
+ struct ecrnx_radar *radar, u8 chain)
+{
+ struct dfs_pattern_detector *dpd = radar->dpd[chain];
+ char info[] = "Type = %3.d\n";
+ struct pri_detector *pde;
+ int i, res, write = 0;
+
+ /* if buf is NULL return size needed for dump */
+ if (buf == NULL) {
+ int size_needed = 0;
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ list_for_each_entry(pde, &dpd->detectors[i], head) {
+ size_needed += ecrnx_radar_dump_pri_detector(NULL, 0, pde);
+ }
+ size_needed += sizeof(info);
+
+ return size_needed;
+ }
+ }
+
+ /* */
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ res = scnprintf(&buf[write], len, info, i);
+
+ write += res;
+ len -= res;
+ list_for_each_entry(pde, &dpd->detectors[i], head) {
+ res = ecrnx_radar_dump_pri_detector(&buf[write], len, pde);
+ write += res;
+ len -= res;
+ }
+ }
+
+ return write;
+}
+
+
+int ecrnx_radar_dump_radar_detected(char *buf, size_t len,
+ struct ecrnx_radar *radar, u8 chain)
+{
+ struct ecrnx_radar_detected *detect = &(radar->detected[chain]);
+ char info[] = "2001/02/02 - 02:20 5126MHz\n";
+ int idx, i, res, write = 0;
+ int count = detect->count;
+
+ if (count > NX_NB_RADAR_DETECTED)
+ count = NX_NB_RADAR_DETECTED;
+
+ if (buf == NULL) {
+ return (count * sizeof(info)) + 1;
+ }
+
+ idx = (detect->index - detect->count) % NX_NB_RADAR_DETECTED;
+
+ for (i = 0; i < count; i++) {
+ struct tm tm;
+ time64_to_tm(detect->time[idx], 0, &tm);
+
+ res = scnprintf(&buf[write], len,
+ "%.4d/%.2d/%.2d - %.2d:%.2d %4.4dMHz\n",
+ (int)tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, detect->freq[idx]);
+ write += res;
+ len -= res;
+
+ idx = (idx + 1 ) % NX_NB_RADAR_DETECTED;
+ }
+
+ return write;
+}