mirror of
https://github.com/shenmintao/aic8800d80.git
synced 2026-11-10 16:49:17 +00:00
Allow CFG80211_VERSION to select wireless API signatures independently of the target kernel. Keep native kernel builds on their existing version checks. Also guard the WEXT-only label and replace overlapping firmware path formatting with bounded appends so OpenWrt builds pass with Werror. Validated all three modules against OpenWrt Linux 6.12.94 with backports 6.18.26, plus native 6.11, 6.17 and 7.2 headers. Refs #94.
1767 lines
55 KiB
C
1767 lines
55 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
|
|
//JP_PATTERN(1, 2, 8, 3846, 3846, 1, 18, RADAR_WAVEFORM_SHORT),
|
|
static const struct radar_detector_specs jp_radar_ref_types_riu[] = {
|
|
JP_PATTERN(0, 0, 4, 1388, 1388, 1, 18, RADAR_WAVEFORM_SHORT),
|
|
JP_PATTERN(1, 0, 4, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
|
|
JP_PATTERN(2, 0, 4, 4000, 4000, 1, 18, RADAR_WAVEFORM_SHORT),
|
|
JP_PATTERN(3, 0, 6, 150, 230, 1, 23, RADAR_WAVEFORM_SHORT),
|
|
JP_PATTERN(4, 6, 12, 200, 500, 1, 16, RADAR_WAVEFORM_SHORT),
|
|
JP_PATTERN(5, 10, 22, 200, 500, 1, 12, RADAR_WAVEFORM_SHORT),
|
|
JP_PATTERN(6, 48, 110, 1000, 2000, 1, 8, RADAR_WAVEFORM_LONG),
|
|
JP_PATTERN(7, 0, 4, 333, 333, 1, 9, RADAR_WAVEFORM_SHORT),
|
|
};
|
|
|
|
static const struct radar_detector_specs jp_radar_ref_types_fcu[] = {
|
|
JP_PATTERN(0, 0, 4, 1388, 1388, 1, 18, RADAR_WAVEFORM_SHORT),
|
|
JP_PATTERN(1, 0, 4, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
|
|
JP_PATTERN(2, 0, 4, 4000, 4000, 1, 18, RADAR_WAVEFORM_SHORT),
|
|
JP_PATTERN(3, 0, 6, 150, 230, 1, 23, RADAR_WAVEFORM_SHORT),
|
|
JP_PATTERN(4, 6, 12, 200, 500, 1, 16, RADAR_WAVEFORM_SHORT),
|
|
JP_PATTERN(5, 10, 22, 200, 500, 1, 12, RADAR_WAVEFORM_SHORT),
|
|
JP_PATTERN(6, 48, 110, 1000, 2000, 1, 8, RADAR_WAVEFORM_LONG),
|
|
JP_PATTERN(7, 0, 4, 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 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 rwnx_radar *radar, 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 rwnx_radar *radar, 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 rwnx_radar *radar, struct pri_detector *pde,
|
|
u16 len, u64 ts, u16 pri)
|
|
{
|
|
struct pri_sequence *ps;
|
|
const struct radar_detector_specs *rs = pde->rs;
|
|
|
|
//if(radar->status != RWNX_RADAR_CAC_BUSY) {
|
|
// return NULL;
|
|
//}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void print_radar_detect_info(struct pri_detector *pde, struct pri_sequence *ps)
|
|
{
|
|
struct radar_detector_specs *rs = (struct radar_detector_specs *)pde->rs;
|
|
struct pulse_elem *p;
|
|
u16 idx = 0;
|
|
|
|
AICWFDBG(LOGINFO, "aic dfs detected: %d, %d, %d\n", pde->window_size, pde->count, pde->max_count);
|
|
AICWFDBG(LOGINFO, "\t rs: %u %u\n", rs->type, rs->type_id);
|
|
AICWFDBG(LOGINFO, "\t rs: width[%u, %u]\n", rs->width_min, rs->width_max);
|
|
AICWFDBG(LOGINFO, "\t rs: pri[%u, %u] %u\n", rs->pri_min, rs->pri_max, rs->num_pri);
|
|
AICWFDBG(LOGINFO, "\t rs: %u/%u %u\n", rs->ppb, rs->ppb_thresh, rs->max_pri_tolerance);
|
|
|
|
AICWFDBG(LOGINFO, "\t ps.pri = %u\n" , ps->pri );
|
|
AICWFDBG(LOGINFO, "\t ps.dur = %u\n" , ps->dur );
|
|
AICWFDBG(LOGINFO, "\t ps.count = %u\n" , ps->count );
|
|
AICWFDBG(LOGINFO, "\t ps.count_falses = %u\n" , ps->count_falses );
|
|
AICWFDBG(LOGINFO, "\t ps.first_ts = %lu\n", (long unsigned int)ps->first_ts );
|
|
AICWFDBG(LOGINFO, "\t ps.last_ts = %lu\n", (long unsigned int)ps->last_ts );
|
|
AICWFDBG(LOGINFO, "\t ps.deadline_ts = %lu\n", (long unsigned int)ps->deadline_ts );
|
|
AICWFDBG(LOGINFO, "\t ps.ppb_thresh = %u\n" , ps->ppb_thresh );
|
|
|
|
AICWFDBG(LOGINFO, "\t pulses -->\n");
|
|
list_for_each_entry_reverse(p, &pde->pulses, head) {
|
|
AICWFDBG(LOGINFO, "\t %u: %lu\n", idx, (long unsigned int)p->ts);
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* 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 rwnx_radar *radar, 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(radar, pde, len, dpd->last_pulse_ts, pri);
|
|
|
|
if (ps != NULL) {
|
|
print_radar_detect_info(pde, ps);
|
|
#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;
|
|
|
|
AICWFDBG(LOGINFO,"set_region %d \n",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;
|
|
|
|
RWNX_DBG(RWNX_FN_ENTRY_STR);
|
|
|
|
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 */
|
|
#ifdef RWNX_RADAR_DUMP_EN
|
|
struct rwnx_radar_dump *rm = radar->rmem;
|
|
#endif
|
|
|
|
/* 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
|
|
#ifdef RWNX_RADAR_DUMP_EN
|
|
if(chain == RWNX_RADAR_RIU) {
|
|
u16 pri;
|
|
struct dfs_pattern_detector *dpd = NULL;
|
|
rm->ps[rm->idx] = pulses[chain][i];
|
|
rm->tm[rm->idx] = now;
|
|
rm->cnt ++;
|
|
|
|
pri = p->rep;
|
|
dpd = radar->dpd[chain];
|
|
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;
|
|
}
|
|
rm->ts[rm->idx] = dpd->last_pulse_ts + jiffies_to_usecs(delta_jiffie);
|
|
} else {
|
|
rm->ts[rm->idx] = dpd->last_pulse_ts + pri;
|
|
}
|
|
rm->idx = (rm->idx + 1) % RWNX_RADARR_DUMP_NB;
|
|
|
|
if(rm->cnt == 1)
|
|
{
|
|
struct radar_detector_specs *spc;
|
|
int k;
|
|
AICWFDBG(LOGINFO, "dpd: en = %d, region = %d, ntype = %d", dpd->enabled, dpd->region, dpd->num_radar_types);
|
|
AICWFDBG(LOGINFO, "id wid.{min, max} pri.{min, max, num}, ppb, thd, tlrn, type");
|
|
for (k = 0; k < dpd->num_radar_types; k++)
|
|
{
|
|
spc =(struct radar_detector_specs *)&dpd->radar_spec[k];
|
|
AICWFDBG(LOGINFO, "%d %3d, %3d %4d %4d %d %2d %2d %d %d",
|
|
spc->type_id,
|
|
spc->width_min,
|
|
spc->width_max,
|
|
spc->pri_min,
|
|
spc->pri_max,
|
|
spc->num_pri,
|
|
spc->ppb,
|
|
spc->ppb_thresh,
|
|
spc->max_pri_tolerance,
|
|
spc->type);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if((radar->status != RWNX_RADAR_CAC_BUSY) && (radar->status != RWNX_RADAR_INSERVICE_BUSY)) {
|
|
break;
|
|
}
|
|
AICWFDBG(LOGTRACE, "+r:%x\n", *(uint32_t *)p);
|
|
if (dfs_pattern_detector_add_pulse(radar, radar->dpd[chain], chain,
|
|
(s16)freq + (2 * p->freq),
|
|
p->rep, (p->len * 2), now)) {
|
|
|
|
u16 idx = radar->detected[chain].index;
|
|
|
|
#ifdef RWNX_RADAR_DUMP_EN
|
|
if (chain == RWNX_RADAR_RIU) {
|
|
int k;
|
|
AICWFDBG(LOGINFO, "\t received pulses %d %d\n", rm->cnt, rm->idx-1);
|
|
for(k=0; k<RWNX_RADARR_DUMP_NB; k++) {
|
|
AICWFDBG(LOGINFO, "\t %2d: %8x %10llu %10llu\n", rm->idx, rm->ps[rm->idx], rm->ts[rm->idx], rm->tm[rm->idx]);
|
|
rm->idx = (rm->idx + 1) % RWNX_RADARR_DUMP_NB;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
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
|
|
#if AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
|
NL80211_RADAR_CAC_FINISHED, GFP_KERNEL, 0);
|
|
#else
|
|
NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
|
|
#endif
|
|
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 */
|
|
|
|
|
|
void rwnx_radar_reset_rmem(struct rwnx_radar *radar)
|
|
{
|
|
#ifdef RWNX_RADAR_DUMP_EN
|
|
if (radar->rmem != NULL) {
|
|
memset((void *)radar->rmem, 0, sizeof(*radar->rmem));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void rwnx_radar_init_rmem(struct rwnx_radar *radar)
|
|
{
|
|
#ifdef RWNX_RADAR_DUMP_EN
|
|
if (radar->rmem == NULL) {
|
|
radar->rmem = kmalloc(sizeof(*radar->rmem), GFP_ATOMIC);
|
|
}
|
|
rwnx_radar_reset_rmem(radar);
|
|
#endif
|
|
}
|
|
|
|
void rwnx_radar_deinit_rmem(struct rwnx_radar *radar)
|
|
{
|
|
#ifdef RWNX_RADAR_DUMP_EN
|
|
if (radar->rmem != NULL) {
|
|
kfree(radar->rmem);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
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);
|
|
rwnx_radar_init_rmem(radar);
|
|
radar->status = RWNX_RADAR_IDLE;
|
|
AICWFDBG(LOGINFO, "DFS: radar st = %d\n", radar->status);
|
|
|
|
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;
|
|
}
|
|
rwnx_radar_deinit_rmem(radar);
|
|
}
|
|
|
|
bool rwnx_radar_set_domain(struct rwnx_radar *radar,
|
|
enum nl80211_dfs_regions region)
|
|
{
|
|
AICWFDBG(LOGINFO, "%s \n",__func__);
|
|
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)
|
|
{
|
|
//printk("%s enable:%u chain:%u \n",__func__,enable,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;
|
|
AICWFDBG(LOGINFO, "%s DFS: cac time = %u ms", __func__, cac_time_ms);
|
|
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
|
|
#if AICWF_CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
|
NL80211_RADAR_CAC_FINISHED, GFP_KERNEL, 0);
|
|
#else
|
|
NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
|
|
#endif
|
|
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;
|
|
}
|