1645 lines
47 KiB
C
1645 lines
47 KiB
C
/**
|
|
******************************************************************************
|
|
*
|
|
* @file rwnx_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) RivieraWaves 2015-2019
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
#include <linux/list.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/ktime.h>
|
|
#include <net/mac80211.h>
|
|
|
|
#include "rwnx_radar.h"
|
|
#include "rwnx_defs.h"
|
|
#include "rwnx_msg_tx.h"
|
|
#include "rwnx_events.h"
|
|
#include "rwnx_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 rwnx_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) {
|
|
#ifdef CREATE_TRACE_POINTS
|
|
trace_radar_detected(chain, dpd->region, pde->freq, i, ps->pri);
|
|
#endif
|
|
// 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 == RWNX_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 = RWNX_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 rwnx_radar_get_center_freq(struct rwnx_hw *rwnx_hw, u8 chain)
|
|
{
|
|
if (chain == RWNX_RADAR_FCU)
|
|
return rwnx_hw->phy.sec_chan.center_freq1;
|
|
|
|
if (chain == RWNX_RADAR_RIU) {
|
|
#ifdef CONFIG_RWNX_FULLMAC
|
|
if (!rwnx_chanctx_valid(rwnx_hw, rwnx_hw->cur_chanctx)) {
|
|
WARN(1, "Radar pulse without channel information");
|
|
} else
|
|
return rwnx_hw->chanctx_table[rwnx_hw->cur_chanctx].chan_def.center_freq1;
|
|
#endif /* CONFIG_RWNX_FULLMAC */
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rwnx_radar_detected(struct rwnx_hw *rwnx_hw)
|
|
{
|
|
#ifdef CONFIG_RWNX_FULLMAC
|
|
struct cfg80211_chan_def chan_def;
|
|
|
|
if (!rwnx_chanctx_valid(rwnx_hw, rwnx_hw->cur_chanctx)) {
|
|
WARN(1, "Radar detected without channel information");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
recopy chan_def in local variable because rwnx_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 = rwnx_hw->chanctx_table[rwnx_hw->cur_chanctx].chan_def;
|
|
|
|
rwnx_radar_cancel_cac(&rwnx_hw->radar);
|
|
cfg80211_radar_event(rwnx_hw->wiphy, &chan_def, GFP_KERNEL);
|
|
|
|
#endif /* CONFIG_RWNX_FULLMAC */
|
|
}
|
|
|
|
static void rwnx_radar_process_pulse(struct work_struct *ws)
|
|
{
|
|
struct rwnx_radar *radar = container_of(ws, struct rwnx_radar,
|
|
detection_work);
|
|
struct rwnx_hw *rwnx_hw = container_of(radar, struct rwnx_hw, radar);
|
|
int chain;
|
|
u32 pulses[RWNX_RADAR_LAST][RWNX_RADAR_PULSE_MAX];
|
|
u16 pulses_count[RWNX_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 = RWNX_RADAR_RIU; chain < RWNX_RADAR_LAST; chain++) {
|
|
int start, count;
|
|
|
|
count = radar->pulses[chain].count;
|
|
start = radar->pulses[chain].index - count;
|
|
if (start < 0)
|
|
start += RWNX_RADAR_PULSE_MAX;
|
|
|
|
pulses_count[chain] = count;
|
|
if (count == 0)
|
|
continue;
|
|
|
|
if ((start + count) > RWNX_RADAR_PULSE_MAX) {
|
|
u16 count1 = (RWNX_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 = RWNX_RADAR_RIU; chain < RWNX_RADAR_LAST; chain++) {
|
|
int i;
|
|
u16 freq;
|
|
|
|
if (pulses_count[chain] == 0)
|
|
continue;
|
|
|
|
freq = rwnx_radar_get_center_freq(rwnx_hw, chain);
|
|
|
|
for (i = 0; i < pulses_count[chain] ; i++) {
|
|
struct radar_pulse *p = (struct radar_pulse *)&pulses[chain][i];
|
|
#ifdef CREATE_TRACE_POINTS
|
|
trace_radar_pulse(chain, p);
|
|
#endif
|
|
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 == RWNX_RADAR_RIU) {
|
|
/* operating chain, inform upper layer to change channel */
|
|
if (radar->dpd[chain]->enabled == RWNX_RADAR_DETECT_REPORT) {
|
|
rwnx_radar_detected(rwnx_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 */
|
|
rwnx_radar_detection_enable(radar,
|
|
RWNX_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_RWNX_FULLMAC
|
|
static void rwnx_radar_cac_work(struct work_struct *ws)
|
|
{
|
|
struct delayed_work *dw = container_of(ws, struct delayed_work, work);
|
|
struct rwnx_radar *radar = container_of(dw, struct rwnx_radar, cac_work);
|
|
struct rwnx_hw *rwnx_hw = container_of(radar, struct rwnx_hw, radar);
|
|
struct rwnx_chanctx *ctxt;
|
|
|
|
if (radar->cac_vif == NULL) {
|
|
WARN(1, "CAC finished but no vif set");
|
|
return;
|
|
}
|
|
|
|
ctxt = &rwnx_hw->chanctx_table[radar->cac_vif->ch_index];
|
|
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);
|
|
rwnx_send_apm_stop_cac_req(rwnx_hw, radar->cac_vif);
|
|
rwnx_chanctx_unlink(radar->cac_vif);
|
|
|
|
radar->cac_vif = NULL;
|
|
}
|
|
#endif /* CONFIG_RWNX_FULLMAC */
|
|
|
|
bool rwnx_radar_detection_init(struct rwnx_radar *radar)
|
|
{
|
|
spin_lock_init(&radar->lock);
|
|
|
|
radar->dpd[RWNX_RADAR_RIU] = dfs_pattern_detector_init(NL80211_DFS_UNSET,
|
|
RWNX_RADAR_RIU);
|
|
if (radar->dpd[RWNX_RADAR_RIU] == NULL)
|
|
return false;
|
|
|
|
radar->dpd[RWNX_RADAR_FCU] = dfs_pattern_detector_init(NL80211_DFS_UNSET,
|
|
RWNX_RADAR_FCU);
|
|
if (radar->dpd[RWNX_RADAR_FCU] == NULL) {
|
|
rwnx_radar_detection_deinit(radar);
|
|
return false;
|
|
}
|
|
|
|
INIT_WORK(&radar->detection_work, rwnx_radar_process_pulse);
|
|
#ifdef CONFIG_RWNX_FULLMAC
|
|
INIT_DELAYED_WORK(&radar->cac_work, rwnx_radar_cac_work);
|
|
radar->cac_vif = NULL;
|
|
#endif /* CONFIG_RWNX_FULLMAC */
|
|
return true;
|
|
}
|
|
|
|
void rwnx_radar_detection_deinit(struct rwnx_radar *radar)
|
|
{
|
|
if (radar->dpd[RWNX_RADAR_RIU]) {
|
|
dfs_pattern_detector_exit(radar->dpd[RWNX_RADAR_RIU]);
|
|
radar->dpd[RWNX_RADAR_RIU] = NULL;
|
|
}
|
|
if (radar->dpd[RWNX_RADAR_FCU]) {
|
|
dfs_pattern_detector_exit(radar->dpd[RWNX_RADAR_FCU]);
|
|
radar->dpd[RWNX_RADAR_FCU] = NULL;
|
|
}
|
|
}
|
|
|
|
bool rwnx_radar_set_domain(struct rwnx_radar *radar,
|
|
enum nl80211_dfs_regions region)
|
|
{
|
|
if (radar->dpd[0] == NULL)
|
|
return false;
|
|
#ifdef CREATE_TRACE_POINTS
|
|
trace_radar_set_region(region);
|
|
#endif
|
|
return (dfs_pattern_detector_set_domain(radar->dpd[RWNX_RADAR_RIU],
|
|
region, RWNX_RADAR_RIU) &&
|
|
dfs_pattern_detector_set_domain(radar->dpd[RWNX_RADAR_FCU],
|
|
region, RWNX_RADAR_FCU));
|
|
}
|
|
|
|
void rwnx_radar_detection_enable(struct rwnx_radar *radar, u8 enable, u8 chain)
|
|
{
|
|
if (chain < RWNX_RADAR_LAST) {
|
|
#ifdef CREATE_TRACE_POINTS
|
|
trace_radar_enable_detection(radar->dpd[chain]->region, enable, chain);
|
|
#endif
|
|
spin_lock_bh(&radar->lock);
|
|
radar->dpd[chain]->enabled = enable;
|
|
spin_unlock_bh(&radar->lock);
|
|
}
|
|
}
|
|
|
|
bool rwnx_radar_detection_is_enable(struct rwnx_radar *radar, u8 chain)
|
|
{
|
|
return radar->dpd[chain]->enabled != RWNX_RADAR_DETECT_DISABLE;
|
|
}
|
|
|
|
#ifdef CONFIG_RWNX_FULLMAC
|
|
void rwnx_radar_start_cac(struct rwnx_radar *radar, u32 cac_time_ms,
|
|
struct rwnx_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 rwnx_radar_cancel_cac(struct rwnx_radar *radar)
|
|
{
|
|
struct rwnx_hw *rwnx_hw = container_of(radar, struct rwnx_hw, radar);
|
|
|
|
if (radar->cac_vif == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (cancel_delayed_work(&radar->cac_work)) {
|
|
struct rwnx_chanctx *ctxt;
|
|
ctxt = &rwnx_hw->chanctx_table[radar->cac_vif->ch_index];
|
|
rwnx_send_apm_stop_cac_req(rwnx_hw, radar->cac_vif);
|
|
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);
|
|
rwnx_chanctx_unlink(radar->cac_vif);
|
|
}
|
|
|
|
radar->cac_vif = NULL;
|
|
}
|
|
|
|
void rwnx_radar_detection_enable_on_cur_channel(struct rwnx_hw *rwnx_hw)
|
|
{
|
|
struct rwnx_chanctx *ctxt;
|
|
|
|
/* If no information on current channel do nothing */
|
|
if (!rwnx_chanctx_valid(rwnx_hw, rwnx_hw->cur_chanctx))
|
|
return;
|
|
|
|
ctxt = &rwnx_hw->chanctx_table[rwnx_hw->cur_chanctx];
|
|
if (ctxt->chan_def.chan->flags & IEEE80211_CHAN_RADAR) {
|
|
rwnx_radar_detection_enable(&rwnx_hw->radar,
|
|
RWNX_RADAR_DETECT_REPORT,
|
|
RWNX_RADAR_RIU);
|
|
} else {
|
|
rwnx_radar_detection_enable(&rwnx_hw->radar,
|
|
RWNX_RADAR_DETECT_DISABLE,
|
|
RWNX_RADAR_RIU);
|
|
}
|
|
}
|
|
#endif /* CONFIG_RWNX_FULLMAC */
|
|
|
|
/*****************************************************************************
|
|
* Debug functions
|
|
*****************************************************************************/
|
|
static
|
|
int rwnx_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 rwnx_radar_dump_pattern_detector(char *buf, size_t len,
|
|
struct rwnx_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 += rwnx_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 = rwnx_radar_dump_pri_detector(&buf[write], len, pde);
|
|
write += res;
|
|
len -= res;
|
|
}
|
|
}
|
|
|
|
return write;
|
|
}
|
|
|
|
|
|
int rwnx_radar_dump_radar_detected(char *buf, size_t len,
|
|
struct rwnx_radar *radar, u8 chain)
|
|
{
|
|
struct rwnx_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;
|
|
}
|