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__
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
#ifndef ATH9K_DFS_H
#define ATH9K_DFS_H
-#include "dfs_pattern_detector.h"
+#include "../dfs_pattern_detector.h"
#if defined(CONFIG_ATH9K_DFS_CERTIFIED)
/**
#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 };
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */