ath9k/ath: move dfs pattern detector to ath
authorJanusz Dziedzic <janusz.dziedzic@tieto.com>
Mon, 14 Oct 2013 09:06:06 +0000 (11:06 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 18 Oct 2013 18:03:54 +0000 (14:03 -0400)
Move the DFS pattern detector code to the ath module so
the other Atheros drivers can make us of it. This makes
no functional changes.

Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
Reviewed-by: Luis R. Rodriguez <mcgrof@do-not-panic.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
12 files changed:
drivers/net/wireless/ath/Makefile
drivers/net/wireless/ath/ath9k/Makefile
drivers/net/wireless/ath/ath9k/dfs.h
drivers/net/wireless/ath/ath9k/dfs_debug.c
drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c [deleted file]
drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h [deleted file]
drivers/net/wireless/ath/ath9k/dfs_pri_detector.c [deleted file]
drivers/net/wireless/ath/ath9k/dfs_pri_detector.h [deleted file]
drivers/net/wireless/ath/dfs_pattern_detector.c [new file with mode: 0644]
drivers/net/wireless/ath/dfs_pattern_detector.h [new file with mode: 0644]
drivers/net/wireless/ath/dfs_pri_detector.c [new file with mode: 0644]
drivers/net/wireless/ath/dfs_pri_detector.h [new file with mode: 0644]

index 363b056..7d023b0 100644 (file)
@@ -12,7 +12,9 @@ obj-$(CONFIG_ATH_COMMON)      += ath.o
 ath-objs :=    main.o \
                regd.o \
                hw.o \
-               key.o
+               key.o \
+               dfs_pattern_detector.o \
+               dfs_pri_detector.o
 
 ath-$(CONFIG_ATH_DEBUG) += debug.o
 ccflags-y += -D__CHECK_ENDIAN__
index 75ee9e7..6205ef5 100644 (file)
@@ -14,9 +14,7 @@ ath9k-$(CONFIG_ATH9K_AHB) += ahb.o
 ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
 ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o
 ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += \
-               dfs.o \
-               dfs_pattern_detector.o \
-               dfs_pri_detector.o
+               dfs.o
 ath9k-$(CONFIG_PM_SLEEP) += wow.o
 
 obj-$(CONFIG_ATH9K) += ath9k.o
index 3c839f0..c6fa3d5 100644 (file)
@@ -17,7 +17,7 @@
 
 #ifndef ATH9K_DFS_H
 #define ATH9K_DFS_H
-#include "dfs_pattern_detector.h"
+#include "../dfs_pattern_detector.h"
 
 #if defined(CONFIG_ATH9K_DFS_CERTIFIED)
 /**
index 990c376..90b8342 100644 (file)
@@ -20,7 +20,7 @@
 
 #include "ath9k.h"
 #include "dfs_debug.h"
-#include "dfs_pattern_detector.h"
+#include "../dfs_pattern_detector.h"
 
 static struct ath_dfs_pool_stats dfs_pool_stats = { 0 };
 
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c
deleted file mode 100644 (file)
index c230ffc..0000000
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright (c) 2012 Neratec Solutions AG
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/slab.h>
-#include <linux/export.h>
-
-#include "dfs_pattern_detector.h"
-#include "dfs_pri_detector.h"
-#include "../ath.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 *radar_types;
-};
-
-/* 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)
-/* percentage of pulse width tolerance */
-#define WIDTH_TOLERANCE 5
-#define WIDTH_LOWER(X) ((X*(100-WIDTH_TOLERANCE)+50)/100)
-#define WIDTH_UPPER(X) ((X*(100+WIDTH_TOLERANCE)+50)/100)
-
-#define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB)     \
-{                                                              \
-       ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),               \
-       (PRF2PRI(PMAX) - PRI_TOLERANCE),                        \
-       (PRF2PRI(PMIN) * PRF + PRI_TOLERANCE), PRF, PPB * PRF,  \
-       PPB_THRESH(PPB), PRI_TOLERANCE,                         \
-}
-
-/* radar types as defined by ETSI EN-301-893 v1.5.1 */
-static const struct radar_detector_specs etsi_radar_ref_types_v15[] = {
-       ETSI_PATTERN(0,  0,  1,  700,  700, 1, 18),
-       ETSI_PATTERN(1,  0,  5,  200, 1000, 1, 10),
-       ETSI_PATTERN(2,  0, 15,  200, 1600, 1, 15),
-       ETSI_PATTERN(3,  0, 15, 2300, 4000, 1, 25),
-       ETSI_PATTERN(4, 20, 30, 2000, 4000, 1, 20),
-       ETSI_PATTERN(5,  0,  2,  300,  400, 3, 10),
-       ETSI_PATTERN(6,  0,  2,  400, 1200, 3, 15),
-};
-
-static const struct radar_types etsi_radar_types_v15 = {
-       .region                 = NL80211_DFS_ETSI,
-       .num_radar_types        = ARRAY_SIZE(etsi_radar_ref_types_v15),
-       .radar_types            = etsi_radar_ref_types_v15,
-};
-
-/* for now, we support ETSI radar types, FCC and JP are TODO */
-static const struct radar_types *dfs_domains[] = {
-       &etsi_radar_types_v15,
-};
-
-/**
- * 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;
-}
-
-/**
- * struct channel_detector - detector elements for a DFS channel
- * @head: list_head
- * @freq: frequency for this channel detector in MHz
- * @detectors: array of dynamically created detector elements for this freq
- *
- * Channel detectors are required to provide multi-channel DFS detection, e.g.
- * to support off-channel scanning. A pattern detector has a list of channels
- * radar pulses have been reported for in the past.
- */
-struct channel_detector {
-       struct list_head head;
-       u16 freq;
-       struct pri_detector **detectors;
-};
-
-/* channel_detector_reset() - reset detector lines for a given channel */
-static void channel_detector_reset(struct dfs_pattern_detector *dpd,
-                                  struct channel_detector *cd)
-{
-       u32 i;
-       if (cd == NULL)
-               return;
-       for (i = 0; i < dpd->num_radar_types; i++)
-               cd->detectors[i]->reset(cd->detectors[i], dpd->last_pulse_ts);
-}
-
-/* channel_detector_exit() - destructor */
-static void channel_detector_exit(struct dfs_pattern_detector *dpd,
-                                 struct channel_detector *cd)
-{
-       u32 i;
-       if (cd == NULL)
-               return;
-       list_del(&cd->head);
-       for (i = 0; i < dpd->num_radar_types; i++) {
-               struct pri_detector *de = cd->detectors[i];
-               if (de != NULL)
-                       de->exit(de);
-       }
-       kfree(cd->detectors);
-       kfree(cd);
-}
-
-static struct channel_detector *
-channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq)
-{
-       u32 sz, i;
-       struct channel_detector *cd;
-
-       cd = kmalloc(sizeof(*cd), GFP_ATOMIC);
-       if (cd == NULL)
-               goto fail;
-
-       INIT_LIST_HEAD(&cd->head);
-       cd->freq = freq;
-       sz = sizeof(cd->detectors) * dpd->num_radar_types;
-       cd->detectors = kzalloc(sz, GFP_ATOMIC);
-       if (cd->detectors == NULL)
-               goto fail;
-
-       for (i = 0; i < dpd->num_radar_types; i++) {
-               const struct radar_detector_specs *rs = &dpd->radar_spec[i];
-               struct pri_detector *de = pri_detector_init(rs);
-               if (de == NULL)
-                       goto fail;
-               cd->detectors[i] = de;
-       }
-       list_add(&cd->head, &dpd->channel_detectors);
-       return cd;
-
-fail:
-       ath_dbg(dpd->common, DFS,
-               "failed to allocate channel_detector for freq=%d\n", freq);
-       channel_detector_exit(dpd, cd);
-       return NULL;
-}
-
-/**
- * channel_detector_get() - get channel detector for given frequency
- * @param dpd instance pointer
- * @param freq frequency in MHz
- * @return pointer to channel detector on success, NULL otherwise
- *
- * Return existing channel detector for the given frequency or return a
- * newly create one.
- */
-static struct channel_detector *
-channel_detector_get(struct dfs_pattern_detector *dpd, u16 freq)
-{
-       struct channel_detector *cd;
-       list_for_each_entry(cd, &dpd->channel_detectors, head) {
-               if (cd->freq == freq)
-                       return cd;
-       }
-       return channel_detector_create(dpd, freq);
-}
-
-/*
- * DFS Pattern Detector
- */
-
-/* dpd_reset(): reset all channel detectors */
-static void dpd_reset(struct dfs_pattern_detector *dpd)
-{
-       struct channel_detector *cd;
-       if (!list_empty(&dpd->channel_detectors))
-               list_for_each_entry(cd, &dpd->channel_detectors, head)
-                       channel_detector_reset(dpd, cd);
-
-}
-static void dpd_exit(struct dfs_pattern_detector *dpd)
-{
-       struct channel_detector *cd, *cd0;
-       if (!list_empty(&dpd->channel_detectors))
-               list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
-                       channel_detector_exit(dpd, cd);
-       kfree(dpd);
-}
-
-static bool
-dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event)
-{
-       u32 i;
-       struct channel_detector *cd;
-
-       /*
-        * 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;
-
-       cd = channel_detector_get(dpd, event->freq);
-       if (cd == NULL)
-               return false;
-
-       dpd->last_pulse_ts = event->ts;
-       /* reset detector on time stamp wraparound, caused by TSF reset */
-       if (event->ts < dpd->last_pulse_ts)
-               dpd_reset(dpd);
-
-       /* do type individual pattern matching */
-       for (i = 0; i < dpd->num_radar_types; i++) {
-               struct pri_detector *pd = cd->detectors[i];
-               struct pri_sequence *ps = pd->add_pulse(pd, event);
-               if (ps != NULL) {
-                       ath_dbg(dpd->common, DFS,
-                               "DFS: radar found on freq=%d: id=%d, pri=%d, "
-                               "count=%d, count_false=%d\n",
-                               event->freq, pd->rs->type_id,
-                               ps->pri, ps->count, ps->count_falses);
-                       channel_detector_reset(dpd, cd);
-                       return true;
-               }
-       }
-       return false;
-}
-
-static struct ath_dfs_pool_stats
-dpd_get_stats(struct dfs_pattern_detector *dpd)
-{
-       return global_dfs_pool_stats;
-}
-
-static bool dpd_set_domain(struct dfs_pattern_detector *dpd,
-                          enum nl80211_dfs_regions region)
-{
-       const struct radar_types *rt;
-       struct channel_detector *cd, *cd0;
-
-       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 channel detectors for previous DFS domain */
-       if (!list_empty(&dpd->channel_detectors))
-               list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
-                       channel_detector_exit(dpd, cd);
-       dpd->radar_spec = rt->radar_types;
-       dpd->num_radar_types = rt->num_radar_types;
-
-       dpd->region = region;
-       return true;
-}
-
-static struct dfs_pattern_detector default_dpd = {
-       .exit           = dpd_exit,
-       .set_dfs_domain = dpd_set_domain,
-       .add_pulse      = dpd_add_pulse,
-       .get_stats      = dpd_get_stats,
-       .region         = NL80211_DFS_UNSET,
-};
-
-struct dfs_pattern_detector *
-dfs_pattern_detector_init(struct ath_common *common,
-                         enum nl80211_dfs_regions region)
-{
-       struct dfs_pattern_detector *dpd;
-
-       if (!config_enabled(CONFIG_CFG80211_CERTIFICATION_ONUS))
-               return NULL;
-
-       dpd = kmalloc(sizeof(*dpd), GFP_KERNEL);
-       if (dpd == NULL)
-               return NULL;
-
-       *dpd = default_dpd;
-       INIT_LIST_HEAD(&dpd->channel_detectors);
-
-       dpd->common = common;
-       if (dpd->set_dfs_domain(dpd, region))
-               return dpd;
-
-       ath_dbg(common, DFS,"Could not set DFS domain to %d", region);
-       kfree(dpd);
-       return NULL;
-}
-EXPORT_SYMBOL(dfs_pattern_detector_init);
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h
deleted file mode 100644 (file)
index dde2652..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (c) 2012 Neratec Solutions AG
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef DFS_PATTERN_DETECTOR_H
-#define DFS_PATTERN_DETECTOR_H
-
-#include <linux/types.h>
-#include <linux/list.h>
-#include <linux/nl80211.h>
-
-/**
- * struct ath_dfs_pool_stats - DFS Statistics for global pools
- */
-struct ath_dfs_pool_stats {
-       u32 pool_reference;
-       u32 pulse_allocated;
-       u32 pulse_alloc_error;
-       u32 pulse_used;
-       u32 pseq_allocated;
-       u32 pseq_alloc_error;
-       u32 pseq_used;
-};
-
-/**
- * struct pulse_event - describing pulses reported by PHY
- * @ts: pulse time stamp in us
- * @freq: channel frequency in MHz
- * @width: pulse duration in us
- * @rssi: rssi of radar event
- */
-struct pulse_event {
-       u64 ts;
-       u16 freq;
-       u8 width;
-       u8 rssi;
-};
-
-/**
- * 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]
- */
-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;
-};
-
-/**
- * struct dfs_pattern_detector - DFS pattern detector
- * @exit(): destructor
- * @set_dfs_domain(): set DFS domain, resets detector lines upon domain changes
- * @add_pulse(): add radar pulse to detector, returns true on detection
- * @region: active DFS region, NL80211_DFS_UNSET until set
- * @num_radar_types: number of different radar types
- * @last_pulse_ts: time stamp of last valid pulse in usecs
- * @radar_detector_specs: array of radar detection specs
- * @channel_detectors: list connecting channel_detector elements
- */
-struct dfs_pattern_detector {
-       void (*exit)(struct dfs_pattern_detector *dpd);
-       bool (*set_dfs_domain)(struct dfs_pattern_detector *dpd,
-                          enum nl80211_dfs_regions region);
-       bool (*add_pulse)(struct dfs_pattern_detector *dpd,
-                         struct pulse_event *pe);
-
-       struct ath_dfs_pool_stats (*get_stats)(struct dfs_pattern_detector *dpd);
-       enum nl80211_dfs_regions region;
-       u8 num_radar_types;
-       u64 last_pulse_ts;
-       /* needed for ath_dbg() */
-       struct ath_common *common;
-
-       const struct radar_detector_specs *radar_spec;
-       struct list_head channel_detectors;
-};
-
-/**
- * dfs_pattern_detector_init() - constructor for pattern detector class
- * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation
- * @return instance pointer on success, NULL otherwise
- */
-extern struct dfs_pattern_detector *
-dfs_pattern_detector_init(struct ath_common *common,
-                         enum nl80211_dfs_regions region);
-#endif /* DFS_PATTERN_DETECTOR_H */
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c
deleted file mode 100644 (file)
index 17b5bf9..0000000
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
- * Copyright (c) 2012 Neratec Solutions AG
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-
-#include "../ath.h"
-#include "dfs_pattern_detector.h"
-#include "dfs_pri_detector.h"
-
-struct ath_dfs_pool_stats global_dfs_pool_stats = {};
-
-#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++)
-#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--)
-
-/**
- * struct pulse_elem - elements in pulse queue
- * @ts: time stamp in usecs
- */
-struct pulse_elem {
-       struct list_head head;
-       u64 ts;
-};
-
-/**
- * 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;
-}
-
-/**
- * DOC: 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++;
-       DFS_POOL_STAT_INC(pool_reference);
-       spin_unlock_bh(&pool_lock);
-}
-
-static void pool_deregister_ref(void)
-{
-       spin_lock_bh(&pool_lock);
-       singleton_pool_references--;
-       DFS_POOL_STAT_DEC(pool_reference);
-       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);
-                       DFS_POOL_STAT_DEC(pulse_allocated);
-                       kfree(p);
-               }
-               list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
-                       list_del(&ps->head);
-                       DFS_POOL_STAT_DEC(pseq_allocated);
-                       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);
-       DFS_POOL_STAT_DEC(pulse_used);
-       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);
-       DFS_POOL_STAT_DEC(pseq_used);
-       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);
-               DFS_POOL_STAT_INC(pseq_used);
-       }
-       spin_unlock_bh(&pool_lock);
-       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);
-               DFS_POOL_STAT_INC(pulse_used);
-       }
-       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);
-}
-
-/* remove pulses older than window */
-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);
-       }
-}
-
-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) {
-                       DFS_POOL_STAT_INC(pulse_alloc_error);
-                       return false;
-               }
-               DFS_POOL_STAT_INC(pulse_allocated);
-               DFS_POOL_STAT_INC(pulse_used);
-       }
-       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;
-}
-
-static bool pseq_handler_create_sequences(struct pri_detector *pde,
-                                         u64 ts, u32 min_count)
-{
-       struct pulse_elem *p;
-       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;
-
-               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 = 0;
-               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;
-               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;
-               new_ps = pool_get_pseq_elem();
-               if (new_ps == NULL) {
-                       new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC);
-                       if (new_ps == NULL) {
-                               DFS_POOL_STAT_INC(pseq_alloc_error);
-                               return false;
-                       }
-                       DFS_POOL_STAT_INC(pseq_allocated);
-                       DFS_POOL_STAT_INC(pseq_used);
-               }
-               memcpy(new_ps, &ps, sizeof(ps));
-               INIT_LIST_HEAD(&new_ps->head);
-               list_add(&new_ps->head, &pde->sequences);
-       }
-       return true;
-}
-
-/* check new ts and add to all matching existing sequences */
-static u32
-pseq_handler_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;
-}
-
-static struct pri_sequence *
-pseq_handler_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 >= pde->rs->ppb_thresh) &&
-                   (ps->count * pde->rs->num_pri >= ps->count_falses))
-                       return ps;
-       }
-       return NULL;
-}
-
-
-/* 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;
-}
-
-static void pri_detector_exit(struct pri_detector *de)
-{
-       pri_detector_reset(de, 0);
-       pool_deregister_ref();
-       kfree(de);
-}
-
-static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de,
-                                                  struct pulse_event *event)
-{
-       u32 max_updated_seq;
-       struct pri_sequence *ps;
-       u64 ts = event->ts;
-       const struct radar_detector_specs *rs = de->rs;
-
-       /* ignore pulses not within width range */
-       if ((rs->width_min > event->width) || (rs->width_max < event->width))
-               return NULL;
-
-       if ((ts - de->last_ts) < rs->max_pri_tolerance)
-               /* if delta to last pulse is too short, don't use this pulse */
-               return NULL;
-       de->last_ts = ts;
-
-       max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts);
-
-       if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) {
-               pri_detector_reset(de, ts);
-               return NULL;
-       }
-
-       ps = pseq_handler_check_detection(de);
-
-       if (ps == NULL)
-               pulse_queue_enqueue(de, ts);
-
-       return ps;
-}
-
-struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs)
-{
-       struct pri_detector *de;
-
-       de = kzalloc(sizeof(*de), GFP_ATOMIC);
-       if (de == NULL)
-               return NULL;
-       de->exit = pri_detector_exit;
-       de->add_pulse = pri_detector_add_pulse;
-       de->reset = pri_detector_reset;
-
-       INIT_LIST_HEAD(&de->sequences);
-       INIT_LIST_HEAD(&de->pulses);
-       de->window_size = rs->pri_max * rs->ppb * rs->num_pri;
-       de->max_count = rs->ppb * 2;
-       de->rs = rs;
-
-       pool_register_ref();
-       return de;
-}
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h
deleted file mode 100644 (file)
index 79f0fff..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (c) 2012 Neratec Solutions AG
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef DFS_PRI_DETECTOR_H
-#define DFS_PRI_DETECTOR_H
-
-#include <linux/list.h>
-
-extern struct ath_dfs_pool_stats global_dfs_pool_stats;
-
-/**
- * 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)
- */
-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;
-};
-
-/**
- * struct pri_detector - PRI detector element for a dedicated radar type
- * @exit(): destructor
- * @add_pulse(): add pulse event, returns pri_sequence if pattern was detected
- * @reset(): clear states and reset to given time stamp
- * @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
- */
-struct pri_detector {
-       void (*exit)     (struct pri_detector *de);
-       struct pri_sequence *
-            (*add_pulse)(struct pri_detector *de, struct pulse_event *e);
-       void (*reset)    (struct pri_detector *de, u64 ts);
-
-/* private: internal use only */
-       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 *pri_detector_init(const struct radar_detector_specs *rs);
-
-#endif /* DFS_PRI_DETECTOR_H */
diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c
new file mode 100644 (file)
index 0000000..a1a69c5
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include "dfs_pattern_detector.h"
+#include "dfs_pri_detector.h"
+#include "ath.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 *radar_types;
+};
+
+/* 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)
+/* percentage of pulse width tolerance */
+#define WIDTH_TOLERANCE 5
+#define WIDTH_LOWER(X) ((X*(100-WIDTH_TOLERANCE)+50)/100)
+#define WIDTH_UPPER(X) ((X*(100+WIDTH_TOLERANCE)+50)/100)
+
+#define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB)     \
+{                                                              \
+       ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),               \
+       (PRF2PRI(PMAX) - PRI_TOLERANCE),                        \
+       (PRF2PRI(PMIN) * PRF + PRI_TOLERANCE), PRF, PPB * PRF,  \
+       PPB_THRESH(PPB), PRI_TOLERANCE,                         \
+}
+
+/* radar types as defined by ETSI EN-301-893 v1.5.1 */
+static const struct radar_detector_specs etsi_radar_ref_types_v15[] = {
+       ETSI_PATTERN(0,  0,  1,  700,  700, 1, 18),
+       ETSI_PATTERN(1,  0,  5,  200, 1000, 1, 10),
+       ETSI_PATTERN(2,  0, 15,  200, 1600, 1, 15),
+       ETSI_PATTERN(3,  0, 15, 2300, 4000, 1, 25),
+       ETSI_PATTERN(4, 20, 30, 2000, 4000, 1, 20),
+       ETSI_PATTERN(5,  0,  2,  300,  400, 3, 10),
+       ETSI_PATTERN(6,  0,  2,  400, 1200, 3, 15),
+};
+
+static const struct radar_types etsi_radar_types_v15 = {
+       .region                 = NL80211_DFS_ETSI,
+       .num_radar_types        = ARRAY_SIZE(etsi_radar_ref_types_v15),
+       .radar_types            = etsi_radar_ref_types_v15,
+};
+
+/* for now, we support ETSI radar types, FCC and JP are TODO */
+static const struct radar_types *dfs_domains[] = {
+       &etsi_radar_types_v15,
+};
+
+/**
+ * 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;
+}
+
+/**
+ * struct channel_detector - detector elements for a DFS channel
+ * @head: list_head
+ * @freq: frequency for this channel detector in MHz
+ * @detectors: array of dynamically created detector elements for this freq
+ *
+ * Channel detectors are required to provide multi-channel DFS detection, e.g.
+ * to support off-channel scanning. A pattern detector has a list of channels
+ * radar pulses have been reported for in the past.
+ */
+struct channel_detector {
+       struct list_head head;
+       u16 freq;
+       struct pri_detector **detectors;
+};
+
+/* channel_detector_reset() - reset detector lines for a given channel */
+static void channel_detector_reset(struct dfs_pattern_detector *dpd,
+                                  struct channel_detector *cd)
+{
+       u32 i;
+       if (cd == NULL)
+               return;
+       for (i = 0; i < dpd->num_radar_types; i++)
+               cd->detectors[i]->reset(cd->detectors[i], dpd->last_pulse_ts);
+}
+
+/* channel_detector_exit() - destructor */
+static void channel_detector_exit(struct dfs_pattern_detector *dpd,
+                                 struct channel_detector *cd)
+{
+       u32 i;
+       if (cd == NULL)
+               return;
+       list_del(&cd->head);
+       for (i = 0; i < dpd->num_radar_types; i++) {
+               struct pri_detector *de = cd->detectors[i];
+               if (de != NULL)
+                       de->exit(de);
+       }
+       kfree(cd->detectors);
+       kfree(cd);
+}
+
+static struct channel_detector *
+channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq)
+{
+       u32 sz, i;
+       struct channel_detector *cd;
+
+       cd = kmalloc(sizeof(*cd), GFP_ATOMIC);
+       if (cd == NULL)
+               goto fail;
+
+       INIT_LIST_HEAD(&cd->head);
+       cd->freq = freq;
+       sz = sizeof(cd->detectors) * dpd->num_radar_types;
+       cd->detectors = kzalloc(sz, GFP_ATOMIC);
+       if (cd->detectors == NULL)
+               goto fail;
+
+       for (i = 0; i < dpd->num_radar_types; i++) {
+               const struct radar_detector_specs *rs = &dpd->radar_spec[i];
+               struct pri_detector *de = pri_detector_init(rs);
+               if (de == NULL)
+                       goto fail;
+               cd->detectors[i] = de;
+       }
+       list_add(&cd->head, &dpd->channel_detectors);
+       return cd;
+
+fail:
+       ath_dbg(dpd->common, DFS,
+               "failed to allocate channel_detector for freq=%d\n", freq);
+       channel_detector_exit(dpd, cd);
+       return NULL;
+}
+
+/**
+ * channel_detector_get() - get channel detector for given frequency
+ * @param dpd instance pointer
+ * @param freq frequency in MHz
+ * @return pointer to channel detector on success, NULL otherwise
+ *
+ * Return existing channel detector for the given frequency or return a
+ * newly create one.
+ */
+static struct channel_detector *
+channel_detector_get(struct dfs_pattern_detector *dpd, u16 freq)
+{
+       struct channel_detector *cd;
+       list_for_each_entry(cd, &dpd->channel_detectors, head) {
+               if (cd->freq == freq)
+                       return cd;
+       }
+       return channel_detector_create(dpd, freq);
+}
+
+/*
+ * DFS Pattern Detector
+ */
+
+/* dpd_reset(): reset all channel detectors */
+static void dpd_reset(struct dfs_pattern_detector *dpd)
+{
+       struct channel_detector *cd;
+       if (!list_empty(&dpd->channel_detectors))
+               list_for_each_entry(cd, &dpd->channel_detectors, head)
+                       channel_detector_reset(dpd, cd);
+
+}
+static void dpd_exit(struct dfs_pattern_detector *dpd)
+{
+       struct channel_detector *cd, *cd0;
+       if (!list_empty(&dpd->channel_detectors))
+               list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
+                       channel_detector_exit(dpd, cd);
+       kfree(dpd);
+}
+
+static bool
+dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event)
+{
+       u32 i;
+       struct channel_detector *cd;
+
+       /*
+        * 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;
+
+       cd = channel_detector_get(dpd, event->freq);
+       if (cd == NULL)
+               return false;
+
+       dpd->last_pulse_ts = event->ts;
+       /* reset detector on time stamp wraparound, caused by TSF reset */
+       if (event->ts < dpd->last_pulse_ts)
+               dpd_reset(dpd);
+
+       /* do type individual pattern matching */
+       for (i = 0; i < dpd->num_radar_types; i++) {
+               struct pri_detector *pd = cd->detectors[i];
+               struct pri_sequence *ps = pd->add_pulse(pd, event);
+               if (ps != NULL) {
+                       ath_dbg(dpd->common, DFS,
+                               "DFS: radar found on freq=%d: id=%d, pri=%d, "
+                               "count=%d, count_false=%d\n",
+                               event->freq, pd->rs->type_id,
+                               ps->pri, ps->count, ps->count_falses);
+                       channel_detector_reset(dpd, cd);
+                       return true;
+               }
+       }
+       return false;
+}
+
+static struct ath_dfs_pool_stats
+dpd_get_stats(struct dfs_pattern_detector *dpd)
+{
+       return global_dfs_pool_stats;
+}
+
+static bool dpd_set_domain(struct dfs_pattern_detector *dpd,
+                          enum nl80211_dfs_regions region)
+{
+       const struct radar_types *rt;
+       struct channel_detector *cd, *cd0;
+
+       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 channel detectors for previous DFS domain */
+       if (!list_empty(&dpd->channel_detectors))
+               list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
+                       channel_detector_exit(dpd, cd);
+       dpd->radar_spec = rt->radar_types;
+       dpd->num_radar_types = rt->num_radar_types;
+
+       dpd->region = region;
+       return true;
+}
+
+static struct dfs_pattern_detector default_dpd = {
+       .exit           = dpd_exit,
+       .set_dfs_domain = dpd_set_domain,
+       .add_pulse      = dpd_add_pulse,
+       .get_stats      = dpd_get_stats,
+       .region         = NL80211_DFS_UNSET,
+};
+
+struct dfs_pattern_detector *
+dfs_pattern_detector_init(struct ath_common *common,
+                         enum nl80211_dfs_regions region)
+{
+       struct dfs_pattern_detector *dpd;
+
+       if (!config_enabled(CONFIG_CFG80211_CERTIFICATION_ONUS))
+               return NULL;
+
+       dpd = kmalloc(sizeof(*dpd), GFP_KERNEL);
+       if (dpd == NULL)
+               return NULL;
+
+       *dpd = default_dpd;
+       INIT_LIST_HEAD(&dpd->channel_detectors);
+
+       dpd->common = common;
+       if (dpd->set_dfs_domain(dpd, region))
+               return dpd;
+
+       ath_dbg(common, DFS,"Could not set DFS domain to %d", region);
+       kfree(dpd);
+       return NULL;
+}
+EXPORT_SYMBOL(dfs_pattern_detector_init);
diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.h b/drivers/net/wireless/ath/dfs_pattern_detector.h
new file mode 100644 (file)
index 0000000..dde2652
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DFS_PATTERN_DETECTOR_H
+#define DFS_PATTERN_DETECTOR_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/nl80211.h>
+
+/**
+ * struct ath_dfs_pool_stats - DFS Statistics for global pools
+ */
+struct ath_dfs_pool_stats {
+       u32 pool_reference;
+       u32 pulse_allocated;
+       u32 pulse_alloc_error;
+       u32 pulse_used;
+       u32 pseq_allocated;
+       u32 pseq_alloc_error;
+       u32 pseq_used;
+};
+
+/**
+ * struct pulse_event - describing pulses reported by PHY
+ * @ts: pulse time stamp in us
+ * @freq: channel frequency in MHz
+ * @width: pulse duration in us
+ * @rssi: rssi of radar event
+ */
+struct pulse_event {
+       u64 ts;
+       u16 freq;
+       u8 width;
+       u8 rssi;
+};
+
+/**
+ * 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]
+ */
+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;
+};
+
+/**
+ * struct dfs_pattern_detector - DFS pattern detector
+ * @exit(): destructor
+ * @set_dfs_domain(): set DFS domain, resets detector lines upon domain changes
+ * @add_pulse(): add radar pulse to detector, returns true on detection
+ * @region: active DFS region, NL80211_DFS_UNSET until set
+ * @num_radar_types: number of different radar types
+ * @last_pulse_ts: time stamp of last valid pulse in usecs
+ * @radar_detector_specs: array of radar detection specs
+ * @channel_detectors: list connecting channel_detector elements
+ */
+struct dfs_pattern_detector {
+       void (*exit)(struct dfs_pattern_detector *dpd);
+       bool (*set_dfs_domain)(struct dfs_pattern_detector *dpd,
+                          enum nl80211_dfs_regions region);
+       bool (*add_pulse)(struct dfs_pattern_detector *dpd,
+                         struct pulse_event *pe);
+
+       struct ath_dfs_pool_stats (*get_stats)(struct dfs_pattern_detector *dpd);
+       enum nl80211_dfs_regions region;
+       u8 num_radar_types;
+       u64 last_pulse_ts;
+       /* needed for ath_dbg() */
+       struct ath_common *common;
+
+       const struct radar_detector_specs *radar_spec;
+       struct list_head channel_detectors;
+};
+
+/**
+ * dfs_pattern_detector_init() - constructor for pattern detector class
+ * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation
+ * @return instance pointer on success, NULL otherwise
+ */
+extern struct dfs_pattern_detector *
+dfs_pattern_detector_init(struct ath_common *common,
+                         enum nl80211_dfs_regions region);
+#endif /* DFS_PATTERN_DETECTOR_H */
diff --git a/drivers/net/wireless/ath/dfs_pri_detector.c b/drivers/net/wireless/ath/dfs_pri_detector.c
new file mode 100644 (file)
index 0000000..43b6081
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "ath.h"
+#include "dfs_pattern_detector.h"
+#include "dfs_pri_detector.h"
+
+struct ath_dfs_pool_stats global_dfs_pool_stats = {};
+
+#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++)
+#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--)
+
+/**
+ * struct pulse_elem - elements in pulse queue
+ * @ts: time stamp in usecs
+ */
+struct pulse_elem {
+       struct list_head head;
+       u64 ts;
+};
+
+/**
+ * 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;
+}
+
+/**
+ * DOC: 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++;
+       DFS_POOL_STAT_INC(pool_reference);
+       spin_unlock_bh(&pool_lock);
+}
+
+static void pool_deregister_ref(void)
+{
+       spin_lock_bh(&pool_lock);
+       singleton_pool_references--;
+       DFS_POOL_STAT_DEC(pool_reference);
+       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);
+                       DFS_POOL_STAT_DEC(pulse_allocated);
+                       kfree(p);
+               }
+               list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
+                       list_del(&ps->head);
+                       DFS_POOL_STAT_DEC(pseq_allocated);
+                       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);
+       DFS_POOL_STAT_DEC(pulse_used);
+       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);
+       DFS_POOL_STAT_DEC(pseq_used);
+       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);
+               DFS_POOL_STAT_INC(pseq_used);
+       }
+       spin_unlock_bh(&pool_lock);
+       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);
+               DFS_POOL_STAT_INC(pulse_used);
+       }
+       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);
+}
+
+/* remove pulses older than window */
+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);
+       }
+}
+
+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) {
+                       DFS_POOL_STAT_INC(pulse_alloc_error);
+                       return false;
+               }
+               DFS_POOL_STAT_INC(pulse_allocated);
+               DFS_POOL_STAT_INC(pulse_used);
+       }
+       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;
+}
+
+static bool pseq_handler_create_sequences(struct pri_detector *pde,
+                                         u64 ts, u32 min_count)
+{
+       struct pulse_elem *p;
+       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;
+
+               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 = 0;
+               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;
+               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;
+               new_ps = pool_get_pseq_elem();
+               if (new_ps == NULL) {
+                       new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC);
+                       if (new_ps == NULL) {
+                               DFS_POOL_STAT_INC(pseq_alloc_error);
+                               return false;
+                       }
+                       DFS_POOL_STAT_INC(pseq_allocated);
+                       DFS_POOL_STAT_INC(pseq_used);
+               }
+               memcpy(new_ps, &ps, sizeof(ps));
+               INIT_LIST_HEAD(&new_ps->head);
+               list_add(&new_ps->head, &pde->sequences);
+       }
+       return true;
+}
+
+/* check new ts and add to all matching existing sequences */
+static u32
+pseq_handler_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;
+}
+
+static struct pri_sequence *
+pseq_handler_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 >= pde->rs->ppb_thresh) &&
+                   (ps->count * pde->rs->num_pri >= ps->count_falses))
+                       return ps;
+       }
+       return NULL;
+}
+
+
+/* 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;
+}
+
+static void pri_detector_exit(struct pri_detector *de)
+{
+       pri_detector_reset(de, 0);
+       pool_deregister_ref();
+       kfree(de);
+}
+
+static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de,
+                                                  struct pulse_event *event)
+{
+       u32 max_updated_seq;
+       struct pri_sequence *ps;
+       u64 ts = event->ts;
+       const struct radar_detector_specs *rs = de->rs;
+
+       /* ignore pulses not within width range */
+       if ((rs->width_min > event->width) || (rs->width_max < event->width))
+               return NULL;
+
+       if ((ts - de->last_ts) < rs->max_pri_tolerance)
+               /* if delta to last pulse is too short, don't use this pulse */
+               return NULL;
+       de->last_ts = ts;
+
+       max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts);
+
+       if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) {
+               pri_detector_reset(de, ts);
+               return NULL;
+       }
+
+       ps = pseq_handler_check_detection(de);
+
+       if (ps == NULL)
+               pulse_queue_enqueue(de, ts);
+
+       return ps;
+}
+
+struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs)
+{
+       struct pri_detector *de;
+
+       de = kzalloc(sizeof(*de), GFP_ATOMIC);
+       if (de == NULL)
+               return NULL;
+       de->exit = pri_detector_exit;
+       de->add_pulse = pri_detector_add_pulse;
+       de->reset = pri_detector_reset;
+
+       INIT_LIST_HEAD(&de->sequences);
+       INIT_LIST_HEAD(&de->pulses);
+       de->window_size = rs->pri_max * rs->ppb * rs->num_pri;
+       de->max_count = rs->ppb * 2;
+       de->rs = rs;
+
+       pool_register_ref();
+       return de;
+}
diff --git a/drivers/net/wireless/ath/dfs_pri_detector.h b/drivers/net/wireless/ath/dfs_pri_detector.h
new file mode 100644 (file)
index 0000000..79f0fff
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DFS_PRI_DETECTOR_H
+#define DFS_PRI_DETECTOR_H
+
+#include <linux/list.h>
+
+extern struct ath_dfs_pool_stats global_dfs_pool_stats;
+
+/**
+ * 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)
+ */
+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;
+};
+
+/**
+ * struct pri_detector - PRI detector element for a dedicated radar type
+ * @exit(): destructor
+ * @add_pulse(): add pulse event, returns pri_sequence if pattern was detected
+ * @reset(): clear states and reset to given time stamp
+ * @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
+ */
+struct pri_detector {
+       void (*exit)     (struct pri_detector *de);
+       struct pri_sequence *
+            (*add_pulse)(struct pri_detector *de, struct pulse_event *e);
+       void (*reset)    (struct pri_detector *de, u64 ts);
+
+/* private: internal use only */
+       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 *pri_detector_init(const struct radar_detector_specs *rs);
+
+#endif /* DFS_PRI_DETECTOR_H */