brintos

brintos / linux-shallow public Read only

0
0
Text · 10.7 KiB · d07c454 Raw
437 lines · c
1/*2 * Copyright (c) 2012 Neratec Solutions AG3 *4 * Permission to use, copy, modify, and/or distribute this software for any5 * purpose with or without fee is hereby granted, provided that the above6 * copyright notice and this permission notice appear in all copies.7 *8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.15 */16 17#include <linux/slab.h>18#include <linux/spinlock.h>19 20#include "ath.h"21#include "dfs_pattern_detector.h"22#include "dfs_pri_detector.h"23 24struct ath_dfs_pool_stats global_dfs_pool_stats = {};25 26#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++)27#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--)28#define GET_PRI_TO_USE(MIN, MAX, RUNTIME) \29	(MIN + PRI_TOLERANCE == MAX - PRI_TOLERANCE ? \30	MIN + PRI_TOLERANCE : RUNTIME)31 32/*33 * struct pulse_elem - elements in pulse queue34 */35struct pulse_elem {36	struct list_head head;37	u64 ts;38};39 40/*41 * pde_get_multiple() - get number of multiples considering a given tolerance42 * Return value: factor if abs(val - factor*fraction) <= tolerance, 0 otherwise43 */44static u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance)45{46	u32 remainder;47	u32 factor;48	u32 delta;49 50	if (fraction == 0)51		return 0;52 53	delta = (val < fraction) ? (fraction - val) : (val - fraction);54 55	if (delta <= tolerance)56		/* val and fraction are within tolerance */57		return 1;58 59	factor = val / fraction;60	remainder = val % fraction;61	if (remainder > tolerance) {62		/* no exact match */63		if ((fraction - remainder) <= tolerance)64			/* remainder is within tolerance */65			factor++;66		else67			factor = 0;68	}69	return factor;70}71 72/*73 * DOC: Singleton Pulse and Sequence Pools74 *75 * Instances of pri_sequence and pulse_elem are kept in singleton pools to76 * reduce the number of dynamic allocations. They are shared between all77 * instances and grow up to the peak number of simultaneously used objects.78 *79 * Memory is freed after all references to the pools are released.80 */81static u32 singleton_pool_references;82static LIST_HEAD(pulse_pool);83static LIST_HEAD(pseq_pool);84static DEFINE_SPINLOCK(pool_lock);85 86static void pool_register_ref(void)87{88	spin_lock_bh(&pool_lock);89	singleton_pool_references++;90	DFS_POOL_STAT_INC(pool_reference);91	spin_unlock_bh(&pool_lock);92}93 94static void pool_deregister_ref(void)95{96	spin_lock_bh(&pool_lock);97	singleton_pool_references--;98	DFS_POOL_STAT_DEC(pool_reference);99	if (singleton_pool_references == 0) {100		/* free singleton pools with no references left */101		struct pri_sequence *ps, *ps0;102		struct pulse_elem *p, *p0;103 104		list_for_each_entry_safe(p, p0, &pulse_pool, head) {105			list_del(&p->head);106			DFS_POOL_STAT_DEC(pulse_allocated);107			kfree(p);108		}109		list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {110			list_del(&ps->head);111			DFS_POOL_STAT_DEC(pseq_allocated);112			kfree(ps);113		}114	}115	spin_unlock_bh(&pool_lock);116}117 118static void pool_put_pulse_elem(struct pulse_elem *pe)119{120	spin_lock_bh(&pool_lock);121	list_add(&pe->head, &pulse_pool);122	DFS_POOL_STAT_DEC(pulse_used);123	spin_unlock_bh(&pool_lock);124}125 126static void pool_put_pseq_elem(struct pri_sequence *pse)127{128	spin_lock_bh(&pool_lock);129	list_add(&pse->head, &pseq_pool);130	DFS_POOL_STAT_DEC(pseq_used);131	spin_unlock_bh(&pool_lock);132}133 134static struct pri_sequence *pool_get_pseq_elem(void)135{136	struct pri_sequence *pse = NULL;137	spin_lock_bh(&pool_lock);138	if (!list_empty(&pseq_pool)) {139		pse = list_first_entry(&pseq_pool, struct pri_sequence, head);140		list_del(&pse->head);141		DFS_POOL_STAT_INC(pseq_used);142	}143	spin_unlock_bh(&pool_lock);144	return pse;145}146 147static struct pulse_elem *pool_get_pulse_elem(void)148{149	struct pulse_elem *pe = NULL;150	spin_lock_bh(&pool_lock);151	if (!list_empty(&pulse_pool)) {152		pe = list_first_entry(&pulse_pool, struct pulse_elem, head);153		list_del(&pe->head);154		DFS_POOL_STAT_INC(pulse_used);155	}156	spin_unlock_bh(&pool_lock);157	return pe;158}159 160static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde)161{162	struct list_head *l = &pde->pulses;163	if (list_empty(l))164		return NULL;165	return list_entry(l->prev, struct pulse_elem, head);166}167 168static bool pulse_queue_dequeue(struct pri_detector *pde)169{170	struct pulse_elem *p = pulse_queue_get_tail(pde);171	if (p != NULL) {172		list_del_init(&p->head);173		pde->count--;174		/* give it back to pool */175		pool_put_pulse_elem(p);176	}177	return (pde->count > 0);178}179 180/* remove pulses older than window */181static void pulse_queue_check_window(struct pri_detector *pde)182{183	u64 min_valid_ts;184	struct pulse_elem *p;185 186	/* there is no delta time with less than 2 pulses */187	if (pde->count < 2)188		return;189 190	if (pde->last_ts <= pde->window_size)191		return;192 193	min_valid_ts = pde->last_ts - pde->window_size;194	while ((p = pulse_queue_get_tail(pde)) != NULL) {195		if (p->ts >= min_valid_ts)196			return;197		pulse_queue_dequeue(pde);198	}199}200 201static bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts)202{203	struct pulse_elem *p = pool_get_pulse_elem();204	if (p == NULL) {205		p = kmalloc(sizeof(*p), GFP_ATOMIC);206		if (p == NULL) {207			DFS_POOL_STAT_INC(pulse_alloc_error);208			return false;209		}210		DFS_POOL_STAT_INC(pulse_allocated);211		DFS_POOL_STAT_INC(pulse_used);212	}213	INIT_LIST_HEAD(&p->head);214	p->ts = ts;215	list_add(&p->head, &pde->pulses);216	pde->count++;217	pde->last_ts = ts;218	pulse_queue_check_window(pde);219	if (pde->count >= pde->max_count)220		pulse_queue_dequeue(pde);221	return true;222}223 224static bool pseq_handler_create_sequences(struct pri_detector *pde,225					  u64 ts, u32 min_count)226{227	struct pulse_elem *p;228	list_for_each_entry(p, &pde->pulses, head) {229		struct pri_sequence ps, *new_ps;230		struct pulse_elem *p2;231		u32 tmp_false_count;232		u64 min_valid_ts;233		u32 delta_ts = ts - p->ts;234 235		if (delta_ts < pde->rs->pri_min)236			/* ignore too small pri */237			continue;238 239		if (delta_ts > pde->rs->pri_max)240			/* stop on too large pri (sorted list) */241			break;242 243		/* build a new sequence with new potential pri */244		ps.count = 2;245		ps.count_falses = 0;246		ps.first_ts = p->ts;247		ps.last_ts = ts;248		ps.pri = GET_PRI_TO_USE(pde->rs->pri_min,249			pde->rs->pri_max, ts - p->ts);250		ps.dur = ps.pri * (pde->rs->ppb - 1)251				+ 2 * pde->rs->max_pri_tolerance;252 253		p2 = p;254		tmp_false_count = 0;255		min_valid_ts = ts - ps.dur;256		/* check which past pulses are candidates for new sequence */257		list_for_each_entry_continue(p2, &pde->pulses, head) {258			u32 factor;259			if (p2->ts < min_valid_ts)260				/* stop on crossing window border */261				break;262			/* check if pulse match (multi)PRI */263			factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri,264						  pde->rs->max_pri_tolerance);265			if (factor > 0) {266				ps.count++;267				ps.first_ts = p2->ts;268				/*269				 * on match, add the intermediate falses270				 * and reset counter271				 */272				ps.count_falses += tmp_false_count;273				tmp_false_count = 0;274			} else {275				/* this is a potential false one */276				tmp_false_count++;277			}278		}279		if (ps.count <= min_count)280			/* did not reach minimum count, drop sequence */281			continue;282 283		/* this is a valid one, add it */284		ps.deadline_ts = ps.first_ts + ps.dur;285		new_ps = pool_get_pseq_elem();286		if (new_ps == NULL) {287			new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC);288			if (new_ps == NULL) {289				DFS_POOL_STAT_INC(pseq_alloc_error);290				return false;291			}292			DFS_POOL_STAT_INC(pseq_allocated);293			DFS_POOL_STAT_INC(pseq_used);294		}295		memcpy(new_ps, &ps, sizeof(ps));296		INIT_LIST_HEAD(&new_ps->head);297		list_add(&new_ps->head, &pde->sequences);298	}299	return true;300}301 302/* check new ts and add to all matching existing sequences */303static u32304pseq_handler_add_to_existing_seqs(struct pri_detector *pde, u64 ts)305{306	u32 max_count = 0;307	struct pri_sequence *ps, *ps2;308	list_for_each_entry_safe(ps, ps2, &pde->sequences, head) {309		u32 delta_ts;310		u32 factor;311 312		/* first ensure that sequence is within window */313		if (ts > ps->deadline_ts) {314			list_del_init(&ps->head);315			pool_put_pseq_elem(ps);316			continue;317		}318 319		delta_ts = ts - ps->last_ts;320		factor = pde_get_multiple(delta_ts, ps->pri,321					  pde->rs->max_pri_tolerance);322		if (factor > 0) {323			ps->last_ts = ts;324			ps->count++;325 326			if (max_count < ps->count)327				max_count = ps->count;328		} else {329			ps->count_falses++;330		}331	}332	return max_count;333}334 335static struct pri_sequence *336pseq_handler_check_detection(struct pri_detector *pde)337{338	struct pri_sequence *ps;339 340	if (list_empty(&pde->sequences))341		return NULL;342 343	list_for_each_entry(ps, &pde->sequences, head) {344		/*345		 * we assume to have enough matching confidence if we346		 * 1) have enough pulses347		 * 2) have more matching than false pulses348		 */349		if ((ps->count >= pde->rs->ppb_thresh) &&350		    (ps->count * pde->rs->num_pri >= ps->count_falses))351			return ps;352	}353	return NULL;354}355 356 357/* free pulse queue and sequences list and give objects back to pools */358static void pri_detector_reset(struct pri_detector *pde, u64 ts)359{360	struct pri_sequence *ps, *ps0;361	struct pulse_elem *p, *p0;362	list_for_each_entry_safe(ps, ps0, &pde->sequences, head) {363		list_del_init(&ps->head);364		pool_put_pseq_elem(ps);365	}366	list_for_each_entry_safe(p, p0, &pde->pulses, head) {367		list_del_init(&p->head);368		pool_put_pulse_elem(p);369	}370	pde->count = 0;371	pde->last_ts = ts;372}373 374static void pri_detector_exit(struct pri_detector *de)375{376	pri_detector_reset(de, 0);377	pool_deregister_ref();378	kfree(de);379}380 381static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de,382						   struct pulse_event *event)383{384	u32 max_updated_seq;385	struct pri_sequence *ps;386	u64 ts = event->ts;387	const struct radar_detector_specs *rs = de->rs;388 389	/* ignore pulses not within width range */390	if ((rs->width_min > event->width) || (rs->width_max < event->width))391		return NULL;392 393	if ((ts - de->last_ts) < rs->max_pri_tolerance)394		/* if delta to last pulse is too short, don't use this pulse */395		return NULL;396	/* radar detector spec needs chirp, but not detected */397	if (rs->chirp && rs->chirp != event->chirp)398		return NULL;399 400	de->last_ts = ts;401 402	max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts);403 404	if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) {405		pri_detector_reset(de, ts);406		return NULL;407	}408 409	ps = pseq_handler_check_detection(de);410 411	if (ps == NULL)412		pulse_queue_enqueue(de, ts);413 414	return ps;415}416 417struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs)418{419	struct pri_detector *de;420 421	de = kzalloc(sizeof(*de), GFP_ATOMIC);422	if (de == NULL)423		return NULL;424	de->exit = pri_detector_exit;425	de->add_pulse = pri_detector_add_pulse;426	de->reset = pri_detector_reset;427 428	INIT_LIST_HEAD(&de->sequences);429	INIT_LIST_HEAD(&de->pulses);430	de->window_size = rs->pri_max * rs->ppb * rs->num_pri;431	de->max_count = rs->ppb * 2;432	de->rs = rs;433 434	pool_register_ref();435	return de;436}437