xref: /freebsd/sys/contrib/dev/athk/dfs_pri_detector.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
1 /*
2  * Copyright (c) 2012 Neratec Solutions AG
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * 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 
24 struct 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 queue
34  */
35 struct pulse_elem {
36 	struct list_head head;
37 	u64 ts;
38 };
39 
40 /*
41  * pde_get_multiple() - get number of multiples considering a given tolerance
42  * Return value: factor if abs(val - factor*fraction) <= tolerance, 0 otherwise
43  */
44 static 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 		else
67 			factor = 0;
68 	}
69 	return factor;
70 }
71 
72 /*
73  * DOC: Singleton Pulse and Sequence Pools
74  *
75  * Instances of pri_sequence and pulse_elem are kept in singleton pools to
76  * reduce the number of dynamic allocations. They are shared between all
77  * 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  */
81 static u32 singleton_pool_references;
82 static LIST_HEAD(pulse_pool);
83 static LIST_HEAD(pseq_pool);
84 static DEFINE_SPINLOCK(pool_lock);
85 
86 static 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 
94 static 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 
118 static 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 
126 static 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 
134 static 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 
147 static 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 
160 static 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 
168 static 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 */
181 static 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 
201 static 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 
224 static 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 falses
270 				 * and reset counter
271 				 */
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 */
303 static u32
304 pseq_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 
335 static struct pri_sequence *
336 pseq_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 we
346 		 * 1) have enough pulses
347 		 * 2) have more matching than false pulses
348 		 */
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 */
358 static 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 
374 static void pri_detector_exit(struct pri_detector *de)
375 {
376 	pri_detector_reset(de, 0);
377 	pool_deregister_ref();
378 	kfree(de);
379 }
380 
381 static 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 
417 struct 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