Hiya, I know this is late, but I've been a bit busy elsewhere. :) Does pulse_queue_enqueue() always allocate a new entry? What situation are pulse entries reused? You likely don't want to be allocating a new pulse entry via malloc() for each pulse; it's going to be quite silly when you potentially handle > 1,000 false positives a second. Adrian On 23 March 2012 08:34, Zefir Kurtisi <zefir.kurtisi@xxxxxxxxxxx> wrote: > This adds a DFS pattern detector to ath9k. It is fed with pulse events > by the radar pulse detector and reports in place whether a pattern > was detected. On detection, the result is reported as radar event to > the DFS management component in the upper layer. > > Currently the ETSI DFS domain is supported with detector lines for > the patterns defined by EN-301-893 v1.5.1. Support for FCC and JP > will be added gradually. > > To include the pattern detector, ath9k must be built with support > for DFS certified config flag set (CONFIG_ATH9K_DFS_CERTIFIED). > > Signed-off-by: Zefir Kurtisi <zefir.kurtisi@xxxxxxxxxxx> > --- > drivers/net/wireless/ath/ath9k/Makefile | 5 +- > .../net/wireless/ath/ath9k/dfs_pattern_detector.c | 300 +++++++++++++++ > .../net/wireless/ath/ath9k/dfs_pattern_detector.h | 104 ++++++ > drivers/net/wireless/ath/ath9k/dfs_pri_detector.c | 390 ++++++++++++++++++++ > drivers/net/wireless/ath/ath9k/dfs_pri_detector.h | 52 +++ > 5 files changed, 850 insertions(+), 1 deletions(-) > create mode 100644 drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c > create mode 100644 drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h > create mode 100644 drivers/net/wireless/ath/ath9k/dfs_pri_detector.c > create mode 100644 drivers/net/wireless/ath/ath9k/dfs_pri_detector.h > > diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile > index 27d95fe..3f0b847 100644 > --- a/drivers/net/wireless/ath/ath9k/Makefile > +++ b/drivers/net/wireless/ath/ath9k/Makefile > @@ -11,7 +11,10 @@ ath9k-$(CONFIG_ATH9K_PCI) += pci.o > 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 > +ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += \ > + dfs.o \ > + dfs_pattern_detector.o \ > + dfs_pri_detector.o > > obj-$(CONFIG_ATH9K) += ath9k.o > > diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c > new file mode 100644 > index 0000000..ea2a6cf > --- /dev/null > +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c > @@ -0,0 +1,300 @@ > +/* > + * 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" > + > +/* > + * 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) > + > +#define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB) \ > +{ \ > + ID, WMIN, 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_KERNEL); > + 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_KERNEL); > + 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: > + pr_err("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; > + bool ts_wraparound; > + struct channel_detector *cd; > + > + if (dpd->region == NL80211_DFS_UNSET) { > + /* > + * pulses received for a non-supported or un-initialized > + * domain are treated as detected radars > + */ > + return true; > + } > + > + cd = channel_detector_get(dpd, event->freq); > + if (cd == NULL) > + return false; > + > + ts_wraparound = (event->ts < dpd->last_pulse_ts); > + dpd->last_pulse_ts = event->ts; > + if (ts_wraparound) { > + /* > + * reset detector on time stamp wraparound > + * with monotonic time stamps, this should never happen > + */ > + pr_warn("DFS: time stamp wraparound detected, resetting\n"); > + dpd_reset(dpd); > + } > + /* do type individual pattern matching */ > + for (i = 0; i < dpd->num_radar_types; i++) { > + if (cd->detectors[i]->add_pulse(cd->detectors[i], event) != 0) { > + channel_detector_reset(dpd, cd); > + return true; > + } > + } > + return false; > +} > + > +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_domain = dpd_set_domain, > + .add_pulse = dpd_add_pulse, > + .region = NL80211_DFS_UNSET, > +}; > + > +struct dfs_pattern_detector * > +dfs_pattern_detector_init(enum nl80211_dfs_regions region) > +{ > + struct dfs_pattern_detector *dpd; > + dpd = kmalloc(sizeof(*dpd), GFP_KERNEL); > + if (dpd == NULL) { > + pr_err("allocation of dfs_pattern_detector failed\n"); > + return NULL; > + } > + *dpd = default_dpd; > + INIT_LIST_HEAD(&dpd->channel_detectors); > + > + if (dpd->set_domain(dpd, region)) > + return dpd; > + > + pr_err("Could not set DFS domain to %d. ", region); > + 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 > new file mode 100644 > index 0000000..fd0328a > --- /dev/null > +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h > @@ -0,0 +1,104 @@ > +/* > + * 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 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_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_domain)(struct dfs_pattern_detector *dpd, > + enum nl80211_dfs_regions region); > + bool (*add_pulse)(struct dfs_pattern_detector *dpd, > + struct pulse_event *pe); > + > + enum nl80211_dfs_regions region; > + u8 num_radar_types; > + u64 last_pulse_ts; > + > + 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 > + */ > +#if defined(CONFIG_ATH9K_DFS_CERTIFIED) > +extern struct dfs_pattern_detector * > +dfs_pattern_detector_init(enum nl80211_dfs_regions region); > +#else > +static inline struct dfs_pattern_detector * > +dfs_pattern_detector_init(enum nl80211_dfs_regions region) > +{ > + return NULL; > +} > +#endif /* CONFIG_ATH9K_DFS_CERTIFIED */ > + > +#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 > new file mode 100644 > index 0000000..edc47e8 > --- /dev/null > +++ b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c > @@ -0,0 +1,390 @@ > +/* > + * 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 "dfs_pattern_detector.h" > +#include "dfs_pri_detector.h" > + > +/** > + * 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 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 unsigned int 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 int singleton_pool_references; > +static LIST_HEAD(pulse_pool); > +static LIST_HEAD(pseq_pool); > + > +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 */ > + list_add(&p->head, &pulse_pool); > + } > + 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; > + if (!list_empty(&pulse_pool)) { > + p = list_first_entry(&pulse_pool, struct pulse_elem, head); > + list_del(&p->head); > + } else { > + p = kmalloc(sizeof(*p), GFP_KERNEL); > + if (p == NULL) { > + pr_err("failed to allocate pulse_elem\n"); > + return false; > + } > + } > + INIT_LIST_HEAD(&p->head); > + p->ts = ts; > + list_add(&p->head, &pde->pulses); > + pde->count++; > + pde->last_ts = ts; > + pulse_queue_check_window(pde); > + if (pde->count >= pde->max_count) > + pulse_queue_dequeue(pde); > + return true; > +} > + > +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; > + int 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 potentially false one */ > + tmp_false_count++; > + } > + } > + if (ps.count < min_count) > + /* did not reach minimum count, drop sequence */ > + continue; > + > + /* this is a valid one, add it */ > + ps.deadline_ts = ps.first_ts + ps.dur; > + > + if (!list_empty(&pseq_pool)) { > + new_ps = list_first_entry(&pseq_pool, > + struct pri_sequence, head); > + list_del(&new_ps->head); > + } else { > + new_ps = kmalloc(sizeof(*new_ps), GFP_KERNEL); > + if (new_ps == NULL) > + return false; > + } > + memcpy(new_ps, &ps, sizeof(ps)); > + INIT_LIST_HEAD(&new_ps->head); > + list_add(&new_ps->head, &pde->sequences); > + } > + return true; > +} > + > +/* 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) { > + int delta_ts; > + int factor; > + > + /* first ensure that sequence is within window */ > + if (ts > ps->deadline_ts) { > + list_del_init(&ps->head); > + list_add(&ps->head, &pseq_pool); > + 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); > + list_add(&ps->head, &pseq_pool); > + } > + list_for_each_entry_safe(p, p0, &pde->pulses, head) { > + list_del_init(&p->head); > + list_add(&p->head, &pulse_pool); > + } > + pde->count = 0; > + pde->last_ts = ts; > +} > + > +static void pri_detector_exit(struct pri_detector *de) > +{ > + pri_detector_reset(de, 0); > + > + singleton_pool_references--; > + if (singleton_pool_references == 0) { > + /* free singleton pools with no references left */ > + struct pri_sequence *ps, *ps0; > + struct pulse_elem *p, *p0; > + > + list_for_each_entry_safe(p, p0, &pulse_pool, head) { > + list_del(&p->head); > + kfree(p); > + } > + list_for_each_entry_safe(ps, ps0, &pseq_pool, head) { > + list_del(&ps->head); > + kfree(ps); > + } > + } > + kfree(de); > +} > + > +static bool 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 false; > + > + if ((ts - de->last_ts) < rs->max_pri_tolerance) > + /* if delta to last pulse is too short, don't use this pulse */ > + return false; > + 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)) { > + pr_err("failed to create pulse sequences\n"); > + pri_detector_reset(de, ts); > + return false; > + } > + > + ps = pseq_handler_check_detection(de); > + > + if (ps != NULL) { > + pr_info("DFS: radar found: pri=%d, count=%d, count_false=%d\n", > + ps->pri, ps->count, ps->count_falses); > + pri_detector_reset(de, ts); > + return true; > + } > + pulse_queue_enqueue(de, ts); > + return false; > +} > + > +struct pri_detector * > +pri_detector_init(const struct radar_detector_specs *rs) > +{ > + struct pri_detector *de; > + de = kzalloc(sizeof(*de), GFP_KERNEL); > + 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; > + > + singleton_pool_references++; > + return de; > +} > diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h > new file mode 100644 > index 0000000..81cde9f > --- /dev/null > +++ b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h > @@ -0,0 +1,52 @@ > +/* > + * 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> > + > +/** > + * struct pri_detector - PRI detector element for a dedicated radar type > + * @exit(): destructor > + * @add_pulse(): add pulse event, returns true 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); > + bool (*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 */ > -- > 1.7.4.1 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-wireless" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html